summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nest/attrs.h1
-rw-r--r--proto/bgp/attrs.c109
-rw-r--r--proto/bgp/bgp.h3
-rw-r--r--proto/bgp/config.Y26
4 files changed, 88 insertions, 51 deletions
diff --git a/nest/attrs.h b/nest/attrs.h
index 630550f3..2618fa35 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -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);
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
-
#endif
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index f330483e..22c73ca9 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -29,6 +29,7 @@ struct attr_desc {
int expected_length;
int expected_flags;
int type;
+ int allow_in_ebgp;
int (*validate)(struct bgp_proto *p, byte *attr, int len);
void (*format)(eattr *ea, byte *buf);
};
@@ -78,32 +79,24 @@ bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
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[] = {
- { NULL, -1, 0, 0, /* Undefined */
+ { NULL, -1, 0, 0, 0, /* Undefined */
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 },
- { "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 },
- { "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 },
- { "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 },
- { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
- bgp_check_local_pref, NULL },
- { "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
+ { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */
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 },
- { "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 },
#if 0
{ 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)
{
ea_list *t, *new;
- unsigned i, cnt;
+ unsigned i, cnt, hash, code;
eattr *a, *d;
u32 seen = 0;
- unsigned hash;
struct bgp_bucket *b;
/* Merge the attribute lists */
@@ -339,8 +331,14 @@ bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp, int originate)
#endif
if (EA_PROTO(a->id) != EAP_BGP)
continue;
- if (EA_ID(a->id) < 32)
- seen |= 1 << EA_ID(a->id);
+ code = 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;
if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL))
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 *old_bgp = (struct bgp_proto *) old->attrs->proto;
- eattr *new_lpref = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
- eattr *old_lpref = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
+ eattr *x, *y;
+ u32 n, o;
/* 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;
- if (new_lpref->u.data < old_lpref->u.data)
+ if (n > o)
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 */
- /* FIXME: Look at MULTI_EXIT_DISC, take the lowest */
/* We don't have interior distances */
/* We prefer external peers */
if (new_bgp->is_internal > old_bgp->is_internal)
@@ -587,12 +618,6 @@ bgp_rte_better(rte *new, rte *old)
}
static int
-bgp_local_pref(struct bgp_proto *p, rta *a)
-{
- return 0; /* FIXME (should be compatible with Cisco defaults?) */
-}
-
-static int
bgp_path_loopy(struct bgp_proto *p, eattr *a)
{
byte *path = a->u.ptr->data;
@@ -680,6 +705,8 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
{ errcode = 5; goto err; }
if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
{ errcode = 4; goto err; }
+ if (!desc->allow_in_ebgp && !bgp->is_internal)
+ continue;
if (desc->validate)
{
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 */
e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
ASSERT(e);
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 722e12e5..ba4f2d4b 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -21,6 +21,9 @@ struct bgp_config {
int multihop; /* Number of hops if multihop */
ip_addr multihop_via; /* Multihop: address to route to */
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 hold_time, initial_hold_time;
unsigned keepalive_time;
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 635c4f64..548f5d03 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -15,7 +15,9 @@ CF_HDR
CF_DECLS
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
@@ -27,6 +29,8 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->hold_time = 240;
BGP_CFG->connect_retry_time = 120;
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 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 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