diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 9a247dec..585bfd16 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -786,13 +786,13 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p put_u16(z+2, p->local_as); } - z = bgp_set_attr_wa(ea->attrs+2, pool, BA_NEXT_HOP, sizeof(ip_addr)); + z = bgp_set_attr_wa(ea->attrs+2, pool, BA_NEXT_HOP, NEXT_HOP_LENGTH); if (p->cf->next_hop_self || !p->is_internal || rta->dest != RTD_ROUTER) - *(ip_addr *)z = p->source_addr; + set_next_hop(z, p->source_addr); else - *(ip_addr *)z = e->attrs->gw; + set_next_hop(z, e->attrs->gw); bgp_set_attr(ea->attrs+3, BA_LOCAL_PREF, 0); @@ -862,7 +862,8 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p else { /* Need to create new one */ - bgp_attach_attr_ip(attrs, pool, BA_NEXT_HOP, p->source_addr); + byte *b = bgp_attach_attr_wa(attrs, pool, BA_NEXT_HOP, NEXT_HOP_LENGTH); + set_next_hop(b, p->source_addr); } if (rr) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index a0bc8923..cbc699bb 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -612,13 +612,17 @@ bgp_start_neighbor(struct bgp_proto *p) #ifdef IPV6 { struct ifa *a; - p->local_link = ipa_or(ipa_build(0xfe80,0,0,0), ipa_and(p->local_addr, ipa_build(0,0,~0,~0))); + p->local_link = IPA_NONE; WALK_LIST(a, p->neigh->iface->addrs) if (a->scope == SCOPE_LINK) { p->local_link = a->ip; break; } + + if (! ipa_nonzero(p->local_link)) + log(L_WARN "%s: Missing link local address on interface %s", p->p.name, p->neigh->iface->name); + DBG("BGP: Selected link-level address %I\n", p->local_link); } #endif diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 83ed9c75..8477f9e5 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -148,6 +148,17 @@ void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code /* attrs.c */ +/* Hack: although BA_NEXT_HOP attribute has type EAF_TYPE_IP_ADDRESS, in IPv6 + * we store two addesses in it - a global address and a link local address. + */ +#ifdef IPV6 +#define NEXT_HOP_LENGTH (2*sizeof(ip_addr)) +static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; ((ip_addr *) b)[1] = IPA_NONE; } +#else +#define NEXT_HOP_LENGTH sizeof(ip_addr) +static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; } +#endif + void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val); byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len); struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory); diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 93cabbec..f244e3c0 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -234,10 +234,10 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; - int size, is_ll; + int size; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; byte *w, *tmp, *tstart; - ip_addr ip, ip_ll; + ip_addr *ipp, ip, ip_ll; ea_list *ea; eattr *nh; neighbor *n; @@ -291,26 +291,31 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) *tmp++ = 1; nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); ASSERT(nh); - ip = *(ip_addr *) nh->u.ptr->data; - is_ll = 0; + + /* We have two addresses here in 'nh'. Really. */ + ipp = (ip_addr *) nh->u.ptr->data; + ip = ipp[0]; + ip_ll = IPA_NONE; + if (ipa_equal(ip, p->source_addr)) - { - is_ll = 1; - ip_ll = p->local_link; - } + ip_ll = p->local_link; else { + /* If we send a route with 'third party' next hop destinated + * in the same interface, we should also send a link local + * next hop address. We use the received one (stored in the + * other part of BA_NEXT_HOP eattr). If we didn't received + * it (for example it is a static route), we do not send link + * local next hop address. It is contrary to RFC 2545, but + * probably the only sane possibility. + */ + n = neigh_find(&p->p, &ip, 0); if (n && n->iface == p->neigh->iface) - { - /* FIXME: We are assuming the global scope addresses use the lower 64 bits - * as an interface identifier which hasn't necessarily to be true. - */ - is_ll = 1; - ip_ll = ipa_or(ipa_build(0xfe800000,0,0,0), ipa_and(ip, ipa_build(0,0,~0,~0))); - } + ip_ll = ipp[1]; } - if (is_ll) + + if (ipa_nonzero(ip_ll)) { *tmp++ = 32; ipa_hton(ip); @@ -326,6 +331,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) 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; @@ -778,9 +784,18 @@ bgp_do_rx_update(struct bgp_conn *conn, if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2) goto bad; - byte *nh = bgp_attach_attr_wa(&a0->eattrs, bgp_linpool, BA_NEXT_HOP, 16); + ip_addr *nh = (ip_addr *) bgp_attach_attr_wa(&a0->eattrs, bgp_linpool, BA_NEXT_HOP, NEXT_HOP_LENGTH); memcpy(nh, x+1, 16); - ipa_ntoh(*(ip_addr *)nh); + ipa_ntoh(nh[0]); + + /* We store received link local address in the other part of BA_NEXT_HOP eattr. */ + if (*x == 32) + { + memcpy(nh+1, x+17, 16); + ipa_ntoh(nh[1]); + } + else + nh[1] = IPA_NONE; /* Also ignore one reserved byte */ len -= *x + 2;