Don't import/export MED and LOCAL_PREF on external links.
Added real comparison of BGP routes (inspired by the Cisco one). Default local preference and default MED are now settable. Defined filter keywords for all BGP attributes we know.
This commit is contained in:
parent
3bbc4ad6ad
commit
56a2bed46b
4 changed files with 88 additions and 51 deletions
|
@ -33,5 +33,4 @@ struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
|
||||||
int int_set_contains(struct adata *list, u32 val);
|
int int_set_contains(struct adata *list, u32 val);
|
||||||
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
|
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct attr_desc {
|
||||||
int expected_length;
|
int expected_length;
|
||||||
int expected_flags;
|
int expected_flags;
|
||||||
int type;
|
int type;
|
||||||
|
int allow_in_ebgp;
|
||||||
int (*validate)(struct bgp_proto *p, byte *attr, int len);
|
int (*validate)(struct bgp_proto *p, byte *attr, int len);
|
||||||
void (*format)(eattr *ea, byte *buf);
|
void (*format)(eattr *ea, byte *buf);
|
||||||
};
|
};
|
||||||
|
@ -78,32 +79,24 @@ bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
bgp_check_local_pref(struct bgp_proto *p, byte *a, int len)
|
|
||||||
{
|
|
||||||
if (!p->is_internal) /* Ignore local preference from EBGP connections */
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct attr_desc bgp_attr_table[] = {
|
static struct attr_desc bgp_attr_table[] = {
|
||||||
{ NULL, -1, 0, 0, /* Undefined */
|
{ NULL, -1, 0, 0, 0, /* Undefined */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, /* BA_ORIGIN */
|
{ "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */
|
||||||
bgp_check_origin, bgp_format_origin },
|
bgp_check_origin, bgp_format_origin },
|
||||||
{ "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, /* BA_AS_PATH */
|
{ "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */
|
||||||
bgp_check_path, NULL },
|
bgp_check_path, NULL },
|
||||||
{ "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, /* BA_NEXT_HOP */
|
{ "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */
|
||||||
bgp_check_next_hop, NULL },
|
bgp_check_next_hop, NULL },
|
||||||
{ "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_MULTI_EXIT_DISC */
|
{ "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_MULTI_EXIT_DISC */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
|
{ "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */
|
||||||
bgp_check_local_pref, NULL },
|
|
||||||
{ "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
|
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_AGGREGATOR */
|
{ "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, /* BA_COMMUNITY */
|
{ "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */
|
||||||
|
NULL, NULL },
|
||||||
|
{ "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
#if 0
|
#if 0
|
||||||
{ 0, 0 }, /* BA_ORIGINATOR_ID */
|
{ 0, 0 }, /* BA_ORIGINATOR_ID */
|
||||||
|
@ -308,10 +301,9 @@ static struct bgp_bucket *
|
||||||
bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp, int originate)
|
bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp, int originate)
|
||||||
{
|
{
|
||||||
ea_list *t, *new;
|
ea_list *t, *new;
|
||||||
unsigned i, cnt;
|
unsigned i, cnt, hash, code;
|
||||||
eattr *a, *d;
|
eattr *a, *d;
|
||||||
u32 seen = 0;
|
u32 seen = 0;
|
||||||
unsigned hash;
|
|
||||||
struct bgp_bucket *b;
|
struct bgp_bucket *b;
|
||||||
|
|
||||||
/* Merge the attribute lists */
|
/* Merge the attribute lists */
|
||||||
|
@ -339,8 +331,14 @@ bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp, int originate)
|
||||||
#endif
|
#endif
|
||||||
if (EA_PROTO(a->id) != EAP_BGP)
|
if (EA_PROTO(a->id) != EAP_BGP)
|
||||||
continue;
|
continue;
|
||||||
if (EA_ID(a->id) < 32)
|
code = EA_ID(a->id);
|
||||||
seen |= 1 << EA_ID(a->id);
|
if (code < ARRAY_SIZE(bgp_attr_table))
|
||||||
|
{
|
||||||
|
if (!bgp_attr_table[code].allow_in_ebgp && !p->is_internal)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code < 32)
|
||||||
|
seen |= 1 << code;
|
||||||
*d = *a;
|
*d = *a;
|
||||||
if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL))
|
if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL))
|
||||||
d->flags |= BAF_PARTIAL;
|
d->flags |= BAF_PARTIAL;
|
||||||
|
@ -562,20 +560,53 @@ bgp_rte_better(rte *new, rte *old)
|
||||||
{
|
{
|
||||||
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto;
|
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto;
|
||||||
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto;
|
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto;
|
||||||
eattr *new_lpref = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
|
eattr *x, *y;
|
||||||
eattr *old_lpref = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
|
u32 n, o;
|
||||||
|
|
||||||
/* Start with local preferences */
|
/* Start with local preferences */
|
||||||
if (new_lpref && old_lpref) /* Somebody might have undefined them */
|
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
|
||||||
|
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
|
||||||
|
n = x ? x->u.data : new_bgp->cf->default_local_pref;
|
||||||
|
o = y ? y->u.data : old_bgp->cf->default_local_pref;
|
||||||
|
if (n > o)
|
||||||
|
return 1;
|
||||||
|
if (n < o)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Use AS path lengths */
|
||||||
|
if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
|
||||||
{
|
{
|
||||||
if (new_lpref->u.data > old_lpref->u.data)
|
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
||||||
|
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
||||||
|
n = x ? as_path_getlen(x->u.ptr) : 100000;
|
||||||
|
o = y ? as_path_getlen(y->u.ptr) : 100000;
|
||||||
|
if (n < o)
|
||||||
return 1;
|
return 1;
|
||||||
if (new_lpref->u.data < old_lpref->u.data)
|
if (n > o)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Use origins */
|
||||||
|
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
|
||||||
|
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
|
||||||
|
n = x ? x->u.data : 2;
|
||||||
|
o = y ? y->u.data : 2;
|
||||||
|
if (n < o)
|
||||||
|
return 1;
|
||||||
|
if (n > o)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Compare MED's */
|
||||||
|
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
|
||||||
|
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
|
||||||
|
n = x ? x->u.data : new_bgp->cf->default_med;
|
||||||
|
o = y ? y->u.data : old_bgp->cf->default_med;
|
||||||
|
if (n < o)
|
||||||
|
return 1;
|
||||||
|
if (n > o)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* A tie breaking procedure according to RFC 1771, section 9.1.2.1 */
|
/* A tie breaking procedure according to RFC 1771, section 9.1.2.1 */
|
||||||
/* FIXME: Look at MULTI_EXIT_DISC, take the lowest */
|
|
||||||
/* We don't have interior distances */
|
/* We don't have interior distances */
|
||||||
/* We prefer external peers */
|
/* We prefer external peers */
|
||||||
if (new_bgp->is_internal > old_bgp->is_internal)
|
if (new_bgp->is_internal > old_bgp->is_internal)
|
||||||
|
@ -586,12 +617,6 @@ bgp_rte_better(rte *new, rte *old)
|
||||||
return (new_bgp->remote_id < old_bgp->remote_id);
|
return (new_bgp->remote_id < old_bgp->remote_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
bgp_local_pref(struct bgp_proto *p, rta *a)
|
|
||||||
{
|
|
||||||
return 0; /* FIXME (should be compatible with Cisco defaults?) */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bgp_path_loopy(struct bgp_proto *p, eattr *a)
|
bgp_path_loopy(struct bgp_proto *p, eattr *a)
|
||||||
{
|
{
|
||||||
|
@ -680,6 +705,8 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
|
||||||
{ errcode = 5; goto err; }
|
{ errcode = 5; goto err; }
|
||||||
if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
|
if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
|
||||||
{ errcode = 4; goto err; }
|
{ errcode = 4; goto err; }
|
||||||
|
if (!desc->allow_in_ebgp && !bgp->is_internal)
|
||||||
|
continue;
|
||||||
if (desc->validate)
|
if (desc->validate)
|
||||||
{
|
{
|
||||||
errcode = desc->validate(bgp, z, l);
|
errcode = desc->validate(bgp, z, l);
|
||||||
|
@ -747,20 +774,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assign local preference if none defined */
|
|
||||||
if (!(seen[BA_LOCAL_PREF/8] & (1 << (BA_LOCAL_PREF%8))))
|
|
||||||
{
|
|
||||||
ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
|
|
||||||
ea->next = a->eattrs;
|
|
||||||
a->eattrs = ea;
|
|
||||||
ea->flags = 0;
|
|
||||||
ea->count = 1;
|
|
||||||
ea->attrs[0].id = EA_CODE(EAP_BGP, BA_LOCAL_PREF);
|
|
||||||
ea->attrs[0].flags = BAF_OPTIONAL;
|
|
||||||
ea->attrs[0].type = EAF_TYPE_INT;
|
|
||||||
ea->attrs[0].u.data = bgp_local_pref(bgp, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the AS path attribute contains our AS, reject the routes */
|
/* If the AS path attribute contains our AS, reject the routes */
|
||||||
e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
||||||
ASSERT(e);
|
ASSERT(e);
|
||||||
|
|
|
@ -21,6 +21,9 @@ struct bgp_config {
|
||||||
int multihop; /* Number of hops if multihop */
|
int multihop; /* Number of hops if multihop */
|
||||||
ip_addr multihop_via; /* Multihop: address to route to */
|
ip_addr multihop_via; /* Multihop: address to route to */
|
||||||
int next_hop_self; /* Always set next hop to local IP address */
|
int next_hop_self; /* Always set next hop to local IP address */
|
||||||
|
int compare_path_lengths; /* Use path lengths when selecting best route */
|
||||||
|
u32 default_local_pref; /* Default value for LOCAL_PREF attribute */
|
||||||
|
u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */
|
||||||
unsigned connect_retry_time;
|
unsigned connect_retry_time;
|
||||||
unsigned hold_time, initial_hold_time;
|
unsigned hold_time, initial_hold_time;
|
||||||
unsigned keepalive_time;
|
unsigned keepalive_time;
|
||||||
|
|
|
@ -15,7 +15,9 @@ CF_HDR
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
||||||
MULTIHOP, STARTUP, VIA, NEXT, HOP, SELF, BGP_PATH)
|
MULTIHOP, STARTUP, VIA, NEXT, HOP, SELF, DEFAULT, PATH, METRIC,
|
||||||
|
BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP,
|
||||||
|
BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ bgp_proto_start: proto_start BGP {
|
||||||
BGP_CFG->hold_time = 240;
|
BGP_CFG->hold_time = 240;
|
||||||
BGP_CFG->connect_retry_time = 120;
|
BGP_CFG->connect_retry_time = 120;
|
||||||
BGP_CFG->initial_hold_time = 240;
|
BGP_CFG->initial_hold_time = 240;
|
||||||
|
BGP_CFG->default_med = ~0;
|
||||||
|
BGP_CFG->compare_path_lengths = 1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -48,9 +52,27 @@ bgp_proto:
|
||||||
| bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; }
|
| bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; }
|
||||||
| bgp_proto MULTIHOP NUM VIA IPA ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; }
|
| bgp_proto MULTIHOP NUM VIA IPA ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; }
|
||||||
| bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; }
|
| bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; }
|
||||||
|
| bgp_proto PATH METRIC NUM ';' { BGP_CFG->compare_path_lengths = $4; }
|
||||||
|
| bgp_proto DEFAULT BGP_MED NUM ';' { BGP_CFG->default_med = $4; }
|
||||||
|
| bgp_proto DEFAULT BGP_LOCAL_PREF NUM ';' { BGP_CFG->default_local_pref = $4; }
|
||||||
;
|
;
|
||||||
|
|
||||||
CF_ADDTO(dynamic_attr, BGP_PATH { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(EAP_BGP, BA_AS_PATH)); })
|
CF_ADDTO(dynamic_attr, BGP_PATH
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(EAP_BGP, BA_AS_PATH)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_MED
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_ORIGIN
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_ORIGIN)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_NEXT_HOP
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(EAP_BGP, BA_NEXT_HOP)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_AGGREGATOR
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_AGGREGATOR)); })
|
||||||
|
CF_ADDTO(dynamic_attr, BGP_COMMUNITY
|
||||||
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_COMMUNITY)); })
|
||||||
|
|
||||||
CF_CODE
|
CF_CODE
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue