From 5b885bf70848908c7fed07c8efba18ea316379d4 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 16 Nov 2010 03:47:07 +0100 Subject: BGP: Allow receiving multicast routes --- proto/bgp/packets.c | 72 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 27 deletions(-) (limited to 'proto/bgp/packets.c') diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 1a1e7b7..8e657cc 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -6,7 +6,7 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#undef LOCAL_DEBUG +#define LOCAL_DEBUG #include "nest/bird.h" #include "nest/iface.h" @@ -228,7 +228,7 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) } static unsigned int -bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains) +bgp_encode_prefixes(struct bgp_bucket_info *bi, byte *w, struct bgp_bucket *buck, unsigned int remains) { byte *start = w; ip_addr a; @@ -246,20 +246,20 @@ bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsig w += bytes; remains -= bytes + 1; rem_node(&px->bucket_node); - fib_delete(&p->prefix_fib, px); + fib_delete(&bi->prefix_fib, px); } return w - start; } static void -bgp_flush_prefixes(struct bgp_proto *p, struct bgp_bucket *buck) +bgp_flush_prefixes(struct bgp_proto *p, struct bgp_bucket_info *bi, struct bgp_bucket *buck) { while (!EMPTY_LIST(buck->prefixes)) { struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes)); log(L_ERR "%s: - route %I/%d skipped", p->p.name, px->n.prefix, px->n.pxlen); rem_node(&px->bucket_node); - fib_delete(&p->prefix_fib, px); + fib_delete(&bi->prefix_fib, px); } } @@ -269,6 +269,7 @@ static byte * bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; + struct bgp_bucket_info *bi = &p->unicast_buckets; struct bgp_bucket *buck; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; byte *w; @@ -277,10 +278,10 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) int a_size = 0; w = buf+2; - if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) + if ((buck = bi->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) { DBG("Withdrawn routes:\n"); - wd_size = bgp_encode_prefixes(p, w, buck, remains); + wd_size = bgp_encode_prefixes(bi, w, buck, remains); w += wd_size; remains -= wd_size; } @@ -288,13 +289,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (remains >= 3072) { - while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) + while ((buck = (struct bgp_bucket *) HEAD(bi->bucket_queue))->send_node.next) { if (EMPTY_LIST(buck->prefixes)) { DBG("Deleting empty bucket %p\n", buck); rem_node(&buck->send_node); - bgp_free_bucket(p, buck); + bgp_free_bucket(bi, buck); continue; } @@ -304,15 +305,15 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (a_size < 0) { log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); - bgp_flush_prefixes(p, buck); + bgp_flush_prefixes(p, bi, buck); rem_node(&buck->send_node); - bgp_free_bucket(p, buck); + bgp_free_bucket(bi, buck); continue; } put_u16(w, a_size); w += a_size + 2; - r_size = bgp_encode_prefixes(p, w, buck, remains - a_size); + r_size = bgp_encode_prefixes(bi, w, buck, remains - a_size); w += r_size; break; } @@ -337,6 +338,7 @@ static byte * bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; + struct bgp_bucket_info *bi = &p->unicast_buckets; struct bgp_bucket *buck; int size, second, rem_stored; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; @@ -349,14 +351,14 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) put_u16(buf, 0); w = buf+4; - if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) + if ((buck = bi->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) { DBG("Withdrawn routes:\n"); tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_UNREACH_NLRI, remains-8); *tmp++ = 0; *tmp++ = BGP_AF_IPV6; - *tmp++ = 1; - ea->attrs[0].u.ptr->length = 3 + bgp_encode_prefixes(p, tmp, buck, remains-11); + *tmp++ = BGP_SAF_UNICAST; + ea->attrs[0].u.ptr->length = 3 + bgp_encode_prefixes(bi, tmp, buck, remains-11); size = bgp_encode_attrs(p, w, ea, remains); ASSERT(size >= 0); w += size; @@ -365,13 +367,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (remains >= 3072) { - while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) + while ((buck = (struct bgp_bucket *) HEAD(bi->bucket_queue))->send_node.next) { if (EMPTY_LIST(buck->prefixes)) { DBG("Deleting empty bucket %p\n", buck); rem_node(&buck->send_node); - bgp_free_bucket(p, buck); + bgp_free_bucket(bi, buck); continue; } @@ -383,9 +385,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (size < 0) { log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); - bgp_flush_prefixes(p, buck); + bgp_flush_prefixes(p, bi, buck); rem_node(&buck->send_node); - bgp_free_bucket(p, buck); + bgp_free_bucket(bi, buck); continue; } w += size; @@ -433,9 +435,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) log(L_ERR "%s: Missing link-local next hop address, skipping corresponding routes", p->p.name); w = w_stored; remains = rem_stored; - bgp_flush_prefixes(p, buck); + bgp_flush_prefixes(p, bi, buck); rem_node(&buck->send_node); - bgp_free_bucket(p, buck); + bgp_free_bucket(bi, buck); continue; case MLL_IGNORE: break; @@ -467,7 +469,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) } *tmp++ = 0; /* No SNPA information */ - tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1)); + tmp += bgp_encode_prefixes(bi, tmp, buck, remains - (8+3+32+1)); ea->attrs[0].u.ptr->length = tmp - tstart; size = bgp_encode_attrs(p, w, ea, remains); ASSERT(size >= 0); @@ -804,6 +806,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3; p->remote_id = id; p->as4_session = conn->want_as4_support && conn->peer_as4_support; + p->multicast_session = conn->want_multicast_support && conn->peer_multicast_support; DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x, AS4 session to %d\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, p->as4_session); @@ -930,7 +933,7 @@ bgp_do_rx_update(struct bgp_conn *conn, } \ else \ af = 0; \ - if (af == BGP_AF_IPV6) + if (af == BGP_AF) static void bgp_do_rx_update(struct bgp_conn *conn, @@ -947,6 +950,7 @@ bgp_do_rx_update(struct bgp_conn *conn, ip_addr prefix; net *n; int err = 0, pxlen; + int multicast; p->mp_reach_len = 0; p->mp_unreach_len = 0; @@ -956,17 +960,30 @@ bgp_do_rx_update(struct bgp_conn *conn, DO_NLRI(mp_unreach) { + multicast = (sub == BGP_SAF_MULTICAST); + + if (multicast) + ASSERT(p->multicast_session); + while (len) { DECODE_PREFIX(x, len); - DBG("Withdraw %I/%d\n", prefix, pxlen); - if (n = net_find(p->p.table, prefix, pxlen)) + DBG("Withdraw %I/%d%s\n", prefix, pxlen, multicast ? " MC" : ""); + if (n = net_find_cast(p->p.table, prefix, pxlen, multicast ? RTC_MULTICAST : RTC_UNICAST)) rte_update(p->p.table, n, &p->p, &p->p, NULL); } } DO_NLRI(mp_reach) { + multicast = (sub == BGP_SAF_MULTICAST); + + if (multicast) + { + ASSERT(p->multicast_session); + a0->cast = RTC_MULTICAST; + } + /* Create fake NEXT_HOP attribute */ if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2) goto bad; @@ -991,13 +1008,14 @@ bgp_do_rx_update(struct bgp_conn *conn, if (bgp_set_next_hop(p, a0)) { a = rta_lookup(a0); + while (len) { rte *e; DECODE_PREFIX(x, len); - DBG("Add %I/%d\n", prefix, pxlen); + DBG("Add %I/%d%s\n", prefix, pxlen, multicast ? " MC" : ""); e = rte_get_temp(rta_clone(a)); - n = net_get(p->p.table, prefix, pxlen); + n = net_get_cast(p->p.table, prefix, pxlen, multicast ? RTC_MULTICAST : RTC_UNICAST); e->net = n; e->pflags = 0; rte_update(p->p.table, n, &p->p, &p->p, e); -- cgit v1.2.3