summaryrefslogtreecommitdiff
path: root/proto/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c83
-rw-r--r--proto/bgp/bgp.h8
-rw-r--r--proto/bgp/packets.c234
3 files changed, 261 insertions, 64 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 40130c63..3d79efd9 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -22,7 +22,11 @@
#include "bgp.h"
-static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP };
+static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH
+#ifndef IPV6
+,BA_NEXT_HOP
+#endif
+};
struct attr_desc {
char *name;
@@ -69,6 +73,9 @@ bgp_check_path(struct bgp_proto *p, byte *a, int len)
static int
bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
{
+#ifdef IPV6
+ return -1;
+#else
ip_addr addr;
memcpy(&addr, a, len);
@@ -77,31 +84,61 @@ bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
return 0;
else
return 8;
+#endif
+}
+
+static int
+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
+}
+
+static int
+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
}
static struct attr_desc bgp_attr_table[] = {
- { NULL, -1, 0, 0, 0, /* Undefined */
+ { NULL, -1, 0, 0, 0, /* Undefined */
NULL, NULL },
- { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */
+ { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */
bgp_check_origin, bgp_format_origin },
- { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */
+ { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */
bgp_check_path, NULL },
- { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */
+ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */
bgp_check_next_hop, NULL },
- { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_MULTI_EXIT_DISC */
+ { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_MULTI_EXIT_DISC */
NULL, NULL },
- { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */
+ { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */
NULL, NULL },
- { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */
+ { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */
NULL, NULL },
{ "aggregator", 6, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */
NULL, NULL },
- { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
+ { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
NULL, NULL },
-#if 0
- { 0, 0 }, /* BA_ORIGINATOR_ID */
- { 0, 0 }, /* BA_CLUSTER_LIST */
-#endif
+ { NULL, }, /* BA_ORIGINATOR_ID */
+ { NULL, }, /* BA_CLUSTER_LIST */
+ { NULL, }, /* BA_DPA */
+ { NULL, }, /* BA_ADVERTISER */
+ { NULL, }, /* BA_RCID_PATH */
+ { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */
+ bgp_check_reach_nlri, NULL },
+ { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
+ bgp_check_unreach_nlri, NULL },
};
#define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name)
@@ -708,8 +745,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
eattr *e;
ea_list *ea;
struct adata *ad;
- neighbor *neigh;
- ip_addr nexthop;
a->proto = &bgp->p;
a->source = RTS_BGP;
@@ -820,6 +855,11 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
}
}
+#ifdef IPV6
+ if (seen[BA_MP_REACH_NLRI / 8] & (1 << (BA_MP_REACH_NLRI % 8)))
+ mandatory = 1;
+#endif
+
/* Check if all mandatory attributes are present */
if (mandatory)
{
@@ -855,19 +895,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
ea->attrs[0].type = EAF_TYPE_INT;
ea->attrs[0].u.data = 0;
}
-
- /* Fill in the remaining rta fields */
- e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
- ASSERT(e);
- nexthop = *(ip_addr *) e->u.ptr->data;
- if (ipa_equal(nexthop, bgp->local_addr))
- {
- DBG("BGP: Loop!\n");
- return NULL;
- }
- neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh;
- a->gw = neigh->addr;
- a->iface = neigh->iface;
return a;
malformed:
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index a9f3413c..37f29792 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -72,6 +72,10 @@ struct bgp_proto {
struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */
unsigned startup_delay; /* Time to delay protocol startup by due to errors */
bird_clock_t last_connect; /* Time of last connect attempt */
+#ifdef IPV6
+ byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */
+ unsigned mp_reach_len, mp_unreach_len;
+#endif
};
struct bgp_prefix {
@@ -181,4 +185,8 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco
#define ORIGIN_EGP 1
#define ORIGIN_INCOMPLETE 2
+/* Address families */
+
+#define BGP_AF_IPV6 2
+
#endif
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 52e93510..45397fbf 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -68,6 +68,8 @@ bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsig
return w - start;
}
+#ifndef IPV6 /* IPv4 version */
+
static byte *
bgp_create_update(struct bgp_conn *conn, byte *buf)
{
@@ -117,6 +119,16 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
return (wd_size || r_size) ? w : NULL;
}
+#else /* IPv6 version */
+
+static byte *
+bgp_create_update(struct bgp_conn *conn, byte *buf)
+{
+ return NULL;
+}
+
+#endif
+
static void
bgp_create_header(byte *buf, unsigned int len, unsigned int type)
{
@@ -321,9 +333,9 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
int b = *pp++; \
int q; \
ll--; \
- if (b > BITS_PER_IP_ADDRESS) { bgp_error(conn, 3, 10, NULL, 0); return; } \
+ if (b > BITS_PER_IP_ADDRESS) { err=10; goto bad; } \
q = (b+7) / 8; \
- if (ll < q) goto malformed; \
+ if (ll < q) { err=1; goto bad; } \
memcpy(&prefix, pp, q); \
pp += q; \
ll -= q; \
@@ -332,42 +344,40 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
pxlen = b; \
} while (0)
+static inline int
+bgp_get_nexthop(struct bgp_proto *bgp, rta *a)
+{
+ neighbor *neigh;
+ ip_addr nexthop;
+ struct eattr *nh = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
+ ASSERT(nh);
+ nexthop = *(ip_addr *) nh->u.ptr->data;
+ if (ipa_equal(nexthop, bgp->local_addr))
+ {
+ DBG("BGP: Loop!\n");
+ return 0;
+ }
+ neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh;
+ a->gw = neigh->addr;
+ a->iface = neigh->iface;
+ return 1;
+}
+
+#ifndef IPV6 /* IPv4 version */
+
static void
-bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
+bgp_do_rx_update(struct bgp_conn *conn,
+ byte *withdrawn, int withdrawn_len,
+ byte *nlri, int nlri_len,
+ byte *attrs, int attr_len)
{
struct bgp_proto *p = conn->bgp;
- byte *withdrawn, *attrs, *nlri;
+ rta *a0;
+ rta *a = NULL;
ip_addr prefix;
- int withdrawn_len, attr_len, nlri_len, pxlen;
net *n;
rte e;
- rta *a0;
- rta *a = NULL;
-
- BGP_TRACE(D_PACKETS, "Got UPDATE");
- if (conn->state != BS_ESTABLISHED)
- { bgp_error(conn, 5, 0, NULL, 0); return; }
- bgp_start_timer(conn->hold_timer, conn->hold_time);
-
- /* Find parts of the packet and check sizes */
- if (len < 23)
- {
- bgp_error(conn, 1, 2, pkt+16, 2);
- return;
- }
- withdrawn = pkt + 21;
- withdrawn_len = get_u16(pkt + 19);
- if (withdrawn_len + 23 > len)
- goto malformed;
- attrs = withdrawn + withdrawn_len + 2;
- attr_len = get_u16(attrs - 2);
- if (withdrawn_len + attr_len + 23 > len)
- goto malformed;
- nlri = attrs + attr_len;
- nlri_len = len - withdrawn_len - attr_len - 23;
- if (!attr_len && nlri_len)
- goto malformed;
- DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
+ int err = 0, pxlen;
/* Withdraw routes */
while (withdrawn_len)
@@ -382,7 +392,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
return;
a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len);
- if (a0 && nlri_len)
+ if (a0 && nlri_len && bgp_get_nexthop(p, a0))
{
a = rta_lookup(a0);
while (nlri_len)
@@ -396,14 +406,166 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
e->pflags = 0;
rte_update(p->p.table, n, &p->p, e);
}
- rta_free(a);
}
- lp_flush(bgp_linpool);
+bad:
+ if (a)
+ rta_free(a);
+ if (err)
+ bgp_error(conn, 3, err, NULL, 0);
return;
+}
-malformed:
+#else /* IPv6 version */
+
+#define DO_NLRI(name) \
+ start = x = p->name##_start; \
+ len = len0 = p->name##_len; \
+ if (len) \
+ { \
+ if (len < 3) goto bad; \
+ af = get_u16(x); \
+ sub = x[2]; \
+ x += 3; \
+ len -= 3; \
+ DBG("\tNLRI AF=%d sub=%d len=%d\n", af, sub, len);\
+ } \
+ else \
+ af = 0; \
+ if (af == BGP_AF_IPV6)
+
+static void
+bgp_do_rx_update(struct bgp_conn *conn,
+ byte *withdrawn, int withdrawn_len,
+ byte *nlri, int nlri_len,
+ byte *attrs, int attr_len)
+{
+ struct bgp_proto *p = conn->bgp;
+ byte *start, *x;
+ int len, len0;
+ unsigned af, sub;
+ rta *a0;
+ rta *a = NULL;
+ ip_addr prefix;
+ net *n;
+ rte e;
+ int err = 0, pxlen;
+
+ p->mp_reach_len = 0;
+ p->mp_unreach_len = 0;
+ a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0);
+ if (!a0)
+ return;
+
+ DO_NLRI(mp_unreach)
+ {
+ while (len)
+ {
+ DECODE_PREFIX(x, len);
+ DBG("Withdraw %I/%d\n", prefix, pxlen);
+ if (n = net_find(p->p.table, prefix, pxlen))
+ rte_update(p->p.table, n, &p->p, NULL);
+ }
+ }
+
+ DO_NLRI(mp_reach)
+ {
+ ea_list *e = lp_alloc(bgp_linpool, sizeof(ea_list) + sizeof(eattr));
+ struct adata *ad = lp_alloc(bgp_linpool, sizeof(struct adata) + 16);
+ int i;
+
+ /* 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);
+ len -= *x + 1;
+ x += *x + 1;
+
+ /* Ignore SNPA info */
+ i = *x++;
+ while (i--)
+ {
+ if (len < 1 || len < 1 + *x)
+ goto bad;
+ len -= *x + 1;
+ x += *x + 1;
+ }
+
+ if (bgp_get_nexthop(p, a0))
+ {
+ a = rta_lookup(a0);
+ while (len)
+ {
+ rte *e;
+ DECODE_PREFIX(x, len);
+ DBG("Add %I/%d\n", prefix, pxlen);
+ e = rte_get_temp(rta_clone(a));
+ n = net_get(p->p.table, prefix, pxlen);
+ e->net = n;
+ e->pflags = 0;
+ rte_update(p->p.table, n, &p->p, e);
+ }
+ rta_free(a);
+ }
+ }
+
+ return;
+
+bad:
+ bgp_error(conn, 3, 9, start, len0);
if (a)
rta_free(a);
+ return;
+}
+
+#endif
+
+static void
+bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
+{
+ struct bgp_proto *p = conn->bgp;
+ byte *withdrawn, *attrs, *nlri;
+ int withdrawn_len, attr_len, nlri_len;
+
+ BGP_TRACE(D_PACKETS, "Got UPDATE");
+ if (conn->state != BS_ESTABLISHED)
+ { bgp_error(conn, 5, 0, NULL, 0); return; }
+ bgp_start_timer(conn->hold_timer, conn->hold_time);
+
+ /* Find parts of the packet and check sizes */
+ if (len < 23)
+ {
+ bgp_error(conn, 1, 2, pkt+16, 2);
+ return;
+ }
+ withdrawn = pkt + 21;
+ withdrawn_len = get_u16(pkt + 19);
+ if (withdrawn_len + 23 > len)
+ goto malformed;
+ attrs = withdrawn + withdrawn_len + 2;
+ attr_len = get_u16(attrs - 2);
+ if (withdrawn_len + attr_len + 23 > len)
+ goto malformed;
+ nlri = attrs + attr_len;
+ nlri_len = len - withdrawn_len - attr_len - 23;
+ if (!attr_len && nlri_len)
+ goto malformed;
+ DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
+
+ lp_flush(bgp_linpool);
+
+ bgp_do_rx_update(conn, withdrawn, withdrawn_len, nlri, nlri_len, attrs, attr_len);
+ return;
+
+malformed:
bgp_error(conn, 3, 1, NULL, 0);
}