Fixes BGP IPv6 link local next hop handling.
When sending 'third party' BGP update, Bird used bogus link local addresses instead of addresses it received before.
This commit is contained in:
parent
ad440a570b
commit
4827b69ff4
4 changed files with 54 additions and 23 deletions
|
@ -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);
|
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 ||
|
if (p->cf->next_hop_self ||
|
||||||
!p->is_internal ||
|
!p->is_internal ||
|
||||||
rta->dest != RTD_ROUTER)
|
rta->dest != RTD_ROUTER)
|
||||||
*(ip_addr *)z = p->source_addr;
|
set_next_hop(z, p->source_addr);
|
||||||
else
|
else
|
||||||
*(ip_addr *)z = e->attrs->gw;
|
set_next_hop(z, e->attrs->gw);
|
||||||
|
|
||||||
bgp_set_attr(ea->attrs+3, BA_LOCAL_PREF, 0);
|
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
|
else
|
||||||
{
|
{
|
||||||
/* Need to create new one */
|
/* 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)
|
if (rr)
|
||||||
|
|
|
@ -612,13 +612,17 @@ bgp_start_neighbor(struct bgp_proto *p)
|
||||||
#ifdef IPV6
|
#ifdef IPV6
|
||||||
{
|
{
|
||||||
struct ifa *a;
|
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)
|
WALK_LIST(a, p->neigh->iface->addrs)
|
||||||
if (a->scope == SCOPE_LINK)
|
if (a->scope == SCOPE_LINK)
|
||||||
{
|
{
|
||||||
p->local_link = a->ip;
|
p->local_link = a->ip;
|
||||||
break;
|
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);
|
DBG("BGP: Selected link-level address %I\n", p->local_link);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -148,6 +148,17 @@ void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code
|
||||||
|
|
||||||
/* attrs.c */
|
/* 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);
|
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);
|
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);
|
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory);
|
||||||
|
|
|
@ -234,10 +234,10 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||||
{
|
{
|
||||||
struct bgp_proto *p = conn->bgp;
|
struct bgp_proto *p = conn->bgp;
|
||||||
struct bgp_bucket *buck;
|
struct bgp_bucket *buck;
|
||||||
int size, is_ll;
|
int size;
|
||||||
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
|
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
|
||||||
byte *w, *tmp, *tstart;
|
byte *w, *tmp, *tstart;
|
||||||
ip_addr ip, ip_ll;
|
ip_addr *ipp, ip, ip_ll;
|
||||||
ea_list *ea;
|
ea_list *ea;
|
||||||
eattr *nh;
|
eattr *nh;
|
||||||
neighbor *n;
|
neighbor *n;
|
||||||
|
@ -291,26 +291,31 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||||
*tmp++ = 1;
|
*tmp++ = 1;
|
||||||
nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
|
nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
|
||||||
ASSERT(nh);
|
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))
|
if (ipa_equal(ip, p->source_addr))
|
||||||
{
|
ip_ll = p->local_link;
|
||||||
is_ll = 1;
|
|
||||||
ip_ll = p->local_link;
|
|
||||||
}
|
|
||||||
else
|
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);
|
n = neigh_find(&p->p, &ip, 0);
|
||||||
if (n && n->iface == p->neigh->iface)
|
if (n && n->iface == p->neigh->iface)
|
||||||
{
|
ip_ll = ipp[1];
|
||||||
/* 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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (is_ll)
|
|
||||||
|
if (ipa_nonzero(ip_ll))
|
||||||
{
|
{
|
||||||
*tmp++ = 32;
|
*tmp++ = 32;
|
||||||
ipa_hton(ip);
|
ipa_hton(ip);
|
||||||
|
@ -326,6 +331,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||||
memcpy(tmp, &ip, 16);
|
memcpy(tmp, &ip, 16);
|
||||||
tmp += 16;
|
tmp += 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
*tmp++ = 0; /* No SNPA information */
|
*tmp++ = 0; /* No SNPA information */
|
||||||
tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1));
|
tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1));
|
||||||
ea->attrs[0].u.ptr->length = tmp - tstart;
|
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)
|
if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2)
|
||||||
goto bad;
|
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);
|
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 */
|
/* Also ignore one reserved byte */
|
||||||
len -= *x + 2;
|
len -= *x + 2;
|
||||||
|
|
Loading…
Reference in a new issue