Parsing of BGP attributes.
This commit is contained in:
parent
08732b7178
commit
c00d31befa
5 changed files with 254 additions and 23 deletions
|
@ -6,10 +6,220 @@
|
||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define LOCAL_DEBUG
|
||||||
|
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
#include "nest/iface.h"
|
#include "nest/iface.h"
|
||||||
#include "nest/protocol.h"
|
#include "nest/protocol.h"
|
||||||
#include "nest/route.h"
|
#include "nest/route.h"
|
||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
|
#include "lib/resource.h"
|
||||||
|
#include "lib/string.h"
|
||||||
|
#include "lib/unaligned.h"
|
||||||
|
|
||||||
#include "bgp.h"
|
#include "bgp.h"
|
||||||
|
|
||||||
|
static int bgp_check_origin(byte *a, int len)
|
||||||
|
{
|
||||||
|
if (len > 2)
|
||||||
|
return 6;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bgp_check_path(byte *a, int len)
|
||||||
|
{
|
||||||
|
while (len)
|
||||||
|
{
|
||||||
|
DBG("Path segment %02x %02x\n", a[0], a[1]);
|
||||||
|
if (len < 2 ||
|
||||||
|
a[0] != BGP_PATH_AS_SET && a[0] != BGP_PATH_AS_SEQUENCE ||
|
||||||
|
2*a[1] + 2 > len)
|
||||||
|
return 11;
|
||||||
|
len -= 2*a[1] + 2;
|
||||||
|
a += 2*a[1] + 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bgp_check_next_hop(byte *a, int len)
|
||||||
|
{
|
||||||
|
ip_addr addr;
|
||||||
|
|
||||||
|
memcpy(&addr, a, len);
|
||||||
|
if (ipa_classify(ipa_ntoh(addr)) & IADDR_HOST)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct attr_desc {
|
||||||
|
int expected_length;
|
||||||
|
int expected_flags;
|
||||||
|
int type;
|
||||||
|
int (*validate)(byte *attr, int len);
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attr_desc bgp_attr_table[] = {
|
||||||
|
{ -1, 0, 0, NULL }, /* Undefined */
|
||||||
|
{ 1, BAF_TRANSITIVE, EAF_TYPE_INT, bgp_check_origin }, /* BA_ORIGIN */
|
||||||
|
{ -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, bgp_check_path }, /* BA_AS_PATH */
|
||||||
|
{ 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, bgp_check_next_hop }, /* BA_NEXT_HOP */
|
||||||
|
{ 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_MULTI_EXIT_DISC */
|
||||||
|
{ 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_LOCAL_PREF */
|
||||||
|
{ 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_ATOMIC_AGGR */
|
||||||
|
{ 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_AGGREGATOR */
|
||||||
|
#if 0
|
||||||
|
/* FIXME: Handle community lists */
|
||||||
|
{ 0, 0 }, /* BA_COMMUNITY */
|
||||||
|
{ 0, 0 }, /* BA_ORIGINATOR_ID */
|
||||||
|
{ 0, 0 }, /* BA_CLUSTER_LIST */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP };
|
||||||
|
|
||||||
|
struct rta *
|
||||||
|
bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool)
|
||||||
|
{
|
||||||
|
struct bgp_proto *bgp = conn->bgp;
|
||||||
|
rta *a = lp_alloc(pool, sizeof(struct rta));
|
||||||
|
unsigned int flags, code, l, errcode, i, type;
|
||||||
|
byte *z, *attr_start;
|
||||||
|
byte seen[256/8];
|
||||||
|
eattr *e;
|
||||||
|
ea_list *ea;
|
||||||
|
struct adata *ad;
|
||||||
|
neighbor *neigh;
|
||||||
|
ip_addr nexthop;
|
||||||
|
|
||||||
|
a->proto = &bgp->p;
|
||||||
|
a->source = RTS_BGP;
|
||||||
|
a->scope = SCOPE_UNIVERSE;
|
||||||
|
a->cast = RTC_UNICAST;
|
||||||
|
a->dest = RTD_ROUTER;
|
||||||
|
a->flags = 0;
|
||||||
|
a->aflags = 0;
|
||||||
|
a->from = bgp->cf->remote_ip;
|
||||||
|
a->eattrs = NULL;
|
||||||
|
|
||||||
|
/* Parse the attributes */
|
||||||
|
bzero(seen, sizeof(seen));
|
||||||
|
DBG("BGP: Parsing attributes\n");
|
||||||
|
while (len)
|
||||||
|
{
|
||||||
|
if (len < 2)
|
||||||
|
goto malformed;
|
||||||
|
attr_start = attr;
|
||||||
|
flags = *attr++;
|
||||||
|
code = *attr++;
|
||||||
|
len -= 2;
|
||||||
|
if (flags & BAF_EXT_LEN)
|
||||||
|
{
|
||||||
|
if (len < 2)
|
||||||
|
goto malformed;
|
||||||
|
l = get_u16(attr);
|
||||||
|
attr += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (len < 1)
|
||||||
|
goto malformed;
|
||||||
|
l = *attr++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
if (l > len)
|
||||||
|
goto malformed;
|
||||||
|
len -= l;
|
||||||
|
z = attr;
|
||||||
|
attr += l;
|
||||||
|
DBG("Attr %02x %02x %d\n", code, flags, l);
|
||||||
|
if (seen[code/8] & (1 << (code%8)))
|
||||||
|
goto malformed;
|
||||||
|
seen[code/8] |= (1 << (code%8));
|
||||||
|
if (code && code < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
|
||||||
|
{
|
||||||
|
struct attr_desc *desc = &bgp_attr_table[code];
|
||||||
|
if (desc->expected_length >= 0 && desc->expected_length != (int) l)
|
||||||
|
{ errcode = 5; goto err; }
|
||||||
|
if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
|
||||||
|
{ errcode = 4; goto err; }
|
||||||
|
if (desc->validate && (errcode = desc->validate(z, l)))
|
||||||
|
goto err;
|
||||||
|
type = desc->type;
|
||||||
|
}
|
||||||
|
else /* Unknown attribute */
|
||||||
|
{ /* FIXME: Send partial bit when forwarding */
|
||||||
|
if (!(flags & BAF_OPTIONAL))
|
||||||
|
{ errcode = 2; goto err; }
|
||||||
|
type = EAF_TYPE_OPAQUE;
|
||||||
|
}
|
||||||
|
ea = lp_alloc(pool, sizeof(struct ea_list) + sizeof(struct eattr));
|
||||||
|
ea->next = a->eattrs;
|
||||||
|
a->eattrs = ea;
|
||||||
|
ea->flags = 0;
|
||||||
|
ea->count = 1;
|
||||||
|
ea->attrs[0].id = EA_CODE(EAP_BGP, code);
|
||||||
|
ea->attrs[0].flags = flags;
|
||||||
|
ea->attrs[0].type = type;
|
||||||
|
if (type & EAF_EMBEDDED)
|
||||||
|
ad = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ad = lp_alloc(pool, sizeof(struct adata) + l);
|
||||||
|
ea->attrs[0].u.ptr = ad;
|
||||||
|
ad->length = l;
|
||||||
|
memcpy(ad->data, z, l);
|
||||||
|
}
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EAF_TYPE_ROUTER_ID:
|
||||||
|
case EAF_TYPE_INT:
|
||||||
|
ea->attrs[0].u.data = get_u32(z);
|
||||||
|
break;
|
||||||
|
case EAF_TYPE_IP_ADDRESS:
|
||||||
|
*(ip_addr *)ad->data = ipa_ntoh(*(ip_addr *)ad->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if all mandatory attributes are present */
|
||||||
|
for(i=0; i < sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++)
|
||||||
|
{
|
||||||
|
code = bgp_mandatory_attrs[i];
|
||||||
|
if (!(seen[code/8] & (1 << (code%8))))
|
||||||
|
{
|
||||||
|
bgp_error(conn, 3, 3, code, 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
neigh = neigh_find(&bgp->p, &nexthop, 0);
|
||||||
|
if (!neigh)
|
||||||
|
{
|
||||||
|
if (bgp->cf->multihop)
|
||||||
|
neigh = neigh_find(&bgp->p, &bgp->cf->multihop_via, 0);
|
||||||
|
else
|
||||||
|
neigh = neigh_find(&bgp->p, &bgp->cf->remote_ip, 0);
|
||||||
|
}
|
||||||
|
if (!neigh || !neigh->iface)
|
||||||
|
{
|
||||||
|
DBG("BGP: No route to peer!\n"); /* FIXME */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
a->gw = neigh->addr;
|
||||||
|
a->iface = neigh->iface;
|
||||||
|
return rta_lookup(a);
|
||||||
|
|
||||||
|
malformed:
|
||||||
|
bgp_error(conn, 3, 1, len, 0);
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
err:
|
||||||
|
bgp_error(conn, 3, errcode, code, 0); /* FIXME: Return attribute data! */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -96,6 +96,8 @@ bgp_close_conn(struct bgp_conn *conn)
|
||||||
{
|
{
|
||||||
bgp_close(p);
|
bgp_close(p);
|
||||||
p->conn = NULL;
|
p->conn = NULL;
|
||||||
|
if (conn->error_flag) /* FIXME: Enable automatically? */
|
||||||
|
p->p.disabled = 1;
|
||||||
proto_notify_state(&p->p, PS_DOWN);
|
proto_notify_state(&p->p, PS_DOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
#ifndef _BIRD_BGP_H_
|
#ifndef _BIRD_BGP_H_
|
||||||
#define _BIRD_BGP_H_
|
#define _BIRD_BGP_H_
|
||||||
|
|
||||||
|
struct linpool;
|
||||||
|
|
||||||
struct bgp_config {
|
struct bgp_config {
|
||||||
struct proto_config c;
|
struct proto_config c;
|
||||||
unsigned int local_as, remote_as;
|
unsigned int local_as, remote_as;
|
||||||
ip_addr remote_ip;
|
ip_addr remote_ip;
|
||||||
int multihop; /* Number of hops if multihop */
|
int multihop; /* Number of hops if multihop */
|
||||||
|
ip_addr multihop_via; /* Multihop: address to route to */
|
||||||
unsigned connect_retry_time;
|
unsigned connect_retry_time;
|
||||||
unsigned hold_time, initial_hold_time;
|
unsigned hold_time, initial_hold_time;
|
||||||
unsigned keepalive_time;
|
unsigned keepalive_time;
|
||||||
|
@ -63,6 +66,8 @@ void bgp_close_conn(struct bgp_conn *c);
|
||||||
|
|
||||||
/* attrs.c */
|
/* attrs.c */
|
||||||
|
|
||||||
|
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool);
|
||||||
|
|
||||||
/* packets.c */
|
/* packets.c */
|
||||||
|
|
||||||
void bgp_schedule_packet(struct bgp_conn *conn, int type);
|
void bgp_schedule_packet(struct bgp_conn *conn, int type);
|
||||||
|
@ -88,7 +93,7 @@ int bgp_rx(struct birdsock *sk, int size);
|
||||||
#define BA_AS_PATH 0x02 /* WM */
|
#define BA_AS_PATH 0x02 /* WM */
|
||||||
#define BA_NEXT_HOP 0x03 /* WM */
|
#define BA_NEXT_HOP 0x03 /* WM */
|
||||||
#define BA_MULTI_EXIT_DISC 0x04 /* ON */
|
#define BA_MULTI_EXIT_DISC 0x04 /* ON */
|
||||||
#define BA_LOCAL_PREF 0x05 /* WM */
|
#define BA_LOCAL_PREF 0x05 /* WD */
|
||||||
#define BA_ATOMIC_AGGR 0x06 /* WD */
|
#define BA_ATOMIC_AGGR 0x06 /* WD */
|
||||||
#define BA_AGGREGATOR 0x07 /* OT */
|
#define BA_AGGREGATOR 0x07 /* OT */
|
||||||
#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */
|
#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */
|
||||||
|
@ -102,6 +107,9 @@ int bgp_rx(struct birdsock *sk, int size);
|
||||||
#define BA_MP_UNREACH_NLRI 0x0f
|
#define BA_MP_UNREACH_NLRI 0x0f
|
||||||
#define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */
|
#define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */
|
||||||
|
|
||||||
|
#define BGP_PATH_AS_SET 1 /* Types of path segments */
|
||||||
|
#define BGP_PATH_AS_SEQUENCE 2
|
||||||
|
|
||||||
/* BGP states */
|
/* BGP states */
|
||||||
|
|
||||||
#define BS_IDLE 0
|
#define BS_IDLE 0
|
||||||
|
|
|
@ -15,7 +15,7 @@ CF_HDR
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
||||||
MULTIHOP, STARTUP)
|
MULTIHOP, STARTUP, VIA)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ bgp_proto:
|
||||||
| bgp_proto STARTUP HOLD TIME NUM ';' { BGP_CFG->initial_hold_time = $5; }
|
| bgp_proto STARTUP HOLD TIME NUM ';' { BGP_CFG->initial_hold_time = $5; }
|
||||||
| bgp_proto CONNECT RETRY TIME NUM ';' { BGP_CFG->connect_retry_time = $5; }
|
| bgp_proto CONNECT RETRY TIME NUM ';' { BGP_CFG->connect_retry_time = $5; }
|
||||||
| bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; }
|
| bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; }
|
||||||
| bgp_proto MULTIHOP NUM ';' { BGP_CFG->multihop = $3; }
|
| bgp_proto MULTIHOP NUM VIA IPA ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; }
|
||||||
;
|
;
|
||||||
|
|
||||||
CF_CODE
|
CF_CODE
|
||||||
|
|
|
@ -218,24 +218,26 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
|
||||||
#define DECODE_PREFIX(pp, ll) do { \
|
#define DECODE_PREFIX(pp, ll) do { \
|
||||||
int b = *pp++; \
|
int b = *pp++; \
|
||||||
int q; \
|
int q; \
|
||||||
ip_addr temp; \
|
|
||||||
ll--; \
|
ll--; \
|
||||||
if (b > BITS_PER_IP_ADDRESS) { bgp_error(conn, 3, 10, b, 0); return; } \
|
if (b > BITS_PER_IP_ADDRESS) { bgp_error(conn, 3, 10, b, 0); return; } \
|
||||||
q = (b+7) / 8; \
|
q = (b+7) / 8; \
|
||||||
if (ll < q) goto too_small; \
|
if (ll < q) goto malformed; \
|
||||||
memcpy(&temp, pp, q); \
|
memcpy(&prefix, pp, q); \
|
||||||
pp += q; \
|
pp += q; \
|
||||||
ll -= q; \
|
ll -= q; \
|
||||||
n.n.prefix = ipa_and(ipa_ntoh(temp), ipa_mkmask(b)); \
|
prefix = ipa_and(ipa_ntoh(prefix), ipa_mkmask(b)); \
|
||||||
n.n.pxlen = b; \
|
pxlen = b; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
/* FIXME: Check validity of prefixes */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
|
bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
|
||||||
{
|
{
|
||||||
|
struct bgp_proto *bgp = conn->bgp;
|
||||||
byte *withdrawn, *attrs, *nlri;
|
byte *withdrawn, *attrs, *nlri;
|
||||||
int withdrawn_len, attr_len, nlri_len;
|
ip_addr prefix;
|
||||||
net n;
|
int withdrawn_len, attr_len, nlri_len, pxlen;
|
||||||
|
net *n;
|
||||||
rte e;
|
rte e;
|
||||||
|
|
||||||
DBG("BGP: UPDATE\n");
|
DBG("BGP: UPDATE\n");
|
||||||
|
@ -246,45 +248,54 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
|
||||||
/* Find parts of the packet and check sizes */
|
/* Find parts of the packet and check sizes */
|
||||||
if (len < 23)
|
if (len < 23)
|
||||||
{
|
{
|
||||||
too_small:
|
|
||||||
bgp_error(conn, 1, 2, len, 2);
|
bgp_error(conn, 1, 2, len, 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
withdrawn = pkt + 21;
|
withdrawn = pkt + 21;
|
||||||
withdrawn_len = get_u16(pkt + 19);
|
withdrawn_len = get_u16(pkt + 19);
|
||||||
if (withdrawn_len + 23 > len)
|
if (withdrawn_len + 23 > len)
|
||||||
goto too_small;
|
{
|
||||||
|
malformed:
|
||||||
|
bgp_error(conn, 3, 1, len, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
attrs = withdrawn + withdrawn_len + 2;
|
attrs = withdrawn + withdrawn_len + 2;
|
||||||
attr_len = get_u16(attrs - 2);
|
attr_len = get_u16(attrs - 2);
|
||||||
if (withdrawn_len + attr_len + 23 > len)
|
if (withdrawn_len + attr_len + 23 > len)
|
||||||
goto too_small;
|
goto malformed;
|
||||||
nlri = attrs + attr_len;
|
nlri = attrs + attr_len;
|
||||||
nlri_len = len - withdrawn_len - attr_len - 23;
|
nlri_len = len - withdrawn_len - attr_len - 23;
|
||||||
if (!attr_len && nlri_len)
|
if (!attr_len && nlri_len)
|
||||||
goto too_small;
|
goto malformed;
|
||||||
DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
|
DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
|
||||||
|
|
||||||
/* Withdraw routes */
|
/* Withdraw routes */
|
||||||
while (withdrawn_len)
|
while (withdrawn_len)
|
||||||
{
|
{
|
||||||
DECODE_PREFIX(withdrawn, withdrawn_len);
|
DECODE_PREFIX(withdrawn, withdrawn_len);
|
||||||
DBG("Withdraw %I/%d\n", n.n.prefix, n.n.pxlen);
|
DBG("Withdraw %I/%d\n", prefix, pxlen);
|
||||||
|
if (n = net_find(bgp->p.table, prefix, pxlen))
|
||||||
|
rte_update(bgp->p.table, n, &bgp->p, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nlri_len)
|
if (nlri_len)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
rta *a = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool);
|
rta *a = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool);
|
||||||
if (a)
|
if (!a)
|
||||||
#endif
|
return;
|
||||||
|
while (nlri_len)
|
||||||
{
|
{
|
||||||
while (nlri_len)
|
rte *e;
|
||||||
{
|
DECODE_PREFIX(nlri, nlri_len); /* FIXME: Uncache rta ! */
|
||||||
DECODE_PREFIX(nlri, nlri_len);
|
DBG("Add %I/%d\n", prefix, pxlen);
|
||||||
DBG("Add %I/%d\n", n.n.prefix, n.n.pxlen);
|
e = rte_get_temp(a);
|
||||||
}
|
n = net_get(bgp->p.table, prefix, pxlen);
|
||||||
|
e->net = n;
|
||||||
|
e->pflags = 0;
|
||||||
|
rte_update(bgp->p.table, n, &bgp->p, e);
|
||||||
}
|
}
|
||||||
lp_flush(bgp_linpool);
|
lp_flush(bgp_linpool);
|
||||||
|
rta_free(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue