diff options
Diffstat (limited to 'proto/bgp/attrs.c')
-rw-r--r-- | proto/bgp/attrs.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index bf747cbd..29d6acb8 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -6,10 +6,220 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +#define LOCAL_DEBUG + #include "nest/bird.h" #include "nest/iface.h" #include "nest/protocol.h" #include "nest/route.h" #include "conf/conf.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "lib/unaligned.h" #include "bgp.h" + +static int bgp_check_origin(byte *a, int len) +{ + if (len > 2) + return 6; + return 0; +} + +static int bgp_check_path(byte *a, int len) +{ + while (len) + { + DBG("Path segment %02x %02x\n", a[0], a[1]); + if (len < 2 || + a[0] != BGP_PATH_AS_SET && a[0] != BGP_PATH_AS_SEQUENCE || + 2*a[1] + 2 > len) + return 11; + len -= 2*a[1] + 2; + a += 2*a[1] + 2; + } + return 0; +} + +static int bgp_check_next_hop(byte *a, int len) +{ + ip_addr addr; + + memcpy(&addr, a, len); + if (ipa_classify(ipa_ntoh(addr)) & IADDR_HOST) + return 0; + else + return 8; +} + +struct attr_desc { + int expected_length; + int expected_flags; + int type; + int (*validate)(byte *attr, int len); +}; + +static struct attr_desc bgp_attr_table[] = { + { -1, 0, 0, NULL }, /* Undefined */ + { 1, BAF_TRANSITIVE, EAF_TYPE_INT, bgp_check_origin }, /* BA_ORIGIN */ + { -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, bgp_check_path }, /* BA_AS_PATH */ + { 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, bgp_check_next_hop }, /* BA_NEXT_HOP */ + { 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_MULTI_EXIT_DISC */ + { 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_LOCAL_PREF */ + { 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_ATOMIC_AGGR */ + { 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_AGGREGATOR */ +#if 0 + /* FIXME: Handle community lists */ + { 0, 0 }, /* BA_COMMUNITY */ + { 0, 0 }, /* BA_ORIGINATOR_ID */ + { 0, 0 }, /* BA_CLUSTER_LIST */ +#endif +}; + +static int bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP }; + +struct rta * +bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool) +{ + struct bgp_proto *bgp = conn->bgp; + rta *a = lp_alloc(pool, sizeof(struct rta)); + unsigned int flags, code, l, errcode, i, type; + byte *z, *attr_start; + byte seen[256/8]; + eattr *e; + ea_list *ea; + struct adata *ad; + neighbor *neigh; + ip_addr nexthop; + + a->proto = &bgp->p; + a->source = RTS_BGP; + a->scope = SCOPE_UNIVERSE; + a->cast = RTC_UNICAST; + a->dest = RTD_ROUTER; + a->flags = 0; + a->aflags = 0; + a->from = bgp->cf->remote_ip; + a->eattrs = NULL; + + /* Parse the attributes */ + bzero(seen, sizeof(seen)); + DBG("BGP: Parsing attributes\n"); + while (len) + { + if (len < 2) + goto malformed; + attr_start = attr; + flags = *attr++; + code = *attr++; + len -= 2; + if (flags & BAF_EXT_LEN) + { + if (len < 2) + goto malformed; + l = get_u16(attr); + attr += 2; + len -= 2; + } + else + { + if (len < 1) + goto malformed; + l = *attr++; + len--; + } + if (l > len) + goto malformed; + len -= l; + z = attr; + attr += l; + 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]; + if (desc->expected_length >= 0 && desc->expected_length != (int) l) + { 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; + type = desc->type; + } + else /* Unknown attribute */ + { /* FIXME: Send partial bit when forwarding */ + if (!(flags & BAF_OPTIONAL)) + { errcode = 2; goto err; } + type = EAF_TYPE_OPAQUE; + } + ea = lp_alloc(pool, sizeof(struct ea_list) + sizeof(struct eattr)); + ea->next = a->eattrs; + a->eattrs = ea; + ea->flags = 0; + ea->count = 1; + ea->attrs[0].id = EA_CODE(EAP_BGP, code); + ea->attrs[0].flags = flags; + ea->attrs[0].type = type; + if (type & EAF_EMBEDDED) + ad = NULL; + else + { + ad = lp_alloc(pool, sizeof(struct adata) + l); + ea->attrs[0].u.ptr = ad; + ad->length = l; + memcpy(ad->data, z, l); + } + switch (type) + { + case EAF_TYPE_ROUTER_ID: + case EAF_TYPE_INT: + ea->attrs[0].u.data = get_u32(z); + break; + case EAF_TYPE_IP_ADDRESS: + *(ip_addr *)ad->data = ipa_ntoh(*(ip_addr *)ad->data); + break; + } + } + + /* Check if all mandatory attributes are present */ + for(i=0; i < sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++) + { + code = bgp_mandatory_attrs[i]; + if (!(seen[code/8] & (1 << (code%8)))) + { + bgp_error(conn, 3, 3, code, 1); + return NULL; + } + } + + /* Fill in the remaining rta fields */ + e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); + ASSERT(e); + nexthop = *(ip_addr *) e->u.ptr->data; + neigh = neigh_find(&bgp->p, &nexthop, 0); + if (!neigh) + { + if (bgp->cf->multihop) + neigh = neigh_find(&bgp->p, &bgp->cf->multihop_via, 0); + else + neigh = neigh_find(&bgp->p, &bgp->cf->remote_ip, 0); + } + if (!neigh || !neigh->iface) + { + DBG("BGP: No route to peer!\n"); /* FIXME */ + return NULL; + } + a->gw = neigh->addr; + a->iface = neigh->iface; + return rta_lookup(a); + +malformed: + bgp_error(conn, 3, 1, len, 0); + return NULL; + +err: + bgp_error(conn, 3, errcode, code, 0); /* FIXME: Return attribute data! */ + return NULL; +} |