Fix a segfault when printing a malformed BGP AS_PATH update due to ASN
extraction.

Better AS size extraction from AS paths: better heuristics (see
bgp_attr_get_as_size).

Also fixes output support for 4-byte ASNs. For example;
    (AS_PATH[T] {500.500 513.65211}) 
becomes: 
    (AS_PATH[T] {500 500} 65211)

Collapses bgp_attr_print switch 2-byte and 4-byte path cases.

bgp_attr_get_as_size function copied from tcpdump Git repo master.

This brings behavior in-line with newer versions on Linux.

Finally, minor output tweak: add a space after BGP update attribute
ORIGINATOR_ID's flags to make consistent with other attributes.


Index: print-bgp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-bgp.c,v
retrieving revision 1.17
diff -u -p -r1.17 print-bgp.c
--- print-bgp.c 16 Jan 2015 06:40:21 -0000      1.17
+++ print-bgp.c 14 Oct 2015 00:15:13 -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,63 @@ trunc:
 }
 #endif
 
+/*
+ * bgp_attr_get_as_size
+ *
+ * Try to find the size of the ASs encoded in an as-path. It is not obvious, as
+ * both Old speakers that do not support 4 byte AS, and the new speakers that 
do
+ * support, exchange AS-Path with the same path-attribute type value 0x02.
+ */
+static int
+bgp_attr_get_as_size (u_int8_t bgpa_type, const u_char *pptr, int len)
+{
+    const u_char *tptr = pptr;
+
+    /*
+     * If the path attribute is the optional AS4 path type, then we already
+     * know, that ASs must be encoded in 4 byte format.
+     */
+    if (bgpa_type == BGPTYPE_AS4_PATH) {
+        return 4;
+    }
+
+    /*
+     * Let us assume that ASs are of 2 bytes in size, and check if the AS-Path
+     * TLV is good. If not, ask the caller to try with AS encoded as 4 bytes
+     * each.
+     */
+    while (tptr < pptr + len) {
+        TCHECK(tptr[0]);
+
+        /*
+         * If we do not find a valid segment type, our guess might be wrong.
+         */
+        if (tptr[0] < BGP_AS_SEG_TYPE_MIN || tptr[0] > BGP_AS_SEG_TYPE_MAX) {
+            goto trunc;
+        }
+        TCHECK(tptr[1]);
+        tptr += 2 + tptr[1] * 2;
+    }
+
+    /*
+     * If we correctly reached end of the AS path attribute data content,
+     * then most likely ASs were indeed encoded as 2 bytes.
+     */
+    if (tptr == pptr + len) {
+        return 2;
+    }
+
+trunc:
+
+    /*
+     * We can come here, either we did not have enough data, or if we
+     * try to decode 4 byte ASs in 2 byte format. Either way, return 4,
+     * so that calller can try to decode each AS as of 4 bytes. If indeed
+     * there was not enough data, it will crib and end the parse anyways.
+     */
+   return 4;
+}
+
 static int
 bgp_attr_print(const struct bgp_attr *attr, const u_char *dat, int len)
 {
@@ -413,7 +473,6 @@ bgp_attr_print(const struct bgp_attr *at
 
        p = dat;
        tlen = len;
-       asn_bytes = 0;
 
        switch (attr->bgpa_type) {
        case BGPTYPE_ORIGIN:
@@ -425,17 +484,8 @@ 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;
@@ -444,11 +494,19 @@ bgp_attr_print(const struct bgp_attr *at
                        printf(" empty");
                        break;
                }
+
+               /*
+                 * BGP updates exchanged between New speakers that support 4
+                 * byte AS, ASs are always encoded in 4 bytes. There is no
+                 * definitive way to find this, just by the packet's
+                 * contents. So, check for packet's TLV's sanity assuming
+                 * 2 bytes first, and it does not pass, assume that ASs are
+                 * encoded in 4 bytes format and move on.
+                 */
+                asn_bytes = bgp_attr_get_as_size(attr->bgpa_type, dat, len);
+
                while (p < dat + len) {
                        TCHECK(p[0]);
-                       if (asn_bytes == 0) {
-                               asn_bytes = (len-2)/p[1];
-                       }
                        printf("%s",
                            tok2str(bgp_as_path_segment_open_values,
                            "?", p[0]));
@@ -456,13 +514,10 @@ bgp_attr_print(const struct bgp_attr *at
                        for (i = 0; i < p[1] * asn_bytes; i += asn_bytes) {
                                TCHECK2(p[2 + i], asn_bytes);
                                printf("%s", i == 0 ? "" : " ");
-                               if (asn_bytes == 2 || EXTRACT_16BITS(&p[2 + i]))
-                                       printf("%u%s",
-                                           EXTRACT_16BITS(&p[2 + i]),
-                                           asn_bytes == 4 ? "." : "");
-                               if (asn_bytes == 4)
-                                       printf("%u",
-                                           EXTRACT_16BITS(&p[2 + i + 2]));
+                               printf("%u",
+                                   asn_bytes == 2 ?
+                                       EXTRACT_16BITS(&p[2 + i]) :
+                                       EXTRACT_32BITS(&p[2 + i]));
                        }
                        TCHECK(p[0]);
                        printf("%s",
@@ -549,7 +604,7 @@ bgp_attr_print(const struct bgp_attr *at
                        break;
                 }
                TCHECK2(p[0], 4);
-               printf("%s",getname(p));
+               printf(" %s", getname(p));
                break;
        case BGPTYPE_CLUSTER_LIST:
                if (len % 4) {

Reply via email to