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:
Chris Hall 2010-08-09 22:31:37 +04:00 committed by Denis Ovsienko
parent d64379e8f3
commit cddb8112b8
4 changed files with 131 additions and 91 deletions

View File

@ -671,58 +671,79 @@ aspath_hash_alloc (void *arg)
return aspath; 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 * 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_header segh;
struct assegment *seg, *prev = NULL, *head = NULL; struct assegment *seg, *prev = NULL, *head = NULL;
size_t bytes = 0;
/* empty aspath (ie iBGP or somesuch) */ assert (length > 0); /* does not expect empty AS_PATH or AS4_PATH */
if (length == 0)
return NULL;
if (BGP_DEBUG (as4, AS4_SEGMENT)) if (BGP_DEBUG (as4, AS4_SEGMENT))
zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu", zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
(unsigned long) length); (unsigned long) length);
/* basic checks */
if ( (STREAM_READABLE(s) < length) /* double check that length does not exceed stream */
|| (STREAM_READABLE(s) < AS_HEADER_SIZE) if (STREAM_READABLE(s) < length)
|| (length % AS16_VALUE_SIZE ))
return NULL; return NULL;
while ( (STREAM_READABLE(s) > AS_HEADER_SIZE) /* deal with each segment in turn */
&& (bytes < length)) while (length > 0)
{ {
int i; int i;
int seg_size; size_t seg_size;
/* softly softly, get the header first on its own */ /* softly softly, get the header first on its own */
if (length >= AS_HEADER_SIZE)
{
segh.type = stream_getc (s); segh.type = stream_getc (s);
segh.length = stream_getc (s); segh.length = stream_getc (s);
seg_size = ASSEGMENT_SIZE(segh.length, use32bit); seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
/* includes the header bytes */
if (BGP_DEBUG (as4, AS4_SEGMENT)) if (BGP_DEBUG (as4, AS4_SEGMENT))
zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d", zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
segh.type, segh.length); segh.type, segh.length);
/* check it.. */ switch (segh.type)
if ( ((bytes + seg_size) > length) {
/* 1771bis 4.3b: seg length contains one or more */ case AS_SEQUENCE:
|| (segh.length == 0) case AS_SET:
/* Paranoia in case someone changes type of segment length. break ;
* Shift both values by 0x10 to make the comparison operate
* on more, than 8 bits (otherwise it's a warning, bug #564). 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); assegment_free_all (head);
return NULL; return NULL;
} }
length -= seg_size ;
/* now its safe to trust lengths */ /* now its safe to trust lengths */
seg = assegment_new (segh.type, segh.length); 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++) for (i = 0; i < segh.length; i++)
seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s); seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
bytes += seg_size;
if (BGP_DEBUG (as4, AS4_SEGMENT)) if (BGP_DEBUG (as4, AS4_SEGMENT))
zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu", zlog_debug ("[AS4SEG] Parse aspath segment: length left: %lu",
(unsigned long) bytes); (unsigned long) length);
prev = seg; prev = seg;
} }
@ -746,30 +765,42 @@ assegments_parse (struct stream *s, size_t length, int use32bit)
return assegment_normalise (head); return assegment_normalise (head);
} }
/* AS path parse function. pnt is a pointer to byte stream and length /* AS path parse function -- parses AS_PATH and AS4_PATH attributes
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. */ * 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 * 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 as;
struct aspath *find; struct aspath *find;
/* If length is odd it's malformed AS path. */ /* Parse each segment and construct normalised list of struct assegment */
/* 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;
memset (&as, 0, sizeof (struct aspath)); 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. */ /* If already same aspath exist then return it. */
find = hash_get (ashash, &as, aspath_hash_alloc); 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 /* aspath_hash_alloc dupes segments too. that probably could be
* optimised out. * optimised out.
*/ */
@ -777,8 +808,6 @@ aspath_parse (struct stream *s, size_t length, int use32bit)
if (as.str) if (as.str)
XFREE (MTYPE_AS_STR, as.str); XFREE (MTYPE_AS_STR, as.str);
if (! find)
return NULL;
find->refcnt++; find->refcnt++;
return find; return find;
@ -1602,7 +1631,7 @@ aspath_segment_add (struct aspath *as, int type)
struct aspath * struct aspath *
aspath_empty (void) aspath_empty (void)
{ {
return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */ return aspath_parse (NULL, 0, 1, 0); /* 32Bit ;-) not AS4_PATH */
} }
struct aspath * struct aspath *

View File

@ -65,7 +65,7 @@ struct aspath
/* Prototypes. */ /* Prototypes. */
extern void aspath_init (void); extern void aspath_init (void);
extern void aspath_finish (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_dup (struct aspath *);
extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *); extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *);
extern struct aspath *aspath_prepend (struct aspath *, struct aspath *); extern struct aspath *aspath_prepend (struct aspath *, struct aspath *);

View File

@ -807,54 +807,78 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
return 0; return 0;
} }
/* Parse AS path information. This function is wrapper of aspath_parse.
/* Parse AS path information. This function is wrapper of *
aspath_parse. */ * Parses AS_PATH or AS4_PATH.
static int *
* 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, 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 ((flag & (BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS)) != require)
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
|| ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
{ {
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, 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_send_with_data (peer,
BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ERR,
BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
startp, total); startp, total);
return -1;
}
/* return NULL ;
* peer with AS4 => will get 4Byte ASnums } ;
* otherwise, will get 16 Bit
/* 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, asp = aspath_parse (peer->ibuf, length,
CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)); as4_path || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV), as4_path) ;
/* In case of IBGP, length will be zero. */ if (asp != NULL)
if (! attr->aspath) {
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); 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_send (peer,
BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ERR,
BGP_NOTIFY_UPDATE_MAL_AS_PATH); BGP_NOTIFY_UPDATE_MAL_AS_PATH);
return -1; } ;
}
/* Forward pointer. */ return asp ;
/* stream_forward_getp (peer->ibuf, length);*/
/* Set aspath attribute flag. */
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
return 0;
} }
static int bgp_attr_aspath_check( struct peer *peer, 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. */ /* Nexthop attribute. */
static int static int
bgp_attr_nexthop (struct peer *peer, bgp_size_t length, 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); ret = bgp_attr_origin (peer, length, attr, flag, startp);
break; break;
case BGP_ATTR_AS_PATH: 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; break;
case BGP_ATTR_AS4_PATH: 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; break;
case BGP_ATTR_NEXT_HOP: case BGP_ATTR_NEXT_HOP:
ret = bgp_attr_nexthop (peer, length, attr, flag, startp); ret = bgp_attr_nexthop (peer, length, attr, flag, startp);

View File

@ -649,7 +649,7 @@ make_aspath (const u_char *data, size_t len, int use32bit)
s = stream_new (len); s = stream_new (len);
stream_put (s, data, len); stream_put (s, data, len);
} }
as = aspath_parse (s, len, use32bit); as = aspath_parse (s, len, use32bit, 0);
if (s) if (s)
stream_free (s); stream_free (s);