0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-20 10:08:43 +00:00

BGP: Support for AS confederations (RFC 5065)

This commit is contained in:
Ondrej Zajicek (work) 2017-01-22 16:32:42 +01:00
parent f8aad5d5b7
commit 5509e17d0c
5 changed files with 277 additions and 185 deletions

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);