Implementation of 4B ASN support for BGP

This commit is contained in:
Ondrej Zajicek 2008-10-26 22:36:08 +01:00
parent 44cb1449ed
commit 11cb620266
9 changed files with 695 additions and 157 deletions

View file

@ -39,7 +39,6 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <v> set_atom fprefix fprefix_s fipa
%type <s> decls declsn one_decl function_params
%type <h> bgp_path
%type <i> bgp_one
CF_GRAMMAR
@ -273,14 +272,12 @@ switch_body: /* EMPTY */ { $$ = NULL; }
/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
bgp_one:
NUM { $$ = $1; }
| '?' { $$ = PM_ANY; }
;
bgp_path:
bgp_one { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = $1; }
| bgp_one bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; }
NUM { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = $1; $$->any = 0; }
| '?' { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = 0; $$->any = 1; }
| NUM bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; $$->any = 0; }
| '?' bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = 0; $$->any = 1; }
;
constant:

View file

@ -69,6 +69,30 @@ pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2)
}
}
static void
pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
{
byte *end = buf + size - 16;
while (p)
{
if (buf > end)
{
strcpy(buf, " ...");
return;
}
if (p->any)
buf += bsprintf(buf, "? ");
else
buf += bsprintf(buf, "%u ", p->val);
p = p->next;
}
*buf = 0;
}
/**
* val_compare - compare two values
* @v1: first value
@ -224,7 +248,7 @@ val_print(struct f_val v)
case T_ENUM: PRINTF( "(enum %x)%d", v.type, v.val.i ); break;
case T_PATH: as_path_format(v.val.ad, buf2, 1020); PRINTF( "(path %s)", buf2 ); break;
case T_CLIST: int_set_format(v.val.ad, buf2, 1020); PRINTF( "(clist %s)", buf2 ); break;
case T_PATH_MASK: debug( "(pathmask " ); { struct f_path_mask *p = v.val.path_mask; while (p) { debug("%d ", p->val); p=p->next; } debug(")" ); } break;
case T_PATH_MASK: pm_format(v.val.path_mask, buf2, 1020); PRINTF( "(pathmask %s)", buf2 ); break;
default: PRINTF( "[unknown type %x]", v.type );
#undef PRINTF
}

View file

@ -14,38 +14,139 @@
#include "lib/unaligned.h"
#include "lib/string.h"
struct adata *
as_path_prepend(struct linpool *pool, struct adata *olda, int as)
/* Global AS4 support, shared by all BGP instances.
* This specifies whether BA_AS_PATH attributes contain 2 or 4 B per ASN
*/
int bgp_as4_support = 1;
static void
put_as(byte *data, u32 as)
{
if (bgp_as4_support)
put_u32(data, as);
else if (as <= 0xFFFF)
put_u16(data, as);
else
bug("put_as: Try to put 32bit AS to 16bit AS Path");
}
static inline u32
get_as(byte *data)
{
return bgp_as4_support ? get_u32(data) : get_u16(data);
}
struct adata *
as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
{
int bs = bgp_as4_support ? 4 : 2;
struct adata *newa;
if (olda->length && olda->data[0] == AS_PATH_SEQUENCE &&
olda->data[1] < 255) /* Starting with sequence => just prepend the AS number */
if (olda->length && olda->data[0] == AS_PATH_SEQUENCE && olda->data[1] < 255)
/* Starting with sequence => just prepend the AS number */
{
newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 2);
newa->length = olda->length + 2;
newa->data[0] = 2;
int nl = olda->length + bs;
newa = lp_alloc(pool, sizeof(struct adata) + nl);
newa->length = nl;
newa->data[0] = AS_PATH_SEQUENCE;
newa->data[1] = olda->data[1] + 1;
memcpy(newa->data+4, olda->data+2, olda->length-2);
memcpy(newa->data + bs + 2, olda->data + 2, olda->length - 2);
}
else /* Create new path segment */
else /* Create new path segment */
{
newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 4);
newa->length = olda->length + 4;
newa->data[0] = 2;
int nl = olda->length + bs + 2;
newa = lp_alloc(pool, sizeof(struct adata) + nl);
newa->length = nl;
newa->data[0] = AS_PATH_SEQUENCE;
newa->data[1] = 1;
memcpy(newa->data+4, olda->data, olda->length);
memcpy(newa->data + bs + 2, olda->data, olda->length);
}
put_u16(newa->data+2, as);
put_as(newa->data + 2, as);
return newa;
}
int
as_path_convert_to_old(struct adata *path, byte *dst, int *new_used)
{
byte *src = path->data;
byte *src_end = src + path->length;
byte *dst_start = dst;
u32 as;
int i, n;
*new_used = 0;
while (src < src_end)
{
n = src[1];
*dst++ = *src++;
*dst++ = *src++;
for(i=0; i<n; i++)
{
as = get_u32(src);
if (as > 0xFFFF)
{
as = AS_TRANS;
*new_used = 1;
}
put_u16(dst, as);
src += 4;
dst += 2;
}
}
return dst - dst_start;
}
int
as_path_convert_to_new(struct adata *path, byte *dst, int req_as)
{
byte *src = path->data;
byte *src_end = src + path->length;
byte *dst_start = dst;
u32 as;
int i, t, n;
while ((src < src_end) && (req_as > 0))
{
t = *src++;
n = *src++;
if (t == AS_PATH_SEQUENCE)
{
if (n > req_as)
n = req_as;
req_as -= n;
}
else // t == AS_PATH_SET
req_as--;
*dst++ = t;
*dst++ = n;
for(i=0; i<n; i++)
{
as = get_u16(src);
put_u32(dst, as);
src += 2;
dst += 4;
}
}
return dst - dst_start;
}
void
as_path_format(struct adata *path, byte *buf, unsigned int size)
{
int bs = bgp_as4_support ? 4 : 2;
byte *p = path->data;
byte *e = p + path->length;
byte *end = buf + size - 8;
byte *end = buf + size - 16;
int sp = 1;
int l, isset;
@ -69,8 +170,8 @@ as_path_format(struct adata *path, byte *buf, unsigned int size)
{
if (!sp)
*buf++ = ' ';
buf += bsprintf(buf, "%d", get_u16(p));
p += 2;
buf += bsprintf(buf, "%u", get_as(p));
p += bs;
sp = 0;
}
if (isset)
@ -86,6 +187,7 @@ as_path_format(struct adata *path, byte *buf, unsigned int size)
int
as_path_getlen(struct adata *path)
{
int bs = bgp_as4_support ? 4 : 2;
int res = 0;
u8 *p = path->data;
u8 *q = p+path->length;
@ -95,8 +197,8 @@ as_path_getlen(struct adata *path)
{
switch (*p++)
{
case AS_PATH_SET: len = *p++; res++; p += 2*len; break;
case AS_PATH_SEQUENCE: len = *p++; res+=len; p += 2*len; break;
case AS_PATH_SET: len = *p++; res++; p += bs * len; break;
case AS_PATH_SEQUENCE: len = *p++; res += len; p += bs * len; break;
default: bug("as_path_getlen: Invalid path segment");
}
}
@ -104,9 +206,11 @@ as_path_getlen(struct adata *path)
}
int
as_path_get_first(struct adata *path)
as_path_get_first(struct adata *path, u32 *orig_as)
{
int res = -1;
int bs = bgp_as4_support ? 4 : 2;
int found = 0;
u32 res = 0;
u8 *p = path->data;
u8 *q = p+path->length;
int len;
@ -117,36 +221,70 @@ as_path_get_first(struct adata *path)
{
case AS_PATH_SET:
if (len = *p++)
res = get_u16(p);
p += 2*len;
{
found = 1;
res = get_as(p);
p += bs * len;
}
break;
case AS_PATH_SEQUENCE:
if (len = *p++)
res = get_u16(p+2*(len-1));
p += 2*len;
{
found = 1;
res = get_as(p + bs * (len - 1));
p += bs * len;
}
break;
default: bug("as_path_get_first: Invalid path segment");
}
}
return res;
*orig_as = res;
return found;
}
int
as_path_is_member(struct adata *path, u32 as)
{
int bs = bgp_as4_support ? 4 : 2;
u8 *p = path->data;
u8 *q = p+path->length;
int i, n;
while (p<q)
{
n = p[1];
p += 2;
for(i=0; i<n; i++)
{
if (get_as(p) == as)
return 1;
p += bs;
}
}
return 0;
}
#define MASK_PLUS do { mask = mask->next; if (!mask) return next == q; \
asterisk = (mask->val == PM_ANY); \
asterisk = mask->any; \
if (asterisk) { mask = mask->next; if (!mask) { return 1; } } \
} while(0)
int
as_path_match(struct adata *path, struct f_path_mask *mask)
{
int bs = bgp_as4_support ? 4 : 2;
int i;
int asterisk = 0;
u8 *p = path->data;
u8 *q = p+path->length;
int len;
u8 *next;
u32 as;
asterisk = (mask->val == PM_ANY);
asterisk = mask->any;
if (asterisk)
{ mask = mask->next; if (!mask) return 1; }
@ -156,20 +294,21 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
len = *p++;
{
u8 *p_save = p;
next = p_save + 2*len;
next = p_save + bs * len;
retry:
p = p_save;
for (i=0; i<len; i++) {
if (asterisk && (get_u16(p) == mask->val)) {
as = get_as(p);
if (asterisk && (as == mask->val)) {
MASK_PLUS;
goto retry;
}
if (!asterisk && (get_u16(p) == mask->val)) {
if (!asterisk && (as == mask->val)) {
p = next;
MASK_PLUS;
goto okay;
}
p+=2;
p += bs;
}
if (!asterisk)
return 0;
@ -180,15 +319,15 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
case AS_PATH_SEQUENCE:
len = *p++;
for (i=0; i<len; i++) {
next = p+2;
if (asterisk && (get_u16(p) == mask->val))
as = get_as(p);
if (asterisk && (as == mask->val))
MASK_PLUS;
else if (!asterisk) {
if (get_u16(p) != mask->val)
if (as != mask->val)
return 0;
MASK_PLUS;
}
p+=2;
p += bs;
}
break;

View file

@ -14,16 +14,29 @@
#define AS_PATH_SET 1 /* Types of path segments */
#define AS_PATH_SEQUENCE 2
struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, int as);
#define AS_PATH_MAXLEN 10000
#define AS_TRANS 23456
/* AS_TRANS is used when we need to store 32bit ASN larger than 0xFFFF
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
*/
struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as);
int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used);
int as_path_convert_to_new(struct adata *path, byte *dst, int req_as);
void as_path_format(struct adata *path, byte *buf, unsigned int size);
int as_path_getlen(struct adata *path);
int as_path_get_first(struct adata *path);
int as_path_get_first(struct adata *path, u32 *orig_as);
int as_path_is_member(struct adata *path, u32 as);
struct f_path_mask {
struct f_path_mask *next;
int val;
u32 val;
int any;
};
#define PM_ANY -1
// #define PM_ANY -1
int as_path_match(struct adata *path, struct f_path_mask *mask);

View file

@ -55,21 +55,37 @@ bgp_format_origin(eattr *a, byte *buf)
}
static int
bgp_check_path(struct bgp_proto *p UNUSED, byte *a, int len)
bgp_check_path(byte *a, int len, int bs, int errcode)
{
while (len)
{
DBG("Path segment %02x %02x\n", a[0], a[1]);
if (len < 2 ||
a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE ||
2*a[1] + 2 > len)
return 11;
len -= 2*a[1] + 2;
a += 2*a[1] + 2;
(a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE) ||
bs * a[1] + 2 > len)
return errcode;
len -= bs * a[1] + 2;
a += bs * a[1] + 2;
}
return 0;
}
static int
bgp_check_as_path(struct bgp_proto *p, byte *a, int len)
{
return bgp_check_path(a, len, (bgp_as4_support && p->as4_support) ? 4 : 2, 11);
}
static int
bgp_check_as4_path(struct bgp_proto *p, byte *a, int len)
{
if (bgp_as4_support && (! p->as4_support))
return bgp_check_path(a, len, 4, 9);
else
return 0;
}
static int
bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len)
{
@ -87,6 +103,14 @@ bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len)
#endif
}
static int
bgp_check_aggregator(struct bgp_proto *p UNUSED, UNUSED byte *a, int len)
{
int exp_len = (bgp_as4_support && p->as4_support) ? 8 : 6;
return (len == exp_len) ? 0 : 5;
}
static int
bgp_check_reach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
{
@ -113,7 +137,7 @@ static struct attr_desc bgp_attr_table[] = {
{ "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 */
bgp_check_path, NULL },
bgp_check_as_path, NULL },
{ "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 */
@ -122,8 +146,8 @@ static struct attr_desc bgp_attr_table[] = {
NULL, NULL },
{ "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 },
{ "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */
bgp_check_aggregator, NULL },
{ "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
NULL, NULL },
{ NULL, }, /* BA_ORIGINATOR_ID */
@ -135,8 +159,18 @@ static struct attr_desc bgp_attr_table[] = {
bgp_check_reach_nlri, NULL },
{ "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
bgp_check_unreach_nlri, NULL },
{ NULL, }, /* BA_EXTENDED_COMM */
{ "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
bgp_check_as4_path, NULL },
{ "as4_aggregator", 8, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
NULL, NULL }
};
/* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH because
* EAF_TYPE_AS_PATH is supposed to have different format (2 or 4 B for each ASN)
* depending on bgp_as4_support variable.
*/
#define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name)
static byte *
@ -170,8 +204,90 @@ bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, unsigned val)
return bgp_set_attr(a->attrs, pool, attr, val);
}
static int
bgp_encode_attr_hdr(byte *dst, unsigned int flags, unsigned code, int len)
{
int wlen;
DBG("\tAttribute %02x (%d bytes, flags %02x)\n", code, len, flags);
if (len < 256)
{
*dst++ = flags;
*dst++ = code;
*dst++ = len;
wlen = 3;
}
else
{
*dst++ = flags | BAF_EXT_LEN;
*dst++ = code;
put_u16(dst, len);
wlen = 4;
}
return wlen;
}
static void
aggregator_convert_to_old(struct adata *aggr, byte *dst, int *new_used)
{
byte *src = aggr->data;
*new_used = 0;
u32 as = get_u32(src);
if (as > 0xFFFF)
{
as = AS_TRANS;
*new_used = 1;
}
put_u16(dst, as);
/* Copy IPv4 address */
memcpy(dst + 2, src + 4, 4);
}
static void
aggregator_convert_to_new(struct adata *aggr, byte *dst)
{
byte *src = aggr->data;
u32 as = get_u16(src);
put_u32(dst, as);
/* Copy IPv4 address */
memcpy(dst + 4, src + 2, 4);
}
static int
bgp_get_attr_len(eattr *a)
{
int len;
if (ATTR_KNOWN(EA_ID(a->id)))
{
int code = EA_ID(a->id);
struct attr_desc *desc = &bgp_attr_table[code];
len = desc->expected_length;
if (len < 0)
{
ASSERT(!(a->type & EAF_EMBEDDED));
len = a->u.ptr->length;
}
}
else
{
ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE);
len = a->u.ptr->length;
}
return len;
}
#define ADVANCE(w, r, l) do { r -= l; w += l; } while (0)
/**
* bgp_encode_attrs - encode BGP attributes
* @p: BGP instance
* @w: buffer
* @attrs: a list of extended attributes
* @remains: remaining space in the buffer
@ -182,11 +298,11 @@ bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, unsigned val)
* Result: Length of the attribute block generated.
*/
unsigned int
bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
{
unsigned int i, code, flags;
byte *start = w;
int len;
int len, rv;
for(i=0; i<attrs->count; i++)
{
@ -198,43 +314,90 @@ bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
if (code == BA_NEXT_HOP)
continue;
#endif
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
if (ATTR_KNOWN(code))
/* When AS4-aware BGP speaker is talking to non-AS4-aware BGP speaker,
* we have to convert our 4B AS_PATH to 2B AS_PATH and send our AS_PATH
* as optional AS4_PATH attribute.
*/
if ((code == BA_AS_PATH) && bgp_as4_support && (! p->as4_support))
{
struct attr_desc *desc = &bgp_attr_table[code];
len = desc->expected_length;
if (len < 0)
{
ASSERT(!(a->type & EAF_EMBEDDED));
len = a->u.ptr->length;
}
}
else
{
ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE);
len = a->u.ptr->length;
if (remains < (len + 4))
goto err_no_buffer;
/* Using temporary buffer because don't know a length of created attr
* and therefore a length of a header. Perhaps i should better always
* use BAF_EXT_LEN. */
byte buf[len];
int new_used;
int nl = as_path_convert_to_old(a->u.ptr, buf, &new_used);
rv = bgp_encode_attr_hdr(w, BAF_TRANSITIVE, BA_AS_PATH, nl);
ADVANCE(w, remains, rv);
memcpy(w, buf, nl);
ADVANCE(w, remains, nl);
if (! new_used)
continue;
if (remains < (len + 4))
goto err_no_buffer;
/* We should discard AS_CONFED_SEQUENCE or AS_CONFED_SET path segments
* here but we don't support confederations and such paths we already
* discarded in bgp_check_as_path().
*/
rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_PATH, len);
ADVANCE(w, remains, rv);
memcpy(w, a->u.ptr->data, len);
ADVANCE(w, remains, len);
continue;
}
DBG("\tAttribute %02x (type %02x, %d bytes, flags %02x)\n", code, a->type, len, flags);
/* The same issue with AGGREGATOR attribute */
if ((code == BA_AGGREGATOR) && bgp_as4_support && (! p->as4_support))
{
int new_used;
len = 6;
if (remains < (len + 3))
goto err_no_buffer;
rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AGGREGATOR, len);
ADVANCE(w, remains, rv);
aggregator_convert_to_old(a->u.ptr, w, &new_used);
ADVANCE(w, remains, len);
if (! new_used)
continue;
len = 8;
if (remains < (len + 3))
goto err_no_buffer;
rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_AGGREGATOR, len);
ADVANCE(w, remains, rv);
memcpy(w, a->u.ptr->data, len);
ADVANCE(w, remains, len);
continue;
}
/* Standard path continues here ... */
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
len = bgp_get_attr_len(a);
if (remains < len + 4)
{
log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes");
break;
}
if (len < 256)
{
*w++ = flags;
*w++ = code;
*w++ = len;
remains -= 3;
}
else
{
*w++ = flags | BAF_EXT_LEN;
*w++ = code;
put_u16(w, len);
w += 2;
remains -= 4;
}
goto err_no_buffer;
rv = bgp_encode_attr_hdr(w, flags, code, len);
ADVANCE(w, remains, rv);
switch (a->type & EAF_TYPE_MASK)
{
case EAF_TYPE_INT:
@ -266,10 +429,13 @@ bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
default:
bug("bgp_encode_attrs: unknown attribute type %02x", a->type);
}
remains -= len;
w += len;
ADVANCE(w, remains, len);
}
return w - start;
err_no_buffer:
log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes");
return w - start;
}
static void
@ -566,10 +732,14 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 0);
else
{
z = bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 4);
z = bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, bgp_as4_support ? 6 : 4);
z[0] = AS_PATH_SEQUENCE;
z[1] = 1; /* 1 AS */
put_u16(z+2, p->local_as);
if (bgp_as4_support)
put_u32(z+2, p->local_as);
else
put_u16(z+2, p->local_as);
}
z = bgp_set_attr(ea->attrs+2, pool, BA_NEXT_HOP, sizeof(ip_addr));
@ -670,8 +840,8 @@ bgp_rte_better(rte *new, rte *old)
{
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
n = x ? as_path_getlen(x->u.ptr) : 100000;
o = y ? as_path_getlen(y->u.ptr) : 100000;
n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
if (n < o)
return 1;
if (n > o)
@ -712,23 +882,118 @@ bgp_rte_better(rte *new, rte *old)
static int
bgp_path_loopy(struct bgp_proto *p, eattr *a)
{
byte *path = a->u.ptr->data;
int len = a->u.ptr->length;
int i, n;
return as_path_is_member(a->u.ptr, p->local_as);
}
while (len > 0)
static struct adata *
bgp_aggregator_convert_to_new(struct adata *old, struct linpool *pool)
{
struct adata *newa = lp_alloc(pool, sizeof(struct adata) + 8);
newa->length = 8;
aggregator_convert_to_new(old, newa->data);
return newa;
}
/* Take last req_as ASNs from path old2 (in 2B format), convert to 4B format
* and append path old4 (in 4B format).
*/
static struct adata *
bgp_merge_as_paths(struct adata *old2, struct adata *old4, int req_as, struct linpool *pool)
{
byte buf[old2->length * 2];
int ol = as_path_convert_to_new(old2, buf, req_as);
int nl = ol + (old4 ? old4->length : 0);
struct adata *newa = lp_alloc(pool, sizeof(struct adata) + nl);
newa->length = nl;
memcpy(newa->data, buf, ol);
if (old4) memcpy(newa->data + ol, old4->data, old4->length);
return newa;
}
/* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC4893 4.2.3 */
static void
bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool)
{
eattr *p2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
eattr *p4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_PATH));
eattr *a2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR));
eattr *a4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR));
if (a2)
{
n = path[1];
len -= 2 + 2*n;
path += 2;
for(i=0; i<n; i++)
u32 a2_as = get_u16(a2->u.ptr->data);
if (a4)
{
if (get_u16(path) == p->local_as)
return 1;
path += 2;
if (a2_as != AS_TRANS)
{
/* Routes were aggregated by old router and therefore AS4_PATH
* and AS4_AGGREGATOR is invalid
*
* Convert AS_PATH and AGGREGATOR to 4B format and finish.
*/
a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
return;
}
else
{
/* Common case, use AS4_AGGREGATOR attribute */
a2->u.ptr = a4->u.ptr;
}
}
else
{
/* Common case, use old AGGREGATOR attribute */
a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
if (a2_as == AS_TRANS)
log(L_WARN "BGP: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing");
}
}
return 0;
else
if (a4)
log(L_WARN "BGP: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing");
int p2_len = as_path_getlen(p2->u.ptr);
int p4_len = p4 ? as_path_getlen(p4->u.ptr) : AS_PATH_MAXLEN;
if (p2_len < p4_len)
p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
else
p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, p4->u.ptr, p2_len - p4_len, pool);
}
static void
bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
{
unsigned id1 = EA_CODE(EAP_BGP, BA_AS4_PATH);
unsigned id2 = EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR);
ea_list **el = &(a->eattrs);
/* We know that ea_lists constructed in bgp_decode_attrs have one attribute per ea_list struct */
while (*el != NULL)
{
unsigned fid = (*el)->attrs[0].id;
if ((fid == id1) || (fid == id2))
{
*el = (*el)->next;
if (p->as4_support)
log(L_WARN "BGP: Unexpected AS4_* attributes received");
}
else
el = &((*el)->next);
}
}
/**
@ -883,6 +1148,15 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
}
}
}
/* When receiving attributes from non-AS4-aware BGP speaker,
* we have to reconstruct 4B AS_PATH and AGGREGATOR attributes
*/
if (bgp_as4_support && (! bgp->as4_support))
bgp_reconstruct_4b_atts(bgp, a, pool);
if (bgp_as4_support)
bgp_remove_as4_attrs(bgp, a);
/* If the AS path attribute contains our AS, reject the routes */
e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
@ -945,11 +1219,11 @@ bgp_get_route_info(rte *e, byte *buf, ea_list *attrs)
{
eattr *p = ea_find(attrs, EA_CODE(EAP_BGP, BA_AS_PATH));
eattr *o = ea_find(attrs, EA_CODE(EAP_BGP, BA_ORIGIN));
int origas;
u32 origas;
buf += bsprintf(buf, " (%d) [", e->pref);
if (p && (origas = as_path_get_first(p->u.ptr)) >= 0)
buf += bsprintf(buf, "AS%d", origas);
if (p && as_path_get_first(p->u.ptr, &origas))
buf += bsprintf(buf, "AS%u", origas);
if (o)
buf += bsprintf(buf, "%c", "ie?"[o->u.data]);
strcpy(buf, "]");

View file

@ -76,6 +76,7 @@ static void bgp_connect(struct bgp_proto *p);
static void bgp_initiate(struct bgp_proto *p);
static void bgp_setup_listen_sk(void);
static void
bgp_close(struct bgp_proto *p UNUSED)
{
@ -611,6 +612,10 @@ bgp_check(struct bgp_config *c)
cf_error("Local AS number must be set");
if (!c->remote_as)
cf_error("Neighbor must be configured");
if (!bgp_as4_support && (c->local_as > 0xFFFF))
cf_error("Local AS number out of range");
if (!bgp_as4_support && (c->remote_as > 0xFFFF))
cf_error("Neighbor AS number out of range");
}
static void

View file

@ -16,7 +16,7 @@ struct eattr;
struct bgp_config {
struct proto_config c;
unsigned int local_as, remote_as;
u32 local_as, remote_as;
ip_addr remote_ip;
int multihop; /* Number of hops if multihop */
ip_addr multihop_via; /* Multihop: address to route to */
@ -47,14 +47,16 @@ struct bgp_conn {
byte *notify_data;
int error_flag; /* Error state, ignore all input */
int primary; /* This connection is primary */
u32 advertised_as; /* Temporary value for AS number received */
unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
struct bgp_proto {
struct proto p;
struct bgp_config *cf; /* Shortcut to BGP configuration */
unsigned local_as, remote_as;
u32 local_as, remote_as;
int is_internal; /* Internal BGP connection (local_as == remote_as) */
int as4_support; /* Peer supports 4B AS numbers [RFC4893] */
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
struct bgp_conn *conn; /* Connection we have established */
@ -100,6 +102,9 @@ struct bgp_bucket {
extern struct linpool *bgp_linpool;
extern int bgp_as4_support;
void bgp_start_timer(struct timer *t, int value);
void bgp_check(struct bgp_config *c);
void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len);
@ -122,7 +127,7 @@ int bgp_rte_better(struct rte *, struct rte *);
void bgp_rt_notify(struct proto *, struct network *, struct rte *, struct rte *, struct ea_list *);
int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);
void bgp_attr_init(struct bgp_proto *);
unsigned int bgp_encode_attrs(byte *w, struct ea_list *attrs, int remains);
unsigned int bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains);
void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck);
void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs);
@ -165,6 +170,8 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco
#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
#define BA_MP_UNREACH_NLRI 0x0f
#define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */
#define BA_AS4_PATH 0x11 /* [RFC4893] */
#define BA_AS4_AGGREGATOR 0x12
/* BGP states */

View file

@ -44,12 +44,10 @@ bgp_proto_start: proto_start BGP {
bgp_proto:
bgp_proto_start proto_name '{'
| bgp_proto proto_item ';'
| bgp_proto LOCAL AS expr ';' {
if ($4 < 0 || $4 > 65535) cf_error("AS number out of range");
BGP_CFG->local_as = $4;
}
| bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; }
| bgp_proto NEIGHBOR ipa AS expr ';' {
if ($5 < 0 || $5 > 65535) cf_error("AS number out of range");
if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed");
BGP_CFG->remote_ip = $3;
BGP_CFG->remote_as = $5;
}

View file

@ -12,6 +12,7 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "conf/conf.h"
#include "lib/unaligned.h"
#include "lib/socket.h"
@ -30,25 +31,10 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
return buf + 2 + conn->notify_size;
}
#ifdef IPV6
static byte *
bgp_create_open(struct bgp_conn *conn, byte *buf)
bgp_put_cap_ipv6(struct bgp_conn *conn UNUSED, byte *buf)
{
struct bgp_proto *p = conn->bgp;
BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)",
BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id);
buf[0] = BGP_VERSION;
put_u16(buf+1, p->local_as);
put_u16(buf+3, p->cf->hold_time);
put_u32(buf+5, p->local_id);
#ifndef IPV6
buf[9] = 0; /* No optional parameters */
return buf+10;
#else
buf += 9;
*buf++ = 8; /* Optional params len */
*buf++ = 2; /* Option: Capability list */
*buf++ = 6; /* Option length */
*buf++ = 1; /* Capability 1: Multiprotocol extensions */
*buf++ = 4; /* Capability data length */
*buf++ = 0; /* We support AF IPv6 */
@ -56,7 +42,53 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* RFU */
*buf++ = 1; /* and SAFI 1 */
return buf;
}
#endif
static byte *
bgp_put_cap_as4(struct bgp_conn *conn, byte *buf)
{
*buf++ = 65; /* Capability 65: Support for 4-octet AS number */
*buf++ = 4; /* Capability data length */
put_u32(buf, conn->bgp->local_as);
return buf + 4;
}
static byte *
bgp_create_open(struct bgp_conn *conn, byte *buf)
{
struct bgp_proto *p = conn->bgp;
byte *cap;
int cap_len;
BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)",
BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id);
buf[0] = BGP_VERSION;
put_u16(buf+1, (p->local_as < 0xFFFF) ? p->local_as : AS_TRANS);
put_u16(buf+3, p->cf->hold_time);
put_u32(buf+5, p->local_id);
/* Skipped 3 B for length field and Capabilities parameter header */
cap = buf + 12;
#ifdef IPV6
cap = bgp_put_cap_ipv6(conn, cap);
#endif
if (bgp_as4_support)
cap = bgp_put_cap_as4(conn, cap);
cap_len = cap - buf - 12;
if (cap_len > 0)
{
buf[9] = cap_len + 2; /* Optional params len */
buf[10] = 2; /* Option: Capability list */
buf[11] = cap_len; /* Option length */
return cap;
}
else
{
buf[9] = 0; /* No optional parameters */
return buf + 10;
}
}
static unsigned int
@ -118,7 +150,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
continue;
}
DBG("Processing bucket %p\n", buck);
a_size = bgp_encode_attrs(w+2, buck->eattrs, 1024);
a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 1024);
put_u16(w, a_size);
w += a_size + 2;
r_size = bgp_encode_prefixes(p, w, buck, remains - a_size);
@ -166,7 +198,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
*tmp++ = BGP_AF_IPV6;
*tmp++ = 1;
ea->attrs[0].u.ptr->length = bgp_encode_prefixes(p, tmp, buck, remains-11);
size = bgp_encode_attrs(w, ea, remains);
size = bgp_encode_attrs(p, w, ea, remains);
w += size;
remains -= size;
}
@ -183,7 +215,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
continue;
}
DBG("Processing bucket %p\n", buck);
size = bgp_encode_attrs(w, buck->eattrs, 1024);
size = bgp_encode_attrs(p, w, buck->eattrs, 1024);
w += size;
remains -= size;
tstart = tmp = bgp_attach_attr(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8);
@ -230,7 +262,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
*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;
w += bgp_encode_attrs(w, ea, remains);
w += bgp_encode_attrs(p, w, ea, remains);
break;
}
}
@ -353,9 +385,49 @@ bgp_tx(sock *sk)
;
}
/* Capatibility negotiation as per RFC 2842 */
void
bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
{
struct bgp_proto *p = conn->bgp;
int cl;
u32 as;
while (len > 0)
{
if (len < 2 || len < 2 + opt[1])
goto err;
cl = opt[1];
switch (opt[0])
{
case 65:
if (cl != 4)
goto err;
p->as4_support = 1;
if (bgp_as4_support)
conn->advertised_as = get_u32(opt + 2);
break;
/* We can safely ignore all other capabilities */
}
len -= 2 + cl;
opt += 2 + cl;
}
return;
err:
bgp_error(conn, 2, 0, NULL, 0);
return;
}
static int
bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
{
int ol;
while (len > 0)
{
if (len < 2 || len < 2 + opt[1])
@ -369,12 +441,14 @@ bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
DBG("\n");
}
#endif
ol = opt[1];
switch (opt[0])
{
case 2:
/* Capatibility negotiation as per RFC 2842 */
/* We can safely ignore all capabilities announced */
bgp_parse_capabilities(conn, opt + 2, ol);
break;
default:
/*
* BGP specs don't tell us to send which option
@ -382,11 +456,11 @@ bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
* to do so. Also, capability negotiation with
* Cisco routers doesn't work without that.
*/
bgp_error(conn, 2, 4, opt, opt[1]);
bgp_error(conn, 2, 4, opt, ol);
return 0;
}
len -= 2 + opt[1];
opt += 2 + opt[1];
len -= 2 + ol;
opt += 2 + ol;
}
return 0;
}
@ -397,7 +471,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
struct bgp_conn *other;
struct bgp_proto *p = conn->bgp;
struct bgp_config *cf = p->cf;
unsigned as, hold;
unsigned hold;
u32 id;
/* Check state */
@ -409,20 +483,27 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
{ bgp_error(conn, 1, 2, pkt+16, 2); return; }
if (pkt[19] != BGP_VERSION)
{ bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */
as = get_u16(pkt+20);
conn->advertised_as = get_u16(pkt+20);
hold = get_u16(pkt+22);
id = get_u32(pkt+24);
BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", as, hold, id);
if (cf->remote_as && as != p->remote_as)
{ bgp_error(conn, 2, 2, pkt+20, -2); return; }
if (hold > 0 && hold < 3)
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
p->remote_id = id;
BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", conn->advertised_as, hold, id);
p->remote_id = id; // ???
if (bgp_parse_options(conn, pkt+29, pkt[28]))
return;
if (hold > 0 && hold < 3)
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
if (!id || id == 0xffffffff || id == p->local_id)
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
if (conn->advertised_as != p->remote_as)
{
bgp_error(conn, 2, 2, (byte *) &(conn->advertised_as), -4); return;
}
/* Check the other connection */
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
switch (other->state)
@ -463,7 +544,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
else
conn->hold_time = p->cf->hold_time;
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
p->remote_as = as;
// p->remote_as = conn->advertised_as;
p->remote_id = id;
DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id);
@ -720,7 +801,7 @@ static struct {
{ 2, 4, "Unsupported optional parameter" },
{ 2, 5, "Authentication failure" },
{ 2, 6, "Unacceptable hold time" },
{ 2, 7, "Required capability missing" }, /* capability negotiation draft */
{ 2, 7, "Required capability missing" }, /* [RFC3392] */
{ 3, 0, "Invalid UPDATE message" },
{ 3, 1, "Malformed attribute list" },
{ 3, 2, "Unrecognized well-known attribute" },