BGP: Support for AS confederations (RFC 5065)
This commit is contained in:
parent
f8aad5d5b7
commit
5509e17d0c
5 changed files with 277 additions and 185 deletions
|
@ -1945,12 +1945,11 @@ avoid routing loops.
|
||||||
|
|
||||||
<p>BIRD supports all requirements of the BGP4 standard as defined in
|
<p>BIRD supports all requirements of the BGP4 standard as defined in
|
||||||
<rfc id="4271"> It also supports the community attributes (<rfc id="1997">),
|
<rfc id="4271"> It also supports the community attributes (<rfc id="1997">),
|
||||||
capability negotiation (<rfc id="5492">), MD5 password authentication (<rfc
|
capability negotiation (<rfc id="5492">), MD5 password authentication
|
||||||
id="2385">), extended communities (<rfc id="4360">), route reflectors (<rfc
|
(<rfc id="2385">), extended communities (<rfc id="4360">), route reflectors
|
||||||
id="4456">), graceful restart (<rfc id="4724">), multiprotocol extensions
|
(<rfc id="4456">), AS confederations (<rfc id="5065">), graceful restart
|
||||||
(<rfc id="4760">), 4B AS numbers (<rfc id="4893">), and 4B AS numbers in
|
(<rfc id="4724">), multiprotocol extensions (<rfc id="4760">), 4B AS numbers
|
||||||
extended communities (<rfc id="5668">).
|
(<rfc id="4893">), and 4B AS numbers in extended communities (<rfc id="5668">).
|
||||||
|
|
||||||
|
|
||||||
For IPv6, it uses the standard multiprotocol extensions defined in
|
For IPv6, it uses the standard multiprotocol extensions defined in
|
||||||
<rfc id="4760"> and applied to IPv6 according to <rfc id="2545">.
|
<rfc id="4760"> and applied to IPv6 according to <rfc id="2545">.
|
||||||
|
@ -2134,6 +2133,21 @@ using the following configuration parameters:
|
||||||
accepting incoming connections. In passive mode, outgoing connections
|
accepting incoming connections. In passive mode, outgoing connections
|
||||||
are not initiated. Default: off.
|
are not initiated. Default: off.
|
||||||
|
|
||||||
|
<tag><label id="bgp-confederation">confederation <m/number/</tag>
|
||||||
|
BGP confederations (<rfc id="5065">) are collections of autonomous
|
||||||
|
systems that act as one entity to external systems, represented by one
|
||||||
|
confederation identifier (instead of AS numbers). This option allows to
|
||||||
|
enable BGP confederation behavior and to specify the local confederation
|
||||||
|
identifier. When BGP confederations are used, all BGP speakers that are
|
||||||
|
members of the BGP confederation should have the same confederation
|
||||||
|
identifier configured. Default: 0 (no confederation).
|
||||||
|
|
||||||
|
<tag><label id="bgp-confederation-member">confederation member <m/switch/</tag>
|
||||||
|
When BGP confederations are used, this option allows to specify whether
|
||||||
|
the BGP neighbor is a member of the same confederation as the local BGP
|
||||||
|
speaker. The option is unnecessary (and ignored) for IBGP sessions, as
|
||||||
|
the same AS number implies the same confederation. Default: no.
|
||||||
|
|
||||||
<tag><label id="bgp-rr-client">rr client</tag>
|
<tag><label id="bgp-rr-client">rr client</tag>
|
||||||
Be a route reflector and treat the neighbor as a route reflection
|
Be a route reflector and treat the neighbor as a route reflection
|
||||||
client. Default: disabled.
|
client. Default: disabled.
|
||||||
|
|
327
nest/a-path.c
327
nest/a-path.c
|
@ -25,7 +25,7 @@
|
||||||
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
|
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
|
||||||
|
|
||||||
int
|
int
|
||||||
as_path_valid(byte *data, uint len, int bs, char *err, uint elen)
|
as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
|
||||||
{
|
{
|
||||||
byte *pos = data;
|
byte *pos = data;
|
||||||
char *err_dsc = NULL;
|
char *err_dsc = NULL;
|
||||||
|
@ -43,9 +43,21 @@ as_path_valid(byte *data, uint len, int bs, char *err, uint elen)
|
||||||
if (len < slen)
|
if (len < slen)
|
||||||
BAD("segment framing error", len);
|
BAD("segment framing error", len);
|
||||||
|
|
||||||
/* XXXX handle CONFED segments */
|
switch (type)
|
||||||
if ((type != AS_PATH_SET) && (type != AS_PATH_SEQUENCE))
|
{
|
||||||
|
case AS_PATH_SET:
|
||||||
|
case AS_PATH_SEQUENCE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AS_PATH_CONFED_SEQUENCE:
|
||||||
|
case AS_PATH_CONFED_SET:
|
||||||
|
if (!confed)
|
||||||
|
BAD("AS_CONFED* segment", type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
BAD("unknown segment", type);
|
BAD("unknown segment", type);
|
||||||
|
}
|
||||||
|
|
||||||
if (pos[1] == 0)
|
if (pos[1] == 0)
|
||||||
BAD("zero-length segment", type);
|
BAD("zero-length segment", type);
|
||||||
|
@ -157,10 +169,13 @@ as_path_contains_confed(const struct adata *path)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
struct adata *
|
||||||
as_path_strip_confed_(byte *dst, const byte *src, uint len)
|
as_path_strip_confed(struct linpool *pool, const struct adata *path)
|
||||||
{
|
{
|
||||||
const byte *end = src + len;
|
struct adata *res = lp_alloc_adata(pool, path->length);
|
||||||
|
const byte *src = path->data;
|
||||||
|
const byte *end = src + path->length;
|
||||||
|
byte *dst = res->data;
|
||||||
|
|
||||||
while (src < end)
|
while (src < end)
|
||||||
{
|
{
|
||||||
|
@ -176,18 +191,15 @@ as_path_strip_confed_(byte *dst, const byte *src, uint len)
|
||||||
|
|
||||||
src += slen;
|
src += slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix the result length */
|
||||||
|
res->length = dst - res->data;
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct adata *
|
struct adata *
|
||||||
as_path_strip_confed(struct linpool *pool, const struct adata *op)
|
as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as)
|
||||||
{
|
|
||||||
struct adata *np = lp_alloc_adata(pool, op->length);
|
|
||||||
as_path_strip_confed_(np->data, op->data, op->length);
|
|
||||||
return np;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct adata *
|
|
||||||
as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as, int strip)
|
|
||||||
{
|
{
|
||||||
struct adata *np;
|
struct adata *np;
|
||||||
const byte *pos = op->data;
|
const byte *pos = op->data;
|
||||||
|
@ -218,10 +230,7 @@ as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as,
|
||||||
{
|
{
|
||||||
byte *dst = np->data + 2 + BS * np->data[1];
|
byte *dst = np->data + 2 + BS * np->data[1];
|
||||||
|
|
||||||
if (strip)
|
memcpy(dst, pos, len);
|
||||||
as_path_strip_confed_(dst, pos, len);
|
|
||||||
else
|
|
||||||
memcpy(dst, pos, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return np;
|
return np;
|
||||||
|
@ -325,46 +334,49 @@ as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
as_path_format(const struct adata *path, byte *buf, uint size)
|
as_path_format(const struct adata *path, byte *bb, uint size)
|
||||||
{
|
{
|
||||||
const byte *p = path->data;
|
buffer buf = { .start = bb, .pos = bb, .end = bb + size }, *b = &buf;
|
||||||
const byte *e = p + path->length;
|
const byte *pos = path->data;
|
||||||
byte *end = buf + size - 16;
|
const byte *end = pos + path->length;
|
||||||
int sp = 1;
|
const char *ops, *cls;
|
||||||
int l, isset;
|
|
||||||
|
|
||||||
while (p < e)
|
b->pos[0] = 0;
|
||||||
|
|
||||||
|
while (pos < end)
|
||||||
|
{
|
||||||
|
uint type = pos[0];
|
||||||
|
uint len = pos[1];
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
if (buf > end)
|
case AS_PATH_SET: ops = "{"; cls = "}"; break;
|
||||||
{
|
case AS_PATH_SEQUENCE: ops = NULL; cls = NULL; break;
|
||||||
strcpy(buf, " ...");
|
case AS_PATH_CONFED_SEQUENCE: ops = "("; cls = ")"; break;
|
||||||
return;
|
case AS_PATH_CONFED_SET: ops = "({"; cls = "})"; break;
|
||||||
}
|
default: bug("Invalid path segment");
|
||||||
isset = (*p++ == AS_PATH_SET);
|
|
||||||
l = *p++;
|
|
||||||
if (isset)
|
|
||||||
{
|
|
||||||
if (!sp)
|
|
||||||
*buf++ = ' ';
|
|
||||||
*buf++ = '{';
|
|
||||||
sp = 0;
|
|
||||||
}
|
|
||||||
while (l-- && buf <= end)
|
|
||||||
{
|
|
||||||
if (!sp)
|
|
||||||
*buf++ = ' ';
|
|
||||||
buf += bsprintf(buf, "%u", get_as(p));
|
|
||||||
p += BS;
|
|
||||||
sp = 0;
|
|
||||||
}
|
|
||||||
if (isset)
|
|
||||||
{
|
|
||||||
*buf++ = ' ';
|
|
||||||
*buf++ = '}';
|
|
||||||
sp = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*buf = 0;
|
|
||||||
|
if (ops)
|
||||||
|
buffer_puts(b, ops);
|
||||||
|
|
||||||
|
while (len--)
|
||||||
|
{
|
||||||
|
buffer_print(b, len ? "%u " : "%u", get_as(pos));
|
||||||
|
pos += BS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls)
|
||||||
|
buffer_puts(b, cls);
|
||||||
|
|
||||||
|
if (pos < end)
|
||||||
|
buffer_puts(b, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle overflow */
|
||||||
|
if (b->pos == b->end)
|
||||||
|
strcpy(b->end - 12, "...");
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -399,66 +411,80 @@ as_path_getlen(const struct adata *path)
|
||||||
int
|
int
|
||||||
as_path_get_last(const struct adata *path, u32 *orig_as)
|
as_path_get_last(const struct adata *path, u32 *orig_as)
|
||||||
{
|
{
|
||||||
|
const byte *pos = path->data;
|
||||||
|
const byte *end = pos + path->length;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
u32 res = 0;
|
u32 val = 0;
|
||||||
const u8 *p = path->data;
|
|
||||||
const u8 *q = p+path->length;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
while (p<q)
|
while (pos < end)
|
||||||
|
{
|
||||||
|
uint type = pos[0];
|
||||||
|
uint len = pos[1];
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
switch (*p++)
|
case AS_PATH_SET:
|
||||||
{
|
case AS_PATH_CONFED_SET:
|
||||||
case AS_PATH_SET:
|
found = 0;
|
||||||
if (len = *p++)
|
break;
|
||||||
{
|
|
||||||
found = 0;
|
case AS_PATH_SEQUENCE:
|
||||||
p += BS * len;
|
case AS_PATH_CONFED_SEQUENCE:
|
||||||
}
|
val = get_as(pos + BS * (len - 1));
|
||||||
break;
|
found = 1;
|
||||||
case AS_PATH_SEQUENCE:
|
break;
|
||||||
if (len = *p++)
|
|
||||||
{
|
default:
|
||||||
found = 1;
|
bug("Invalid path segment");
|
||||||
res = get_as(p + BS * (len - 1));
|
|
||||||
p += BS * len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: bug("Invalid path segment");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos += BS * len;
|
||||||
|
}
|
||||||
|
|
||||||
if (found)
|
if (found)
|
||||||
*orig_as = res;
|
*orig_as = val;
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32
|
u32
|
||||||
as_path_get_last_nonaggregated(const struct adata *path)
|
as_path_get_last_nonaggregated(const struct adata *path)
|
||||||
{
|
{
|
||||||
const u8 *p = path->data;
|
const byte *pos = path->data;
|
||||||
const u8 *q = p+path->length;
|
const byte *end = pos + path->length;
|
||||||
u32 res = 0;
|
u32 val = 0;
|
||||||
int len;
|
|
||||||
|
|
||||||
while (p<q)
|
while (pos < end)
|
||||||
|
{
|
||||||
|
uint type = pos[0];
|
||||||
|
uint len = pos[1];
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
switch (*p++)
|
case AS_PATH_SET:
|
||||||
{
|
case AS_PATH_CONFED_SET:
|
||||||
case AS_PATH_SET:
|
return val;
|
||||||
return res;
|
|
||||||
|
|
||||||
case AS_PATH_SEQUENCE:
|
case AS_PATH_SEQUENCE:
|
||||||
if (len = *p++)
|
case AS_PATH_CONFED_SEQUENCE:
|
||||||
res = get_as(p + BS * (len - 1));
|
val = get_as(pos + BS * (len - 1));
|
||||||
p += BS * len;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default: bug("Invalid path segment");
|
default:
|
||||||
}
|
bug("Invalid path segment");
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
pos += BS * len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -468,11 +494,47 @@ as_path_get_first(const struct adata *path, u32 *last_as)
|
||||||
|
|
||||||
if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
|
if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
|
||||||
return 0;
|
return 0;
|
||||||
else
|
|
||||||
|
*last_as = get_as(p+2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
as_path_get_first_regular(const struct adata *path, u32 *last_as)
|
||||||
|
{
|
||||||
|
const byte *pos = path->data;
|
||||||
|
const byte *end = pos + path->length;
|
||||||
|
|
||||||
|
while (pos < end)
|
||||||
|
{
|
||||||
|
uint type = pos[0];
|
||||||
|
uint len = pos[1];
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
*last_as = get_as(p+2);
|
case AS_PATH_SET:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AS_PATH_SEQUENCE:
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*last_as = get_as(pos);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
case AS_PATH_CONFED_SEQUENCE:
|
||||||
|
case AS_PATH_CONFED_SET:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
bug("Invalid path segment");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos += BS * len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -597,43 +659,50 @@ struct pm_pos
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_path(const struct adata *path, struct pm_pos *pos)
|
parse_path(const struct adata *path, struct pm_pos *pp)
|
||||||
{
|
{
|
||||||
const u8 *p = path->data;
|
const byte *pos = path->data;
|
||||||
const u8 *q = p + path->length;
|
const byte *end = pos + path->length;
|
||||||
struct pm_pos *opos = pos;
|
struct pm_pos *op = pp;
|
||||||
int i, len;
|
uint i;
|
||||||
|
|
||||||
|
while (pos < end)
|
||||||
|
{
|
||||||
|
uint type = pos[0];
|
||||||
|
uint len = pos[1];
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
while (p < q)
|
switch (type)
|
||||||
switch (*p++)
|
{
|
||||||
|
case AS_PATH_SET:
|
||||||
|
case AS_PATH_CONFED_SET:
|
||||||
|
pp->set = 1;
|
||||||
|
pp->mark = 0;
|
||||||
|
pp->val.sp = pos - 1;
|
||||||
|
pp++;
|
||||||
|
|
||||||
|
pos += BS * len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AS_PATH_SEQUENCE:
|
||||||
|
case AS_PATH_CONFED_SEQUENCE:
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
case AS_PATH_SET:
|
pp->set = 0;
|
||||||
pos->set = 1;
|
pp->mark = 0;
|
||||||
pos->mark = 0;
|
pp->val.asn = get_as(pos);
|
||||||
pos->val.sp = p;
|
pp++;
|
||||||
len = *p;
|
|
||||||
p += 1 + BS * len;
|
|
||||||
pos++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AS_PATH_SEQUENCE:
|
pos += BS;
|
||||||
len = *p++;
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
pos->set = 0;
|
|
||||||
pos->mark = 0;
|
|
||||||
pos->val.asn = get_as(p);
|
|
||||||
p += BS;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
bug("as_path_match: Invalid path component");
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
return pos - opos;
|
default:
|
||||||
|
bug("Invalid path segment");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp - op;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -85,15 +85,19 @@ t_path_format(void)
|
||||||
bt_debug("Prepending ASN: %10u \n", i);
|
bt_debug("Prepending ASN: %10u \n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BUFFER_SIZE 26
|
#define BUFFER_SIZE 120
|
||||||
byte buf[BUFFER_SIZE] = {};
|
byte buf[BUFFER_SIZE] = {};
|
||||||
|
|
||||||
|
as_path_format(&empty_as_path, buf, BUFFER_SIZE);
|
||||||
|
bt_assert_msg(strcmp(buf, "") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
|
||||||
|
|
||||||
as_path_format(as_path, buf, BUFFER_SIZE);
|
as_path_format(as_path, buf, BUFFER_SIZE);
|
||||||
bt_assert_msg(strcmp(buf, "4294967294 4294967293 ...") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
|
bt_assert_msg(strcmp(buf, "4294967294 4294967293 4294967292 4294967291 4294967290 4294967289 4294967288 4294967287 4294967286 4294967285") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
|
||||||
|
|
||||||
#define SMALL_BUFFER_SIZE 25
|
#define SMALL_BUFFER_SIZE 25
|
||||||
byte buf2[SMALL_BUFFER_SIZE] = {};
|
byte buf2[SMALL_BUFFER_SIZE] = {};
|
||||||
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
|
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
|
||||||
bt_assert_msg(strcmp(buf2, "4294967294 ...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
|
bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
|
||||||
|
|
||||||
rfree(lp);
|
rfree(lp);
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,13 @@
|
||||||
|
|
||||||
struct f_tree;
|
struct f_tree;
|
||||||
|
|
||||||
int as_path_valid(byte *data, uint len, int bs, char *err, uint elen);
|
int as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen);
|
||||||
int as_path_16to32(byte *dst, byte *src, uint len);
|
int as_path_16to32(byte *dst, byte *src, uint len);
|
||||||
int as_path_32to16(byte *dst, byte *src, uint len);
|
int as_path_32to16(byte *dst, byte *src, uint len);
|
||||||
int as_path_contains_as4(const struct adata *path);
|
int as_path_contains_as4(const struct adata *path);
|
||||||
int as_path_contains_confed(const struct adata *path);
|
int as_path_contains_confed(const struct adata *path);
|
||||||
struct adata *as_path_strip_confed(struct linpool *pool, const struct adata *op);
|
struct adata *as_path_strip_confed(struct linpool *pool, const struct adata *op);
|
||||||
struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as, int strip);
|
struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as);
|
||||||
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
|
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
|
||||||
void as_path_cut(struct adata *path, uint num);
|
void as_path_cut(struct adata *path, uint num);
|
||||||
struct adata *as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2);
|
struct adata *as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2);
|
||||||
|
@ -44,6 +44,7 @@ void as_path_format(const struct adata *path, byte *buf, uint size);
|
||||||
int as_path_getlen(const struct adata *path);
|
int as_path_getlen(const struct adata *path);
|
||||||
int as_path_getlen_int(const struct adata *path, int bs);
|
int as_path_getlen_int(const struct adata *path, int bs);
|
||||||
int as_path_get_first(const struct adata *path, u32 *orig_as);
|
int as_path_get_first(const struct adata *path, u32 *orig_as);
|
||||||
|
int as_path_get_first_regular(const struct adata *path, u32 *last_as);
|
||||||
int as_path_get_last(const struct adata *path, u32 *last_as);
|
int as_path_get_last(const struct adata *path, u32 *last_as);
|
||||||
u32 as_path_get_last_nonaggregated(const struct adata *path);
|
u32 as_path_get_last_nonaggregated(const struct adata *path);
|
||||||
int as_path_contains(const struct adata *path, u32 as, int min);
|
int as_path_contains(const struct adata *path, u32 as, int min);
|
||||||
|
@ -51,7 +52,7 @@ int as_path_match_set(const struct adata *path, struct f_tree *set);
|
||||||
struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
|
struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
|
||||||
|
|
||||||
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
||||||
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as, 0); }
|
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
|
||||||
|
|
||||||
|
|
||||||
#define PM_ASN 0
|
#define PM_ASN 0
|
||||||
|
|
|
@ -41,24 +41,6 @@
|
||||||
* specifies that such updates should be ignored, but that is generally
|
* specifies that such updates should be ignored, but that is generally
|
||||||
* a bad idea.
|
* a bad idea.
|
||||||
*
|
*
|
||||||
* Error checking of optional transitive attributes is done according to
|
|
||||||
* draft-ietf-idr-optional-transitive-03, but errors are handled always
|
|
||||||
* as withdraws.
|
|
||||||
*
|
|
||||||
* Unexpected AS_CONFED_* segments in AS_PATH are logged and removed,
|
|
||||||
* but unknown segments cause a session drop with Malformed AS_PATH
|
|
||||||
* error (see validate_path()). The behavior in such case is not
|
|
||||||
* explicitly specified by RFC 4271. RFC 5065 specifies that
|
|
||||||
* inconsistent AS_CONFED_* segments should cause a session drop, but
|
|
||||||
* implementations that pass invalid AS_CONFED_* segments are
|
|
||||||
* widespread.
|
|
||||||
*
|
|
||||||
* Error handling of AS4_* attributes is done as specified by
|
|
||||||
* draft-ietf-idr-rfc4893bis-03. There are several possible
|
|
||||||
* inconsistencies between AGGREGATOR and AS4_AGGREGATOR that are not
|
|
||||||
* handled by that draft, these are logged and ignored (see
|
|
||||||
* bgp_reconstruct_4b_attrs()).
|
|
||||||
*
|
|
||||||
* BGP attribute table has several hooks:
|
* BGP attribute table has several hooks:
|
||||||
*
|
*
|
||||||
* export - Hook that validates and normalizes attribute during export phase.
|
* export - Hook that validates and normalizes attribute during export phase.
|
||||||
|
@ -281,11 +263,19 @@ bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
|
||||||
static void
|
static void
|
||||||
bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
|
bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
|
||||||
{
|
{
|
||||||
|
struct bgp_proto *p = s->proto;
|
||||||
|
int as_length = s->as4_session ? 4 : 2;
|
||||||
|
int as_confed = p->cf->confederation && p->is_interior;
|
||||||
char err[128];
|
char err[128];
|
||||||
|
|
||||||
if (!as_path_valid(data, len, (s->as4_session ? 4 : 2), err, sizeof(err)))
|
if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
|
||||||
WITHDRAW("Malformed AS_PATH attribute - %s", err);
|
WITHDRAW("Malformed AS_PATH attribute - %s", err);
|
||||||
|
|
||||||
|
/* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
|
||||||
|
if (p->is_interior && !p->is_internal &&
|
||||||
|
((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
|
||||||
|
WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
|
||||||
|
|
||||||
if (!s->as4_session)
|
if (!s->as4_session)
|
||||||
{
|
{
|
||||||
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
|
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
|
||||||
|
@ -603,11 +593,20 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt
|
||||||
if (len < 6)
|
if (len < 6)
|
||||||
DISCARD(BAD_LENGTH, "AS4_PATH", len);
|
DISCARD(BAD_LENGTH, "AS4_PATH", len);
|
||||||
|
|
||||||
if (!as_path_valid(data, len, 4, err, sizeof(err)))
|
if (!as_path_valid(data, len, 4, 1, err, sizeof(err)))
|
||||||
DISCARD("Malformed AS4_PATH attribute - %s", err);
|
DISCARD("Malformed AS4_PATH attribute - %s", err);
|
||||||
|
|
||||||
/* XXXX remove CONFED segments */
|
struct adata *a = lp_alloc_adata(s->pool, len);
|
||||||
bgp_set_attr_data(to, s->pool, BA_AS4_PATH, flags, data, len);
|
memcpy(a->data, data, len);
|
||||||
|
|
||||||
|
/* AS_CONFED* segments are invalid in AS4_PATH; RFC 6793 6 */
|
||||||
|
if (as_path_contains_confed(a))
|
||||||
|
{
|
||||||
|
REPORT("Discarding AS_CONFED* segment from AS4_PATH attribute");
|
||||||
|
a = as_path_strip_confed(s->pool, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1042,7 +1041,7 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len)
|
||||||
if (bgp_as_path_loopy(p, attrs, p->local_as))
|
if (bgp_as_path_loopy(p, attrs, p->local_as))
|
||||||
goto withdraw;
|
goto withdraw;
|
||||||
|
|
||||||
/* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4 */
|
/* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4.0 */
|
||||||
if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
|
if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
|
||||||
goto withdraw;
|
goto withdraw;
|
||||||
|
|
||||||
|
@ -1322,15 +1321,7 @@ bgp_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const adata null_adata; /* adata of length 0 */
|
static adata null_adata; /* adata of length 0 */
|
||||||
|
|
||||||
static inline void
|
|
||||||
bgp_path_prepend(ea_list **attrs, struct linpool *pool, int seg, u32 as, int strip)
|
|
||||||
{
|
|
||||||
eattr *a = bgp_find_attr(*attrs, BA_AS_PATH);
|
|
||||||
adata *d = as_path_prepend2(pool, a ? a->u.ptr : &null_adata, seg, as, strip);
|
|
||||||
bgp_set_attr_ptr(attrs, pool, BA_AS_PATH, 0, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
bgp_cluster_list_prepend(ea_list **attrs, struct linpool *pool, u32 id)
|
bgp_cluster_list_prepend(ea_list **attrs, struct linpool *pool, u32 id)
|
||||||
|
@ -1352,23 +1343,33 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
|
||||||
if (! bgp_find_attr(attrs, BA_ORIGIN))
|
if (! bgp_find_attr(attrs, BA_ORIGIN))
|
||||||
bgp_set_attr_u32(&attrs, pool, BA_ORIGIN, 0, src ? ORIGIN_INCOMPLETE : ORIGIN_IGP);
|
bgp_set_attr_u32(&attrs, pool, BA_ORIGIN, 0, src ? ORIGIN_INCOMPLETE : ORIGIN_IGP);
|
||||||
|
|
||||||
|
/* AS_PATH attribute */
|
||||||
|
a = bgp_find_attr(attrs, BA_AS_PATH);
|
||||||
|
adata *ad = a ? a->u.ptr : &null_adata;
|
||||||
|
|
||||||
|
/* AS_PATH attribute - strip AS_CONFED* segments outside confederation */
|
||||||
|
if ((!p->cf->confederation || !p->is_interior) && as_path_contains_confed(ad))
|
||||||
|
ad = as_path_strip_confed(pool, ad);
|
||||||
|
|
||||||
/* AS_PATH attribute - keep or prepend ASN */
|
/* AS_PATH attribute - keep or prepend ASN */
|
||||||
if (p->is_internal ||
|
if (p->is_internal ||
|
||||||
(p->rs_client && src && src->rs_client))
|
(p->rs_client && src && src->rs_client))
|
||||||
{
|
{
|
||||||
/* IBGP or route server -> just ensure there is one */
|
/* IBGP or route server -> just ensure there is one */
|
||||||
if (! bgp_find_attr(attrs, BA_AS_PATH))
|
if (!a)
|
||||||
bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, lp_alloc_adata(pool, 0));
|
bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, &null_adata);
|
||||||
}
|
}
|
||||||
else if (p->is_interior)
|
else if (p->is_interior)
|
||||||
{
|
{
|
||||||
/* Confederation -> prepend ASN as CONFED_SEQUENCE, keep CONFED_* segments */
|
/* Confederation -> prepend ASN as AS_CONFED_SEQUENCE */
|
||||||
bgp_path_prepend(&attrs, pool, AS_PATH_CONFED_SEQUENCE, p->public_as, 0);
|
ad = as_path_prepend2(pool, ad, AS_PATH_CONFED_SEQUENCE, p->public_as);
|
||||||
|
bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
|
||||||
}
|
}
|
||||||
else /* Regular EBGP (no RS, no confederation) */
|
else /* Regular EBGP (no RS, no confederation) */
|
||||||
{
|
{
|
||||||
/* Regular EBGP -> prepend ASN as regular segment, strip CONFED_* segments */
|
/* Regular EBGP -> prepend ASN as regular sequence */
|
||||||
bgp_path_prepend(&attrs, pool, AS_PATH_SEQUENCE, p->public_as, 1);
|
ad = as_path_prepend2(pool, ad, AS_PATH_SEQUENCE, p->public_as);
|
||||||
|
bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
|
||||||
|
|
||||||
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */
|
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */
|
||||||
a = bgp_find_attr(attrs, BA_MULTI_EXIT_DISC);
|
a = bgp_find_attr(attrs, BA_MULTI_EXIT_DISC);
|
||||||
|
@ -1460,10 +1461,12 @@ bgp_get_neighbor(rte *r)
|
||||||
eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
|
||||||
u32 as;
|
u32 as;
|
||||||
|
|
||||||
if (e && as_path_get_first(e->u.ptr, &as))
|
if (e && as_path_get_first_regular(e->u.ptr, &as))
|
||||||
return as;
|
return as;
|
||||||
else
|
|
||||||
return ((struct bgp_proto *) r->attrs->src->proto)->remote_as;
|
/* If AS_PATH is not defined, we treat rte as locally originated */
|
||||||
|
struct bgp_proto *p = (void *) r->attrs->src->proto;
|
||||||
|
return p->cf->confederation ?: p->local_as;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -1662,7 +1665,7 @@ bgp_rte_mergable(rte *pri, rte *sec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC 4271 9.1.2.2. d) Prefer external peers */
|
/* RFC 4271 9.1.2.2. d) Prefer external peers */
|
||||||
if (pri_bgp->is_internal != sec_bgp->is_internal)
|
if (pri_bgp->is_interior != sec_bgp->is_interior)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
|
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
|
||||||
|
@ -1852,6 +1855,7 @@ bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
|
||||||
/* Handle AS_PATH attribute */
|
/* Handle AS_PATH attribute */
|
||||||
if (p2 && p4)
|
if (p2 && p4)
|
||||||
{
|
{
|
||||||
|
/* Both as_path_getlen() and as_path_cut() take AS_CONFED* as zero length */
|
||||||
int p2_len = as_path_getlen(p2->u.ptr);
|
int p2_len = as_path_getlen(p2->u.ptr);
|
||||||
int p4_len = as_path_getlen(p4->u.ptr);
|
int p4_len = as_path_getlen(p4->u.ptr);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue