summaryrefslogtreecommitdiff
path: root/proto/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c20
-rw-r--r--proto/bgp/bgp.c15
-rw-r--r--proto/bgp/bgp.h12
-rw-r--r--proto/bgp/packets.c60
4 files changed, 72 insertions, 35 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index a015c2b3..8a849e73 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -786,18 +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)
- {
- if (ipa_nonzero(p->cf->source_addr))
- *(ip_addr *)z = p->cf->source_addr;
- else
- *(ip_addr *)z = p->local_addr;
- }
+ rta->dest != RTD_ROUTER ||
+ (!p->is_internal && (e->attrs->iface != p->neigh->iface)))
+ 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);
@@ -860,14 +855,15 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
}
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)))
+ if (a && !p->cf->next_hop_self && (p->is_internal || (!p->is_internal && e->attrs->iface == p->neigh->iface)))
{
/* Leave the original next hop attribute, will check later where does it point */
}
else
{
/* Need to create new one */
- bgp_attach_attr_ip(attrs, pool, BA_NEXT_HOP, p->local_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 41c8d533..cbc699bb 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -500,10 +500,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
DBG("BGP: Connecting\n");
s = sk_new(p->p.pool);
s->type = SK_TCP_ACTIVE;
- if (ipa_nonzero(p->cf->source_addr))
- s->saddr = p->cf->source_addr;
- else
- s->saddr = p->local_addr;
+ s->saddr = p->source_addr;
s->daddr = p->cf->remote_ip;
s->dport = BGP_PORT;
s->ttl = p->cf->multihop ? : 1;
@@ -609,17 +606,23 @@ static void
bgp_start_neighbor(struct bgp_proto *p)
{
p->local_addr = p->neigh->iface->addr->ip;
- DBG("BGP: local=%I remote=%I\n", p->local_addr, p->next_hop);
+ p->source_addr = ipa_nonzero(p->cf->source_addr) ? p->cf->source_addr : p->local_addr;
+
+ DBG("BGP: local=%I remote=%I\n", p->source_addr, p->next_hop);
#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 d5448a68..8477f9e5 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -79,6 +79,7 @@ struct bgp_proto {
ip_addr next_hop; /* Either the peer or multihop_via */
struct neighbor *neigh; /* Neighbor entry corresponding to next_hop */
ip_addr local_addr; /* Address of the local end of the link to next_hop */
+ ip_addr source_addr; /* Address used as advertised next hop, usually local_addr */
struct event *event; /* Event for respawning and shutting process */
struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */
unsigned int hash_size, hash_count, hash_limit;
@@ -147,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 1370ee70..27adc166 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,42 @@ 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;
- if (ipa_equal(ip, p->local_addr))
- {
- is_ll = 1;
- ip_ll = p->local_link;
- }
+
+ /* 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))
+ 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 can't use
+ * 'third party' next hop and we have to use local IP address
+ * as next hop. Sending original next hop address without
+ * link local address seems to be a natural way to solve that
+ * problem, but it is contrary to RFC 2545 and Quagga does not
+ * accept such routes.
+ */
+
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)));
+ if (ipa_nonzero(ipp[1]))
+ ip_ll = ipp[1];
+ else
+ {
+ ip = p->source_addr;
+ ip_ll = p->local_link;
+ }
}
}
- if (is_ll)
+
+ if (ipa_nonzero(ip_ll))
{
*tmp++ = 32;
ipa_hton(ip);
@@ -326,6 +342,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 +795,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;