On Tue, Oct 27, 2015 at 12:59:20AM -0600, Kevin Reay wrote:
> I did add an additional check for "zero" ASNs to the 2-byte default,
> inspired by a quick glance at Wireshark's heuristics. I now flip
> through each segment's ASNs inside of bgp_attr_get_as_size(), looking
> for any zero-value 16bit ASNs that would indicate the set isn't valid
> for the assumed byte-length (ie. the first two bytes of a 4-byte ASN).
> I'd need to read through the RFCs in detail to ensure this is witout
> issue though.

I've attached the updated version of the previous tcpdump bgp AS_PATH
size discovery function.

It uses the original 2-byte size assumption with the addition of a
check for any zero-value ASNs. After some research, it appears this is
a valid approach according to RFC7607 (they aren't valid in an
AS_PATH). 

The additional check appears to further improve the success in my test
cases as well as obviously correcting the original in-tree behaviour.

Any feedback would be great.
Index: print-bgp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-bgp.c,v
retrieving revision 1.18
diff -u -p -r1.18 print-bgp.c
--- print-bgp.c 20 Oct 2015 11:29:07 -0000      1.18
+++ print-bgp.c 29 Oct 2015 20:48:06 -0000
@@ -140,6 +140,9 @@ struct bgp_attr {
 #define BGP_CONFED_AS_SEQUENCE 3 /* draft-ietf-idr-rfc3065bis-01 */
 #define BGP_CONFED_AS_SET      4 /* draft-ietf-idr-rfc3065bis-01  */
 
+#define BGP_AS_SEG_TYPE_MIN    BGP_AS_SET
+#define BGP_AS_SEG_TYPE_MAX    BGP_CONFED_AS_SET
+
 static struct tok bgp_as_path_segment_open_values[] = {
        { BGP_AS_SET,                   " {" },
        { BGP_AS_SEQUENCE,              " " },
@@ -400,6 +403,64 @@ trunc:
 }
 #endif
 
+/*
+ * Try to determine the size of the ASs encoded in an AS-path. It is
+ * not obvious as both speaker types exchange AS-paths with the same
+ * path-attribute type.
+ */
+static int
+bgp_attr_get_as_size(u_int8_t bgpa_type, const u_char *dat, int len)
+{
+       const u_char *p;
+       int i;
+
+       p = dat;
+
+       /* AS4 path types are always encoded in 4-byte format */
+       if (bgpa_type == BGPTYPE_AS4_PATH)
+               return 4;
+
+       /*
+        * Start by assuming 2-byte ASs. Iterate through the path data using the
+        * segment length values. Switch to 4-bytes if we encounter an invalid
+        * field value/if the AS-Path length is invalid for the assumed size.
+        */
+       while (p < dat + len) {
+               TCHECK(p[0]);
+
+               /* check segment type: invalid value means wrong size */
+               if (p[0] < BGP_AS_SEG_TYPE_MIN || p[0] > BGP_AS_SEG_TYPE_MAX)
+                       goto trunc;
+
+               TCHECK2(p[1], 1 + p[1] * 2);
+
+               /* check segment length: invalid indicates wrong size */
+               if (p[1] == 0)
+                       goto trunc;
+
+               /*
+                * Look for zero-value ASs which are likely the first 2 bytes of
+                * a 4-byte AS. RFC7607 asserts a zero in an AS_PATH is invalid.
+                */
+               for (i = 0; i < p[1]; i++) {
+                       if (EXTRACT_16BITS(&p[2 + i * 2]) == 0)
+                               goto trunc;
+               }
+
+               p += 2 + p[1] * 2;
+       }
+
+       /* matching length: it's very likely the ASs were encoded as 2-bytes */
+       if (p == dat + len)
+               return 2;
+trunc:
+       /*
+        * Either there was not enough data or we tried to decode 4-byte ASs
+        * with an incorrect size of 2-bytes.
+        */
+       return 4;
+}
+
 static int
 bgp_attr_print(const struct bgp_attr *attr, const u_char *dat, int len)
 {
@@ -425,17 +486,7 @@ bgp_attr_print(const struct bgp_attr *at
                }
                break;
        case BGPTYPE_AS4_PATH:
-               asn_bytes = 4;
-               /* FALLTHROUGH */
        case BGPTYPE_AS_PATH:
-       /*
-        * 2-byte speakers will receive AS4_PATH as well AS_PATH (2-byte).
-        * 4-byte speakers will only receive AS_PATH but it will be 4-byte.
-        * To identify which is the case, compare the length of the path
-        * segment value in bytes, with the path segment length from the
-        * message (counted in # of AS)
-        */
-        
                if (len % 2) {
                        printf(" invalid len");
                        break;
@@ -445,15 +496,13 @@ bgp_attr_print(const struct bgp_attr *at
                        printf(" empty");
                        break;
                }
+               asn_bytes = bgp_attr_get_as_size(attr->bgpa_type, dat, len);
                while (p < dat + len) {
                        TCHECK2(p[0], 2);
-                       if (asn_bytes == 0) {
-                               if (p[1] == 0) {
-                               /* invalid: segment contains one or more AS */
-                                       printf(" malformed");
-                                       break;
-                               }
-                               asn_bytes = (len-2)/p[1];
+                       if (p[1] == 0) {
+                               /* invalid: segment contains no AS */
+                               printf(" malformed");
+                               break;
                        }
                        printf("%s",
                            tok2str(bgp_as_path_segment_open_values,

Reply via email to