Changes handling of AS_PATH_CONFED_* segments in AS_PATH.
Although standard says that if we receive AS_PATH_CONFED_* (and we are not a part of a confederation) segment, we should drop session, nobody does that and it is unwise to do that. Now we drop session just in case that peer ASN is in AS_PATH_CONFED_* segment (to detect peer that considers BIRD as a part of its confederation).
This commit is contained in:
parent
4323099da9
commit
29c430f856
1 changed files with 103 additions and 84 deletions
|
@ -55,25 +55,97 @@ bgp_format_origin(eattr *a, byte *buf, int buflen)
|
|||
}
|
||||
|
||||
static int
|
||||
bgp_check_path(byte *a, int len, int bs, int errcode)
|
||||
path_segment_contains(byte *p, int bs, u32 asn)
|
||||
{
|
||||
while (len)
|
||||
int i;
|
||||
int len = p[1];
|
||||
p += 2;
|
||||
|
||||
for(i=0; i<len; i++)
|
||||
{
|
||||
DBG("Path segment %02x %02x\n", a[0], a[1]);
|
||||
if (len < 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;
|
||||
u32 asn2 = (bs == 4) ? get_u32(p) : get_u16(p);
|
||||
if (asn2 == asn)
|
||||
return 1;
|
||||
p += bs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */
|
||||
static int
|
||||
bgp_check_as_path(struct bgp_proto *p, byte *a, int len)
|
||||
validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, unsigned int *ilength)
|
||||
{
|
||||
return bgp_check_path(a, len, p->as4_session ? 4 : 2, 11);
|
||||
int res = 0;
|
||||
u8 *a, *dst;
|
||||
int len, plen, copy;
|
||||
|
||||
dst = a = idata;
|
||||
len = *ilength;
|
||||
|
||||
while (len)
|
||||
{
|
||||
if (len < 2)
|
||||
return -1;
|
||||
|
||||
plen = 2 + bs * a[1];
|
||||
if (len < plen)
|
||||
return -1;
|
||||
|
||||
switch (a[0])
|
||||
{
|
||||
case AS_PATH_SET:
|
||||
copy = 1;
|
||||
res++;
|
||||
break;
|
||||
|
||||
case AS_PATH_SEQUENCE:
|
||||
copy = 1;
|
||||
res += a[1];
|
||||
break;
|
||||
|
||||
case AS_PATH_CONFED_SEQUENCE:
|
||||
case AS_PATH_CONFED_SET:
|
||||
if (as_path && path_segment_contains(a, bs, p->remote_as))
|
||||
{
|
||||
log(L_WARN "%s: AS_CONFED_* segment with peer ASN found, misconfigured confederation?", p->p.name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment",
|
||||
p->p.name, as_path ? "AS" : "AS4");
|
||||
copy = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (copy)
|
||||
{
|
||||
if (dst != a)
|
||||
memmove(dst, a, plen);
|
||||
dst += plen;
|
||||
}
|
||||
|
||||
len -= plen;
|
||||
a += plen;
|
||||
}
|
||||
|
||||
*ilength = dst - idata;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int
|
||||
validate_as_path(struct bgp_proto *p, byte *a, int *len)
|
||||
{
|
||||
return validate_path(p, 1, p->as4_session ? 4 : 2, a, len);
|
||||
}
|
||||
|
||||
static inline int
|
||||
validate_as4_path(struct bgp_proto *p, struct adata *path)
|
||||
{
|
||||
return validate_path(p, 0, 4, path->data, &path->length);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -160,7 +232,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_as_path, NULL },
|
||||
NULL, NULL }, /* is checked by validate_as_path() as a special case */
|
||||
{ "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, 1, /* BA_MULTI_EXIT_DISC */
|
||||
|
@ -1061,73 +1133,6 @@ as4_aggregator_valid(struct adata *aggr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
as4_path_sanitize_and_get_length(struct adata *path)
|
||||
{
|
||||
int res = 0;
|
||||
u8 *p, *dst;
|
||||
int len, plen, copy;
|
||||
|
||||
dst = p = path->data;
|
||||
len = path->length;
|
||||
|
||||
while (len)
|
||||
{
|
||||
if (len <= 2) /* We do not allow empty segments */
|
||||
goto inconsistent_path;
|
||||
|
||||
switch (p[0])
|
||||
{
|
||||
case AS_PATH_SET:
|
||||
plen = 2 + 4 * p[1];
|
||||
copy = 1;
|
||||
res++;
|
||||
break;
|
||||
|
||||
case AS_PATH_SEQUENCE:
|
||||
plen = 2 + 4 * p[1];
|
||||
copy = 1;
|
||||
res += p[1];
|
||||
break;
|
||||
|
||||
case AS_PATH_CONFED_SEQUENCE:
|
||||
case AS_PATH_CONFED_SET:
|
||||
log(L_WARN "BGP: AS4_PATH attribute contains AS_CONFED_* segment, skipping segment");
|
||||
plen = 2 + 4 * p[1];
|
||||
copy = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto unknown_segment;
|
||||
}
|
||||
|
||||
if (len < plen)
|
||||
goto inconsistent_path;
|
||||
|
||||
if (copy)
|
||||
{
|
||||
if (dst != p)
|
||||
memmove(dst, p, plen);
|
||||
dst += plen;
|
||||
}
|
||||
|
||||
len -= plen;
|
||||
p += plen;
|
||||
}
|
||||
|
||||
path->length = dst - path->data;
|
||||
return res;
|
||||
|
||||
inconsistent_path:
|
||||
log(L_WARN "BGP: AS4_PATH attribute is inconsistent, skipping attribute");
|
||||
return -1;
|
||||
|
||||
unknown_segment:
|
||||
log(L_WARN "BGP: AS4_PATH attribute contains unknown segment, skipping attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC 4893 4.2.3 */
|
||||
static void
|
||||
|
@ -1141,7 +1146,7 @@ bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool)
|
|||
|
||||
if (a4 && !as4_aggregator_valid(a4->u.ptr))
|
||||
{
|
||||
log(L_WARN "BGP: AS4_AGGREGATOR attribute is invalid, skipping attribute");
|
||||
log(L_WARN "%s: AS4_AGGREGATOR attribute is invalid, skipping attribute", p->p.name);
|
||||
a4 = NULL;
|
||||
a4_removed = 1;
|
||||
}
|
||||
|
@ -1177,15 +1182,18 @@ bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool)
|
|||
a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
|
||||
|
||||
if ((a2_as == AS_TRANS) && !a4_removed)
|
||||
log(L_WARN "BGP: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing");
|
||||
log(L_WARN "%s: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing", p->p.name);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (a4)
|
||||
log(L_WARN "BGP: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing");
|
||||
log(L_WARN "%s: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing", p->p.name);
|
||||
|
||||
int p2_len = as_path_getlen(p2->u.ptr);
|
||||
int p4_len = p4 ? as4_path_sanitize_and_get_length(p4->u.ptr) : -1;
|
||||
int p4_len = p4 ? validate_as4_path(p, p4->u.ptr) : -1;
|
||||
|
||||
if (p4 && (p4_len < 0))
|
||||
log(L_WARN "%s: AS4_PATH attribute is malformed, skipping attribute", p->p.name);
|
||||
|
||||
if ((p4_len <= 0) || (p2_len < p4_len))
|
||||
p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
|
||||
|
@ -1200,7 +1208,7 @@ bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
|
|||
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 */
|
||||
/* 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;
|
||||
|
@ -1302,6 +1310,12 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
|
|||
if (errcode < 0)
|
||||
continue;
|
||||
}
|
||||
else if (code == BA_AS_PATH)
|
||||
{
|
||||
/* Special case as it might also trim the attribute */
|
||||
if (validate_as_path(bgp, z, &l) < 0)
|
||||
{ errcode = 11; goto err; }
|
||||
}
|
||||
type = desc->type;
|
||||
}
|
||||
else /* Unknown attribute */
|
||||
|
@ -1310,6 +1324,11 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
|
|||
{ errcode = 2; goto err; }
|
||||
type = EAF_TYPE_OPAQUE;
|
||||
}
|
||||
|
||||
// Only OPTIONAL and TRANSITIVE attributes may have non-zero PARTIAL flag
|
||||
// if (!((flags & BAF_OPTIONAL) && (flags & BAF_TRANSITIVE)) && (flags & BAF_PARTIAL))
|
||||
// { errcode = 4; goto err; }
|
||||
|
||||
seen[code/8] |= (1 << (code%8));
|
||||
ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
|
||||
ea->next = a->eattrs;
|
||||
|
|
Loading…
Reference in a new issue