diff options
author | Martin Mares <mj@ucw.cz> | 2000-03-31 23:21:37 +0000 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 2000-03-31 23:21:37 +0000 |
commit | c00d31befab5a7e932231f7a8050547c72c94631 (patch) | |
tree | 7695b995629f05e35fb04311a626a2866d4e9a02 | |
parent | 08732b71784b640aebbea88d4452f4c5987d0a09 (diff) |
Parsing of BGP attributes.
-rw-r--r-- | proto/bgp/attrs.c | 210 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 2 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 10 | ||||
-rw-r--r-- | proto/bgp/config.Y | 4 | ||||
-rw-r--r-- | proto/bgp/packets.c | 51 |
5 files changed, 254 insertions, 23 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; +} diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index e4157565..e95e3344 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -96,6 +96,8 @@ bgp_close_conn(struct bgp_conn *conn) { bgp_close(p); p->conn = NULL; + if (conn->error_flag) /* FIXME: Enable automatically? */ + p->p.disabled = 1; proto_notify_state(&p->p, PS_DOWN); } } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index e93b06eb..eadf2b04 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -9,11 +9,14 @@ #ifndef _BIRD_BGP_H_ #define _BIRD_BGP_H_ +struct linpool; + struct bgp_config { struct proto_config c; unsigned int local_as, remote_as; ip_addr remote_ip; int multihop; /* Number of hops if multihop */ + ip_addr multihop_via; /* Multihop: address to route to */ unsigned connect_retry_time; unsigned hold_time, initial_hold_time; unsigned keepalive_time; @@ -63,6 +66,8 @@ void bgp_close_conn(struct bgp_conn *c); /* attrs.c */ +struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool); + /* packets.c */ void bgp_schedule_packet(struct bgp_conn *conn, int type); @@ -88,7 +93,7 @@ int bgp_rx(struct birdsock *sk, int size); #define BA_AS_PATH 0x02 /* WM */ #define BA_NEXT_HOP 0x03 /* WM */ #define BA_MULTI_EXIT_DISC 0x04 /* ON */ -#define BA_LOCAL_PREF 0x05 /* WM */ +#define BA_LOCAL_PREF 0x05 /* WD */ #define BA_ATOMIC_AGGR 0x06 /* WD */ #define BA_AGGREGATOR 0x07 /* OT */ #define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */ @@ -102,6 +107,9 @@ int bgp_rx(struct birdsock *sk, int size); #define BA_MP_UNREACH_NLRI 0x0f #define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */ +#define BGP_PATH_AS_SET 1 /* Types of path segments */ +#define BGP_PATH_AS_SEQUENCE 2 + /* BGP states */ #define BS_IDLE 0 diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 0633e1cd..f83191ad 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -15,7 +15,7 @@ CF_HDR CF_DECLS CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, - MULTIHOP, STARTUP) + MULTIHOP, STARTUP, VIA) CF_GRAMMAR @@ -46,7 +46,7 @@ bgp_proto: | bgp_proto STARTUP HOLD TIME NUM ';' { BGP_CFG->initial_hold_time = $5; } | bgp_proto CONNECT RETRY TIME NUM ';' { BGP_CFG->connect_retry_time = $5; } | bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; } - | bgp_proto MULTIHOP NUM ';' { BGP_CFG->multihop = $3; } + | bgp_proto MULTIHOP NUM VIA IPA ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; } ; CF_CODE diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 3bddbc90..7764999e 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -218,24 +218,26 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) #define DECODE_PREFIX(pp, ll) do { \ int b = *pp++; \ int q; \ - ip_addr temp; \ ll--; \ if (b > BITS_PER_IP_ADDRESS) { bgp_error(conn, 3, 10, b, 0); return; } \ q = (b+7) / 8; \ - if (ll < q) goto too_small; \ - memcpy(&temp, pp, q); \ + if (ll < q) goto malformed; \ + memcpy(&prefix, pp, q); \ pp += q; \ ll -= q; \ - n.n.prefix = ipa_and(ipa_ntoh(temp), ipa_mkmask(b)); \ - n.n.pxlen = b; \ + prefix = ipa_and(ipa_ntoh(prefix), ipa_mkmask(b)); \ + pxlen = b; \ } while (0) +/* FIXME: Check validity of prefixes */ static void bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) { + struct bgp_proto *bgp = conn->bgp; byte *withdrawn, *attrs, *nlri; - int withdrawn_len, attr_len, nlri_len; - net n; + ip_addr prefix; + int withdrawn_len, attr_len, nlri_len, pxlen; + net *n; rte e; DBG("BGP: UPDATE\n"); @@ -246,45 +248,54 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) /* Find parts of the packet and check sizes */ if (len < 23) { - too_small: bgp_error(conn, 1, 2, len, 2); return; } withdrawn = pkt + 21; withdrawn_len = get_u16(pkt + 19); if (withdrawn_len + 23 > len) - goto too_small; + { + malformed: + bgp_error(conn, 3, 1, len, 0); + return; + } attrs = withdrawn + withdrawn_len + 2; attr_len = get_u16(attrs - 2); if (withdrawn_len + attr_len + 23 > len) - goto too_small; + goto malformed; nlri = attrs + attr_len; nlri_len = len - withdrawn_len - attr_len - 23; if (!attr_len && nlri_len) - goto too_small; + goto malformed; DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len); /* Withdraw routes */ while (withdrawn_len) { DECODE_PREFIX(withdrawn, withdrawn_len); - DBG("Withdraw %I/%d\n", n.n.prefix, n.n.pxlen); + DBG("Withdraw %I/%d\n", prefix, pxlen); + if (n = net_find(bgp->p.table, prefix, pxlen)) + rte_update(bgp->p.table, n, &bgp->p, NULL); } if (nlri_len) { -#if 0 rta *a = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool); - if (a) -#endif + if (!a) + return; + while (nlri_len) { - while (nlri_len) - { - DECODE_PREFIX(nlri, nlri_len); - DBG("Add %I/%d\n", n.n.prefix, n.n.pxlen); - } + rte *e; + DECODE_PREFIX(nlri, nlri_len); /* FIXME: Uncache rta ! */ + DBG("Add %I/%d\n", prefix, pxlen); + e = rte_get_temp(a); + n = net_get(bgp->p.table, prefix, pxlen); + e->net = n; + e->pflags = 0; + rte_update(bgp->p.table, n, &bgp->p, e); } lp_flush(bgp_linpool); + rta_free(a); } } |