diff options
-rw-r--r-- | proto/bgp/attrs.c | 126 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 3 | ||||
-rw-r--r-- | proto/bgp/packets.c | 110 |
3 files changed, 152 insertions, 87 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 3d79efd..ff434e5 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -93,10 +93,8 @@ 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 + return -1; } static int @@ -105,10 +103,8 @@ 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 + return -1; } static struct attr_desc bgp_attr_table[] = { @@ -143,20 +139,54 @@ static struct attr_desc bgp_attr_table[] = { #define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name) +static byte * +bgp_set_attr(eattr *e, struct linpool *pool, unsigned attr, unsigned val) +{ + ASSERT(ATTR_KNOWN(attr)); + e->id = EA_CODE(EAP_BGP, attr); + e->type = bgp_attr_table[attr].type; + e->flags = bgp_attr_table[attr].expected_flags; + if (e->type & EAF_EMBEDDED) + { + e->u.data = val; + return NULL; + } + else + { + e->u.ptr = lp_alloc(pool, sizeof(struct adata) + val); + e->u.ptr->length = val; + return e->u.ptr->data; + } +} + byte * -bgp_encode_attrs(byte *w, struct bgp_bucket *buck) +bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, unsigned val) +{ + ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); + a->next = *to; + *to = a; + a->flags = EALF_SORTED; + a->count = 1; + return bgp_set_attr(a->attrs, pool, attr, val); +} + +unsigned int +bgp_encode_attrs(byte *w, ea_list *attrs, int remains) { - int remains = 1024; unsigned int i, code, flags; byte *start = w; int len; - w += 2; - for(i=0; i<buck->eattrs->count; i++) + for(i=0; i<attrs->count; i++) { - eattr *a = &buck->eattrs->attrs[i]; + eattr *a = &attrs->attrs[i]; ASSERT(EA_PROTO(a->id) == EAP_BGP); code = EA_ID(a->id); +#ifdef IPV6 + /* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */ + if (code == BA_NEXT_HOP) + continue; +#endif flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL); if (ATTR_KNOWN(code)) { @@ -228,8 +258,7 @@ bgp_encode_attrs(byte *w, struct bgp_bucket *buck) remains -= len; w += len; } - put_u16(start, w-start-2); - return w; + return w - start; } static void @@ -523,60 +552,36 @@ static int bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool) { ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 4*sizeof(eattr)); - eattr *a = ea->attrs; rta *rta = e->attrs; + byte *z; ea->next = *attrs; *attrs = ea; ea->flags = EALF_SORTED; ea->count = 4; - 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 = ORIGIN_INCOMPLETE; - else - a->u.data = ORIGIN_IGP; - a++; + bgp_set_attr(ea->attrs, pool, BA_ORIGIN, + (rta->source == RTS_RIP_EXT || rta->source == RTS_OSPF_EXT) ? ORIGIN_INCOMPLETE : ORIGIN_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; - } + bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 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 = bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 4); z[0] = AS_PATH_SEQUENCE; z[1] = 1; /* 1 AS */ put_u16(z+2, p->local_as); } - a++; - 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); + z = bgp_set_attr(ea->attrs+2, pool, BA_NEXT_HOP, sizeof(ip_addr)); if (p->cf->next_hop_self || !p->is_internal || rta->dest != RTD_ROUTER) - *(ip_addr *)a->u.ptr->data = p->local_addr; + *(ip_addr *)z = p->local_addr; else - *(ip_addr *)a->u.ptr->data = e->attrs->gw; - a++; + *(ip_addr *)z = e->attrs->gw; - a->id = EA_CODE(EAP_BGP, BA_LOCAL_PREF); - a->flags = BAF_OPTIONAL; - a->type = EAF_TYPE_INT; - a->u.data = 0; + bgp_set_attr(ea->attrs+3, pool, BA_LOCAL_PREF, 0); return 0; /* Leave decision to the filters */ } @@ -602,8 +607,8 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p { eattr *a; - if (!p->is_internal) - *attrs = bgp_path_prepend(pool, ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)), *attrs, p->local_as); + if (!p->is_internal && (a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)))) + *attrs = bgp_path_prepend(pool, a, *attrs, p->local_as); a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); if (a && (p->is_internal || (!p->is_internal && e->attrs->iface == p->neigh->iface))) @@ -613,18 +618,7 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p else { /* Need to create new one */ - ea_list *ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); - ea->next = *attrs; - *attrs = ea; - ea->flags = EALF_SORTED; - ea->count = 1; - a = ea->attrs; - 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); - *(ip_addr *)a->u.ptr->data = p->local_addr; + *(ip_addr *) bgp_attach_attr(attrs, pool, BA_NEXT_HOP, sizeof(ip_addr)) = p->local_addr; } return 0; /* Leave decision to the filters */ @@ -884,17 +878,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin /* If there's no local preference, define one */ if (!(seen[0] && (1 << BA_LOCAL_PREF))) - { - 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 = 0; - } + bgp_attach_attr(&a->eattrs, pool, BA_LOCAL_PREF, 0); return a; malformed: diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 37f2979..38baccd 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -115,13 +115,14 @@ void bgp_close_conn(struct bgp_conn *c); /* attrs.c */ +byte *bgp_attach_attr(struct ea_list **to, struct linpool *, unsigned attr, unsigned val); struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory); 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 *); void bgp_attr_init(struct bgp_proto *); -byte *bgp_encode_attrs(byte *w, struct bgp_bucket *buck); +unsigned int bgp_encode_attrs(byte *w, struct ea_list *attrs, int remains); void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck); /* packets.c */ diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 45397fb..e9ba050 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -76,10 +76,11 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; - byte *w, *wold; + byte *w; ip_addr ip; int wd_size = 0; int r_size = 0; + int a_size = 0; BGP_TRACE(D_PACKETS, "Sending UPDATE"); w = buf+2; @@ -104,9 +105,10 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) continue; } DBG("Processing bucket %p\n", buck); - w = bgp_encode_attrs(w, buck); - remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - (w-buf); - r_size = bgp_encode_prefixes(p, w, buck, remains); + a_size = bgp_encode_attrs(w+2, buck->eattrs, 1024); + put_u16(w, a_size); + w += a_size + 2; + r_size = bgp_encode_prefixes(p, w, buck, remains - a_size); w += r_size; break; } @@ -124,7 +126,94 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) static byte * bgp_create_update(struct bgp_conn *conn, byte *buf) { - return NULL; + struct bgp_proto *p = conn->bgp; + struct bgp_bucket *buck; + int size, is_ll; + int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; + byte *w, *tmp, *tstart; + ip_addr ip, ip_ll; + ea_list *ea; + eattr *nh; + neighbor *n; + + BGP_TRACE(D_PACKETS, "Sending UPDATE"); + put_u16(buf, 0); + w = buf+4; + + if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) + { + DBG("Withdrawn routes:\n"); + tmp = bgp_attach_attr(&ea, bgp_linpool, BA_MP_UNREACH_NLRI, remains-8); + *tmp++ = 0; + *tmp++ = BGP_AF_IPV6; + *tmp++ = 1; + ea->attrs[0].u.ptr->length = bgp_encode_prefixes(p, tmp, buck, remains-11); + size = bgp_encode_attrs(w, ea, remains); + w += size; + remains -= size; + } + + if (remains >= 2048) + { + while ((buck = (struct bgp_bucket *) HEAD(p->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); + continue; + } + DBG("Processing bucket %p\n", buck); + size = bgp_encode_attrs(w, buck->eattrs, 1024); + w += size; + remains -= size; + tstart = tmp = bgp_attach_attr(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8); + *tmp++ = 0; + *tmp++ = BGP_AF_IPV6; + *tmp++ = 1; + nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); + ASSERT(nh); + ip = *(ip_addr *) nh->u.ptr->data; + if (ipa_equal(ip, p->local_addr)) + is_ll = 1; + else + { + n = neigh_find(&p->p, &ip, 0); + if (n && n->iface == p->neigh->iface) + is_ll = 1; + else + is_ll = 0; + } + if (is_ll) + { + *tmp++ = 32; + ip_ll = ipa_or(ipa_build(0xfe80,0,0,0), ipa_and(ip, ipa_build(0,0,~0,~0))); + ipa_hton(ip); + memcpy(tmp, &ip, 16); + ipa_hton(ip_ll); + memcpy(tmp+16, &ip_ll, 16); + tmp += 32; + } + else + { + *tmp++ = 16; + ipa_hton(ip); + memcpy(tmp, &ip, 16); + tmp += 16; + } + *tmp++ = 0; /* No SNPA information */ + tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1)); + ea->attrs[0].u.ptr->length = tmp - tstart; + w += bgp_encode_attrs(w, ea, remains); + break; + } + } + + size = w - (buf+4); + put_u16(buf+2, size); + lp_flush(bgp_linpool); + return size ? w : NULL; } #endif @@ -476,16 +565,7 @@ bgp_do_rx_update(struct bgp_conn *conn, /* 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); + memcpy(bgp_attach_attr(&a0->eattrs, bgp_linpool, BA_NEXT_HOP, 16), x+1, 16); len -= *x + 1; x += *x + 1; |