From Olivier Montanuy via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5458
authoralagoutte <alagoutte@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 24 Aug 2011 21:58:14 +0000 (21:58 +0000)
committeralagoutte <alagoutte@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 24 Aug 2011 21:58:14 +0000 (21:58 +0000)
BGP add-path (Additional Paths) support for IPv4 unicast

From me:
Fix some whitespace/tab...

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@38724 f5534014-38df-0310-8fa8-9805f1628bb7

epan/dissectors/packet-bgp.c

index 96a8aecf62d0ec90853106e95edc5f37c5b655b0..744eb69d1d0831b63dd1f9c3f8cdc4b7e9339c3c 100644 (file)
@@ -37,6 +37,7 @@
  * draft-ietf-idr-bgp-ext-communities-05
  * draft-knoll-idr-qos-attribute-03
  * draft-nalawade-kapoor-tunnel-safi-05
+ * draft-ietf-idr-add-paths-04 Additional-Path for BGP-4
  *
  * TODO:
  * Destination Preference Attribute for BGP (work in progress)
@@ -316,6 +317,7 @@ static const value_string capability_vals[] = {
     { BGP_CAPABILITY_GRACEFUL_RESTART, "Graceful Restart capability" },
     { BGP_CAPABILITY_4_OCTET_AS_NUMBER, "Support for 4-octet AS number capability" },
     { BGP_CAPABILITY_DYNAMIC_CAPABILITY, "Support for Dynamic capability" },
+    { BGP_CAPABILITY_ADDITIONAL_PATHS, "Support for Additional Paths" },
     { BGP_CAPABILITY_ROUTE_REFRESH_CISCO, "Route refresh capability" },
     { BGP_CAPABILITY_ORF_CISCO, "Cooperative route filtering capability" },
     { 0, NULL }
@@ -361,6 +363,7 @@ static int hf_bgp_mp_unreach_nlri_ipv4_prefix = -1;
 static int hf_bgp_mp_nlri_tnl_id = -1;
 static int hf_bgp_withdrawn_prefix = -1;
 static int hf_bgp_nlri_prefix = -1;
+static int hf_bgp_nlri_path_id = -1;
 
 static gint ett_bgp = -1;
 static gint ett_bgp_prefix = -1;
@@ -396,6 +399,108 @@ static gboolean bgp_desegment = TRUE;
 
 static gint bgp_asn_len = 0;
 
+/*
+ * Detect IPv4 prefixes  conform to BGP Additional Path but NOT conform to standard BGP
+ *
+ * A real BGP speaker would rely on the BGP Additional Path in the BGP Open messages.
+ * But it is not suitable for a packet analyse because the BGP sessions are not supposed to 
+ * restart very often, and Open messages from both sides of the session would be needed
+ * to determine the result of the capability negociation.
+ * Code inspired from the decode_prefix4 function
+ */
+static int
+detect_add_path_prefix4(tvbuff_t *tvb, gint offset, gint end) {
+    guint32 addr_len;
+    guint8 prefix_len;
+    gint o;
+    /* Must be compatible with BGP Additional Path  */
+    for (o = offset + 4; o < end; o += 4) {
+        prefix_len = tvb_get_guint8(tvb, o);
+        if( prefix_len > 32) {
+            return 0; /* invalid prefix lenght - not BGP add-path */
+        }
+        addr_len = (prefix_len + 7) / 8;
+        o += 1 + addr_len;
+        if( o > end ) {
+            return 0; /* invalid offset - not BGP add-path */
+        }
+        if (prefix_len % 8) {
+            /* detect bits set after the end of the prefix */
+            if( tvb_get_guint8(tvb, o - 1 )  & (0xFF >> (prefix_len % 8)) ) {
+                return 0; /* invalid prefix content - not BGP add-path */
+            }
+        }
+    }
+    /* Must NOT be compatible with standard BGP */
+    for (o = offset; o < end; ) {
+        prefix_len = tvb_get_guint8(tvb, o);
+        if( prefix_len > 32) {
+            return 1; /* invalid prefix lenght - may be BGP add-path */
+        }
+        addr_len = (prefix_len + 7) / 8;
+        o += 1 + addr_len;
+        if( o > end ) {
+            return 1; /* invalid offset - may be BGP add-path */
+        }
+        if (prefix_len % 8) {
+            /* detect bits set after the end of the prefix */
+            if( tvb_get_guint8(tvb, o - 1 ) & (0xFF >> (prefix_len % 8)) ) {
+                return 1; /* invalid prefix content - may be BGP add-path (or a bug) */
+            }
+        }
+    }
+    return 0; /* valid - do not assume Additional Path */
+}
+/*
+ * Decode an IPv4 prefix with Path Identifier
+ * Code inspired from the decode_prefix4 function
+ */
+static int
+decode_path_prefix4(proto_tree *tree, int hf_path_id, int hf_addr, tvbuff_t *tvb, gint offset,
+                    const char *tag)
+{
+    proto_item *ti;
+    proto_tree *prefix_tree;
+    union {
+       guint8 addr_bytes[4];
+       guint32 addr;
+    } ip_addr;        /* IP address                         */
+    guint8 plen;      /* prefix length                      */
+    int    length;    /* number of octets needed for prefix */
+    guint32 path_identifier;
+    /* snarf path identifier length and prefix */
+    path_identifier = tvb_get_ntohl(tvb, offset);
+    plen = tvb_get_guint8(tvb, offset + 4);
+    length = ipv4_addr_and_mask(tvb, offset + 4 + 1, ip_addr.addr_bytes, plen);
+    if (length < 0) {
+        proto_tree_add_text(tree, tvb, offset + 4 , 1, "%s length %u invalid (> 32)",
+            tag, plen);
+        return -1;
+    }
+    /* put prefix into protocol tree */
+    ti = proto_tree_add_text(tree, tvb, offset,
+                             4 + 1 + length, "%s/%u PathId %u ",
+                            ip_to_str(ip_addr.addr_bytes), plen, path_identifier);
+    prefix_tree = proto_item_add_subtree(ti, ett_bgp_prefix);
+    if (hf_path_id != -1) {
+        proto_tree_add_uint(prefix_tree, hf_path_id, tvb, offset, 4,
+                            path_identifier);
+    } else {
+        proto_tree_add_text(prefix_tree, tvb, offset, 4,
+                            "%s Path Id: %u", tag, path_identifier);
+    }
+    proto_tree_add_text(prefix_tree, tvb, offset + 4, 1, "%s prefix length: %u",
+                        tag, plen);
+    if (hf_addr != -1) {
+        proto_tree_add_ipv4(prefix_tree, hf_addr, tvb, offset + 4 + 1, length,
+            ip_addr.addr);
+    } else {
+        proto_tree_add_text(prefix_tree, tvb, offset + 4 + 1, length,
+            "%s prefix: %s", tag, ip_to_str(ip_addr.addr_bytes));
+    }
+    return(4 + 1 + length);
+}
+
 /*
  * Decode an IPv4 prefix.
  */
@@ -1377,6 +1482,48 @@ dissect_bgp_capability_item(tvbuff_t *tvb, int *p, proto_tree *tree, int ctype,
                 }
             }
             break;
+        case BGP_CAPABILITY_ADDITIONAL_PATHS:
+            proto_tree_add_text(tree, tvb, *p - 2, 1,
+                                "Capability code: %s (%d)", val_to_str(ctype,
+                                                                       capability_vals, "Unknown capability"), ctype);
+            if (clen != 4) {
+                proto_tree_add_text(tree, tvb, *p,
+                                    clen, "Capability value: Invalid");
+                proto_tree_add_text(tree, tvb, *p,
+                                    clen, "Capability value: Unknown");
+            }
+            else { /* AFI SAFI Send-receive*/
+                proto_tree_add_text(tree, tvb, *p - 1,
+                                    1, "Capability length: %u byte%s", clen,
+                                    plurality(clen, "", "s"));
+                ti = proto_tree_add_text(tree, tvb, *p, clen, "Capability value");
+                subtree = proto_item_add_subtree(ti, ett_bgp_option);
+               /* AFI */
+                i = tvb_get_ntohs(tvb, *p);
+                proto_tree_add_text(subtree, tvb, *p,
+                                    2, "Address family identifier: %s (%u)",
+                                    val_to_str(i, afn_vals, "Unknown"), i);
+                *p += 2;
+                /* SAFI */
+                i = tvb_get_guint8(tvb, *p);
+                proto_tree_add_text(subtree, tvb, *p,
+                                    1, "Subsequent address family identifier: %s (%u)",
+                                    val_to_str(i, bgpattr_nlri_safi,
+                                               i >= 128 ? "Vendor specific" : "Unknown"), i);
+                (*p)++;
+                /* Send-Receive */
+                i = tvb_get_guint8(tvb, *p);
+                proto_tree_add_text(subtree, tvb, *p, 1,
+                                    "Flags: 0x%02x (%sSend,%sReceive)", i,
+                                    ((i&BGP_ADDPATH_SEND)? "":"Dont"),
+                                    ((i&BGP_ADDPATH_RECEIVE)? "":"Dont"));
+                 /* Note: flags may be provided as a bitfield subtree */
+               (*p)++;
+
+            }
+            *p += clen;
+            break;
+
         case BGP_CAPABILITY_ROUTE_REFRESH_CISCO:
         case BGP_CAPABILITY_ROUTE_REFRESH:
             proto_tree_add_text(tree, tvb, *p - 2, 1,
@@ -1630,17 +1777,28 @@ dissect_bgp_update(tvbuff_t *tvb, proto_tree *tree)
     if (len > 0) {
         ti = proto_tree_add_text(tree, tvb, o, len, "Withdrawn routes:");
         subtree = proto_item_add_subtree(ti, ett_bgp_unfeas);
-
         /* parse each prefix */
-        end = o + len;
-        while (o < end) {
-            i = decode_prefix4(subtree, hf_bgp_withdrawn_prefix, tvb, o, len,
-                "Withdrawn route");
-            if (i < 0)
-                return;
-            o += i;
+                end = o + len;
+        /* Heuristic to detect if IPv4 prefix are using Path Identifiers */
+        if( detect_add_path_prefix4(tvb, o, end) ) {
+            /* IPv4 prefixes with Path Id */
+            while (o < end) {
+                i = decode_path_prefix4(subtree, hf_bgp_nlri_path_id, hf_bgp_withdrawn_prefix, tvb, o, 
+                    "Withdrawn route");
+                if (i < 0)
+                    return;
+                o += i;
+            }
+        } else {
+            while (o < end) {
+                i = decode_prefix4(subtree, hf_bgp_withdrawn_prefix, tvb, o, len,
+                    "Withdrawn route");
+                if (i < 0)
+                    return;
+                o += i;
+            }
         }
-    }
+   }
 
     /* check for advertisements */
     len = tvb_get_ntohs(tvb, o);
@@ -2655,12 +2813,25 @@ dissect_bgp_update(tvbuff_t *tvb, proto_tree *tree)
                    plurality(len, "", "s"));
             subtree = proto_item_add_subtree(ti, ett_bgp_nlri);
             end = o + len;
-            while (o < end) {
-                i = decode_prefix4(subtree, hf_bgp_nlri_prefix, tvb, o, 0,
-                    "NLRI");
-                if (i < 0)
-                    return;
-                o += i;
+            /* Heuristic to detect if IPv4 prefix are using Path Identifiers */
+            if( detect_add_path_prefix4(tvb, o, end) ) {
+                /* IPv4 prefixes with Path Id */
+                while (o < end) {
+                    i = decode_path_prefix4(subtree, hf_bgp_nlri_path_id, hf_bgp_nlri_prefix, tvb, o, 
+                                            "NLRI");
+                    if (i < 0)
+                       return;
+                    o += i;
+                }
+            } else {
+                /* Standard prefixes */
+                while (o < end) {
+                    i = decode_prefix4(subtree, hf_bgp_nlri_prefix, tvb, o, 0,
+                           "NLRI");
+                    if (i < 0)
+                        return;
+                    o += i;
+                }
             }
         }
     }
@@ -3212,6 +3383,9 @@ proto_register_bgp(void)
       { &hf_bgp_nlri_prefix,
         { "NLRI prefix", "bgp.nlri_prefix", FT_IPv4, BASE_NONE,
           NULL, 0x0, NULL, HFILL}},
+      { &hf_bgp_nlri_path_id,
+        { "NLRI path id", "bgp.nlri_path_id", FT_UINT32, BASE_DEC,
+          NULL, 0x0, NULL, HFILL}},
       { &hf_bgp_origin,
         { "Origin", "bgp.origin", FT_UINT8, BASE_DEC,
           VALS(bgpattr_origin), 0x0, NULL, HFILL}},