From 0b2aa3a0a8b095bdef1eddda117d173af75dede2 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Sun, 14 Oct 2007 22:32:21 +0000 Subject: [bgpd] Merge AS4 support 2007-10-14 Paul Jakma * NEWS: Note that MRT dumps are now version 2 * (general) Merge in Juergen Kammer's AS4 patch. 2007-09-27 Paul Jakma * bgp_aspath.c: (assegment_normalise) remove duplicates from from sets. (aspath_reconcile_as4) disregard a broken part of the RFC around error handling in path reconciliation. * aspath_test.c: Test dupe-weeding from sets. Test that reconciliation merges AS_PATH and AS4_PATH where former is shorter than latter. 2007-09-26 Paul Jakma * aspath_test.c: Test AS4_PATH reconcilation where length of AS_PATH and AS4_PATH is same. 2007-09-25 Paul Jakma * bgp_open.c: (peek_for_as4_capability) Fix to work. * bgp_packet.c: (bgp_open_receive) Fix sanity check of as4. * tests/bgp_capability_test.c: (general) Extend tests to validate peek_for_as4_capability. Add test of full OPEN Option block, with multiple capabilities, both as a series of Option, and a single option. Add some crap to beginning of stream, to prevent code depending on getp == 0. 2007-09-18 Paul Jakma * bgp_open.c: (bgp_capability_as4) debug printf inline with others. (peek_for_as4_capability) There's no need to signal failure, as failure is better dealt with through full capability parser - just return the AS4, simpler. * bgp_packet.c: (bgp_open_receive) Update to match peek_for_as4_capability change. Allow use of BGP_AS_TRANS by 2b speakers. Use NOTIFY_OPEN_ERR rather than CEASE for OPEN parsing errors. (bgp_capability_msg_parse) missing argument to debug print (bgp_capability_receive) missing return values. * tests/bgp_capability_test.c: (parse_test) update for changes to peek_for_as4_capability 2007-07-25 Paul Jakma * Remove 2-byte size macros, just make existing macros take argument to indicate which size to use. Adjust all users - typically they want '1'. * bgp_aspath.c: (aspath_has_as4) New, return 1 if there are any as4's in a path. (aspath_put) Return the number of bytes actually written, to fix the bug Juergen noted: Splitting of segments will change the number of bytes written from that already written to the AS_PATH header. (aspath_snmp_pathseg) Pass 2-byte flag to aspath_put. SNMP is still defined as 2b. (aspath_aggregate) fix latent bug. (aspath_reconcile_as4) AS_PATH+NEW_AS_PATH reconciliation function. (aspath_key_make) Hash the AS_PATH string, rather than just taking the addition of assegment ASes as the hash value, hopefully sligthly more collision resistant. (bgp_attr_munge_as4_attrs) Collide the NEW_ attributes together with the OLD 2-byte forms, code Juergen had in bgp_attr_parse but re-organised a bit. (bgp_attr_parse) Bunch of code from Juergen moves to previous function. (bgp_packet_attribute) Compact significantly by just /always/ using extended-length attr header. Fix bug Juergen noted, by using aspath_put's (new) returned size value for the attr header rather than the (guesstimate) of aspath_size() - the two could differ when aspath_put had to split large segments, unlikely this bug was ever hit in the 'wild'. (bgp_dump_routes_attr) Always use extended-len and use aspath_put return for header length. Output 4b ASN for AS_PATH and AGGREGATOR. * bgp_ecommunity.c: (ecommunity_{hash_make,cmp}) fix hash callback declarations to match prototypes. (ecommunity_gettoken) Updated for ECOMMUNITY_ENCODE_AS4, complete rewrite of Juergen's changes (no asdot support) * bgp_open.c: (bgp_capability_as4) New, does what it says on the tin. (peek_for_as4_capability) Rewritten to use streams and bgp_capability_as4. * bgp_packet.c: (bgp_open_send) minor edit checked (in the abstract at least) with Juergen. Changes are to be more accepting, e.g, allow AS_TRANS on a 2-byte session. * (general) Update all commands to use CMD_AS_RANGE. * bgp_vty.c: (bgp_clear) Fix return vals to use CMD_.. Remove stuff replicated by VTY_GET_LONG (bgp_clear_vty) Return bgp_clear directly to vty. * tests/aspath_test.c: Exercise 32bit parsing. Test reconcile function. * tests/ecommunity_test.c: New, test AS4 ecommunity changes, positive test only at this time, error cases not tested yet. 2007-07-25 Juergen Kammer * (general) AS4 support. * bgpd.h: as_t changes to 4-bytes. * bgp_aspath.h: Add BGP_AS4_MAX and BGP_AS_TRANS defines. * bgp_aspath.c: AS_VALUE_SIZE becomes 4-byte, AS16_VALUE_SIZE added for 2-byte. Add AS16 versions of length calc macros. (aspath_count_numas) New, count number of ASes. (aspath_has_as4) New, return 1 if there are any as4's in a path. (assegments_parse) Interpret assegment as 4 or 2 byte, according to how the caller instructs us, with a new argument. (aspath_parse) Add use32bit argument to pass to assegments_parse. Adjust all its callers to pass 1, unless otherwise noted. (assegment_data_put) Adjust to be able to write 2 or 4 byte AS, according to new use32bit argument. (aspath_put) Adjust to write 2 or 4. (aspath_gettoken) Use a long for passed in asno. * bgp_attr.c: (attr_str) Add BGP_ATTR_AS4_PATH and BGP_ATTR_AS4_AGGREGATOR. (bgp_attr_aspath) Call aspath_parse with right 2/4 arg, as determined by received-capability flag. (bgp_attr_aspath_check) New, code previously in attr_aspath but moved to new func so it can be run after NEW_AS_PATH reconciliation. (bgp_attr_as4_path) New, handle NEW_AS_PATH. (bgp_attr_aggregator) Adjust to cope with 2/4 byte ASes. (bgp_attr_as4_aggregator) New, read NEW_AGGREGATOR. (bgp_attr_parse) Add handoffs to previous parsers for the two new AS4 NEW_ attributes. Various checks added for NEW/OLD reconciliation. (bgp_packet_attribute) Support 2/4 for AS_PATH and AGGREGATOR, detect when NEW_ attrs need to be sent. * bgp_debug.{c,h}: Add 'debug bgp as4'. * bgp_dump.c: MRTv2 support, unconditionally enabled, which supports AS4. Based on patches from Erik (RIPE?). * bgp_ecommunity.c: (ecommunity_ecom2str) ECOMMUNITY_ENCODE_AS4 support. * bgp_open.c: (peek_for_as4_capability) New, peek for AS4 capability prior to full capability parsing, so we know which ASN to use for struct peer lookup. (bgp_open_capability) Always send AS4 capability. * bgp_packet.c: (bgp_open_send) AS4 handling for AS field (bgp_open_receive) Peek for AS4 capability first, and figure out which AS to believe. * bgp_vty.c: (bgp_show_peer) Print AS4 cap * tests/aspath_test.c: Support asn32 changes, call aspath_parse with 16 bit. * vtysh/extract.pl: AS4 compatibility for router bgp ASNUMBER * vtysh/extract.pl.in: AS4 compatibility for router bgp ASNUMBER * vtysh/vtysh.c: AS4 compatibility for router bgp ASNUMBER --- ChangeLog | 4 + NEWS | 6 + bgpd/ChangeLog | 128 ++++++++++++++ bgpd/bgp_aspath.c | 357 ++++++++++++++++++++++++++++++-------- bgpd/bgp_aspath.h | 14 +- bgpd/bgp_attr.c | 411 ++++++++++++++++++++++++++++++++++++++------ bgpd/bgp_debug.c | 118 +++++++++++++ bgpd/bgp_debug.h | 7 + bgpd/bgp_dump.c | 265 +++++++++++++++++----------- bgpd/bgp_dump.h | 22 ++- bgpd/bgp_ecommunity.c | 130 ++++++++++---- bgpd/bgp_ecommunity.h | 5 +- bgpd/bgp_open.c | 116 +++++++++++++ bgpd/bgp_open.h | 6 + bgpd/bgp_packet.c | 88 ++++++++-- bgpd/bgp_routemap.c | 8 +- bgpd/bgp_vty.c | 175 ++++++++++--------- bgpd/bgp_vty.h | 2 + bgpd/bgpd.h | 12 +- doc/BGP-TypeCode | 2 + tests/ChangeLog | 36 ++++ tests/Makefile.am | 4 +- tests/aspath_test.c | 245 ++++++++++++++++++++++---- tests/bgp_capability_test.c | 157 ++++++++++++++--- vtysh/ChangeLog | 5 + vtysh/extract.pl.in | 4 +- vtysh/vtysh.c | 2 +- 27 files changed, 1897 insertions(+), 432 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2501a078..6cde4262 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2007-10-14 Paul Jakma + + * NEWS: Note that MRT dumps are now version 2 + 2007-09-07 Paul Jakma * configure.ac: Bump version to 0.99.9 diff --git a/NEWS b/NEWS index 0a9269c5..e045ec10 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,11 @@ * Changes in Quagga 0.99.2 +- [bgpd] 4-byte AS support added +- [bgpd] MRT format changes to version 2. Those relying on + bgpd MRT table dumps may need to update their tools. + +* Changes in Quagga 0.99.2 + - [bgpd] Work queues added to bgpd to split up update processing, particularly beneficial when a peer session goes down. AS_PATH parsing rewritten to be clearer, more robust and ready for 4-byte. diff --git a/bgpd/ChangeLog b/bgpd/ChangeLog index c98485b4..5a31d9cb 100644 --- a/bgpd/ChangeLog +++ b/bgpd/ChangeLog @@ -1,3 +1,131 @@ +2007-09-27 Paul Jakma + + * bgp_aspath.c: (assegment_normalise) remove duplicates from + from sets. + (aspath_reconcile_as4) disregard a broken part of the RFC around + error handling in path reconciliation. + +2007-09-25 Paul Jakma + + * bgp_open.c: (peek_for_as4_capability) Fix to work. + * bgp_packet.c: (bgp_open_receive) Fix sanity check of as4. + +2007-09-18 Paul Jakma + + * bgp_open.c: (bgp_capability_as4) debug printf inline with others. + (peek_for_as4_capability) There's no need to signal failure, as + failure is better dealt with through full capability parser - + just return the AS4, simpler. + * bgp_packet.c: (bgp_open_receive) Update to match + peek_for_as4_capability change. + Allow use of BGP_AS_TRANS by 2b speakers. + Use NOTIFY_OPEN_ERR rather than CEASE for OPEN parsing errors. + (bgp_capability_msg_parse) missing argument to debug print + (bgp_capability_receive) missing return values. + + +2007-07-25 Paul Jakma + + * Remove 2-byte size macros, just make existing macros take + argument to indicate which size to use. + Adjust all users - typically they want '1'. + * bgp_aspath.c: (aspath_has_as4) New, return 1 if there are any + as4's in a path. + (aspath_put) Return the number of bytes actually written, to + fix the bug Juergen noted: Splitting of segments will change + the number of bytes written from that already written to the + AS_PATH header. + (aspath_snmp_pathseg) Pass 2-byte flag to aspath_put. SNMP + is still defined as 2b. + (aspath_aggregate) fix latent bug. + (aspath_reconcile_as4) AS_PATH+NEW_AS_PATH reconciliation + function. + (aspath_key_make) Hash the AS_PATH string, rather than + just taking the addition of assegment ASes as the hash value, + hopefully sligthly more collision resistant. + (bgp_attr_munge_as4_attrs) Collide the NEW_ attributes + together with the OLD 2-byte forms, code Juergen + had in bgp_attr_parse but re-organised a bit. + (bgp_attr_parse) Bunch of code from Juergen moves + to previous function. + (bgp_packet_attribute) Compact significantly by + just /always/ using extended-length attr header. + Fix bug Juergen noted, by using aspath_put's + (new) returned size value for the attr header rather + than the (guesstimate) of aspath_size() - the two could + differ when aspath_put had to split large segments, unlikely + this bug was ever hit in the 'wild'. + (bgp_dump_routes_attr) Always use extended-len and + use aspath_put return for header length. Output 4b ASN + for AS_PATH and AGGREGATOR. + * bgp_ecommunity.c: (ecommunity_{hash_make,cmp}) fix + hash callback declarations to match prototypes. + (ecommunity_gettoken) Updated for ECOMMUNITY_ENCODE_AS4, + complete rewrite of Juergen's changes (no asdot support) + * bgp_open.c: (bgp_capability_as4) New, does what it says + on the tin. + (peek_for_as4_capability) Rewritten to use streams and + bgp_capability_as4. + * bgp_packet.c: (bgp_open_send) minor edit + checked (in the abstract at least) with Juergen. + Changes are to be more accepting, e.g, allow AS_TRANS on + a 2-byte session. + * (general) Update all commands to use CMD_AS_RANGE. + * bgp_vty.c: (bgp_clear) Fix return vals to use CMD_.. + Remove stuff replicated by VTY_GET_LONG + (bgp_clear_vty) Return bgp_clear directly to vty. + + +2007-07-25 Juergen Kammer + + * (general) AS4 support. + * bgpd.h: as_t changes to 4-bytes. + * bgp_aspath.h: Add BGP_AS4_MAX and BGP_AS_TRANS defines. + * bgp_aspath.c: AS_VALUE_SIZE becomes 4-byte, AS16_VALUE_SIZE + added for 2-byte. + Add AS16 versions of length calc macros. + (aspath_count_numas) New, count number of ASes. + (aspath_has_as4) New, return 1 if there are any as4's in a + path. + (assegments_parse) Interpret assegment as 4 or 2 byte, + according to how the caller instructs us, with a new + argument. + (aspath_parse) Add use32bit argument to pass to + assegments_parse. Adjust all its callers to pass 1, unless + otherwise noted. + (assegment_data_put) Adjust to be able to write 2 or 4 byte + AS, according to new use32bit argument. + (aspath_put) Adjust to write 2 or 4. + (aspath_gettoken) Use a long for passed in asno. + * bgp_attr.c: (attr_str) Add BGP_ATTR_AS4_PATH and + BGP_ATTR_AS4_AGGREGATOR. + (bgp_attr_aspath) Call aspath_parse with right 2/4 arg, as + determined by received-capability flag. + (bgp_attr_aspath_check) New, code previously in attr_aspath + but moved to new func so it can be run after NEW_AS_PATH + reconciliation. + (bgp_attr_as4_path) New, handle NEW_AS_PATH. + (bgp_attr_aggregator) Adjust to cope with 2/4 byte ASes. + (bgp_attr_as4_aggregator) New, read NEW_AGGREGATOR. + (bgp_attr_parse) Add handoffs to previous parsers for the two + new AS4 NEW_ attributes. + Various checks added for NEW/OLD reconciliation. + (bgp_packet_attribute) Support 2/4 for AS_PATH and + AGGREGATOR, detect when NEW_ attrs need to be sent. + * bgp_debug.{c,h}: Add 'debug bgp as4'. + * bgp_dump.c: MRTv2 support, unconditionally enabled, which + supports AS4. Based on patches from Erik (RIPE?). + * bgp_ecommunity.c: (ecommunity_ecom2str) ECOMMUNITY_ENCODE_AS4 + support. + * bgp_open.c: (peek_for_as4_capability) New, peek for AS4 + capability prior to full capability parsing, so we know which + ASN to use for struct peer lookup. + (bgp_open_capability) Always send AS4 capability. + * bgp_packet.c: (bgp_open_send) AS4 handling for AS field + (bgp_open_receive) Peek for AS4 capability first, and figure + out which AS to believe. + * bgp_vty.c: (bgp_show_peer) Print AS4 cap + 2007-09-17 Paul Jakma * bgp_open.c: (bgp_capability_mp) We were setting diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index b328e38a..d7e985d4 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -28,15 +28,20 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "str.h" #include "log.h" #include "stream.h" +#include "jhash.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_attr.h" /* Attr. Flags and Attr. Type Code. */ #define AS_HEADER_SIZE 2 -/* Two octet is used for AS value. */ +/* Now FOUR octets are used for AS value. */ #define AS_VALUE_SIZE sizeof (as_t) +/* This is the old one */ +#define AS16_VALUE_SIZE sizeof (as16_t) /* Maximum protocol segment length value */ #define AS_SEGMENT_MAX 255 @@ -46,16 +51,20 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * sizes and lengths. At present (200508) they sort of match, however * the ONLY functions which should now about the on-wire syntax are * aspath_put, assegment_put and assegment_parse. + * + * aspath_put returns bytes written, the only definitive record of + * size of wire-format attribute.. */ /* Calculated size in bytes of ASN segment data to hold N ASN's */ -#define ASSEGMENT_DATA_SIZE(N) ((N) * AS_VALUE_SIZE) +#define ASSEGMENT_DATA_SIZE(N,S) \ + ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) ) /* Calculated size of segment struct to hold N ASN's */ -#define ASSEGMENT_SIZE(N) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N)) +#define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S)) /* AS segment octet length. */ -#define ASSEGMENT_LEN(X) ASSEGMENT_SIZE((X)->length) +#define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S) /* AS_SEQUENCE segments can be packed together */ /* Can the types of X and Y be considered for packing? */ @@ -85,7 +94,7 @@ static struct stream *snmp_stream; static inline as_t * assegment_data_new (int num) { - return (XCALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num))); + return (XCALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1))); } static inline void @@ -150,7 +159,7 @@ assegment_dup (struct assegment *seg) struct assegment *new; new = assegment_new (seg->type, seg->length); - memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length) ); + memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) ); return new; } @@ -197,7 +206,7 @@ assegment_prepend_asns (struct assegment *seg, as_t asnum, int num) for (i = 0; i < num; i++) newas[i] = asnum; - memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length)); + memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1)); XFREE (MTYPE_AS_SEG_DATA, seg->as); seg->as = newas; seg->length += num; @@ -215,12 +224,12 @@ assegment_append_asns (struct assegment *seg, as_t *asnos, int num) as_t *newas; newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as, - ASSEGMENT_DATA_SIZE (seg->length + num)); + ASSEGMENT_DATA_SIZE (seg->length + num, 1)); if (newas) { seg->as = newas; - memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num)); + memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1)); seg->length += num; return seg; } @@ -263,7 +272,26 @@ assegment_normalise (struct assegment *head) * and because it helps other lesser implementations ;) */ if (seg->type == AS_SET || seg->type == AS_CONFED_SET) - qsort (seg->as, seg->length, sizeof(as_t), int_cmp); + { + int tail = 0; + int i; + + qsort (seg->as, seg->length, sizeof(as_t), int_cmp); + + /* weed out dupes */ + for (i=1; i < seg->length; i++) + { + if (seg->as[tail] == seg->as[i]) + continue; + + tail++; + if (tail < i) + seg->as[tail] = seg->as[i]; + } + /* seg->length can be 0.. */ + if (seg->length) + seg->length = tail + 1; + } /* read ahead from the current, pinned segment while the segments * are packable/mergeable. Append all following packable segments @@ -420,6 +448,12 @@ aspath_count_hops (struct aspath *aspath) return count; } +/* Estimate size aspath /might/ take if encoded into an + * ASPATH attribute. + * + * This is a quick estimate, not definitive! aspath_put() + * may return a different number!! + */ unsigned int aspath_size (struct aspath *aspath) { @@ -428,7 +462,7 @@ aspath_size (struct aspath *aspath) while (seg) { - size += ASSEGMENT_SIZE(seg->length); + size += ASSEGMENT_SIZE(seg->length, 1); seg = seg->next; } return size; @@ -454,6 +488,39 @@ aspath_highest (struct aspath *aspath) return highest; } +/* Return 1 if there are any 4-byte ASes in the path */ +unsigned int +aspath_has_as4 (struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + unsigned int i; + + while (seg) + { + for (i = 0; i < seg->length; i++) + if (seg->as[i] > BGP_AS_MAX) + return 1; + seg = seg->next; + } + return 0; +} + +/* Return number of as numbers in in path */ +unsigned int +aspath_count_numas (struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + unsigned int num; + + num=0; + while (seg) + { + num += seg->length; + seg = seg->next; + } + return num; +} + /* Convert aspath structure to string expression. */ static char * aspath_make_str_count (struct aspath *as) @@ -478,6 +545,9 @@ aspath_make_str_count (struct aspath *as) * 2 chars for segment delimiters, and the final '\0'. * Hopefully this is large enough to avoid hitting the realloc * code below for most common sequences. + * + * With 32bit ASNs, this range will increase, but only worth changing + * once there are significant numbers of ASN >= 100000 */ #define ASN_STR_LEN (5 + 1) str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1, @@ -510,6 +580,9 @@ aspath_make_str_count (struct aspath *as) * have been wrong. need 5 chars for ASN, a seperator each and * potentially two segment delimiters, plus a space between each * segment and trailing zero. + * + * This may need to revised if/when significant numbers of + * ASNs >= 100000 are assigned and in-use on the internet... */ #define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1) if ( (len + SEGMENT_STR_LEN(seg)) > str_size) @@ -605,7 +678,7 @@ aspath_hash_alloc (void *arg) { struct aspath *aspath; - /* New aspath strucutre is needed. */ + /* New aspath structure is needed. */ aspath = aspath_dup (arg); /* Malformed AS path value. */ @@ -620,7 +693,7 @@ aspath_hash_alloc (void *arg) /* parse as-segment byte stream in struct assegment */ static struct assegment * -assegments_parse (struct stream *s, size_t length) +assegments_parse (struct stream *s, size_t length, int use32bit) { struct assegment_header segh; struct assegment *seg, *prev = NULL, *head = NULL; @@ -630,27 +703,37 @@ assegments_parse (struct stream *s, size_t length) if (length == 0) return NULL; + 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 % AS_VALUE_SIZE)) + || (length % AS16_VALUE_SIZE )) return NULL; while ( (STREAM_READABLE(s) > AS_HEADER_SIZE) && (bytes < length)) { int i; + int seg_size; /* softly softly, get the header first on its own */ segh.type = stream_getc (s); segh.length = stream_getc (s); + seg_size = ASSEGMENT_SIZE(segh.length, use32bit); + + 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 + ASSEGMENT_SIZE(segh.length)) > length) + 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 */ - || ((sizeof segh.length > 1) && segh.length > AS_SEGMENT_MAX)) + || ((sizeof segh.length > 1) && (segh.length > AS_SEGMENT_MAX)) ) { if (head) assegment_free_all (head); @@ -666,9 +749,13 @@ assegments_parse (struct stream *s, size_t length) head = prev = seg; for (i = 0; i < segh.length; i++) - seg->as[i] = stream_getw (s); + seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s); + + bytes += seg_size; - bytes += ASSEGMENT_SIZE(segh.length); + if (BGP_DEBUG (as4, AS4_SEGMENT)) + zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu", + (unsigned long) bytes); prev = seg; } @@ -680,16 +767,22 @@ assegments_parse (struct stream *s, size_t 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. */ struct aspath * -aspath_parse (struct stream *s, size_t length) +aspath_parse (struct stream *s, size_t length, int use32bit) { struct aspath as; struct aspath *find; /* If length is odd it's malformed AS path. */ - if (length % AS_VALUE_SIZE) + /* 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; - as.segments = assegments_parse (s, length); + memset (&as, 0, sizeof (struct aspath)); + as.segments = assegments_parse (s, length, use32bit); /* If already same aspath exist then return it. */ find = hash_get (ashash, &as, aspath_hash_alloc); @@ -698,6 +791,8 @@ aspath_parse (struct stream *s, size_t length) * optimised out. */ assegment_free_all (as.segments); + if (as.str) + XFREE (MTYPE_AS_STR, as.str); if (! find) return NULL; @@ -707,13 +802,21 @@ aspath_parse (struct stream *s, size_t length) } static inline void -assegment_data_put (struct stream *s, as_t *as, int num) +assegment_data_put (struct stream *s, as_t *as, int num, int use32bit) { int i; assert (num <= AS_SEGMENT_MAX); for (i = 0; i < num; i++) - stream_putw (s, as[i]); + if ( use32bit ) + stream_putl (s, as[i]); + else + { + if ( as[i] <= BGP_AS_MAX ) + stream_putw(s, as[i]); + else + stream_putw(s, BGP_AS_TRANS); + } } static inline size_t @@ -728,38 +831,51 @@ assegment_header_put (struct stream *s, u_char type, int length) } /* write aspath data to stream */ -void -aspath_put (struct stream *s, struct aspath *as) +size_t +aspath_put (struct stream *s, struct aspath *as, int use32bit ) { struct assegment *seg = as->segments; + size_t bytes = 0; if (!seg || seg->length == 0) - return; + return 0; if (seg) { - while (seg && (ASSEGMENT_LEN (seg) <= STREAM_WRITEABLE(s))) + /* + * Hey, what do we do when we have > STREAM_WRITABLE(s) here? + * At the moment, we would write out a partial aspath, and our peer + * will complain and drop the session :-/ + * + * The general assumption here is that many things tested will + * never happen. And, in real live, up to now, they have not. + */ + while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s))) { + struct assegment *next = seg->next; int written = 0; + int asns_packed = 0; size_t lenp; /* Overlength segments have to be split up */ while ( (seg->length - written) > AS_SEGMENT_MAX) { assegment_header_put (s, seg->type, AS_SEGMENT_MAX); - assegment_data_put (s, seg->as, AS_SEGMENT_MAX); + assegment_data_put (s, seg->as, AS_SEGMENT_MAX, use32bit); written += AS_SEGMENT_MAX; + bytes += ASSEGMENT_SIZE (written, use32bit); } /* write the final segment, probably is also the first */ lenp = assegment_header_put (s, seg->type, seg->length - written); - assegment_data_put (s, (seg->as + written), seg->length - written); + assegment_data_put (s, (seg->as + written), seg->length - written, + use32bit); /* Sequence-type segments can be 'packed' together * Case of a segment which was overlength and split up * will be missed here, but that doesn't matter. */ - if (seg->next && ASSEGMENTS_PACKABLE (seg, seg->next)) + while (next && ASSEGMENTS_PACKABLE (seg, next)) { /* NB: We should never normally get here given we * normalise aspath data when parse them. However, better @@ -771,17 +887,21 @@ aspath_put (struct stream *s, struct aspath *as) */ /* Next segment's data can fit in this one */ - assegment_data_put (s, seg->next->as, seg->next->length); + assegment_data_put (s, next->as, next->length, use32bit); /* update the length of the segment header */ - stream_putc_at (s, lenp, - seg->length - written + seg->next->length); - seg = seg->next->next; /* skip to past next */ + stream_putc_at (s, lenp, seg->length - written + next->length); + asns_packed += next->length; + + next = next->next; } - else - seg = seg->next; + + bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed, + use32bit); + seg = next; } } + return bytes; } /* This is for SNMP BGP4PATHATTRASPATHSEGMENT @@ -803,7 +923,7 @@ aspath_snmp_pathseg (struct aspath *as, size_t *varlen) *varlen = 0; return NULL; } - aspath_put (snmp_stream, as); + aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */ *varlen = stream_get_endp (snmp_stream); return stream_pnt(snmp_stream); @@ -861,8 +981,9 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2) int from; struct assegment *seg1 = as1->segments; struct assegment *seg2 = as2->segments; - struct aspath *aspath; + struct aspath *aspath = NULL; struct assegment *asset; + struct assegment *prevseg = NULL; match = 0; minlen = 0; @@ -871,7 +992,7 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2) /* First of all check common leading sequence. */ while (seg1 && seg2) - { + { /* Check segment type. */ if (seg1->type != seg2->type) break; @@ -885,11 +1006,19 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2) if (match) { + struct assegment *seg = assegment_new (seg1->type, 0); + + seg = assegment_append_asns (seg, seg1->as, match); + if (! aspath) - aspath = aspath_new (); - aspath->segments = assegment_new (seg1->type, 0); - aspath->segments = assegment_append_asns (aspath->segments, - seg1->as, match); + { + aspath = aspath_new (); + aspath->segments = seg; + } + else + prevseg->next = seg; + + prevseg = seg; } if (match != minlen || match != seg1->length @@ -1174,6 +1303,108 @@ aspath_cmp_left (struct aspath *aspath1, struct aspath *aspath2) return 0; } +/* Truncate an aspath after a number of hops, and put the hops remaining + * at the front of another aspath. Needed for AS4 compat. + * + * Returned aspath is a /new/ aspath, which should either by free'd or + * interned by the caller, as desired. + */ +struct aspath * +aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path) +{ + struct assegment *seg, *newseg, *prevseg = NULL; + struct aspath *newpath = NULL, *mergedpath; + int hops, cpasns = 0; + + if (!aspath) + return NULL; + + seg = aspath->segments; + + /* CONFEDs should get reconciled too.. */ + hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath)) + - aspath_count_hops (as4path); + + if (hops < 0) + { + if (BGP_DEBUG (as4, AS4)) + zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH"); + /* Something's gone wrong. The RFC says we should now ignore AS4_PATH, + * which is daft behaviour - it contains vital loop-detection + * information which must have been removed from AS_PATH. + */ + hops = aspath_count_hops (aspath); + } + + if (!hops) + return aspath_dup (as4path); + + if ( BGP_DEBUG(as4, AS4)) + zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now", + aspath->str, as4path->str); + + while (seg && hops > 0) + { + switch (seg->type) + { + case AS_SET: + case AS_CONFED_SET: + hops--; + cpasns = seg->length; + break; + case AS_CONFED_SEQUENCE: + /* Should never split a confed-sequence, if hop-count + * suggests we must then something's gone wrong somewhere. + * + * Most important goal is to preserve AS_PATHs prime function + * as loop-detector, so we fudge the numbers so that the entire + * confed-sequence is merged in. + */ + if (hops < seg->length) + { + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls" + " across 2/4 ASN boundary somewhere, broken.."); + hops = seg->length; + } + case AS_SEQUENCE: + cpasns = MIN(seg->length, hops); + hops -= seg->length; + } + + assert (cpasns <= seg->length); + + newseg = assegment_new (seg->type, 0); + newseg = assegment_append_asns (newseg, seg->as, cpasns); + + if (!newpath) + { + newpath = aspath_new (); + newpath->segments = newseg; + } + else + prevseg->next = newseg; + + prevseg = newseg; + seg = seg->next; + } + + /* We may be able to join some segments here, and we must + * do this because... we want normalised aspaths in out hash + * and we do not want to stumble in aspath_put. + */ + mergedpath = aspath_merge (newpath, aspath_dup(as4path)); + aspath_free (newpath); + mergedpath->segments = assegment_normalise (mergedpath->segments); + aspath_str_update (mergedpath); + + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] result of synthesizing is %s", + mergedpath->str); + + return mergedpath; +} + /* Compare leftmost AS value for MED check. If as1's leftmost AS and as2's leftmost AS is same return 1. (confederation as-path only). */ @@ -1273,7 +1504,7 @@ aspath_segment_add (struct aspath *as, int type) struct aspath * aspath_empty (void) { - return aspath_parse (NULL, 0); + return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */ } struct aspath * @@ -1316,7 +1547,7 @@ enum as_token /* Return next token and point for string parse. */ static const char * -aspath_gettoken (const char *buf, enum as_token *token, u_short *asno) +aspath_gettoken (const char *buf, enum as_token *token, u_long *asno) { const char *p = buf; @@ -1360,16 +1591,17 @@ aspath_gettoken (const char *buf, enum as_token *token, u_short *asno) if (isdigit ((int) *p)) { u_short asval; - + *token = as_token_asval; asval = (*p - '0'); p++; + while (isdigit ((int) *p)) - { - asval *= 10; - asval += (*p - '0'); - p++; - } + { + asval *= 10; + asval += (*p - '0'); + p++; + } *asno = asval; return p; } @@ -1384,7 +1616,7 @@ aspath_str2aspath (const char *str) { enum as_token token = as_token_unknown; u_short as_type; - u_short asno = 0; + u_long asno = 0; struct aspath *aspath; int needtype; @@ -1451,24 +1683,11 @@ aspath_key_make (void *p) { struct aspath * aspath = (struct aspath *) p; unsigned int key = 0; - unsigned int i; - struct assegment *seg = aspath->segments; - struct assegment *prev = NULL; - while (seg) - { - /* segment types should be part of the hash - * otherwise seq(1) and set(1) will hash to same value - */ - if (!(prev && seg->type == AS_SEQUENCE && seg->type == prev->type)) - key += seg->type; - - for (i = 0; i < seg->length; i++) - key += seg->as[i]; - - prev = seg; - seg = seg->next; - } + if (!aspath->str) + aspath_str_update (aspath); + + key = jhash (aspath->str, strlen(aspath->str), 2334325); return key; } diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index efec24c2..3bb616f7 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -31,7 +31,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_PRIVATE_AS_MIN 64512U #define BGP_PRIVATE_AS_MAX 65535U -#define BGP_AS_MAX 65535U +/* we leave BGP_AS_MAX as the 16bit AS MAX number. */ +#define BGP_AS_MAX 65535U +#define BGP_AS4_MAX 4294967295U +/* Transition 16Bit AS as defined by IANA */ +#define BGP_AS_TRANS 23456U /* AS_PATH segment data in abstracted form, no limit is placed on length */ struct assegment @@ -61,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); +extern struct aspath *aspath_parse (struct stream *, size_t, 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 *); @@ -88,7 +92,11 @@ extern unsigned int aspath_count_hops (struct aspath *); extern unsigned int aspath_count_confeds (struct aspath *); extern unsigned int aspath_size (struct aspath *); extern as_t aspath_highest (struct aspath *); -extern void aspath_put (struct stream *, struct aspath *); +extern size_t aspath_put (struct stream *, struct aspath *, int); + +extern struct aspath *aspath_reconcile_as4 (struct aspath *, struct aspath *); +extern unsigned int aspath_has_as4 (struct aspath *); +extern unsigned int aspath_count_numas (struct aspath *); /* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */ extern u_char *aspath_snmp_pathseg (struct aspath *, size_t *); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 9d13ca6e..b463b3c0 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -56,8 +56,10 @@ static struct message attr_str [] = { BGP_ATTR_RCID_PATH, "RCID_PATH" }, { BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI" }, { BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" }, - { BGP_ATTR_EXT_COMMUNITIES, "BGP_ATTR_EXT_COMMUNITIES" }, - { BGP_ATTR_AS_PATHLIMIT, "BGP_ATTR_AS_PATHLIMIT" }, + { BGP_ATTR_EXT_COMMUNITIES, "EXT_COMMUNITIES" }, + { BGP_ATTR_AS4_PATH, "AS4_PATH" }, + { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" }, + { BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" }, { 0, NULL } }; int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]); @@ -794,8 +796,6 @@ static int bgp_attr_aspath (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag, u_char *startp) { - struct bgp *bgp; - struct aspath *aspath; bgp_size_t total; total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); @@ -813,8 +813,14 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, return -1; } + /* + * peer with AS4 => will get 4Byte ASnums + * otherwise, will get 16 Bit + */ + attr->aspath = aspath_parse (peer->ibuf, length, + CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)); + /* In case of IBGP, length will be zero. */ - attr->aspath = aspath_parse (peer->ibuf, length); if (! attr->aspath) { zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length); @@ -824,6 +830,28 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, 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; +} + +static int bgp_attr_aspath_check( struct peer *peer, + struct attr *attr) +{ + /* These checks were part of bgp_attr_aspath, but with + * as4 we should to check aspath things when + * aspath synthesizing with as4_path has already taken place. + * Otherwise we check ASPATH and use the synthesized thing, and that is + * not right. + * So do the checks later, i.e. here + */ + struct bgp *bgp = peer->bgp; + struct aspath *aspath; + bgp = peer->bgp; /* First AS check for EBGP. */ @@ -851,11 +879,20 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, attr->aspath = aspath_intern (aspath); } - /* Forward pointer. */ -/* stream_forward_getp (peer->ibuf, length);*/ + return 0; + +} + +/* 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. */ - attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH); return 0; } @@ -981,18 +1018,27 @@ static int bgp_attr_aggregator (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag) { + int wantedlen = 6; struct attr_extra *attre = bgp_attr_extra_get (attr); - if (length != 6) + /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */ + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + wantedlen = 8; + + if (length != wantedlen) { - zlog (peer->log, LOG_ERR, "Aggregator length is not 6 [%d]", length); + zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length); bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } - attre->aggregator_as = stream_getw (peer->ibuf); + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + attre->aggregator_as = stream_getl (peer->ibuf); + else + attre->aggregator_as = stream_getw (peer->ibuf); attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf); /* Set atomic aggregate flag. */ @@ -1001,6 +1047,145 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length, return 0; } +/* New Aggregator attribute */ +static int +bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length, + struct attr *attr, as_t *as4_aggregator_as, + struct in_addr *as4_aggregator_addr) +{ + if (length != 8) + { + zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length); + + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + return -1; + } + *as4_aggregator_as = stream_getl (peer->ibuf); + as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR); + + return 0; +} + +/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH. + */ +static int +bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, + struct aspath *as4_path, as_t as4_aggregator, + struct in_addr *as4_aggregator_addr) +{ + int ignore_as4_path = 0; + struct aspath *newpath; + struct attr_extra *attre = attr->extra; + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) + { + /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR + * if given. + * It is worth a warning though, because the peer really + * should not send them + */ + if (BGP_DEBUG(as4, AS4)) + { + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH))) + zlog_debug ("[AS4] %s %s AS4_PATH", + peer->host, "AS4 capable peer, yet it sent"); + + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR))) + zlog_debug ("[AS4] %s %s AS4_AGGREGATOR", + peer->host, "AS4 capable peer, yet it sent"); + } + + return 0; + } + + if (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)) + && !(attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH)))) + { + /* Hu? This is not supposed to happen at all! + * got as4_path and no aspath, + * This should already + * have been handled by 'well known attributes missing' + * But... yeah, paranoia + * Take this as a "malformed attribute" + */ + zlog (peer->log, LOG_ERR, + "%s BGP not AS4 capable peer sent AS4_PATH but" + " no AS_PATH, cant do anything here", peer->host); + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* We have a asn16 peer. First, look for AS4_AGGREGATOR + * because that may override AS4_PATH + */ + if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) ) + { + assert (attre); + + if ( attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) ) + { + /* received both. + * if the as_number in aggregator is not AS_TRANS, + * then AS4_AGGREGATOR and AS4_PATH shall be ignored + * and the Aggregator shall be taken as + * info on the aggregating node, and the AS_PATH + * shall be taken as the AS_PATH + * otherwise + * the Aggregator shall be ignored and the + * AS4_AGGREGATOR shall be taken as the + * Aggregating node and the AS_PATH is to be + * constructed "as in all other cases" + */ + if ( attre->aggregator_as != BGP_AS_TRANS ) + { + /* ignore */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] %s BGP not AS4 capable peer" + " send AGGREGATOR != AS_TRANS and" + " AS4_AGGREGATOR, so ignore" + " AS4_AGGREGATOR and AS4_PATH", peer->host); + ignore_as4_path = 1; + } + else + { + /* "New_aggregator shall be taken as aggregator" */ + attre->aggregator_as = as4_aggregator; + attre->aggregator_addr.s_addr = as4_aggregator_addr->s_addr; + } + } + else + { + /* We received a AS4_AGGREGATOR but no AGGREGATOR. + * That is bogus - but reading the conditions + * we have to handle AS4_AGGREGATOR as if it were + * AGGREGATOR in that case + */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] %s BGP not AS4 capable peer send" + " AS4_AGGREGATOR but no AGGREGATOR, will take" + " it as if AGGREGATOR with AS_TRANS had been there", peer->host); + attre->aggregator_as = as4_aggregator; + /* sweep it under the carpet and simulate a "good" AGGREGATOR */ + attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)); + } + } + + /* need to reconcile NEW_AS_PATH and AS_PATH */ + if ( !ignore_as4_path && (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH))) ) + { + newpath = aspath_reconcile_as4 (attr->aspath, as4_path); + aspath_unintern (attr->aspath); + attr->aspath = aspath_intern (newpath); + } + return 0; +} + /* Community attribute. */ static int bgp_attr_community (struct peer *peer, bgp_size_t length, @@ -1318,11 +1503,16 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, { int ret; u_char flag; - u_char type; + u_char type = 0; bgp_size_t length; u_char *startp, *endp; u_char *attr_endp; u_char seen[BGP_ATTR_BITMAP_SIZE]; + /* we need the as4_path only until we have synthesized the as_path with it */ + /* same goes for as4_aggregator */ + struct aspath *as4_path = NULL; + as_t as4_aggregator = 0; + struct in_addr as4_aggregator_addr = { 0 }; /* Initialize bitmap. */ memset (seen, 0, BGP_ATTR_BITMAP_SIZE); @@ -1339,7 +1529,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, /* XXX warning: long int format, int arg (arg 5) */ zlog (peer->log, LOG_WARNING, "%s error BGP attribute length %lu is smaller than min len", - peer->host, endp - STREAM_PNT (BGP_INPUT (peer))); + peer->host, + (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, @@ -1401,6 +1592,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_AS_PATH: ret = bgp_attr_aspath (peer, length, attr, flag, startp); break; + case BGP_ATTR_AS4_PATH: + ret = bgp_attr_as4_path (peer, length, attr, &as4_path ); + break; case BGP_ATTR_NEXT_HOP: ret = bgp_attr_nexthop (peer, length, attr, flag, startp); break; @@ -1416,6 +1610,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_AGGREGATOR: ret = bgp_attr_aggregator (peer, length, attr, flag); break; + case BGP_ATTR_AS4_AGGREGATOR: + ret = bgp_attr_as4_aggregator (peer, length, attr, &as4_aggregator, &as4_aggregator_addr); + break; case BGP_ATTR_COMMUNITIES: ret = bgp_attr_community (peer, length, attr, flag); break; @@ -1480,6 +1677,51 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return -1; } + /* + * At this place we can see whether we got AS4_PATH and/or + * AS4_AGGREGATOR from a 16Bit peer and act accordingly. + * We can not do this before we've read all attributes because + * the as4 handling does not say whether AS4_PATH has to be sent + * after AS_PATH or not - and when AS4_AGGREGATOR will be send + * in relationship to AGGREGATOR. + * So, to be defensive, we are not relying on any order and read + * all attributes first, including these 32bit ones, and now, + * afterwards, we look what and if something is to be done for as4. + */ + if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, + as4_aggregator, &as4_aggregator_addr)) + return -1; + + /* At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath + * so we can chuck as4_aggregator and as4_path alltogether in + * order to save memory + */ + if ( as4_path ) + { + aspath_unintern( as4_path ); /* unintern - it is in the hash */ + as4_path = NULL; + /* The flag that we got this is still there, but that does not + * do any trouble + */ + } + /* + * The "rest" of the code does nothing with as4_aggregator. + * there is no memory attached specifically which is not part + * of the attr. + * so ignoring just means do nothing. + */ + /* + * Finally do the checks on the aspath we did not do yet + * because we waited for a potentially synthesized aspath. + */ + if ( attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH))) + { + ret = bgp_attr_aspath_check( peer, attr ); + if ( ret < 0 ) + return ret; + } + /* Finally intern unknown attribute. */ if (attr->extra && attr->extra->transit) attr->extra->transit = transit_intern (attr->extra->transit); @@ -1530,8 +1772,11 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, struct prefix_rd *prd, u_char *tag) { size_t cp; - unsigned int aspath_data_size; + size_t aspath_sizep; struct aspath *aspath; + int send_as4_path = 0; + int send_as4_aggregator = 0; + int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0; if (! bgp) bgp = bgp_get_default (); @@ -1578,25 +1823,27 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, else aspath = attr->aspath; - /* AS path attribute extended length bit check. */ - aspath_data_size = aspath_size (aspath); - if (aspath_data_size > 255) - { - stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putw (s, aspath_data_size); - } - else - { - stream_putc (s, BGP_ATTR_FLAG_TRANS); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putc (s, aspath_data_size); - } - aspath_put (s, aspath); - - if (aspath != attr->aspath) - aspath_free (aspath); - + /* If peer is not AS4 capable, then: + * - send the created AS_PATH out as AS4_PATH (optional, transitive), + * but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path segment + * types are in it (i.e. exclude them if they are there) + * AND do this only if there is at least one asnum > 65535 in the path! + * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and change + * all ASnums > 65535 to BGP_AS_TRANS + */ + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + aspath_sizep = stream_get_endp (s); + stream_putw (s, 0); + stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, use32bit)); + + /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs + * in the path + */ + if (!use32bit && aspath_has_as4 (aspath)) + send_as4_path = 1; /* we'll do this later, at the correct place */ + /* Nexthop attribute. */ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP) { @@ -1645,10 +1892,36 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)) { assert (attr->extra); + + /* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_AGGREGATOR); - stream_putc (s, 6); - stream_putw (s, attr->extra->aggregator_as); + + if (use32bit) + { + /* AS4 capable peer */ + stream_putc (s, 8); + stream_putl (s, attr->extra->aggregator_as); + } + else + { + /* 2-byte AS peer */ + stream_putc (s, 6); + + /* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */ + if ( attr->extra->aggregator_as > 65535 ) + { + stream_putw (s, BGP_AS_TRANS); + + /* we have to send AS4_AGGREGATOR, too. + * we'll do that later in order to send attributes in ascending + * order. + */ + send_as4_aggregator = 1; + } + else + stream_putw (s, (u_int16_t) attr->extra->aggregator_as); + } stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); } @@ -1873,6 +2146,47 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } } } + + if ( send_as4_path ) + { + /* If the peer is NOT As4 capable, AND */ + /* there are ASnums > 65535 in path THEN + * give out AS4_PATH */ + + /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET + * path segments! + * Hm, I wonder... confederation things *should* only be at + * the beginning of an aspath, right? Then we should use + * aspath_delete_confed_seq for this, because it is already + * there! (JK) + * Folks, talk to me: what is reasonable here!? + */ + aspath = aspath_delete_confed_seq (aspath); + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS4_PATH); + aspath_sizep = stream_get_endp (s); + stream_putw (s, 0); + stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, 1)); + } + + if (aspath != attr->aspath) + aspath_free (aspath); + + if ( send_as4_aggregator ) + { + assert (attr->extra); + + /* send AS4_AGGREGATOR, at this place */ + /* this section of code moved here in order to ensure the correct + * *ascending* order of attributes + */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AS4_AGGREGATOR); + stream_putc (s, 8); + stream_putl (s, attr->extra->aggregator_as); + stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); + } /* AS-Pathlimit */ if (attr->pathlimit.ttl) @@ -1967,7 +2281,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, { unsigned long cp; unsigned long len; - unsigned int aspathlen; + size_t aspath_lenp; struct aspath *aspath; /* Remember current pointer. */ @@ -1983,20 +2297,13 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, stream_putc (s, attr->origin); aspath = attr->aspath; - - if ( (aspathlen = aspath_size (aspath)) > 255 ) - { - stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putw (s, aspathlen); - } - else - { - stream_putc (s, BGP_ATTR_FLAG_TRANS); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putc (s, aspathlen); - } - aspath_put (s, aspath); + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + aspath_lenp = stream_get_endp (s); + stream_putw (s, 0); + + stream_putw_at (s, aspath_lenp, aspath_put (s, aspath, 1)); /* Nexthop attribute. */ /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */ @@ -2044,8 +2351,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, assert (attr->extra); stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_AGGREGATOR); - stream_putc (s, 6); - stream_putw (s, attr->extra->aggregator_as); + stream_putc (s, 8); + stream_putl (s, attr->extra->aggregator_as); stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); } diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 60284a2d..acb1de7f 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -36,6 +36,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_debug.h" #include "bgpd/bgp_community.h" +unsigned long conf_bgp_debug_as4; unsigned long conf_bgp_debug_fsm; unsigned long conf_bgp_debug_events; unsigned long conf_bgp_debug_packet; @@ -45,6 +46,7 @@ unsigned long conf_bgp_debug_update; unsigned long conf_bgp_debug_normal; unsigned long conf_bgp_debug_zebra; +unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_fsm; unsigned long term_bgp_debug_events; unsigned long term_bgp_debug_packet; @@ -297,6 +299,92 @@ debug (unsigned int option) return bgp_debug_option & option; } +DEFUN (debug_bgp_as4, + debug_bgp_as4_cmd, + "debug bgp as4", + DEBUG_STR + BGP_STR + "BGP AS4 actions\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (as4, AS4); + else + { + TERM_DEBUG_ON (as4, AS4); + vty_out (vty, "BGP as4 debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_as4, + no_debug_bgp_as4_cmd, + "no debug bgp as4", + NO_STR + DEBUG_STR + BGP_STR + "BGP AS4 actions\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (as4, AS4); + else + { + TERM_DEBUG_OFF (as4, AS4); + vty_out (vty, "BGP as4 debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_as4, + undebug_bgp_as4_cmd, + "undebug bgp as4", + UNDEBUG_STR + DEBUG_STR + BGP_STR + "BGP AS4 actions\n") + +DEFUN (debug_bgp_as4_segment, + debug_bgp_as4_segment_cmd, + "debug bgp as4 segment", + DEBUG_STR + BGP_STR + "BGP AS4 aspath segment handling\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (as4, AS4_SEGMENT); + else + { + TERM_DEBUG_ON (as4, AS4_SEGMENT); + vty_out (vty, "BGP as4 segment debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_as4_segment, + no_debug_bgp_as4_segment_cmd, + "no debug bgp as4 segment", + NO_STR + DEBUG_STR + BGP_STR + "BGP AS4 aspath segment handling\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (as4, AS4_SEGMENT); + else + { + TERM_DEBUG_OFF (as4, AS4_SEGMENT); + vty_out (vty, "BGP as4 segment debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_as4_segment, + undebug_bgp_as4_segment_cmd, + "undebug bgp as4 segment", + UNDEBUG_STR + DEBUG_STR + BGP_STR + "BGP AS4 aspath segment handling\n") + DEFUN (debug_bgp_fsm, debug_bgp_fsm_cmd, "debug bgp fsm", @@ -651,6 +739,8 @@ DEFUN (no_debug_bgp_all, TERM_DEBUG_OFF (keepalive, KEEPALIVE); TERM_DEBUG_OFF (update, UPDATE_IN); TERM_DEBUG_OFF (update, UPDATE_OUT); + TERM_DEBUG_OFF (as4, AS4); + TERM_DEBUG_OFF (as4, AS4_SEGMENT); TERM_DEBUG_OFF (fsm, FSM); TERM_DEBUG_OFF (filter, FILTER); TERM_DEBUG_OFF (zebra, ZEBRA); @@ -693,6 +783,10 @@ DEFUN (show_debugging_bgp, vty_out (vty, " BGP filter debugging is on%s", VTY_NEWLINE); if (BGP_DEBUG (zebra, ZEBRA)) vty_out (vty, " BGP zebra debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (as4, AS4)) + vty_out (vty, " BGP as4 debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (as4, AS4_SEGMENT)) + vty_out (vty, " BGP as4 aspath segment debugging is on%s", VTY_NEWLINE); vty_out (vty, "%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -708,6 +802,18 @@ bgp_config_write_debug (struct vty *vty) write++; } + if (CONF_BGP_DEBUG (as4, AS4)) + { + vty_out (vty, "debug bgp as4%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (as4, AS4_SEGMENT)) + { + vty_out (vty, "debug bgp as4 segment%s", VTY_NEWLINE); + write++; + } + if (CONF_BGP_DEBUG (events, EVENTS)) { vty_out (vty, "debug bgp events%s", VTY_NEWLINE); @@ -771,6 +877,11 @@ bgp_debug_init (void) install_element (ENABLE_NODE, &show_debugging_bgp_cmd); + install_element (ENABLE_NODE, &debug_bgp_as4_cmd); + install_element (CONFIG_NODE, &debug_bgp_as4_cmd); + install_element (ENABLE_NODE, &debug_bgp_as4_segment_cmd); + install_element (CONFIG_NODE, &debug_bgp_as4_segment_cmd); + install_element (ENABLE_NODE, &debug_bgp_fsm_cmd); install_element (CONFIG_NODE, &debug_bgp_fsm_cmd); install_element (ENABLE_NODE, &debug_bgp_events_cmd); @@ -788,6 +899,13 @@ bgp_debug_init (void) install_element (ENABLE_NODE, &debug_bgp_zebra_cmd); install_element (CONFIG_NODE, &debug_bgp_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_as4_cmd); + install_element (ENABLE_NODE, &undebug_bgp_as4_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_as4_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_as4_segment_cmd); + install_element (ENABLE_NODE, &undebug_bgp_as4_segment_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_as4_segment_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_fsm_cmd); install_element (ENABLE_NODE, &undebug_bgp_fsm_cmd); install_element (CONFIG_NODE, &no_debug_bgp_fsm_cmd); diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 7015cb77..71e110ce 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_DEBUG_H #define _QUAGGA_BGP_DEBUG_H +#include "bgp_attr.h" + /* sort of packet direction */ #define DUMP_ON 1 #define DUMP_SEND 2 @@ -56,6 +58,7 @@ extern void bgp_packet_dump (struct stream *); extern int debug (unsigned int option); +extern unsigned long conf_bgp_debug_as4; extern unsigned long conf_bgp_debug_fsm; extern unsigned long conf_bgp_debug_events; extern unsigned long conf_bgp_debug_packet; @@ -65,6 +68,7 @@ extern unsigned long conf_bgp_debug_update; extern unsigned long conf_bgp_debug_normal; extern unsigned long conf_bgp_debug_zebra; +extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_fsm; extern unsigned long term_bgp_debug_events; extern unsigned long term_bgp_debug_packet; @@ -74,6 +78,9 @@ extern unsigned long term_bgp_debug_update; extern unsigned long term_bgp_debug_normal; extern unsigned long term_bgp_debug_zebra; +#define BGP_DEBUG_AS4 0x01 +#define BGP_DEBUG_AS4_SEGMENT 0x02 + #define BGP_DEBUG_FSM 0x01 #define BGP_DEBUG_EVENTS 0x01 #define BGP_DEBUG_PACKET 0x01 diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 601ff2b2..e815ea3c 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -26,6 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "prefix.h" #include "thread.h" +#include "linklist.h" #include "bgpd/bgp_table.h" #include "bgpd/bgpd.h" @@ -53,7 +54,8 @@ enum MRT_MSG_TYPES { MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */ MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */ MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */ - MSG_TABLE_DUMP /* routing table dump */ + MSG_TABLE_DUMP, /* routing table dump */ + MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */ }; static int bgp_dump_interval_func (struct thread *); @@ -191,137 +193,189 @@ bgp_dump_set_size (struct stream *s, int type) } static void -bgp_dump_routes_entry (struct prefix *p, struct bgp_info *info, int afi, - int type, unsigned int seq) +bgp_dump_routes_index_table(struct bgp *bgp) { - struct stream *obuf; - struct attr *attr; struct peer *peer; - int plen; - int safi = 0; + struct listnode *node; + uint16_t peerno = 0; + struct stream *obuf; - /* Make dump stream. */ obuf = bgp_dump_obuf; stream_reset (obuf); - attr = info->attr; - peer = info->peer; + /* MRT header */ + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE); - /* We support MRT's old format. */ - if (type == MSG_TABLE_DUMP) + /* Collector BGP ID */ + stream_put_in_addr (obuf, &bgp->router_id); + + /* View name */ + if(bgp->name) { - bgp_dump_header (obuf, MSG_TABLE_DUMP, afi); - stream_putw (obuf, 0); /* View # */ - stream_putw (obuf, seq); /* Sequence number. */ + stream_putw (obuf, strlen(bgp->name)); + stream_put(obuf, bgp->name, strlen(bgp->name)); } else { - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY); - - stream_putl (obuf, info->uptime); /* Time Last Change */ - stream_putw (obuf, afi); /* Address Family */ - stream_putc (obuf, safi); /* SAFI */ + stream_putw(obuf, 0); } - if (afi == AFI_IP) - { - if (type == MSG_TABLE_DUMP) - { - /* Prefix */ - stream_put_in_addr (obuf, &p->u.prefix4); - stream_putc (obuf, p->prefixlen); - - /* Status */ - stream_putc (obuf, 1); - - /* Originated */ - stream_putl (obuf, info->uptime); - - /* Peer's IP address */ - stream_put_in_addr (obuf, &peer->su.sin.sin_addr); + /* Peer count */ + stream_putw (obuf, listcount(bgp->peer)); - /* Peer's AS number. */ - stream_putw (obuf, peer->as); - - /* Dump attribute. */ - bgp_dump_routes_attr (obuf, attr, p); - } - else - { - /* Next-Hop-Len */ - stream_putc (obuf, IPV4_MAX_BYTELEN); - stream_put_in_addr (obuf, &attr->nexthop); - stream_putc (obuf, p->prefixlen); - plen = PSIZE (p->prefixlen); - stream_put (obuf, &p->u.prefix4, plen); - bgp_dump_routes_attr (obuf, attr, p); - } - } -#ifdef HAVE_IPV6 - else if (afi == AFI_IP6) + /* Walk down all peers */ + for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) { - if (type == MSG_TABLE_DUMP) - { - /* Prefix */ - stream_write (obuf, (u_char *)&p->u.prefix6, IPV6_MAX_BYTELEN); - stream_putc (obuf, p->prefixlen); - /* Status */ - stream_putc (obuf, 1); + /* Peer's type */ + if (sockunion_family(&peer->su) == AF_INET) + { + stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP); + } +#ifdef HAVE_IPV6 + else if (sockunion_family(&peer->su) == AF_INET6) + { + stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6); + } +#endif /* HAVE_IPV6 */ - /* Originated */ - stream_putl (obuf, info->uptime); + /* Peer's BGP ID */ + stream_put_in_addr (obuf, &peer->remote_id); - /* Peer's IP address */ - stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, - IPV6_MAX_BYTELEN); + /* Peer's IP address */ + if (sockunion_family(&peer->su) == AF_INET) + { + stream_put_in_addr (obuf, &peer->su.sin.sin_addr); + } +#ifdef HAVE_IPV6 + else if (sockunion_family(&peer->su) == AF_INET6) + { + stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, + IPV6_MAX_BYTELEN); + } +#endif /* HAVE_IPV6 */ - /* Peer's AS number. */ - stream_putw (obuf, peer->as); + /* Peer's AS number. */ + /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */ + stream_putl (obuf, peer->as); - /* Dump attribute. */ - bgp_dump_routes_attr (obuf, attr, p); - } - else - { - ; - } + /* Store the peer number for this peer */ + peer->table_dump_index = peerno; + peerno++; } -#endif /* HAVE_IPV6 */ - /* Set length. */ - bgp_dump_set_size (obuf, type); + bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); fflush (bgp_dump_routes.fp); } + /* Runs under child process. */ -static void -bgp_dump_routes_func (int afi) +static unsigned int +bgp_dump_routes_func (int afi, int first_run, unsigned int seq) { struct stream *obuf; - struct bgp_node *rn; struct bgp_info *info; + struct bgp_node *rn; struct bgp *bgp; struct bgp_table *table; - unsigned int seq = 0; - - obuf = bgp_dump_obuf; bgp = bgp_get_default (); if (!bgp) - return; + return seq; if (bgp_dump_routes.fp == NULL) - return; + return seq; + + /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers, + so this should only be done on the first call to bgp_dump_routes_func. + ( this function will be called once for ipv4 and once for ipv6 ) */ + if(first_run) + bgp_dump_routes_index_table(bgp); + + obuf = bgp_dump_obuf; + stream_reset(obuf); /* Walk down each BGP route. */ table = bgp->rib[afi][SAFI_UNICAST]; for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - for (info = rn->info; info; info = info->next) - bgp_dump_routes_entry (&rn->p, info, afi, MSG_TABLE_DUMP, seq++); + { + if(!rn->info) + continue; + + stream_reset(obuf); + + /* MRT header */ + if (afi == AFI_IP) + { + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST); + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST); + } +#endif /* HAVE_IPV6 */ + + /* Sequence number */ + stream_putl(obuf, seq); + + /* Prefix length */ + stream_putc (obuf, rn->p.prefixlen); + + /* Prefix */ + if (afi == AFI_IP) + { + /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ + stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8); + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ + stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8); + } +#endif /* HAVE_IPV6 */ + + /* Save where we are now, so we can overwride the entry count later */ + int sizep = stream_get_endp(obuf); + + /* Entry count */ + uint16_t entry_count = 0; + + /* Entry count, note that this is overwritten later */ + stream_putw(obuf, 0); + + for (info = rn->info; info; info = info->next) + { + entry_count++; + + /* Peer index */ + stream_putw(obuf, info->peer->table_dump_index); + + /* Originated */ + stream_putl (obuf, info->uptime); + + /* Dump attribute. */ + /* Skip prefix & AFI/SAFI for MP_NLRI */ + bgp_dump_routes_attr (obuf, info->attr, &rn->p); + } + + /* Overwrite the entry count, now that we know the right number */ + stream_putw_at (obuf, sizep, entry_count); + + seq++; + + bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); + + } + + fflush (bgp_dump_routes.fp); + + return seq; } static int @@ -337,9 +391,9 @@ bgp_dump_interval_func (struct thread *t) /* In case of bgp_dump_routes, we need special route dump function. */ if (bgp_dump->type == BGP_DUMP_ROUTES) { - bgp_dump_routes_func (AFI_IP); + unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0); #ifdef HAVE_IPV6 - bgp_dump_routes_func (AFI_IP6); + bgp_dump_routes_func (AFI_IP6, 0, seq); #endif /* HAVE_IPV6 */ /* Close the file now. For a RIB dump there's no point in leaving * it open until the next scheduled dump starts. */ @@ -356,13 +410,21 @@ bgp_dump_interval_func (struct thread *t) /* Dump common information. */ static void -bgp_dump_common (struct stream *obuf, struct peer *peer) +bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) { char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* Source AS number and Destination AS number. */ - stream_putw (obuf, peer->as); - stream_putw (obuf, peer->local_as); + if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) + { + stream_putl (obuf, peer->as); + stream_putl (obuf, peer->local_as); + } + else + { + stream_putw (obuf, peer->as); + stream_putw (obuf, peer->local_as); + } if (peer->su.sa.sa_family == AF_INET) { @@ -408,8 +470,8 @@ bgp_dump_state (struct peer *peer, int status_old, int status_new) obuf = bgp_dump_obuf; stream_reset (obuf); - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE); - bgp_dump_common (obuf, peer); + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4); + bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/ stream_putw (obuf, status_old); stream_putw (obuf, status_new); @@ -437,8 +499,15 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, stream_reset (obuf); /* Dump header and common part. */ - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE); - bgp_dump_common (obuf, peer); + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) + { + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4); + } + else + { + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE); + } + bgp_dump_common (obuf, peer, 0); /* Packet contents. */ stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet)); diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h index 36447e93..6bb1197b 100644 --- a/bgpd/bgp_dump.h +++ b/bgpd/bgp_dump.h @@ -25,14 +25,28 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* type value */ #define MSG_PROTOCOL_BGP4MP 16 /* subtype value */ -#define BGP4MP_STATE_CHANGE 0 -#define BGP4MP_MESSAGE 1 -#define BGP4MP_ENTRY 2 -#define BGP4MP_SNAPSHOT 3 +#define BGP4MP_STATE_CHANGE 0 +#define BGP4MP_MESSAGE 1 +#define BGP4MP_ENTRY 2 +#define BGP4MP_SNAPSHOT 3 +#define BGP4MP_MESSAGE_AS4 4 +#define BGP4MP_STATE_CHANGE_AS4 5 #define BGP_DUMP_HEADER_SIZE 12 #define BGP_DUMP_MSG_HEADER 40 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE 1 +#define TABLE_DUMP_V2_RIB_IPV4_UNICAST 2 +#define TABLE_DUMP_V2_RIB_IPV4_MULTICAST 3 +#define TABLE_DUMP_V2_RIB_IPV6_UNICAST 4 +#define TABLE_DUMP_V2_RIB_IPV6_MULTICAST 5 +#define TABLE_DUMP_V2_RIB_GENERIC 6 + +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP 0 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6 1 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS2 0 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4 2 + extern void bgp_dump_init (void); extern void bgp_dump_state (struct peer *, int, int); extern void bgp_dump_packet (struct peer *, int, struct stream *); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 64f4438f..9e7ae1b3 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -27,6 +27,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_aspath.h" /* Hash of community attribute. */ struct hash *ecomhash; @@ -228,8 +229,9 @@ ecommunity_unintern (struct ecommunity *ecom) /* Utinity function to make hash key. */ unsigned int -ecommunity_hash_make (struct ecommunity *ecom) +ecommunity_hash_make (void *arg) { + const struct ecommunity *ecom = arg; int c; unsigned int key; u_int8_t *pnt; @@ -245,9 +247,11 @@ ecommunity_hash_make (struct ecommunity *ecom) /* Compare two Extended Communities Attribute structure. */ int -ecommunity_cmp (const struct ecommunity *ecom1, - const struct ecommunity *ecom2) +ecommunity_cmp (void *arg1, void *arg2) { + const struct ecommunity *ecom1 = arg1; + const struct ecommunity *ecom2 = arg2; + if (ecom1->size == ecom2->size && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0) return 1; @@ -256,7 +260,7 @@ ecommunity_cmp (const struct ecommunity *ecom1, /* Initialize Extended Comminities related hash. */ void -ecommunity_init () +ecommunity_init (void) { ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); } @@ -279,11 +283,12 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval, int dot = 0; int digit = 0; int separator = 0; - u_int32_t val_low = 0; - u_int32_t val_high = 0; const char *p = str; + char *endptr; struct in_addr ip; - char ipstr[INET_ADDRSTRLEN + 1]; + as_t as = 0; + u_int32_t val = 0; + char buf[INET_ADDRSTRLEN + 1]; /* Skip white space. */ while (isspace ((int) *p)) @@ -346,32 +351,50 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval, goto error; } + /* What a mess, there are several possibilities: + * + * a) A.B.C.D:MN + * b) EF:OPQR + * c) GHJK:MN + * + * A.B.C.D: Four Byte IP + * EF: Two byte ASN + * GHJK: Four-byte ASN + * MN: Two byte value + * OPQR: Four byte value + * + */ while (isdigit ((int) *p) || *p == ':' || *p == '.') { - if (*p == ':') + if (*p == ':') { if (separator) goto error; separator = 1; digit = 0; - + + if ((p - str) > INET_ADDRSTRLEN) + goto error; + memset (buf, 0, INET_ADDRSTRLEN + 1); + memcpy (buf, str, p - str); + if (dot) { - if ((p - str) > INET_ADDRSTRLEN) - goto error; - - memset (ipstr, 0, INET_ADDRSTRLEN + 1); - memcpy (ipstr, str, p - str); - - ret = inet_aton (ipstr, &ip); + /* Parsing A.B.C.D in: + * A.B.C.D:MN + */ + ret = inet_aton (buf, &ip); if (ret == 0) - goto error; + goto error; } - else - val_high = val_low; - - val_low = 0; + else + { + /* ASN */ + as = strtoul (buf, &endptr, 10); + if (*endptr != '\0' || as == BGP_AS4_MAX) + goto error; + } } else if (*p == '.') { @@ -384,35 +407,58 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval, else { digit = 1; - val_low *= 10; - val_low += (*p - '0'); + + /* We're past the IP/ASN part */ + if (separator) + { + val *= 10; + val += (*p - '0'); + } } p++; } /* Low digit part must be there. */ - if (! digit || ! separator) + if (!digit || !separator) goto error; /* Encode result into routing distinguisher. */ if (dot) { + if (val > UINT16_MAX) + goto error; + eval->val[0] = ECOMMUNITY_ENCODE_IP; eval->val[1] = 0; memcpy (&eval->val[2], &ip, sizeof (struct in_addr)); - eval->val[6] = (val_low >> 8) & 0xff; - eval->val[7] = val_low & 0xff; + eval->val[6] = (val >> 8) & 0xff; + eval->val[7] = val & 0xff; + } + else if (as > BGP_AS_MAX) + { + if (val > UINT16_MAX) + goto error; + + eval->val[0] = ECOMMUNITY_ENCODE_AS4; + eval->val[1] = 0; + eval->val[2] = (as >>24) & 0xff; + eval->val[3] = (as >>16) & 0xff; + eval->val[4] = (as >>8) & 0xff; + eval->val[5] = as & 0xff; + eval->val[6] = (val >> 8) & 0xff; + eval->val[7] = val & 0xff; } else { eval->val[0] = ECOMMUNITY_ENCODE_AS; eval->val[1] = 0; - eval->val[2] = (val_high >>8) & 0xff; - eval->val[3] = val_high & 0xff; - eval->val[4] = (val_low >>24) & 0xff; - eval->val[5] = (val_low >>16) & 0xff; - eval->val[6] = (val_low >>8) & 0xff; - eval->val[7] = val_low & 0xff; + + eval->val[2] = (as >>8) & 0xff; + eval->val[3] = as & 0xff; + eval->val[4] = (val >>24) & 0xff; + eval->val[5] = (val >>16) & 0xff; + eval->val[6] = (val >>8) & 0xff; + eval->val[7] = val & 0xff; } *token = ecommunity_token_val; return p; @@ -533,7 +579,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) u_int8_t *pnt; int encode = 0; int type = 0; -#define ECOMMUNITY_STR_DEFAULT_LEN 26 +#define ECOMMUNITY_STR_DEFAULT_LEN 27 int str_size; int str_pnt; char *str_buf; @@ -576,7 +622,8 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) /* High-order octet of type. */ encode = *pnt++; - if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP) + if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP + && encode != ECOMMUNITY_ENCODE_AS4) { len = sprintf (str_buf + str_pnt, "?"); str_pnt += len; @@ -618,6 +665,21 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) } /* Put string into buffer. */ + if (encode == ECOMMUNITY_ENCODE_AS4) + { + eas.as = (*pnt++ << 24); + eas.as |= (*pnt++ << 16); + eas.as |= (*pnt++ << 8); + eas.as |= (*pnt++); + + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + len = sprintf( str_buf + str_pnt, "%s%d:%d", prefix, + eas.as, eas.val ); + str_pnt += len; + first = 0; + } if (encode == ECOMMUNITY_ENCODE_AS) { eas.as = (*pnt++ << 8); diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 7b2564ad..69014237 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* High-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ENCODE_AS 0x00 #define ECOMMUNITY_ENCODE_IP 0x01 +#define ECOMMUNITY_ENCODE_AS4 0x02 /* Low-order octet of the Extended Communityes type field. */ #define ECOMMUNITY_ROUTE_TARGET 0x02 @@ -71,9 +72,9 @@ extern struct ecommunity *ecommunity_parse (u_int8_t *, u_short); extern struct ecommunity *ecommunity_dup (struct ecommunity *); extern struct ecommunity *ecommunity_merge (struct ecommunity *, struct ecommunity *); extern struct ecommunity *ecommunity_intern (struct ecommunity *); -extern int ecommunity_cmp (const struct ecommunity *, const struct ecommunity *); +extern int ecommunity_cmp (void *, void *); extern void ecommunity_unintern (struct ecommunity *); -extern unsigned int ecommunity_hash_make (struct ecommunity *); +extern unsigned int ecommunity_hash_make (void *); extern struct ecommunity *ecommunity_str2com (const char *, int, int); extern char *ecommunity_ecom2str (struct ecommunity *, int); extern int ecommunity_match (const struct ecommunity *, const struct ecommunity *); diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index cd235770..38431d4c 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -34,6 +34,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" #include "bgpd/bgp_vty.h" /* BGP-4 Multiprotocol Extentions lead us to the complex world. We can @@ -427,6 +428,19 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) return 0; } +static as_t +bgp_capability_as4 (struct peer *peer, struct capability_header *hdr) +{ + as_t as4 = stream_getl (BGP_INPUT(peer)); + + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u", + peer->host, as4); + SET_FLAG (peer->cap, PEER_CAP_AS4_RCV); + + return as4; +} + static struct message capcode_str[] = { { 0, ""}, @@ -507,6 +521,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) case CAPABILITY_CODE_ORF: case CAPABILITY_CODE_ORF_OLD: case CAPABILITY_CODE_RESTART: + case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: /* Check length. */ if (caphdr.length < cap_minsizes[caphdr.code]) @@ -566,6 +581,14 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) case CAPABILITY_CODE_DYNAMIC: SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV); break; + case CAPABILITY_CODE_AS4: + /* Already handled as a special-case parsing of the capabilities + * at the beginning of OPEN processing. So we care not a jot + * for the value really, only error case. + */ + if (!bgp_capability_as4 (peer, &caphdr)) + return -1; + break; default: if (caphdr.code > 128) { @@ -615,6 +638,86 @@ strict_capability_same (struct peer *peer) return 1; } +/* peek into option, stores ASN to *as4 if the AS4 capability was found. + * Returns 0 if no as4 found, as4cap value otherwise. + */ +as_t +peek_for_as4_capability (struct peer *peer, u_char length) +{ + struct stream *s = BGP_INPUT (peer); + size_t orig_getp = stream_get_getp (s); + size_t end = orig_getp + length; + as_t as4 = 0; + + /* The full capability parser will better flag the error.. */ + if (STREAM_READABLE(s) < length) + return 0; + + if (BGP_DEBUG (as4, AS4)) + zlog_info ("%s [AS4] rcv OPEN w/ OPTION parameter len: %u," + " peeking for as4", + peer->host, length); + /* the error cases we DONT handle, we ONLY try to read as4 out of + * correctly formatted options. + */ + while (stream_get_getp(s) < end) + { + u_char opt_type; + u_char opt_length; + + /* Check the length. */ + if (stream_get_getp (s) + 2 > end) + goto end; + + /* Fetch option type and length. */ + opt_type = stream_getc (s); + opt_length = stream_getc (s); + + /* Option length check. */ + if (stream_get_getp (s) + opt_length > end) + goto end; + + if (opt_type == BGP_OPEN_OPT_CAP) + { + unsigned long capd_start = stream_get_getp (s); + unsigned long capd_end = capd_start + opt_length; + + assert (capd_end <= end); + + while (stream_get_getp (s) < capd_end) + { + struct capability_header hdr; + + if (stream_get_getp (s) + 2 > capd_end) + goto end; + + hdr.code = stream_getc (s); + hdr.length = stream_getc (s); + + if ((stream_get_getp(s) + hdr.length) > capd_end) + goto end; + + if (hdr.code == CAPABILITY_CODE_AS4) + { + if (hdr.length != CAPABILITY_CODE_AS4_LEN) + goto end; + + if (BGP_DEBUG (as4, AS4)) + zlog_info ("[AS4] found AS4 capability, about to parse"); + as4 = bgp_capability_as4 (peer, &hdr); + + goto end; + } + stream_forward_getp (s, hdr.length); + } + } + } + +end: + stream_set_getp (s, orig_getp); + return as4; +} + /* Parse open option */ int bgp_open_option_parse (struct peer *peer, u_char length, int *capability) @@ -815,6 +918,7 @@ bgp_open_capability (struct stream *s, struct peer *peer) unsigned long cp; afi_t afi; safi_t safi; + as_t local_as; /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); @@ -901,6 +1005,18 @@ bgp_open_capability (struct stream *s, struct peer *peer) stream_putc (s, CAPABILITY_CODE_REFRESH); stream_putc (s, CAPABILITY_CODE_REFRESH_LEN); + /* AS4 */ + SET_FLAG (peer->cap, PEER_CAP_AS4_ADV); + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2); + stream_putc (s, CAPABILITY_CODE_AS4); + stream_putc (s, CAPABILITY_CODE_AS4_LEN); + if ( peer->change_local_as ) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + stream_putl (s, local_as ); + /* ORF capability. */ for (afi = AFI_IP ; afi < AFI_MAX ; afi++) for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 436eb01c..59265dc9 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -48,6 +48,11 @@ struct capability_orf_entry } __attribute__ ((packed)); #pragma pack() +struct capability_as4 +{ + uint32_t as4; +}; + struct graceful_restart_af { u_int16_t afi; @@ -100,6 +105,7 @@ struct capability_gr extern int bgp_open_option_parse (struct peer *, u_char, int *); extern void bgp_open_capability (struct stream *, struct peer *); extern void bgp_capability_vty_out (struct vty *, struct peer *); +extern as_t peek_for_as4_capability (struct peer *, u_char); extern int bgp_afi_safi_valid_indices (afi_t, safi_t *); #endif /* _QUAGGA_BGP_OPEN_H */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 17ac1f73..1fa2fdfd 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -804,7 +804,8 @@ bgp_open_send (struct peer *peer) /* Set open packet values. */ stream_putc (s, BGP_VERSION_4); /* BGP version */ - stream_putw (s, local_as); /* My Autonomous System*/ + stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as + : BGP_AS_TRANS); stream_putw (s, send_holdtime); /* Hold Time */ stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */ @@ -1168,6 +1169,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) u_int16_t holdtime; u_int16_t send_holdtime; as_t remote_as; + as_t as4 = 0; struct peer *realpeer; struct in_addr remote_id; int capability; @@ -1186,10 +1188,75 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Receive OPEN message log */ if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcv OPEN, version %d, remote-as %d, holdtime %d, id %s", - peer->host, version, remote_as, holdtime, - inet_ntoa (remote_id)); - + zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %d," + " holdtime %d, id %s", + peer->host, version, remote_as, holdtime, + inet_ntoa (remote_id)); + + /* BEGIN to read the capability here, but dont do it yet */ + capability = 0; + optlen = stream_getc (peer->ibuf); + + if (optlen != 0) + { + /* We need the as4 capability value *right now* because + * if it is there, we have not got the remote_as yet, and without + * that we do not know which peer is connecting to us now. + */ + as4 = peek_for_as4_capability (peer, optlen); + } + + /* Just in case we have a silly peer who sends AS4 capability set to 0 */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4) + { + zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (remote_as == BGP_AS_TRANS) + { + /* Take the AS4 from the capability. We must have received the + * capability now! Otherwise we have a asn16 peer who uses + * BGP_AS_TRANS, for some unknown reason. + */ + if (as4 == BGP_AS_TRANS) + { + zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (!as4 && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." + " Odd, but proceeding.", peer->host); + else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 fits " + "in 2-bytes, very odd peer.", peer->host, as4); + if (as4) + remote_as = as4; + } + else + { + /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */ + /* If we have got the capability, peer->as4cap must match remote_as */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) + && as4 != remote_as) + { + /* raise error, log this, close session */ + zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" + " mismatch with 16bit 'myasn' %u in open", + peer->host, as4, remote_as); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + } + /* Lookup peer from Open packet. */ if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) { @@ -1364,8 +1431,6 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) peer->v_keepalive = peer->v_holdtime / 3; /* Open option part parse. */ - capability = 0; - optlen = stream_getc (peer->ibuf); if (optlen != 0) { ret = bgp_open_option_parse (peer, optlen, &capability); @@ -2049,8 +2114,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) if (!bgp_afi_safi_valid_indices (afi, &safi)) { if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid", - peer->host, afi, safi); + zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid " + "(%u/%u)", peer->host, afi, safi); continue; } @@ -2097,7 +2162,6 @@ int bgp_capability_receive (struct peer *peer, bgp_size_t size) { u_char *pnt; - int ret; /* Fetch pointer. */ pnt = stream_pnt (peer->ibuf); @@ -2113,7 +2177,7 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size) bgp_notify_send (peer, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_BAD_MESTYPE); - return; + return -1; } /* Status must be Established. */ @@ -2122,7 +2186,7 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size) plog_err (peer->log, "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return; + return -1; } /* Parse packet. */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 305d6796..d51375b7 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3337,7 +3337,7 @@ DEFUN (no_set_atomic_aggregate, DEFUN (set_aggregator_as, set_aggregator_as_cmd, - "set aggregator as <1-65535> A.B.C.D", + "set aggregator as CMD_AS_RANGE A.B.C.D", SET_STR "BGP aggregator attribute\n" "AS number of aggregator\n" @@ -3349,7 +3349,7 @@ DEFUN (set_aggregator_as, struct in_addr address; char *argstr; - VTY_GET_INTEGER_RANGE ("AS Path", as, argv[0], 1, BGP_AS_MAX); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); ret = inet_aton (argv[1], &address); if (ret == 0) @@ -3386,7 +3386,7 @@ DEFUN (no_set_aggregator_as, if (argv == 0) return bgp_route_set_delete (vty, vty->index, "aggregator as", NULL); - VTY_GET_INTEGER_RANGE ("AS Path", as, argv[0], 1, BGP_AS_MAX); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); ret = inet_aton (argv[1], &address); if (ret == 0) @@ -3409,7 +3409,7 @@ DEFUN (no_set_aggregator_as, ALIAS (no_set_aggregator_as, no_set_aggregator_as_val_cmd, - "no set aggregator as <1-65535> A.B.C.D", + "no set aggregator as CMD_AS_RANGE A.B.C.D", NO_STR SET_STR "BGP aggregator attribute\n" diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 3eeb5f92..927e99a1 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -308,7 +308,7 @@ DEFUN_DEPRECATED (neighbor_version, /* "router bgp" commands. */ DEFUN (router_bgp, router_bgp_cmd, - "router bgp <1-65535>", + "router bgp CMD_AS_RANGE", ROUTER_STR BGP_STR AS_STR) @@ -318,7 +318,7 @@ DEFUN (router_bgp, struct bgp *bgp; const char *name = NULL; - VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, 65535); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); if (argc == 2) name = argv[1]; @@ -348,7 +348,7 @@ DEFUN (router_bgp, ALIAS (router_bgp, router_bgp_view_cmd, - "router bgp <1-65535> view WORD", + "router bgp CMD_AS_RANGE view WORD", ROUTER_STR BGP_STR AS_STR @@ -358,7 +358,7 @@ ALIAS (router_bgp, /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, - "no router bgp <1-65535>", + "no router bgp CMD_AS_RANGE", NO_STR ROUTER_STR BGP_STR @@ -368,7 +368,7 @@ DEFUN (no_router_bgp, struct bgp *bgp; const char *name = NULL; - VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, 65535); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); if (argc == 2) name = argv[1]; @@ -388,7 +388,7 @@ DEFUN (no_router_bgp, ALIAS (no_router_bgp, no_router_bgp_view_cmd, - "no router bgp <1-65535> view WORD", + "no router bgp CMD_AS_RANGE view WORD", NO_STR ROUTER_STR BGP_STR @@ -539,7 +539,7 @@ ALIAS (no_bgp_cluster_id, DEFUN (bgp_confederation_identifier, bgp_confederation_identifier_cmd, - "bgp confederation identifier <1-65535>", + "bgp confederation identifier CMD_AS_RANGE", "BGP specific commands\n" "AS confederation parameters\n" "AS number\n" @@ -550,7 +550,7 @@ DEFUN (bgp_confederation_identifier, bgp = vty->index; - VTY_GET_INTEGER ("AS", as, argv[0]); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); bgp_confederation_id_set (bgp, as); @@ -571,7 +571,7 @@ DEFUN (no_bgp_confederation_identifier, bgp = vty->index; if (argc == 1) - VTY_GET_INTEGER ("AS", as, argv[0]); + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); bgp_confederation_id_unset (bgp); @@ -580,7 +580,7 @@ DEFUN (no_bgp_confederation_identifier, ALIAS (no_bgp_confederation_identifier, no_bgp_confederation_identifier_arg_cmd, - "no bgp confederation identifier <1-65535>", + "no bgp confederation identifier CMD_AS_RANGE", NO_STR "BGP specific commands\n" "AS confederation parameters\n" @@ -589,7 +589,7 @@ ALIAS (no_bgp_confederation_identifier, DEFUN (bgp_confederation_peers, bgp_confederation_peers_cmd, - "bgp confederation peers .<1-65535>", + "bgp confederation peers .CMD_AS_RANGE", "BGP specific commands\n" "AS confederation parameters\n" "Peer ASs in BGP confederation\n" @@ -603,7 +603,7 @@ DEFUN (bgp_confederation_peers, for (i = 0; i < argc; i++) { - VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, 65535); + VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX); if (bgp->as == as) { @@ -619,7 +619,7 @@ DEFUN (bgp_confederation_peers, DEFUN (no_bgp_confederation_peers, no_bgp_confederation_peers_cmd, - "no bgp confederation peers .<1-65535>", + "no bgp confederation peers .CMD_AS_RANGE", NO_STR "BGP specific commands\n" "AS confederation parameters\n" @@ -634,8 +634,8 @@ DEFUN (no_bgp_confederation_peers, for (i = 0; i < argc; i++) { - VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, 65535); - + VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX); + bgp_confederation_peers_remove (bgp, as); } return CMD_SUCCESS; @@ -1249,7 +1249,7 @@ peer_remote_as_vty (struct vty *vty, const char *peer_str, bgp = vty->index; /* Get AS number. */ - VTY_GET_INTEGER_RANGE ("AS", as, as_str, 1, 65535); + VTY_GET_INTEGER_RANGE ("AS", as, as_str, 1, BGP_AS4_MAX); /* If peer is peer group, call proper function. */ ret = str2sockunion (peer_str, &su); @@ -1288,7 +1288,7 @@ peer_remote_as_vty (struct vty *vty, const char *peer_str, DEFUN (neighbor_remote_as, neighbor_remote_as_cmd, - NEIGHBOR_CMD2 "remote-as <1-65535>", + NEIGHBOR_CMD2 "remote-as CMD_AS_RANGE", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a BGP neighbor\n" @@ -1352,7 +1352,7 @@ DEFUN (no_neighbor, ALIAS (no_neighbor, no_neighbor_remote_as_cmd, - NO_NEIGHBOR_CMD "remote-as <1-65535>", + NO_NEIGHBOR_CMD "remote-as CMD_AS_RANGE", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR @@ -1382,7 +1382,7 @@ DEFUN (no_neighbor_peer_group, DEFUN (no_neighbor_peer_group_remote_as, no_neighbor_peer_group_remote_as_cmd, - "no neighbor WORD remote-as <1-65535>", + "no neighbor WORD remote-as CMD_AS_RANGE", NO_STR NEIGHBOR_STR "Neighbor tag\n" @@ -1404,7 +1404,7 @@ DEFUN (no_neighbor_peer_group_remote_as, DEFUN (neighbor_local_as, neighbor_local_as_cmd, - NEIGHBOR_CMD2 "local-as <1-65535>", + NEIGHBOR_CMD2 "local-as CMD_AS_RANGE", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" @@ -1423,7 +1423,7 @@ DEFUN (neighbor_local_as, DEFUN (neighbor_local_as_no_prepend, neighbor_local_as_no_prepend_cmd, - NEIGHBOR_CMD2 "local-as <1-65535> no-prepend", + NEIGHBOR_CMD2 "local-as CMD_AS_RANGE no-prepend", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" @@ -1462,7 +1462,7 @@ DEFUN (no_neighbor_local_as, ALIAS (no_neighbor_local_as, no_neighbor_local_as_val_cmd, - NO_NEIGHBOR_CMD2 "local-as <1-65535>", + NO_NEIGHBOR_CMD2 "local-as CMD_AS_RANGE", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 @@ -1471,7 +1471,7 @@ ALIAS (no_neighbor_local_as, ALIAS (no_neighbor_local_as, no_neighbor_local_as_val2_cmd, - NO_NEIGHBOR_CMD2 "local-as <1-65535> no-prepend", + NO_NEIGHBOR_CMD2 "local-as CMD_AS_RANGE no-prepend", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 @@ -4037,7 +4037,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error (vty, peer, afi, safi, ret); } - return 0; + return CMD_SUCCESS; } /* Clear specified neighbors. */ @@ -4051,13 +4051,13 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) { vty_out (vty, "Malformed address: %s%s", arg, VTY_NEWLINE); - return -1; + return CMD_WARNING; } peer = peer_lookup (bgp, &su); if (! peer) { vty_out (vty, "%%BGP: Unknown neighbor - \"%s\"%s", arg, VTY_NEWLINE); - return -1; + return CMD_WARNING; } if (stype == BGP_CLEAR_SOFT_NONE) @@ -4068,7 +4068,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error (vty, peer, afi, safi, ret); - return 0; + return CMD_SUCCESS; } /* Clear all peer-group members. */ @@ -4080,7 +4080,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (! group) { vty_out (vty, "%%BGP: No such peer-group %s%s", arg, VTY_NEWLINE); - return -1; + return CMD_WARNING; } for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) @@ -4099,7 +4099,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error (vty, peer, afi, safi, ret); } - return 0; + return CMD_SUCCESS; } if (sort == clear_external) @@ -4117,22 +4117,21 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error (vty, peer, afi, safi, ret); } - return 0; + return CMD_SUCCESS; } if (sort == clear_as) { as_t as; unsigned long as_ul; - char *endptr = NULL; int find = 0; - as_ul = strtoul(arg, &endptr, 10); - - if ((as_ul == ULONG_MAX) || (*endptr != '\0') || (as_ul > USHRT_MAX)) + VTY_GET_LONG ("AS", as_ul, arg); + + if (!as_ul) { vty_out (vty, "Invalid AS number%s", VTY_NEWLINE); - return -1; + return CMD_WARNING; } as = (as_t) as_ul; @@ -4153,10 +4152,10 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (! find) vty_out (vty, "%%BGP: No peer is configured with AS %s%s", arg, VTY_NEWLINE); - return 0; + return CMD_SUCCESS; } - return 0; + return CMD_SUCCESS; } static int @@ -4164,7 +4163,6 @@ bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi, enum clear_sort sort, enum bgp_clear_type stype, const char *arg) { - int ret; struct bgp *bgp; /* BGP structure lookup. */ @@ -4187,11 +4185,7 @@ bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi, } } - ret = bgp_clear (vty, bgp, afi, safi, sort, stype, arg); - if (ret < 0) - return CMD_WARNING; - - return CMD_SUCCESS; + return bgp_clear (vty, bgp, afi, safi, sort, stype, arg); } DEFUN (clear_ip_bgp_all, @@ -4328,7 +4322,7 @@ ALIAS (clear_ip_bgp_external, DEFUN (clear_ip_bgp_as, clear_ip_bgp_as_cmd, - "clear ip bgp <1-65535>", + "clear ip bgp CMD_AS_RANGE", CLEAR_STR IP_STR BGP_STR @@ -4339,14 +4333,14 @@ DEFUN (clear_ip_bgp_as, ALIAS (clear_ip_bgp_as, clear_bgp_as_cmd, - "clear bgp <1-65535>", + "clear bgp CMD_AS_RANGE", CLEAR_STR BGP_STR "Clear peers with the AS number\n") ALIAS (clear_ip_bgp_as, clear_bgp_ipv6_as_cmd, - "clear bgp ipv6 <1-65535>", + "clear bgp ipv6 CMD_AS_RANGE", CLEAR_STR BGP_STR "Address family\n" @@ -4858,7 +4852,7 @@ ALIAS (clear_bgp_external_soft_out, DEFUN (clear_ip_bgp_as_soft_out, clear_ip_bgp_as_soft_out_cmd, - "clear ip bgp <1-65535> soft out", + "clear ip bgp CMD_AS_RANGE soft out", CLEAR_STR IP_STR BGP_STR @@ -4872,7 +4866,7 @@ DEFUN (clear_ip_bgp_as_soft_out, ALIAS (clear_ip_bgp_as_soft_out, clear_ip_bgp_as_out_cmd, - "clear ip bgp <1-65535> out", + "clear ip bgp CMD_AS_RANGE out", CLEAR_STR IP_STR BGP_STR @@ -4881,7 +4875,7 @@ ALIAS (clear_ip_bgp_as_soft_out, DEFUN (clear_ip_bgp_as_ipv4_soft_out, clear_ip_bgp_as_ipv4_soft_out_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft out", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) soft out", CLEAR_STR IP_STR BGP_STR @@ -4902,7 +4896,7 @@ DEFUN (clear_ip_bgp_as_ipv4_soft_out, ALIAS (clear_ip_bgp_as_ipv4_soft_out, clear_ip_bgp_as_ipv4_out_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) out", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) out", CLEAR_STR IP_STR BGP_STR @@ -4914,7 +4908,7 @@ ALIAS (clear_ip_bgp_as_ipv4_soft_out, DEFUN (clear_ip_bgp_as_vpnv4_soft_out, clear_ip_bgp_as_vpnv4_soft_out_cmd, - "clear ip bgp <1-65535> vpnv4 unicast soft out", + "clear ip bgp CMD_AS_RANGE vpnv4 unicast soft out", CLEAR_STR IP_STR BGP_STR @@ -4930,7 +4924,7 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft_out, ALIAS (clear_ip_bgp_as_vpnv4_soft_out, clear_ip_bgp_as_vpnv4_out_cmd, - "clear ip bgp <1-65535> vpnv4 unicast out", + "clear ip bgp CMD_AS_RANGE vpnv4 unicast out", CLEAR_STR IP_STR BGP_STR @@ -4941,7 +4935,7 @@ ALIAS (clear_ip_bgp_as_vpnv4_soft_out, DEFUN (clear_bgp_as_soft_out, clear_bgp_as_soft_out_cmd, - "clear bgp <1-65535> soft out", + "clear bgp CMD_AS_RANGE soft out", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -4954,7 +4948,7 @@ DEFUN (clear_bgp_as_soft_out, ALIAS (clear_bgp_as_soft_out, clear_bgp_ipv6_as_soft_out_cmd, - "clear bgp ipv6 <1-65535> soft out", + "clear bgp ipv6 CMD_AS_RANGE soft out", CLEAR_STR BGP_STR "Address family\n" @@ -4964,7 +4958,7 @@ ALIAS (clear_bgp_as_soft_out, ALIAS (clear_bgp_as_soft_out, clear_bgp_as_out_cmd, - "clear bgp <1-65535> out", + "clear bgp CMD_AS_RANGE out", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -4972,7 +4966,7 @@ ALIAS (clear_bgp_as_soft_out, ALIAS (clear_bgp_as_soft_out, clear_bgp_ipv6_as_out_cmd, - "clear bgp ipv6 <1-65535> out", + "clear bgp ipv6 CMD_AS_RANGE out", CLEAR_STR BGP_STR "Address family\n" @@ -5762,7 +5756,7 @@ ALIAS (clear_bgp_external_in_prefix_filter, DEFUN (clear_ip_bgp_as_soft_in, clear_ip_bgp_as_soft_in_cmd, - "clear ip bgp <1-65535> soft in", + "clear ip bgp CMD_AS_RANGE soft in", CLEAR_STR IP_STR BGP_STR @@ -5776,7 +5770,7 @@ DEFUN (clear_ip_bgp_as_soft_in, ALIAS (clear_ip_bgp_as_soft_in, clear_ip_bgp_as_in_cmd, - "clear ip bgp <1-65535> in", + "clear ip bgp CMD_AS_RANGE in", CLEAR_STR IP_STR BGP_STR @@ -5785,7 +5779,7 @@ ALIAS (clear_ip_bgp_as_soft_in, DEFUN (clear_ip_bgp_as_in_prefix_filter, clear_ip_bgp_as_in_prefix_filter_cmd, - "clear ip bgp <1-65535> in prefix-filter", + "clear ip bgp CMD_AS_RANGE in prefix-filter", CLEAR_STR IP_STR BGP_STR @@ -5799,7 +5793,7 @@ DEFUN (clear_ip_bgp_as_in_prefix_filter, DEFUN (clear_ip_bgp_as_ipv4_soft_in, clear_ip_bgp_as_ipv4_soft_in_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft in", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) soft in", CLEAR_STR IP_STR BGP_STR @@ -5820,7 +5814,7 @@ DEFUN (clear_ip_bgp_as_ipv4_soft_in, ALIAS (clear_ip_bgp_as_ipv4_soft_in, clear_ip_bgp_as_ipv4_in_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) in", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) in", CLEAR_STR IP_STR BGP_STR @@ -5832,7 +5826,7 @@ ALIAS (clear_ip_bgp_as_ipv4_soft_in, DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter, clear_ip_bgp_as_ipv4_in_prefix_filter_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) in prefix-filter", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) in prefix-filter", CLEAR_STR IP_STR BGP_STR @@ -5853,7 +5847,7 @@ DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter, DEFUN (clear_ip_bgp_as_vpnv4_soft_in, clear_ip_bgp_as_vpnv4_soft_in_cmd, - "clear ip bgp <1-65535> vpnv4 unicast soft in", + "clear ip bgp CMD_AS_RANGE vpnv4 unicast soft in", CLEAR_STR IP_STR BGP_STR @@ -5869,7 +5863,7 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft_in, ALIAS (clear_ip_bgp_as_vpnv4_soft_in, clear_ip_bgp_as_vpnv4_in_cmd, - "clear ip bgp <1-65535> vpnv4 unicast in", + "clear ip bgp CMD_AS_RANGE vpnv4 unicast in", CLEAR_STR IP_STR BGP_STR @@ -5880,7 +5874,7 @@ ALIAS (clear_ip_bgp_as_vpnv4_soft_in, DEFUN (clear_bgp_as_soft_in, clear_bgp_as_soft_in_cmd, - "clear bgp <1-65535> soft in", + "clear bgp CMD_AS_RANGE soft in", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -5893,7 +5887,7 @@ DEFUN (clear_bgp_as_soft_in, ALIAS (clear_bgp_as_soft_in, clear_bgp_ipv6_as_soft_in_cmd, - "clear bgp ipv6 <1-65535> soft in", + "clear bgp ipv6 CMD_AS_RANGE soft in", CLEAR_STR BGP_STR "Address family\n" @@ -5903,7 +5897,7 @@ ALIAS (clear_bgp_as_soft_in, ALIAS (clear_bgp_as_soft_in, clear_bgp_as_in_cmd, - "clear bgp <1-65535> in", + "clear bgp CMD_AS_RANGE in", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -5911,7 +5905,7 @@ ALIAS (clear_bgp_as_soft_in, ALIAS (clear_bgp_as_soft_in, clear_bgp_ipv6_as_in_cmd, - "clear bgp ipv6 <1-65535> in", + "clear bgp ipv6 CMD_AS_RANGE in", CLEAR_STR BGP_STR "Address family\n" @@ -5920,7 +5914,7 @@ ALIAS (clear_bgp_as_soft_in, DEFUN (clear_bgp_as_in_prefix_filter, clear_bgp_as_in_prefix_filter_cmd, - "clear bgp <1-65535> in prefix-filter", + "clear bgp CMD_AS_RANGE in prefix-filter", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -5933,7 +5927,7 @@ DEFUN (clear_bgp_as_in_prefix_filter, ALIAS (clear_bgp_as_in_prefix_filter, clear_bgp_ipv6_as_in_prefix_filter_cmd, - "clear bgp ipv6 <1-65535> in prefix-filter", + "clear bgp ipv6 CMD_AS_RANGE in prefix-filter", CLEAR_STR BGP_STR "Address family\n" @@ -6248,7 +6242,7 @@ ALIAS (clear_bgp_external_soft, DEFUN (clear_ip_bgp_as_soft, clear_ip_bgp_as_soft_cmd, - "clear ip bgp <1-65535> soft", + "clear ip bgp CMD_AS_RANGE soft", CLEAR_STR IP_STR BGP_STR @@ -6261,7 +6255,7 @@ DEFUN (clear_ip_bgp_as_soft, DEFUN (clear_ip_bgp_as_ipv4_soft, clear_ip_bgp_as_ipv4_soft_cmd, - "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft", + "clear ip bgp CMD_AS_RANGE ipv4 (unicast|multicast) soft", CLEAR_STR IP_STR BGP_STR @@ -6281,7 +6275,7 @@ DEFUN (clear_ip_bgp_as_ipv4_soft, DEFUN (clear_ip_bgp_as_vpnv4_soft, clear_ip_bgp_as_vpnv4_soft_cmd, - "clear ip bgp <1-65535> vpnv4 unicast soft", + "clear ip bgp CMD_AS_RANGE vpnv4 unicast soft", CLEAR_STR IP_STR BGP_STR @@ -6296,7 +6290,7 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft, DEFUN (clear_bgp_as_soft, clear_bgp_as_soft_cmd, - "clear bgp <1-65535> soft", + "clear bgp CMD_AS_RANGE soft", CLEAR_STR BGP_STR "Clear peers with the AS number\n" @@ -6308,7 +6302,7 @@ DEFUN (clear_bgp_as_soft, ALIAS (clear_bgp_as_soft, clear_bgp_ipv6_as_soft_cmd, - "clear bgp ipv6 <1-65535> soft", + "clear bgp ipv6 CMD_AS_RANGE soft", CLEAR_STR BGP_STR "Address family\n" @@ -6688,7 +6682,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) peer->open_out + peer->update_out + peer->keepalive_out + peer->notify_out + peer->refresh_out + peer->dynamic_cap_out, - 0, 0, (unsigned long)peer->obuf->count); + 0, 0, (unsigned long) peer->obuf->count); vty_out (vty, "%8s", peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN)); @@ -7271,6 +7265,18 @@ bgp_show_peer (struct vty *vty, struct peer *p) { vty_out (vty, " Neighbor capabilities:%s", VTY_NEWLINE); + /* AS4 */ + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV)) + { + vty_out (vty, " 4 Byte AS:"); + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV)) + vty_out (vty, " advertised"); + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV)) + vty_out (vty, " %sreceived", + CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV) ? "and " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } /* Dynamic */ if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_RCV) || CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV)) @@ -7389,21 +7395,18 @@ bgp_show_peer (struct vty *vty, struct peer *p) } if (p->t_gr_restart) - { - vty_out (vty, " The remaining time of restart timer is %ld%s", - thread_timer_remain_second (p->t_gr_restart), VTY_NEWLINE); - } + vty_out (vty, " The remaining time of restart timer is %ld%s", + thread_timer_remain_second (p->t_gr_restart), VTY_NEWLINE); + if (p->t_gr_stale) - { - vty_out (vty, " The remaining time of stalepath timer is %ld%s", - thread_timer_remain_second (p->t_gr_stale), VTY_NEWLINE); - } + vty_out (vty, " The remaining time of stalepath timer is %ld%s", + thread_timer_remain_second (p->t_gr_stale), VTY_NEWLINE); } /* Packet counts. */ vty_out (vty, " Message statistics:%s", VTY_NEWLINE); vty_out (vty, " Inq depth is 0%s", VTY_NEWLINE); - vty_out (vty, " Outq depth is %lu%s", (unsigned long)p->obuf->count, VTY_NEWLINE); + vty_out (vty, " Outq depth is %lu%s", (unsigned long) p->obuf->count, VTY_NEWLINE); vty_out (vty, " Sent Rcvd%s", VTY_NEWLINE); vty_out (vty, " Opens: %10d %10d%s", p->open_out, p->open_in, VTY_NEWLINE); vty_out (vty, " Notifications: %10d %10d%s", p->notify_out, p->notify_in, VTY_NEWLINE); @@ -7907,7 +7910,7 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient, vty_out (vty, "4 "); - vty_out (vty, "%5d ", rsclient->as); + vty_out (vty, "%11d ", rsclient->as); rmname = ROUTE_MAP_EXPORT_NAME(&rsclient->filter[afi][safi]); if ( rmname && strlen (rmname) > 13 ) diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index d3b4e2b4..2df8aaa5 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_VTY_H #define _QUAGGA_BGP_VTY_H +#define CMD_AS_RANGE "<1-4294967295>" + extern void bgp_vty_init (void); extern const char *afi_safi_print (afi_t, safi_t); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 3fba6042..e04575d8 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -25,7 +25,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "sockunion.h" /* Typedef BGP specific types. */ -typedef u_int16_t as_t; +typedef u_int32_t as_t; +typedef u_int16_t as16_t; /* we may still encounter 16 Bit asnums */ typedef u_int16_t bgp_size_t; /* BGP master for system wide configurations and variables. */ @@ -287,6 +288,9 @@ struct peer int status; int ostatus; + /* Peer index, used for dumping TABLE_DUMP_V2 format */ + uint16_t table_dump_index; + /* Peer information */ int fd; /* File descriptor */ int ttl; /* TTL of TCP connection to the peer. */ @@ -316,7 +320,7 @@ struct peer u_char afc_recv[AFI_MAX][SAFI_MAX]; /* Capability flags (reset in bgp_stop) */ - u_char cap; + u_int16_t cap; #define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */ #define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */ #define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */ @@ -324,6 +328,8 @@ struct peer #define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */ #define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */ #define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */ +#define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */ +#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */ /* Capability flags (reset in bgp_stop) */ u_int16_t af_cap[AFI_MAX][SAFI_MAX]; @@ -591,6 +597,8 @@ struct bgp_nlri #define BGP_ATTR_MP_REACH_NLRI 14 #define BGP_ATTR_MP_UNREACH_NLRI 15 #define BGP_ATTR_EXT_COMMUNITIES 16 +#define BGP_ATTR_AS4_PATH 17 +#define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 /* BGP update origin. */ diff --git a/doc/BGP-TypeCode b/doc/BGP-TypeCode index 0904e19f..5663c4d7 100644 --- a/doc/BGP-TypeCode +++ b/doc/BGP-TypeCode @@ -19,6 +19,8 @@ 14 MP_REACH_NLRI [RFC 2283] 15 MP_UNREACH_NLRI [RFC 2283] 16 EXT_COMMUNITIES [draft-ramachandra-bgp-ext-communities-09.txt] + 17 AS4_PATH [RFC 4893] + 18 AS4_AGGREGATOR [RFC 4893] 254 RCID_PATH [RFC 1863] 255 ADVERTISER [RFC 1863] ========================================================================= diff --git a/tests/ChangeLog b/tests/ChangeLog index 46ab9766..94f58749 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,28 @@ +2007-09-27 Paul Jakma + + * aspath_test.c: Test dupe-weeding from sets. + Test that reconciliation merges AS_PATH and AS4_PATH where + former is shorter than latter. + +2007-09-26 Paul Jakma + + * aspath_test.c: Test AS4_PATH reconcilation where length + of AS_PATH and AS4_PATH is same. + +2007-09-25 Paul Jakma + + * bgp_capability_test.c: (general) Extend tests to validate + peek_for_as4_capability. + Add test of full OPEN Option block, with multiple capabilities, + both as a series of Option, and a single option. + Add some crap to beginning of stream, to prevent code depending + on getp == 0. + +2007-09-18 Paul Jakma + + * bgp_capability_test.c: (parse_test) update for changes to + peek_for_as4_capability + 2007-09-17 Paul Jakma * bgp_capability_test.c: Test that peer's adv_recv and adv_nego get @@ -9,6 +34,17 @@ * bgp_capability_test.c: new, capability parser unit tests. * Makefile.am: add previous. +2007-07-25 Paul Jakma + + * aspath_test.c: Exercise 32bit parsing. Test reconcile + function. + * ecommunity_test.c: New, test AS4 ecommunity changes, positive + test only at this time, error cases not tested yet. + +2006-12-01 Juergen Kammer + + * aspath_test.c: Support asn32 changes, call aspath_parse with 16 bit. + 2006-08-26 Paul Jakma * heavy-wq.c: (slow_func_del,slow_func) update to match workqueue diff --git a/tests/Makefile.am b/tests/Makefile.am index a63416ff..2045496e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - aspathtest testprivs teststream testbgpcap + aspathtest testprivs teststream testbgpcap ecommtest testsig_SOURCES = test-sig.c testbuffer_SOURCES = test-buffer.c testmemory_SOURCES = test-memory.c @@ -13,6 +13,7 @@ heavywq_SOURCES = heavy-wq.c main.c heavythread_SOURCES = heavy-thread.c main.c aspathtest_SOURCES = aspath_test.c testbgpcap_SOURCES = bgp_capability_test.c +ecommtest_SOURCES = ecommunity_test.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -24,3 +25,4 @@ heavywq_LDADD = ../lib/libzebra.la @LIBCAP@ -lm heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm aspathtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a testbgpcap_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a +ecommtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 1d28dbed..c12d07a5 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -7,6 +7,13 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" +#define VT100_RESET "\x1b[0m" +#define VT100_RED "\x1b[31m" +#define VT100_GREEN "\x1b[32m" +#define VT100_YELLOW "\x1b[33m" +#define OK VT100_GREEN "OK" VT100_RESET +#define FAILED VT100_RED "failed" VT100_RESET + /* need these to link in libbgp */ struct zebra_privs_t *bgpd_privs = NULL; struct thread_master *master = NULL; @@ -312,8 +319,87 @@ static struct test_segment { /* We shouldn't ever /generate/ such paths. However, we should * cope with them fine. */ - "8466 3 52737 4096 3456 {7099,8153,8153,8153}", - "8466 3 52737 4096 3456 {7099,8153,8153,8153}", + "8466 3 52737 4096 3456 {7099,8153}", + "8466 3 52737 4096 3456 {7099,8153}", + 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 18 */ + "reconcile_lead_asp", + "seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)", + { 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, + 0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 }, + 24, + { "6435 59408 21665 {23456} 23456 23456 23456", + "6435 59408 21665 {23456} 23456 23456 23456", + 7, 0, NOT_ALL_PRIVATE, 23456, 1, 6435 }, + }, + { /* 19 */ + "reconcile_new_asp", + "set(2457,61697,4369), seq(1842,41591,51793)", + { + 0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11, + 0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51 }, + 16, + { "{2457,4369,61697} 1842 41591 51793", + "{2457,4369,61697} 1842 41591 51793", + 4, 0, NOT_ALL_PRIVATE, 51793, 1, 2457 }, + }, + { /* 20 */ + "reconcile_confed", + "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)" + " set(23456,23456,23456), seq(23456,23456,23456)", + { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15, + 0x4,0x3, 0x01,0xc8, 0x00,0x7c, 0x03,0x14, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, + 0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 }, + 40, + { "(123 456 789) [124,456,788] 6435 59408 21665" + " {23456} 23456 23456 23456", + "6435 59408 21665 {23456} 23456 23456 23456", + 7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435 }, + }, + { /* 21 */ + "reconcile_start_trans", + "seq(23456,23456,23456) seq(6435,59408,21665)", + { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, }, + 16, + { "23456 23456 23456 6435 59408 21665", + "23456 23456 23456 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 21665, 1, 23456 }, + }, + { /* 22 */ + "reconcile_start_trans4", + "seq(1842,41591,51793) seq(6435,59408,21665)", + { 0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, }, + 16, + { "1842 41591 51793 6435 59408 21665", + "1842 41591 51793 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 41591, 1, 1842 }, + }, + { /* 23 */ + "reconcile_start_trans_error", + "seq(23456,23456,23456) seq(6435,59408)", + { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x2, 0x19,0x23, 0xe8,0x10, }, + 14, + { "23456 23456 23456 6435 59408", + "23456 23456 23456 6435 59408", + 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456 }, + }, + { /* 24 */ + "redundantset2", + "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)", + { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80, + 0x1,0x5, 0x1b,0xbb, 0x1f,0xd9, 0x1f,0xd9, 0x1f,0xd9, 0x1b,0xbb,}, + 24, + { + /* We should weed out duplicate set members. */ + "8466 3 52737 4096 3456 {7099,8153}", + "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, }, { NULL, NULL, {0}, 0, { NULL, 0, 0 } } @@ -432,13 +518,43 @@ static struct tests { { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } }, }; -struct tests aggregate_tests[] = +struct tests reconcile_tests[] = { - { &test_segments[0], &test_segments[1], - { "{3,4,4096,8466,8722,52737}", - "{3,4,4096,8466,8722,52737}", - 1, 0, NOT_ALL_PRIVATE, 52737, 1, NULL_ASN }, + { &test_segments[18], &test_segments[19], + { "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + 7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[19], &test_segments[18], + /* AS_PATH (19) has more hops than NEW_AS_PATH, + * so just AS_PATH should be used (though, this practice + * is bad imho). + */ + { "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456", + "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456", + 11, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[20], &test_segments[19], + { "(123 456 789) [124,456,788] 6435 59408 21665" + " {2457,4369,61697} 1842 41591 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + 7, 4, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[21], &test_segments[22], + { "1842 41591 51793 6435 59408 21665", + "1842 41591 51793 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 }, + }, + { &test_segments[23], &test_segments[22], + { "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665", + "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665", + 11, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 }, }, + { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } }, +}; + +struct tests aggregate_tests[] = +{ { &test_segments[0], &test_segments[2], { "8466 3 52737 4096 {4,8722}", "8466 3 52737 4096 {4,8722}", @@ -459,6 +575,13 @@ struct tests aggregate_tests[] = "8466 {2,3,4,4096,8722,52737}", 2, 0, NOT_ALL_PRIVATE, 2, 20000, 8466 }, }, + + { &test_segments[5], &test_segments[18], + { "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", + "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", + 4, 0, NOT_ALL_PRIVATE, 41590, 1, 6435 }, + }, + { NULL, NULL, { NULL, 0, 0} }, }; @@ -498,7 +621,7 @@ struct compare_tests /* make an aspath from a data stream */ static struct aspath * -make_aspath (const u_char *data, size_t len) +make_aspath (const u_char *data, size_t len, int use32bit) { struct stream *s = NULL; struct aspath *as; @@ -508,7 +631,7 @@ make_aspath (const u_char *data, size_t len) s = stream_new (len); stream_put (s, data, len); } - as = aspath_parse (s, len); + as = aspath_parse (s, len, use32bit); if (s) stream_free (s); @@ -535,18 +658,27 @@ printbytes (const u_char *bytes, int len) static int validate (struct aspath *as, const struct test_spec *sp) { - size_t bytes; + size_t bytes, bytes4; int fails = 0; const u_char *out; - struct aspath *asinout, *asconfeddel, *asstr; + static struct stream *s; + struct aspath *asinout, *asconfeddel, *asstr, *as4; out = aspath_snmp_pathseg (as, &bytes); - asinout = make_aspath (out, bytes); + asinout = make_aspath (out, bytes, 0); + + /* Excercise AS4 parsing a bit, with a dogfood test */ + if (!s) + s = stream_new (4096); + bytes4 = aspath_put (s, as, 1); + as4 = make_aspath (STREAM_DATA(s), bytes4, 1); asstr = aspath_str2aspath (sp->shouldbe); asconfeddel = aspath_delete_confed_seq (aspath_dup (asinout)); + printf ("got: %s\n", aspath_print(as)); + /* the parsed path should match the specified 'shouldbe' string. * We should pass the "eat our own dog food" test, be able to output * this path and then input it again. Ie the path resulting from: @@ -562,6 +694,10 @@ validate (struct aspath *as, const struct test_spec *sp) * * aspath_str2aspath() and shouldbe should match * + * We do the same for: + * + * aspath_parse(aspath_put(as,USE32BIT)) + * * Confederation related tests: * - aspath_delete_confed_seq(aspath) should match shouldbe_confed * - aspath_delete_confed_seq should be idempotent. @@ -571,6 +707,8 @@ validate (struct aspath *as, const struct test_spec *sp) || (aspath_key_make (as) != aspath_key_make (asinout)) /* by string */ || strcmp(aspath_print (asinout), sp->shouldbe) + /* By 4-byte parsing */ + || strcmp(aspath_print (as4), sp->shouldbe) /* by various path counts */ || (aspath_count_hops (as) != sp->hops) || (aspath_count_confeds (as) != sp->confeds) @@ -580,7 +718,7 @@ validate (struct aspath *as, const struct test_spec *sp) failed++; fails++; printf ("shouldbe:\n%s\n", sp->shouldbe); - printf ("got:\n%s\n", aspath_print(as)); + printf ("as4:\n%s\n", aspath_print (as4)); printf ("hash keys: in: %d out->in: %d\n", aspath_key_make (as), aspath_key_make (asinout)); printf ("hops: %d, counted %d %d\n", sp->hops, @@ -635,11 +773,13 @@ validate (struct aspath *as, const struct test_spec *sp) aspath_private_as_check (as)); } aspath_unintern (asinout); + aspath_unintern (as4); + aspath_free (asconfeddel); aspath_free (asstr); + stream_reset (s); return fails; - } static void @@ -650,9 +790,9 @@ empty_get_test () printf ("empty_get_test, as: %s\n",aspath_print (as)); if (!validate (as, &sp)) - printf ("OK\n"); + printf ("%s\n", OK); else - printf ("failed!\n"); + printf ("%s!\n", FAILED); printf ("\n"); @@ -667,14 +807,14 @@ parse_test (struct test_segment *t) printf ("%s: %s\n", t->name, t->desc); - asp = make_aspath (t->asdata, t->len); + asp = make_aspath (t->asdata, t->len, 0); printf ("aspath: %s\nvalidating...:\n", aspath_print (asp)); if (!validate (asp, &t->sp)) - printf ("OK\n"); + printf (OK "\n"); else - printf ("failed\n"); + printf (FAILED "\n"); printf ("\n"); aspath_unintern (asp); @@ -689,8 +829,8 @@ prepend_test (struct tests *t) printf ("prepend %s: %s\n", t->test1->name, t->test1->desc); printf ("to %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath (t->test1->asdata, t->test1->len); - asp2 = make_aspath (t->test2->asdata, t->test2->len); + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); ascratch = aspath_dup (asp2); aspath_unintern (asp2); @@ -700,9 +840,9 @@ prepend_test (struct tests *t) printf ("aspath: %s\n", aspath_print (asp2)); if (!validate (asp2, &t->sp)) - printf ("OK\n"); + printf ("%s\n", OK); else - printf ("failed!\n"); + printf ("%s!\n", FAILED); printf ("\n"); aspath_unintern (asp1); @@ -717,7 +857,7 @@ empty_prepend_test (struct test_segment *t) printf ("empty prepend %s: %s\n", t->name, t->desc); - asp1 = make_aspath (t->asdata, t->len); + asp1 = make_aspath (t->asdata, t->len, 0); asp2 = aspath_empty (); ascratch = aspath_dup (asp2); @@ -728,15 +868,41 @@ empty_prepend_test (struct test_segment *t) printf ("aspath: %s\n", aspath_print (asp2)); if (!validate (asp2, &t->sp)) - printf ("OK\n"); + printf (OK "\n"); else - printf ("failed!\n"); + printf (FAILED "!\n"); printf ("\n"); aspath_unintern (asp1); aspath_free (asp2); } +/* as2+as4 reconciliation testing */ +static void +as4_reconcile_test (struct tests *t) +{ + struct aspath *asp1, *asp2, *ascratch; + + printf ("reconciling %s:\n %s\n", t->test1->name, t->test1->desc); + printf ("with %s:\n %s\n", t->test2->name, t->test2->desc); + + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); + + ascratch = aspath_reconcile_as4 (asp1, asp2); + + if (!validate (ascratch, &t->sp)) + printf (OK "\n"); + else + printf (FAILED "!\n"); + + printf ("\n"); + aspath_unintern (asp1); + aspath_unintern (asp2); + aspath_free (ascratch); +} + + /* aggregation testing */ static void aggregate_test (struct tests *t) @@ -746,17 +912,15 @@ aggregate_test (struct tests *t) printf ("aggregate %s: %s\n", t->test1->name, t->test1->desc); printf ("with %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath (t->test1->asdata, t->test1->len); - asp2 = make_aspath (t->test2->asdata, t->test2->len); + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); ascratch = aspath_aggregate (asp1, asp2); - printf ("aspath: %s\n", aspath_print (ascratch)); - if (!validate (ascratch, &t->sp)) - printf ("OK\n"); + printf (OK "\n"); else - printf ("failed!\n"); + printf (FAILED "!\n"); printf ("\n"); aspath_unintern (asp1); @@ -782,8 +946,8 @@ cmp_test () printf ("left cmp %s: %s\n", t1->name, t1->desc); printf ("and %s: %s\n", t2->name, t2->desc); - asp1 = make_aspath (t1->asdata, t1->len); - asp2 = make_aspath (t2->asdata, t2->len); + asp1 = make_aspath (t1->asdata, t1->len, 0); + asp2 = make_aspath (t2->asdata, t2->len, 0); if (aspath_cmp_left (asp1, asp2) != left_compare[i].shouldbe_cmp || aspath_cmp_left (asp2, asp1) != left_compare[i].shouldbe_cmp @@ -792,7 +956,8 @@ cmp_test () || aspath_cmp_left_confed (asp2, asp1) != left_compare[i].shouldbe_confed) { - printf ("failed\n"); + failed++; + printf (FAILED "\n"); printf ("result should be: cmp: %d, confed: %d\n", left_compare[i].shouldbe_cmp, left_compare[i].shouldbe_confed); @@ -801,10 +966,9 @@ cmp_test () aspath_cmp_left_confed (asp1, asp2)); printf("path1: %s\npath2: %s\n", aspath_print (asp1), aspath_print (asp2)); - failed++; } else - printf ("OK\n"); + printf (OK "\n"); printf ("\n"); aspath_unintern (asp1); @@ -831,6 +995,13 @@ main (void) while (aggregate_tests[i].test1) aggregate_test (&aggregate_tests[i++]); + i = 0; + + while (reconcile_tests[i].test1) + as4_reconcile_test (&reconcile_tests[i++]); + + i = 0; + cmp_test(); i = 0; diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 020dfb0f..6771b579 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -15,8 +15,9 @@ #define VT100_YELLOW "\x1b[33m" -#define OPEN 0 -#define DYNCAP 1 +#define CAPABILITY 0 +#define DYNCAP 1 +#define OPT_PARAM 2 /* need these to link in libbgp */ struct zebra_privs_t *bgpd_privs = NULL; @@ -34,7 +35,8 @@ static struct test_segment { #define SHOULD_PARSE 0 #define SHOULD_ERR -1 int parses; /* whether it should parse or not */ - + int peek_for; /* what peek_for_as4_capability should say */ + /* AFI/SAFI validation */ int validate_afi; afi_t afi; @@ -76,54 +78,55 @@ static struct test_segment mp_segments[] = { "MP4", "MP IP/Uni", { 0x1, 0x4, 0x0, 0x1, 0x0, 0x1 }, - 6, SHOULD_PARSE, AFI_IP, SAFI_UNICAST, + 6, SHOULD_PARSE, 0, + 1, AFI_IP, SAFI_UNICAST, VALID_AFI, }, { "MPv6", "MP IPv6/Uni", { 0x1, 0x4, 0x0, 0x2, 0x0, 0x1 }, - 6, SHOULD_PARSE, + 6, SHOULD_PARSE, 0, 1, AFI_IP6, SAFI_UNICAST, VALID_AFI, }, /* 5 */ { "MP2", "MP IP/Multicast", { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x2 }, - 6, SHOULD_PARSE, + 6, SHOULD_PARSE, 0, 1, AFI_IP, SAFI_MULTICAST, VALID_AFI, }, /* 6 */ { "MP3", "MP IP6/VPNv4", { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x80 }, - 6, SHOULD_PARSE, /* parses, but invalid afi,safi */ + 6, SHOULD_PARSE, 0, /* parses, but invalid afi,safi */ 1, AFI_IP6, BGP_SAFI_VPNV4, INVALID_AFI, }, /* 7 */ { "MP5", "MP IP6/MPLS-VPN", { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x4 }, - 6, SHOULD_PARSE, + 6, SHOULD_PARSE, 0, 1, AFI_IP6, SAFI_MPLS_VPN, VALID_AFI, }, /* 8 */ { "MP6", "MP IP4/VPNv4", { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80 }, - 6, SHOULD_PARSE, + 6, SHOULD_PARSE, 0, 1, AFI_IP, BGP_SAFI_VPNV4, VALID_AFI, }, /* 9 */ { "MP7", "MP IP4/VPNv6", { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x81 }, - 6, SHOULD_PARSE, /* parses, but invalid afi,safi tuple */ + 6, SHOULD_PARSE, 0, /* parses, but invalid afi,safi tuple */ 1, AFI_IP, BGP_SAFI_VPNV6, INVALID_AFI, }, /* 10 */ { "MP8", "MP unknown AFI", { CAPABILITY_CODE_MP, 0x4, 0x0, 0xa, 0x0, 0x81 }, - 6, SHOULD_PARSE, + 6, SHOULD_PARSE, 0, 1, 0xa, 0x81, INVALID_AFI, /* parses, but unknown */ }, /* 11 */ @@ -136,7 +139,7 @@ static struct test_segment mp_segments[] = { "MP-overflow", "MP IP4/Unicast, length too long", { CAPABILITY_CODE_MP, 0x6, 0x0, 0x1, 0x0, 0x1 }, - 6, SHOULD_ERR, + 6, SHOULD_ERR, 0, 1, AFI_IP, SAFI_UNICAST, VALID_AFI, }, { NULL, NULL, {0}, 0, 0} @@ -290,8 +293,8 @@ static struct test_segment misc_segments[] = /* 19 */ { "AS4", "AS4 capability", - { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12 }, - 6, SHOULD_PARSE, + { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12 }, /* AS: 2882400018 */ + 6, SHOULD_PARSE, 2882400018, }, /* 20 */ { "GR", @@ -367,7 +370,7 @@ static struct test_segment misc_segments[] = { NULL, NULL, {0}, 0, 0} }; - +/* DYNAMIC message */ struct test_segment dynamic_cap_msgs[] = { { "DynCap", @@ -397,18 +400,97 @@ struct test_segment dynamic_cap_msgs[] = }, { NULL, NULL, {0}, 0, 0} }; + +/* Entire Optional-Parameters block */ +struct test_segment opt_params[] = +{ + { "Cap-singlets", + "One capability per Optional-Param", + { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x02, 0x02, 0x80, 0x00, /* RR (old) */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + }, + 24, SHOULD_PARSE, + }, + { "Cap-series", + "Series of capability, one Optional-Param", + { 0x02, 0x10, + 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x80, 0x00, /* RR (old) */ + 0x02, 0x00, /* RR */ + }, + 18, SHOULD_PARSE, + }, + { "AS4more", + "AS4 capability after other caps (singlets)", + { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x02, 0x02, 0x80, 0x00, /* RR (old) */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + 0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */ + }, + 32, SHOULD_PARSE, 196614, + }, + { "AS4series", + "AS4 capability, in series of capabilities", + { 0x02, 0x16, + 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x80, 0x00, /* RR (old) */ + 0x02, 0x00, /* RR */ + 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */ + }, + 24, SHOULD_PARSE, 196614, + }, + { "AS4real", + "AS4 capability, in series of capabilities", + { + 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/uni */ + 0x02, 0x02, 0x80, 0x00, /* RR old */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + 0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06, /* AS4 */ + }, + 32, SHOULD_PARSE, 196614, + }, + { "AS4real2", + "AS4 capability, in series of capabilities", + { + 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, + 0x02, 0x02, 0x80, 0x00, + 0x02, 0x02, 0x02, 0x00, + 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0xfc, 0x03, + 0x02, 0x09, 0x82, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x80, 0x03, + 0x02, 0x09, 0x03, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x40, 0x03, + 0x02, 0x02, 0x42, 0x00, + }, + 58, SHOULD_PARSE, 64515, + }, + + { NULL, NULL, {0}, 0, 0} +}; + /* basic parsing test */ static void parse_test (struct peer *peer, struct test_segment *t, int type) { int ret; int capability = 0; + as_t as4 = 0; int oldfailed = failed; + int len = t->len; +#define RANDOM_FUZZ 35 stream_reset (peer->ibuf); + stream_put (peer->ibuf, NULL, RANDOM_FUZZ); + stream_set_getp (peer->ibuf, RANDOM_FUZZ); + switch (type) { - case OPEN: + case CAPABILITY: stream_putc (peer->ibuf, BGP_OPEN_OPT_CAP); stream_putc (peer->ibuf, t->len); break; @@ -422,11 +504,20 @@ parse_test (struct peer *peer, struct test_segment *t, int type) stream_write (peer->ibuf, t->data, t->len); printf ("%s: %s\n", t->name, t->desc); - + switch (type) { - case OPEN: - ret = bgp_open_option_parse (peer, t->len + 2, &capability); + case CAPABILITY: + len += 2; /* to cover the OPT-Param header */ + case OPT_PARAM: + printf ("len: %u\n", len); + /* peek_for_as4 wants getp at capibility*/ + as4 = peek_for_as4_capability (peer, len); + printf ("peek_for_as4: as4 is %u\n", as4); + /* and it should leave getp as it found it */ + assert (stream_get_getp (peer->ibuf) == RANDOM_FUZZ); + + ret = bgp_open_option_parse (peer, len, &capability); break; case DYNCAP: ret = bgp_capability_receive (peer, t->len); @@ -458,18 +549,27 @@ parse_test (struct peer *peer, struct test_segment *t, int type) } } + if (as4 != t->peek_for) + { + printf ("as4 %u != %u\n", as4, t->peek_for); + failed++; + } + printf ("parsed?: %s\n", ret ? "no" : "yes"); if (ret != t->parses) failed++; if (tty) - printf ("%s\n", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET + printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET : VT100_GREEN "OK" VT100_RESET); else - printf ("%s\n", (failed > oldfailed) ? "failed!" : "OK" ); + printf ("%s", (failed > oldfailed) ? "failed!" : "OK" ); + + if (failed) + printf (" (%u)", failed); - printf ("\n"); + printf ("\n\n"); } static struct bgp *bgp; @@ -485,10 +585,12 @@ main (void) conf_bgp_debug_events = -1UL; conf_bgp_debug_packet = -1UL; conf_bgp_debug_normal = -1UL; + conf_bgp_debug_as4 = -1UL; term_bgp_debug_fsm = -1UL; term_bgp_debug_events = -1UL; term_bgp_debug_packet = -1UL; term_bgp_debug_normal = -1UL; + term_bgp_debug_as4 = -1UL; master = thread_master_create (); bgp_master_init (); @@ -500,6 +602,7 @@ main (void) return -1; peer = peer_create_accept (bgp); + peer->host = "foo"; for (i = AFI_IP; i < AFI_MAX; i++) for (j = SAFI_UNICAST; j < SAFI_MAX; j++) @@ -510,18 +613,22 @@ main (void) i = 0; while (mp_segments[i].name) - parse_test (peer, &mp_segments[i++], OPEN); + parse_test (peer, &mp_segments[i++], CAPABILITY); /* These tests assume mp_segments tests set at least * one of the afc_nego's */ i = 0; while (test_segments[i].name) - parse_test (peer, &test_segments[i++], OPEN); + parse_test (peer, &test_segments[i++], CAPABILITY); i = 0; while (misc_segments[i].name) - parse_test (peer, &misc_segments[i++], OPEN); + parse_test (peer, &misc_segments[i++], CAPABILITY); + + i = 0; + while (opt_params[i].name) + parse_test (peer, &opt_params[i++], OPT_PARAM); SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV); peer->status = Established; diff --git a/vtysh/ChangeLog b/vtysh/ChangeLog index f2ba3332..de62763c 100644 --- a/vtysh/ChangeLog +++ b/vtysh/ChangeLog @@ -18,6 +18,11 @@ precision commands: send to all daemons. (vtysh_init_vty) Install new log timestamp precision commands. +2007-02-12 Juergen Kammer + * extract.pl: AS4 compatibility for router bgp ASNUMBER + * extract.pl.in: AS4 compatibility for router bgp ASNUMBER + * vtysh.c: AS4 compatibility for router bgp ASNUMBER + 2006-07-27 Andrew J. Schorr * vtysh_main.c: (usage) Add new -d and -E options. And note that diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 723fe8d6..789819c6 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -37,8 +37,8 @@ $ignore{'"router ripng"'} = "ignore"; $ignore{'"router ospf"'} = "ignore"; $ignore{'"router ospf <0-65535>"'} = "ignore"; $ignore{'"router ospf6"'} = "ignore"; -$ignore{'"router bgp <1-65535>"'} = "ignore"; -$ignore{'"router bgp <1-65535> view WORD"'} = "ignore"; +$ignore{'"router bgp CMD_AS_RANGE"'} = "ignore"; +$ignore{'"router bgp CMD_AS_RANGE view WORD"'} = "ignore"; $ignore{'"router isis WORD"'} = "ignore"; $ignore{'"router zebra"'} = "ignore"; $ignore{'"address-family ipv4"'} = "ignore"; diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c7efa91b..9f47515d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -838,7 +838,7 @@ DEFUNSH (VTYSH_ALL, DEFUNSH (VTYSH_BGPD, router_bgp, router_bgp_cmd, - "router bgp <1-65535>", + "router bgp CMD_AS_RANGE", ROUTER_STR BGP_STR AS_STR) -- cgit v1.2.1