summaryrefslogtreecommitdiffstats
path: root/proto/bgp
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2009-04-29 18:58:24 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2009-04-29 18:58:24 +0200
commit4827b69ff43661f4f34d437999b0edaac76f7355 (patch)
tree4e36a37b04fa4ba6bf363887fe4ecc65016e7fee /proto/bgp
parentad440a570b37e8674ef35f3a18df48f0eb2579eb (diff)
downloadbird-4827b69ff43661f4f34d437999b0edaac76f7355.tar
bird-4827b69ff43661f4f34d437999b0edaac76f7355.zip
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.
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c9
-rw-r--r--proto/bgp/bgp.c6
-rw-r--r--proto/bgp/bgp.h11
-rw-r--r--proto/bgp/packets.c51
4 files changed, 54 insertions, 23 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 9a247de..585bfd1 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 a0bc892..cbc699b 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 83ed9c7..8477f9e 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 93cabbe..f244e3c 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;