diff options
Diffstat (limited to 'proto/bgp')
-rw-r--r-- | proto/bgp/attrs.c | 83 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 8 | ||||
-rw-r--r-- | proto/bgp/packets.c | 234 |
3 files changed, 261 insertions, 64 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 40130c63..3d79efd9 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -22,7 +22,11 @@ #include "bgp.h" -static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP }; +static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH +#ifndef IPV6 +,BA_NEXT_HOP +#endif +}; struct attr_desc { char *name; @@ -69,6 +73,9 @@ bgp_check_path(struct bgp_proto *p, byte *a, int len) static int bgp_check_next_hop(struct bgp_proto *p, byte *a, int len) { +#ifdef IPV6 + return -1; +#else ip_addr addr; memcpy(&addr, a, len); @@ -77,31 +84,61 @@ bgp_check_next_hop(struct bgp_proto *p, byte *a, int len) return 0; else return 8; +#endif +} + +static int +bgp_check_reach_nlri(struct bgp_proto *p, byte *a, int len) +{ +#ifdef IPV6 + p->mp_reach_start = a; + p->mp_reach_len = len; + return 0; +#else + return -1; +#endif +} + +static int +bgp_check_unreach_nlri(struct bgp_proto *p, byte *a, int len) +{ +#ifdef IPV6 + p->mp_unreach_start = a; + p->mp_unreach_len = len; + return 0; +#else + return -1; +#endif } static struct attr_desc bgp_attr_table[] = { - { NULL, -1, 0, 0, 0, /* Undefined */ + { NULL, -1, 0, 0, 0, /* Undefined */ NULL, NULL }, - { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* 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, 1, /* 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, 1, /* 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, 0, /* 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, 0, /* BA_LOCAL_PREF */ + { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ NULL, NULL }, - { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ + { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ NULL, NULL }, { "aggregator", 6, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ NULL, NULL }, - { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ + { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ NULL, NULL }, -#if 0 - { 0, 0 }, /* BA_ORIGINATOR_ID */ - { 0, 0 }, /* BA_CLUSTER_LIST */ -#endif + { NULL, }, /* BA_ORIGINATOR_ID */ + { NULL, }, /* BA_CLUSTER_LIST */ + { NULL, }, /* BA_DPA */ + { NULL, }, /* BA_ADVERTISER */ + { NULL, }, /* BA_RCID_PATH */ + { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */ + bgp_check_reach_nlri, NULL }, + { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */ + bgp_check_unreach_nlri, NULL }, }; #define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name) @@ -708,8 +745,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin eattr *e; ea_list *ea; struct adata *ad; - neighbor *neigh; - ip_addr nexthop; a->proto = &bgp->p; a->source = RTS_BGP; @@ -820,6 +855,11 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin } } +#ifdef IPV6 + if (seen[BA_MP_REACH_NLRI / 8] & (1 << (BA_MP_REACH_NLRI % 8))) + mandatory = 1; +#endif + /* Check if all mandatory attributes are present */ if (mandatory) { @@ -855,19 +895,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin ea->attrs[0].type = EAF_TYPE_INT; ea->attrs[0].u.data = 0; } - - /* 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; - if (ipa_equal(nexthop, bgp->local_addr)) - { - DBG("BGP: Loop!\n"); - return NULL; - } - neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh; - a->gw = neigh->addr; - a->iface = neigh->iface; return a; malformed: diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index a9f3413c..37f29792 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -72,6 +72,10 @@ struct bgp_proto { struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */ unsigned startup_delay; /* Time to delay protocol startup by due to errors */ bird_clock_t last_connect; /* Time of last connect attempt */ +#ifdef IPV6 + byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */ + unsigned mp_reach_len, mp_unreach_len; +#endif }; struct bgp_prefix { @@ -181,4 +185,8 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco #define ORIGIN_EGP 1 #define ORIGIN_INCOMPLETE 2 +/* Address families */ + +#define BGP_AF_IPV6 2 + #endif diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 52e93510..45397fbf 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -68,6 +68,8 @@ bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsig return w - start; } +#ifndef IPV6 /* IPv4 version */ + static byte * bgp_create_update(struct bgp_conn *conn, byte *buf) { @@ -117,6 +119,16 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) return (wd_size || r_size) ? w : NULL; } +#else /* IPv6 version */ + +static byte * +bgp_create_update(struct bgp_conn *conn, byte *buf) +{ + return NULL; +} + +#endif + static void bgp_create_header(byte *buf, unsigned int len, unsigned int type) { @@ -321,9 +333,9 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) int b = *pp++; \ int q; \ ll--; \ - if (b > BITS_PER_IP_ADDRESS) { bgp_error(conn, 3, 10, NULL, 0); return; } \ + if (b > BITS_PER_IP_ADDRESS) { err=10; goto bad; } \ q = (b+7) / 8; \ - if (ll < q) goto malformed; \ + if (ll < q) { err=1; goto bad; } \ memcpy(&prefix, pp, q); \ pp += q; \ ll -= q; \ @@ -332,42 +344,40 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) pxlen = b; \ } while (0) +static inline int +bgp_get_nexthop(struct bgp_proto *bgp, rta *a) +{ + neighbor *neigh; + ip_addr nexthop; + struct eattr *nh = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); + ASSERT(nh); + nexthop = *(ip_addr *) nh->u.ptr->data; + if (ipa_equal(nexthop, bgp->local_addr)) + { + DBG("BGP: Loop!\n"); + return 0; + } + neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh; + a->gw = neigh->addr; + a->iface = neigh->iface; + return 1; +} + +#ifndef IPV6 /* IPv4 version */ + static void -bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) +bgp_do_rx_update(struct bgp_conn *conn, + byte *withdrawn, int withdrawn_len, + byte *nlri, int nlri_len, + byte *attrs, int attr_len) { struct bgp_proto *p = conn->bgp; - byte *withdrawn, *attrs, *nlri; + rta *a0; + rta *a = NULL; ip_addr prefix; - int withdrawn_len, attr_len, nlri_len, pxlen; net *n; rte e; - rta *a0; - rta *a = NULL; - - BGP_TRACE(D_PACKETS, "Got UPDATE"); - if (conn->state != BS_ESTABLISHED) - { bgp_error(conn, 5, 0, NULL, 0); return; } - bgp_start_timer(conn->hold_timer, conn->hold_time); - - /* Find parts of the packet and check sizes */ - if (len < 23) - { - bgp_error(conn, 1, 2, pkt+16, 2); - return; - } - withdrawn = pkt + 21; - withdrawn_len = get_u16(pkt + 19); - if (withdrawn_len + 23 > len) - goto malformed; - attrs = withdrawn + withdrawn_len + 2; - attr_len = get_u16(attrs - 2); - if (withdrawn_len + attr_len + 23 > len) - goto malformed; - nlri = attrs + attr_len; - nlri_len = len - withdrawn_len - attr_len - 23; - if (!attr_len && nlri_len) - goto malformed; - DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len); + int err = 0, pxlen; /* Withdraw routes */ while (withdrawn_len) @@ -382,7 +392,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) return; a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len); - if (a0 && nlri_len) + if (a0 && nlri_len && bgp_get_nexthop(p, a0)) { a = rta_lookup(a0); while (nlri_len) @@ -396,14 +406,166 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) e->pflags = 0; rte_update(p->p.table, n, &p->p, e); } - rta_free(a); } - lp_flush(bgp_linpool); +bad: + if (a) + rta_free(a); + if (err) + bgp_error(conn, 3, err, NULL, 0); return; +} -malformed: +#else /* IPv6 version */ + +#define DO_NLRI(name) \ + start = x = p->name##_start; \ + len = len0 = p->name##_len; \ + if (len) \ + { \ + if (len < 3) goto bad; \ + af = get_u16(x); \ + sub = x[2]; \ + x += 3; \ + len -= 3; \ + DBG("\tNLRI AF=%d sub=%d len=%d\n", af, sub, len);\ + } \ + else \ + af = 0; \ + if (af == BGP_AF_IPV6) + +static void +bgp_do_rx_update(struct bgp_conn *conn, + byte *withdrawn, int withdrawn_len, + byte *nlri, int nlri_len, + byte *attrs, int attr_len) +{ + struct bgp_proto *p = conn->bgp; + byte *start, *x; + int len, len0; + unsigned af, sub; + rta *a0; + rta *a = NULL; + ip_addr prefix; + net *n; + rte e; + int err = 0, pxlen; + + p->mp_reach_len = 0; + p->mp_unreach_len = 0; + a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0); + if (!a0) + return; + + DO_NLRI(mp_unreach) + { + while (len) + { + DECODE_PREFIX(x, len); + DBG("Withdraw %I/%d\n", prefix, pxlen); + if (n = net_find(p->p.table, prefix, pxlen)) + rte_update(p->p.table, n, &p->p, NULL); + } + } + + DO_NLRI(mp_reach) + { + ea_list *e = lp_alloc(bgp_linpool, sizeof(ea_list) + sizeof(eattr)); + struct adata *ad = lp_alloc(bgp_linpool, sizeof(struct adata) + 16); + int i; + + /* Create fake NEXT_HOP attribute */ + if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2) + goto bad; + e->next = a0->eattrs; + a0->eattrs = e; + e->flags = EALF_SORTED; + e->count = 1; + e->attrs[0].id = EA_CODE(EAP_BGP, BA_NEXT_HOP); + e->attrs[0].flags = BAF_TRANSITIVE; + e->attrs[0].type = EAF_TYPE_IP_ADDRESS; + e->attrs[0].u.ptr = ad; + ad->length = 16; + memcpy(ad->data, x+1, 16); + len -= *x + 1; + x += *x + 1; + + /* Ignore SNPA info */ + i = *x++; + while (i--) + { + if (len < 1 || len < 1 + *x) + goto bad; + len -= *x + 1; + x += *x + 1; + } + + if (bgp_get_nexthop(p, a0)) + { + a = rta_lookup(a0); + while (len) + { + rte *e; + DECODE_PREFIX(x, len); + DBG("Add %I/%d\n", prefix, pxlen); + e = rte_get_temp(rta_clone(a)); + n = net_get(p->p.table, prefix, pxlen); + e->net = n; + e->pflags = 0; + rte_update(p->p.table, n, &p->p, e); + } + rta_free(a); + } + } + + return; + +bad: + bgp_error(conn, 3, 9, start, len0); if (a) rta_free(a); + return; +} + +#endif + +static void +bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) +{ + struct bgp_proto *p = conn->bgp; + byte *withdrawn, *attrs, *nlri; + int withdrawn_len, attr_len, nlri_len; + + BGP_TRACE(D_PACKETS, "Got UPDATE"); + if (conn->state != BS_ESTABLISHED) + { bgp_error(conn, 5, 0, NULL, 0); return; } + bgp_start_timer(conn->hold_timer, conn->hold_time); + + /* Find parts of the packet and check sizes */ + if (len < 23) + { + bgp_error(conn, 1, 2, pkt+16, 2); + return; + } + withdrawn = pkt + 21; + withdrawn_len = get_u16(pkt + 19); + if (withdrawn_len + 23 > len) + goto malformed; + attrs = withdrawn + withdrawn_len + 2; + attr_len = get_u16(attrs - 2); + if (withdrawn_len + attr_len + 23 > len) + goto malformed; + nlri = attrs + attr_len; + nlri_len = len - withdrawn_len - attr_len - 23; + if (!attr_len && nlri_len) + goto malformed; + DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len); + + lp_flush(bgp_linpool); + + bgp_do_rx_update(conn, withdrawn, withdrawn_len, nlri, nlri_len, attrs, attr_len); + return; + +malformed: bgp_error(conn, 3, 1, NULL, 0); } |