summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--proto/bgp/attrs.c245
-rw-r--r--proto/bgp/bgp.c8
-rw-r--r--proto/bgp/bgp.h4
3 files changed, 239 insertions, 18 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 6428193..0fdc3b6 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -19,8 +19,194 @@
#include "bgp.h"
+void
+bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa)
+{
+ DBG("BGP: Got route %I/%d\n", n->n.prefix, n->n.pxlen);
+}
+
+static ea_list *
+bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list *old, struct linpool *pool)
+{
+ ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 3*sizeof(eattr));
+ eattr *a = ea->attrs;
+ rta *rta = e->attrs;
+
+ ea->next = old;
+ ea->flags = EALF_SORTED;
+ ea->count = 3;
+
+ a->id = EA_CODE(EAP_BGP, BA_ORIGIN);
+ a->flags = BAF_TRANSITIVE;
+ a->type = EAF_TYPE_INT;
+ if (rta->source == RTS_RIP_EXT || rta->source == RTS_OSPF_EXT)
+ a->u.data = 2; /* Incomplete */
+ else
+ a->u.data = 0; /* IGP */
+
+ a->id = EA_CODE(EAP_BGP, BA_AS_PATH);
+ a->flags = BAF_TRANSITIVE;
+ a->type = EAF_TYPE_AS_PATH;
+ if (p->is_internal)
+ {
+ a->u.ptr = lp_alloc(pool, sizeof(struct adata));
+ a->u.ptr->length = 0;
+ }
+ else
+ {
+ byte *z;
+ a->u.ptr = lp_alloc(pool, sizeof(struct adata) + 4);
+ a->u.ptr->length = 4;
+ z = a->u.ptr->data;
+ z[0] = 2; /* AS_SEQUENCE */
+ z[1] = 1; /* 1 AS */
+ put_u16(z+2, p->local_as);
+ }
+
+ a->id = EA_CODE(EAP_BGP, BA_NEXT_HOP);
+ a->flags = BAF_TRANSITIVE;
+ a->type = EAF_TYPE_IP_ADDRESS;
+ a->u.ptr = lp_alloc(pool, sizeof(struct adata) + sizeof(ip_addr));
+ a->u.ptr->length = sizeof(ip_addr);
+
+ /* FIXME: These rules are bogus!!! */
+ if (rta->dest == RTD_ROUTER)
+ *(ip_addr *)a->u.ptr->data = e->attrs->gw;
+ else
+ {
+ /* FIXME: Next hop == self ... how to do that? */
+ *(ip_addr *)a->u.ptr->data = IPA_NONE;
+ }
+
+ return ea;
+}
+
+ea_list *
+bgp_path_prepend(struct linpool *pool, eattr *a, ea_list *old, int as)
+{
+ struct ea_list *e = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
+ struct adata *olda = a->u.ptr;
+ struct adata *newa;
+
+ e->next = old;
+ e->flags = EALF_SORTED;
+ e->count = 1;
+ e->attrs[0].id = EA_CODE(EAP_BGP, BA_AS_PATH);
+ e->attrs[0].flags = BAF_TRANSITIVE;
+ e->attrs[0].type = EAF_TYPE_AS_PATH;
+ if (olda->length && olda->data[0] == 2 && olda->data[1] < 255) /* Starting with sequence => just prepend the AS number */
+ {
+ newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 2);
+ newa->length = olda->length + 2;
+ newa->data[0] = 2;
+ newa->data[1] = olda->data[1] + 1;
+ memcpy(newa->data+4, olda->data+2, olda->length-2);
+ }
+ else /* Create new path segment */
+ {
+ newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 4);
+ newa->length = olda->length + 4;
+ newa->data[0] = 2;
+ newa->data[1] = 1;
+ memcpy(newa->data+4, olda->data, olda->length);
+ }
+ put_u16(newa->data+2, as);
+ e->attrs[0].u.ptr = newa;
+ return e;
+}
+
+static ea_list *
+bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list *old, struct linpool *pool)
+{
+ if (!p->is_internal)
+ old = bgp_path_prepend(pool, ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)), old, p->local_as);
+
+ /* FIXME: Set NEXT_HOP to self */
+
+ return old;
+}
+
+int
+bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
+{
+ rte *e = *new;
+ struct bgp_proto *p = (struct bgp_proto *) P;
+ struct bgp_proto *new_bgp = (e->attrs->proto->proto == &proto_bgp) ? (struct bgp_proto *) e->attrs->proto : NULL;
+
+ if (e->attrs->dest != RTD_ROUTER) /* FIXME: This is a debugging kludge, remove some day */
+ return -1;
+ if (new_bgp)
+ {
+ if (p->local_as == new_bgp->local_as && p->is_internal && new_bgp->is_internal)
+ return -1; /* Don't redistribute internal routes with IBGP */
+ *attrs = bgp_update_attrs(p, e, *attrs, pool);
+ }
+ else
+ *attrs = bgp_create_attrs(p, e, *attrs, pool);
+ if (p == new_bgp) /* FIXME: Use a more realistic check based on the NEXT_HOP attribute */
+ return 1;
+ return 0; /* Leave the decision to the filter */
+}
+
+int
+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));
+
+ /* Start with local preferences */
+ if (new_lpref && old_lpref) /* Somebody might have undefined them */
+ {
+ if (new_lpref->u.data > old_lpref->u.data)
+ return 1;
+ if (new_lpref->u.data < old_lpref->u.data)
+ 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)
+ return 0;
+ if (new_bgp->is_internal < old_bgp->is_internal)
+ return 1;
+ /* Finally we compare BGP identifiers */
+ return (new_bgp->remote_id < old_bgp->remote_id);
+}
+
static int
-bgp_check_origin(byte *a, int len)
+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;
+ int len = a->u.ptr->length;
+ int i, n;
+
+ while (len > 0)
+ {
+ n = path[1];
+ len -= 2 - 2*n;
+ path += 2;
+ for(i=0; i<n; i++)
+ {
+ if (get_u16(path) == p->local_as)
+ return 1;
+ path += 2;
+ }
+ }
+ return 0;
+}
+
+static int
+bgp_check_origin(struct bgp_proto *p, byte *a, int len)
{
if (len > 2)
return 6;
@@ -28,7 +214,7 @@ bgp_check_origin(byte *a, int len)
}
static void
-bgp_format_origin(struct eattr *a, byte *buf)
+bgp_format_origin(eattr *a, byte *buf)
{
static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
@@ -36,7 +222,7 @@ bgp_format_origin(struct eattr *a, byte *buf)
}
static int
-bgp_check_path(byte *a, int len)
+bgp_check_path(struct bgp_proto *p, byte *a, int len)
{
while (len)
{
@@ -52,7 +238,7 @@ bgp_check_path(byte *a, int len)
}
static int
-bgp_check_next_hop(byte *a, int len)
+bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
{
ip_addr addr;
@@ -63,13 +249,21 @@ bgp_check_next_hop(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;
+}
+
struct attr_desc {
char *name; /* FIXME: Use the same names as in filters */
int expected_length;
int expected_flags;
int type;
- int (*validate)(byte *attr, int len);
- void (*format)(struct eattr *ea, byte *buf);
+ int (*validate)(struct bgp_proto *p, byte *attr, int len);
+ void (*format)(eattr *ea, byte *buf);
};
static struct attr_desc bgp_attr_table[] = {
@@ -84,7 +278,7 @@ static struct attr_desc bgp_attr_table[] = {
{ "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_MULTI_EXIT_DISC */
NULL, NULL },
{ "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
- NULL, NULL },
+ bgp_check_local_pref, NULL },
{ "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
NULL, NULL },
{ "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_AGGREGATOR */
@@ -104,7 +298,8 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
{
struct bgp_proto *bgp = conn->bgp;
rta *a = lp_alloc(pool, sizeof(struct rta));
- unsigned int flags, code, l, errcode, i, type;
+ unsigned int flags, code, l, i, type;
+ int errcode;
byte *z, *attr_start;
byte seen[256/8];
eattr *e;
@@ -157,7 +352,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
DBG("Attr %02x %02x %d\n", code, flags, l);
if (seen[code/8] & (1 << (code%8)))
goto malformed;
- seen[code/8] |= (1 << (code%8));
if (code && code < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
{
struct attr_desc *desc = &bgp_attr_table[code];
@@ -165,8 +359,14 @@ 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->validate && (errcode = desc->validate(z, l)))
- goto err;
+ if (desc->validate)
+ {
+ errcode = desc->validate(bgp, z, l);
+ if (errcode > 0)
+ goto err;
+ if (errcode < 0)
+ continue;
+ }
type = desc->type;
}
else /* Unknown attribute */
@@ -175,7 +375,8 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
{ errcode = 2; goto err; }
type = EAF_TYPE_OPAQUE;
}
- ea = lp_alloc(pool, sizeof(struct ea_list) + sizeof(struct eattr));
+ seen[code/8] |= (1 << (code%8));
+ ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
ea->next = a->eattrs;
a->eattrs = ea;
ea->flags = 0;
@@ -218,6 +419,26 @@ 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);
+ if (bgp_path_loopy(bgp, e))
+ return NULL;
+
/* Fill in the remaining rta fields */
e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
ASSERT(e);
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index f325aa3..11729db 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -28,12 +28,6 @@ static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "Ope
static void bgp_connect(struct bgp_proto *p);
static void bgp_setup_sk(struct bgp_proto *p, struct bgp_conn *conn, sock *s);
-static void
-bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa)
-{
- DBG("BGP: Got route %I/%d\n", n->n.prefix, n->n.pxlen);
-}
-
static struct proto *
bgp_init(struct proto_config *C)
{
@@ -42,6 +36,8 @@ bgp_init(struct proto_config *C)
struct bgp_proto *p = (struct bgp_proto *) P;
P->rt_notify = bgp_rt_notify;
+ P->rte_better = bgp_rte_better;
+ P->import_control = bgp_import_control;
p->cf = c;
p->local_as = c->local_as;
p->remote_as = c->remote_as;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 8aaac88..45f7e0b 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -69,6 +69,10 @@ void bgp_close_conn(struct bgp_conn *c);
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool);
int bgp_get_attr(struct eattr *e, byte *buf);
+int bgp_rte_better(struct rte *, struct rte *);
+void bgp_rt_notify(struct proto *, struct network *, struct rte *, struct rte *, struct ea_list *);
+int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);
+struct ea_list *bgp_path_prepend(struct linpool *pool, struct eattr *a, struct ea_list *old, int as);
/* packets.c */