mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-03 08:56:13 +00:00
bgpd: fix handling of AS path data
* bgpd/bgp_aspath.c * assegments_parse(): add handling of AS4_PATH input, update bounds checks, add check for AS segment type * aspath_parse(): add handling of AS4_PATH input, expect assegments_parse() to do length checking * aspath_empty(): update for the new function prototype * bgpd/bgp_aspath.h: ditto * tests/aspath_test.c: ditto * bgpd/bgp_attr.c * bgp_attr_aspath(): add handling of AS4_PATH input, update flags checks, change returned type * bgp_attr_as4_path(): discard, superseded by bgp_attr_aspath() * bgp_attr_parse(): update respectively
This commit is contained in:
parent
d64379e8f3
commit
cddb8112b8
@ -671,58 +671,79 @@ aspath_hash_alloc (void *arg)
|
||||
return aspath;
|
||||
}
|
||||
|
||||
/* parse as-segment byte stream in struct assegment */
|
||||
/* parse as-segment byte stream in struct assegment
|
||||
*
|
||||
* Returns NULL if the AS_PATH or AS4_PATH is not valid.
|
||||
*/
|
||||
static struct assegment *
|
||||
assegments_parse (struct stream *s, size_t length, int use32bit)
|
||||
assegments_parse (struct stream *s, size_t length, int use32bit, int as4_path)
|
||||
{
|
||||
struct assegment_header segh;
|
||||
struct assegment *seg, *prev = NULL, *head = NULL;
|
||||
size_t bytes = 0;
|
||||
|
||||
/* empty aspath (ie iBGP or somesuch) */
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
assert (length > 0); /* does not expect empty AS_PATH or AS4_PATH */
|
||||
|
||||
if (BGP_DEBUG (as4, AS4_SEGMENT))
|
||||
zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
|
||||
(unsigned long) length);
|
||||
/* basic checks */
|
||||
if ( (STREAM_READABLE(s) < length)
|
||||
|| (STREAM_READABLE(s) < AS_HEADER_SIZE)
|
||||
|| (length % AS16_VALUE_SIZE ))
|
||||
|
||||
/* double check that length does not exceed stream */
|
||||
if (STREAM_READABLE(s) < length)
|
||||
return NULL;
|
||||
|
||||
while ( (STREAM_READABLE(s) > AS_HEADER_SIZE)
|
||||
&& (bytes < length))
|
||||
/* deal with each segment in turn */
|
||||
while (length > 0)
|
||||
{
|
||||
int i;
|
||||
int seg_size;
|
||||
size_t seg_size;
|
||||
|
||||
/* softly softly, get the header first on its own */
|
||||
if (length >= AS_HEADER_SIZE)
|
||||
{
|
||||
segh.type = stream_getc (s);
|
||||
segh.length = stream_getc (s);
|
||||
|
||||
seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
|
||||
/* includes the header bytes */
|
||||
|
||||
if (BGP_DEBUG (as4, AS4_SEGMENT))
|
||||
zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
|
||||
segh.type, segh.length);
|
||||
|
||||
/* check it.. */
|
||||
if ( ((bytes + seg_size) > length)
|
||||
/* 1771bis 4.3b: seg length contains one or more */
|
||||
|| (segh.length == 0)
|
||||
/* Paranoia in case someone changes type of segment length.
|
||||
* Shift both values by 0x10 to make the comparison operate
|
||||
* on more, than 8 bits (otherwise it's a warning, bug #564).
|
||||
switch (segh.type)
|
||||
{
|
||||
case AS_SEQUENCE:
|
||||
case AS_SET:
|
||||
break ;
|
||||
|
||||
case AS_CONFED_SEQUENCE:
|
||||
case AS_CONFED_SET:
|
||||
if (!as4_path)
|
||||
break ;
|
||||
/* RFC4893 3: "invalid for the AS4_PATH attribute" */
|
||||
/* fall through */
|
||||
|
||||
default: /* reject unknown or invalid AS_PATH segment types */
|
||||
seg_size = 0 ;
|
||||
} ;
|
||||
}
|
||||
else
|
||||
seg_size = 0 ;
|
||||
|
||||
/* Stop now if segment is not valid (discarding anything collected to date)
|
||||
*
|
||||
* RFC4271 4.3, Path Attributes, b) AS_PATH:
|
||||
*
|
||||
* "path segment value field contains one or more AS numbers"
|
||||
*/
|
||||
|| ((sizeof segh.length > 1) && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX)) )
|
||||
if ((seg_size == 0) || (seg_size > length) || (segh.length == 0))
|
||||
{
|
||||
if (head)
|
||||
assegment_free_all (head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length -= seg_size ;
|
||||
|
||||
/* now its safe to trust lengths */
|
||||
seg = assegment_new (segh.type, segh.length);
|
||||
|
||||
@ -734,11 +755,9 @@ assegments_parse (struct stream *s, size_t length, int use32bit)
|
||||
for (i = 0; i < segh.length; i++)
|
||||
seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
|
||||
|
||||
bytes += seg_size;
|
||||
|
||||
if (BGP_DEBUG (as4, AS4_SEGMENT))
|
||||
zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu",
|
||||
(unsigned long) bytes);
|
||||
zlog_debug ("[AS4SEG] Parse aspath segment: length left: %lu",
|
||||
(unsigned long) length);
|
||||
|
||||
prev = seg;
|
||||
}
|
||||
@ -746,30 +765,42 @@ assegments_parse (struct stream *s, size_t length, int use32bit)
|
||||
return assegment_normalise (head);
|
||||
}
|
||||
|
||||
/* AS path parse function. pnt is a pointer to byte stream and length
|
||||
is length of byte stream. If there is same AS path in the the AS
|
||||
path hash then return it else make new AS path structure. */
|
||||
/* AS path parse function -- parses AS_PATH and AS4_PATH attributes
|
||||
*
|
||||
* Requires: s -- stream, currently positioned before first segment
|
||||
* of AS_PATH or AS4_PATH (ie after attribute header)
|
||||
* length -- length of the value of the AS_PATH or AS4_PATH
|
||||
* use32bit -- true <=> 4Byte ASN, otherwise 2Byte ASN
|
||||
* as4_path -- true <=> AS4_PATH, otherwise AS_PATH
|
||||
*
|
||||
* Returns: if valid: address of struct aspath in the hash of known aspaths,
|
||||
* with reference count incremented.
|
||||
* else: NULL
|
||||
*
|
||||
* NB: empty AS path (length == 0) is valid. The returned struct aspath will
|
||||
* have segments == NULL and str == zero length string (unique).
|
||||
*/
|
||||
struct aspath *
|
||||
aspath_parse (struct stream *s, size_t length, int use32bit)
|
||||
aspath_parse (struct stream *s, size_t length, int use32bit, int as4_path)
|
||||
{
|
||||
struct aspath as;
|
||||
struct aspath *find;
|
||||
|
||||
/* If length is odd it's malformed AS path. */
|
||||
/* Nit-picking: if (use32bit == 0) it is malformed if odd,
|
||||
* otherwise its malformed when length is larger than 2 and (length-2)
|
||||
* is not dividable by 4.
|
||||
* But... this time we're lazy
|
||||
*/
|
||||
if (length % AS16_VALUE_SIZE )
|
||||
return NULL;
|
||||
|
||||
/* Parse each segment and construct normalised list of struct assegment */
|
||||
memset (&as, 0, sizeof (struct aspath));
|
||||
as.segments = assegments_parse (s, length, use32bit);
|
||||
if (length != 0)
|
||||
{
|
||||
as.segments = assegments_parse (s, length, use32bit, as4_path);
|
||||
|
||||
if (as.segments == NULL)
|
||||
return NULL ; /* Invalid AS_PATH or AS4_PATH */
|
||||
} ;
|
||||
|
||||
/* If already same aspath exist then return it. */
|
||||
find = hash_get (ashash, &as, aspath_hash_alloc);
|
||||
|
||||
assert(find) ; /* valid aspath, so must find or create */
|
||||
|
||||
/* aspath_hash_alloc dupes segments too. that probably could be
|
||||
* optimised out.
|
||||
*/
|
||||
@ -777,8 +808,6 @@ aspath_parse (struct stream *s, size_t length, int use32bit)
|
||||
if (as.str)
|
||||
XFREE (MTYPE_AS_STR, as.str);
|
||||
|
||||
if (! find)
|
||||
return NULL;
|
||||
find->refcnt++;
|
||||
|
||||
return find;
|
||||
@ -1602,7 +1631,7 @@ aspath_segment_add (struct aspath *as, int type)
|
||||
struct aspath *
|
||||
aspath_empty (void)
|
||||
{
|
||||
return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */
|
||||
return aspath_parse (NULL, 0, 1, 0); /* 32Bit ;-) not AS4_PATH */
|
||||
}
|
||||
|
||||
struct aspath *
|
||||
|
@ -65,7 +65,7 @@ struct aspath
|
||||
/* Prototypes. */
|
||||
extern void aspath_init (void);
|
||||
extern void aspath_finish (void);
|
||||
extern struct aspath *aspath_parse (struct stream *, size_t, int);
|
||||
extern struct aspath *aspath_parse (struct stream *, size_t, int, int);
|
||||
extern struct aspath *aspath_dup (struct aspath *);
|
||||
extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *);
|
||||
extern struct aspath *aspath_prepend (struct aspath *, struct aspath *);
|
||||
|
103
bgpd/bgp_attr.c
103
bgpd/bgp_attr.c
@ -807,54 +807,78 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse AS path information. This function is wrapper of
|
||||
aspath_parse. */
|
||||
static int
|
||||
/* Parse AS path information. This function is wrapper of aspath_parse.
|
||||
*
|
||||
* Parses AS_PATH or AS4_PATH.
|
||||
*
|
||||
* Returns: if valid: address of struct aspath in the hash of known aspaths,
|
||||
* with reference count incremented.
|
||||
* else: NULL
|
||||
*
|
||||
* NB: empty AS path (length == 0) is valid. The returned struct aspath will
|
||||
* have segments == NULL and str == zero length string (unique).
|
||||
*/
|
||||
static struct aspath *
|
||||
bgp_attr_aspath (struct peer *peer, bgp_size_t length,
|
||||
struct attr *attr, u_char flag, u_char *startp)
|
||||
struct attr *attr, u_char flag, u_char *startp, int as4_path)
|
||||
{
|
||||
bgp_size_t total;
|
||||
u_char require ;
|
||||
struct aspath *asp ;
|
||||
|
||||
total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
|
||||
/* Check the attribute flags */
|
||||
require = as4_path ? BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
|
||||
: BGP_ATTR_FLAG_TRANS ;
|
||||
|
||||
/* Flag check. */
|
||||
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
|
||||
|| ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
|
||||
if ((flag & (BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS)) != require)
|
||||
{
|
||||
const char* path_type ;
|
||||
bgp_size_t total;
|
||||
|
||||
path_type = as4_path ? "AS4_PATH" : "AS_PATH" ;
|
||||
|
||||
if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS))
|
||||
zlog (peer->log, LOG_ERR,
|
||||
"As-Path attribute flag isn't transitive %d", flag);
|
||||
"%s attribute flag isn't transitive %d", path_type, flag) ;
|
||||
|
||||
if ((flag & BGP_ATTR_FLAG_OPTIONAL) != (require & BGP_ATTR_FLAG_OPTIONAL))
|
||||
zlog (peer->log, LOG_ERR,
|
||||
"%s attribute flag must %sbe optional %d", path_type,
|
||||
(flag & BGP_ATTR_FLAG_OPTIONAL) ? "not " : "", flag) ;
|
||||
|
||||
total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
|
||||
|
||||
bgp_notify_send_with_data (peer,
|
||||
BGP_NOTIFY_UPDATE_ERR,
|
||||
BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
|
||||
startp, total);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* peer with AS4 => will get 4Byte ASnums
|
||||
* otherwise, will get 16 Bit
|
||||
return NULL ;
|
||||
} ;
|
||||
|
||||
/* Parse the AS_PATH/AS4_PATH body.
|
||||
*
|
||||
* For AS_PATH peer with AS4 => 4Byte ASN otherwise 2Byte ASN
|
||||
* AS4_PATH 4Byte ASN
|
||||
*/
|
||||
attr->aspath = aspath_parse (peer->ibuf, length,
|
||||
CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV));
|
||||
asp = aspath_parse (peer->ibuf, length,
|
||||
as4_path || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV), as4_path) ;
|
||||
|
||||
/* In case of IBGP, length will be zero. */
|
||||
if (! attr->aspath)
|
||||
if (asp != NULL)
|
||||
{
|
||||
attr->flag |= ATTR_FLAG_BIT (as4_path ? BGP_ATTR_AS4_PATH
|
||||
: BGP_ATTR_AS_PATH) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length);
|
||||
|
||||
/* TODO: should BGP_NOTIFY_UPDATE_MAL_AS_PATH be sent for AS4_PATH ?? */
|
||||
bgp_notify_send (peer,
|
||||
BGP_NOTIFY_UPDATE_ERR,
|
||||
BGP_NOTIFY_UPDATE_MAL_AS_PATH);
|
||||
return -1;
|
||||
}
|
||||
} ;
|
||||
|
||||
/* Forward pointer. */
|
||||
/* stream_forward_getp (peer->ibuf, length);*/
|
||||
|
||||
/* Set aspath attribute flag. */
|
||||
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
|
||||
|
||||
return 0;
|
||||
return asp ;
|
||||
}
|
||||
|
||||
static int bgp_attr_aspath_check( struct peer *peer,
|
||||
@ -912,21 +936,6 @@ static int bgp_attr_aspath_check( struct peer *peer,
|
||||
|
||||
}
|
||||
|
||||
/* Parse AS4 path information. This function is another wrapper of
|
||||
aspath_parse. */
|
||||
static int
|
||||
bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
|
||||
struct attr *attr, struct aspath **as4_path)
|
||||
{
|
||||
*as4_path = aspath_parse (peer->ibuf, length, 1);
|
||||
|
||||
/* Set aspath attribute flag. */
|
||||
if (as4_path)
|
||||
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nexthop attribute. */
|
||||
static int
|
||||
bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
|
||||
@ -1657,10 +1666,12 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
|
||||
ret = bgp_attr_origin (peer, length, attr, flag, startp);
|
||||
break;
|
||||
case BGP_ATTR_AS_PATH:
|
||||
ret = bgp_attr_aspath (peer, length, attr, flag, startp);
|
||||
attr->aspath = bgp_attr_aspath (peer, length, attr, flag, startp, 0);
|
||||
ret = attr->aspath ? 0 : -1 ;
|
||||
break;
|
||||
case BGP_ATTR_AS4_PATH:
|
||||
ret = bgp_attr_as4_path (peer, length, attr, &as4_path );
|
||||
as4_path = bgp_attr_aspath (peer, length, attr, flag, startp, 1);
|
||||
ret = as4_path ? 0 : -1 ;
|
||||
break;
|
||||
case BGP_ATTR_NEXT_HOP:
|
||||
ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
|
||||
|
@ -649,7 +649,7 @@ make_aspath (const u_char *data, size_t len, int use32bit)
|
||||
s = stream_new (len);
|
||||
stream_put (s, data, len);
|
||||
}
|
||||
as = aspath_parse (s, len, use32bit);
|
||||
as = aspath_parse (s, len, use32bit, 0);
|
||||
|
||||
if (s)
|
||||
stream_free (s);
|
||||
|
Loading…
Reference in New Issue
Block a user