From Anand V. Narwani:
[obnox/wireshark/wip.git] / packet-bgp.c
index 1f93bc4e8e515e5f707e8e347cd5da89a5969d55..6c98783a73599ef9eaeda8b5953727e133e23f4f 100644 (file)
@@ -2,16 +2,18 @@
  * Routines for BGP packet dissection.
  * Copyright 1999, Jun-ichiro itojun Hagino <itojun@itojun.org>
  *
- * $Id: packet-bgp.c,v 1.43 2001/07/03 02:49:38 guy Exp $
+ * $Id: packet-bgp.c,v 1.59 2002/05/21 21:55:46 guy Exp $
  *
  * Supports:
  * RFC1771 A Border Gateway Protocol 4 (BGP-4)
  * RFC1965 Autonomous System Confederations for BGP
  * RFC1997 BGP Communities Attribute
+ * RFC2547 BGP/MPLS VPNs
  * RFC2796 BGP Route Reflection An alternative to full mesh IBGP
  * RFC2842 Capabilities Advertisement with BGP-4
  * RFC2858 Multiprotocol Extensions for BGP-4
  * RFC2918 Route Refresh Capability for BGP-4
+ * RFC3107 Carrying Label Information in BGP-4
  * Draft Ramahandra on Extended Communities Extentions
  *
  * TODO:
 # include "snprintf.h"
 #endif
 
-#include "packet.h"
+#include <epan/packet.h>
 #include "packet-bgp.h"
 #include "packet-ipv6.h"
+#include "afn.h"
+#include "prefs.h"
 
 static const value_string bgptypevals[] = {
     { BGP_OPEN, "OPEN Message" },
@@ -164,43 +168,53 @@ static const value_string bgpext_com_type[] = {
     { BGP_EXT_COM_RT_1, "Route Target" },
     { BGP_EXT_COM_RO_0, "Route Origin" },
     { BGP_EXT_COM_RO_1, "Route Origin" },
+    { BGP_EXT_COM_LINKBAND, "Link Bandwidth" },
+    { BGP_EXT_COM_VPN_ORIGIN, "OSPF Domain" },
+    { BGP_EXT_COM_OSPF_RTYPE, "OSPF Route Type" },
+    { BGP_EXT_COM_OSPF_RID, "OSPF Router ID" },
     { 0, NULL },
 };
 
+static const value_string bgpext_ospf_rtype[] = {
+  { BGP_OSPF_RTYPE_RTR, "Router" },  
+  { BGP_OSPF_RTYPE_NET, "Network" },  
+  { BGP_OSPF_RTYPE_SUM, "Summary" },  
+  { BGP_OSPF_RTYPE_EXT, "External" },  
+  { BGP_OSPF_RTYPE_NSSA,"NSSA External" },
+  { BGP_OSPF_RTYPE_SHAM,"MPLS-VPN Sham" },  
+  { 0, NULL },
+};
+
+
 /* MUST be resized if a longer named extended community is added */
 #define MAX_SIZE_OF_EXT_COM_NAMES       20
 
-static const value_string afnumber[] = {
-    { 0, "Reserved" },
-    { AFNUM_INET, "IPv4" },
-    { AFNUM_INET6, "IPv6" },
-    { AFNUM_NSAP, "NSAP" },
-    { AFNUM_HDLC, "HDLC" },
-    { AFNUM_BBN1822, "BBN 1822" },
-    { AFNUM_802, "802" },
-    { AFNUM_E163, "E.163" },
-    { AFNUM_E164, "E.164" },
-    { AFNUM_F69, "F.69" },
-    { AFNUM_X121, "X.121" },
-    { AFNUM_IPX, "IPX" },
-    { AFNUM_ATALK, "Appletalk" },
-    { AFNUM_DECNET, "Decnet IV" },
-    { AFNUM_BANYAN, "Banyan Vines" },
-    { AFNUM_E164NSAP, "E.164 with NSAP subaddress" },
-    { 65535, "Reserved" },
-    { 0, NULL },
-};
-
 /* Subsequent address family identifier, RFC2858 */
 static const value_string bgpattr_nlri_safi[] = {
     { 0, "Reserved" },
     { SAFNUM_UNICAST, "Unicast" },
     { SAFNUM_MULCAST, "Multicast" },
     { SAFNUM_UNIMULC, "Unicast+Multicast" },
+    { SAFNUM_MPLS_LABEL, "MPLS Labeled Prefix"},
     { SAFNUM_LBVPNIP, "Labeled VPN-IPv4" },        /* draft-rosen-rfc2547bis-03 */
     { 0, NULL },
 };
 
+/* ORF Type, draft-ietf-idr-route-filter-04.txt */
+static const value_string orf_type_vals[] = {
+    { 2,       "Communities ORF-Type" },
+    { 3,       "Extended Communities ORF-Type" },
+    { 0,       NULL },
+};
+
+/* ORF Send/Receive, draft-ietf-idr-route-filter-04.txt */
+static const value_string orf_send_recv_vals[] = {
+    { 1,       "Receive" },
+    { 2,       "Send" },
+    { 3,       "Both" },
+    { 0,       NULL },
+};
+
 /* Maximal size of an IP address string */
 #define MAX_SIZE_OF_IP_ADDR_STRING      16
 
@@ -227,6 +241,10 @@ static gint ett_bgp_cluster_list = -1;  /* cluster list tree          */
 static gint ett_bgp_options = -1;       /* optional parameters tree   */
 static gint ett_bgp_option = -1;        /* an optional parameter tree */
 static gint ett_bgp_extended_communities = -1 ; /* extended communities list tree */
+
+/* desegmentation */
+static gboolean bgp_desegment = TRUE;
+
 /*
  * Decode an IPv4 prefix.
  */
@@ -285,21 +303,39 @@ decode_prefix6(tvbuff_t *tvb, gint offset, char *buf, int buflen)
  * Decode an MPLS label stack
  */
 static int
-decode_MPLS_stack(tvbuff_t *tvb, gint offset, char *buf, int buflen)
+decode_MPLS_stack(tvbuff_t *tvb, gint offset, char *buf, size_t buflen)
 {
     guint32     label_entry;    /* an MPLS label enrty (label + COS field + stack bit   */
     gint        index;          /* index for the label stack                            */
+    char        junk_buf[256];  /* tmp                                                  */
 
     index = offset ;
     label_entry = 0x000000 ;
 
     buf[0] = '\0' ;
 
-    while ((label_entry && 0x000001) == 0) {
+    while ((label_entry & 0x000001) == 0) {
 
         label_entry = tvb_get_ntoh24(tvb, index) ;
-        snprintf(buf, buflen,"%s%u%s", buf, (label_entry >> 4), ((label_entry && 0x000001) == 0) ? "," : " (bottom)");
+
+        /* withdrawn routes may contain 0 or 0x800000 in the first label */
+        if((index-offset)==0&&(label_entry==0||label_entry==0x800000)) {
+            snprintf(buf, buflen, "0 (withdrawn)");
+            return (1);
+        }
+
+        snprintf(junk_buf, sizeof(junk_buf),"%u%s", (label_entry >> 4), ((label_entry & 0x000001) == 0) ? "," : " (bottom)");
+       if (strlen(buf) + strlen(junk_buf) + 1 <= buflen)
+           strcat(buf, junk_buf);
         index += 3 ;
+
+       if ((label_entry & 0x000001) == 0) {
+           /* real MPLS multi-label stack in BGP? - maybe later; for now, it must be a bogus packet */
+           strcpy(junk_buf, " (BOGUS: Bottom of Stack NOT set!)");
+           if (strlen(buf) + strlen(junk_buf) + 1 <= buflen)
+               strcat(buf, junk_buf);
+           break;
+       }         
     }
 
     return((index - offset) / 3);
@@ -324,6 +360,7 @@ mp_addr_to_str (guint16 afi, guint8 safi, tvbuff_t *tvb, gint offset, char *buf,
                         case SAFNUM_UNICAST:
                         case SAFNUM_MULCAST:
                         case SAFNUM_UNIMULC:
+                        case SAFNUM_MPLS_LABEL:
                                 length = 4 ;
                                 tvb_memcpy(tvb, ip4addr, offset, 4);
                                snprintf(buf, buflen, "%s", ip_to_str(ip4addr));
@@ -396,6 +433,30 @@ decode_prefix_MP(guint16 afi, guint8 safi, tvbuff_t *tvb, gint offset, char *buf
                         case SAFNUM_UNIMULC:
                                 length = decode_prefix4(tvb, offset, buf, buflen) - 1 ;
                                 break;
+                        case SAFNUM_MPLS_LABEL:
+                                plen =  tvb_get_guint8(tvb,offset) ;
+                                labnum = decode_MPLS_stack(tvb, offset + 1, lab_stk, sizeof(lab_stk));
+
+                                offset += (1 + labnum * 3);
+                                plen -= (labnum * 3*8);
+                                if (plen < 0 || 32 < plen) {
+                                        length = 0 ;
+                                        break ;
+                                }
+
+                                length = (plen + 7) / 8;
+                                memset(ip4addr, 0, sizeof(ip4addr));
+                                tvb_memcpy(tvb, ip4addr, offset, length);
+                                if (plen % 8)
+                                        ip4addr[length - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
+
+                                snprintf(buf,buflen, "Label Stack=%s IP=%s/%d",
+                                         lab_stk,
+                                         ip_to_str(ip4addr),
+                                         plen);
+                                length += (labnum*3) ;
+                                break;
+
                         case SAFNUM_LBVPNIP:
                                 plen =  tvb_get_guint8(tvb,offset) ;
 
@@ -476,6 +537,13 @@ decode_prefix_MP(guint16 afi, guint8 safi, tvbuff_t *tvb, gint offset, char *buf
 /*
  * Dissect a BGP OPEN message.
  */
+static const value_string community_vals[] = {
+       { BGP_COMM_NO_EXPORT,           "NO_EXPORT" },
+       { BGP_COMM_NO_ADVERTISE,        "NO_ADVERTISE" },
+       { BGP_COMM_NO_EXPORT_SUBCONFED, "NO_EXPORT_SUBCONFED" },
+       { 0,                            NULL }
+};
+
 static void
 dissect_bgp_open(tvbuff_t *tvb, int offset, proto_tree *tree)
 {
@@ -486,13 +554,18 @@ dissect_bgp_open(tvbuff_t *tvb, int offset, proto_tree *tree)
     int             plen;      /* parameter length      */
     int             ctype;     /* capability type       */
     int             clen;      /* capability length     */
+    int             cend;      /* capabilities end      */
     int             ostart;    /* options start         */
     int             oend;      /* options end           */
     int             p;         /* tvb offset counter    */
     proto_item      *ti;       /* tree item             */
     proto_tree      *subtree;  /* subtree for options   */
+    proto_tree      *subtree1; /* subtree for an option */
     proto_tree      *subtree2; /* subtree for an option */
     proto_tree      *subtree3; /* subtree for an option */
+    guint8          orfnum;    /* number of ORFs */
+    guint8          orftype;        /* ORF Type */
+    guint8          orfsendrecv;    /* ORF Send/Receive */
 
     /* snarf OPEN message */
     tvb_memcpy(tvb, bgpo.bgpo_marker, offset, BGP_MIN_OPEN_MSG_SIZE);
@@ -541,127 +614,178 @@ dissect_bgp_open(tvbuff_t *tvb, int offset, proto_tree *tree)
                 break;
             case BGP_OPTION_CAPABILITY:
                 /* grab the capability code */
+               cend = p - 1 + plen;
                 ctype = tvb_get_guint8(tvb, p++);
                 clen = tvb_get_guint8(tvb, p++);
-
-                /* check the capability type */
-                switch (ctype) {
-                case BGP_CAPABILITY_RESERVED:
-                    ti = proto_tree_add_text(subtree, tvb, p - 4,
-                         2 + plen, "Reserved capability (%u %s)", 2 + plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
-                    proto_tree_add_text(subtree2, tvb, p - 4,
-                         1, "Parameter type: Capabilities (2)");
-                    proto_tree_add_text(subtree2, tvb, p - 3,
-                         1, "Parameter length: %u %s", plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    proto_tree_add_text(subtree2, tvb, p - 2,
-                         1, "Capability code: Reserved (0)");
-                    proto_tree_add_text(subtree2, tvb, p - 1,
-                         1, "Capability length: %u %s", clen,
-                         (clen == 1) ? "byte" : "bytes");
-                    if (clen != 0) {
-                        proto_tree_add_text(subtree2, tvb, p,
-                             clen, "Capability value: Unknown");
-                    }
-                    p += clen;
-                    break;
-                case BGP_CAPABILITY_MULTIPROTOCOL:
-                    ti = proto_tree_add_text(subtree, tvb, p - 4,
-                         2 + plen,
-                         "Multiprotocol extensions capability (%u %s)",
-                         2 + plen, (plen == 1) ? "byte" : "bytes");
-                    subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
-                    proto_tree_add_text(subtree2, tvb, p - 4,
-                         1, "Parameter type: Capabilities (2)");
-                    proto_tree_add_text(subtree2, tvb, p - 3,
-                         1, "Parameter length: %u %s", plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    proto_tree_add_text(subtree2, tvb, p - 2,
-                         1, "Capability code: Multiprotocol extensions (%d)",
-                         ctype);
-                    if (clen != 4) {
-                        proto_tree_add_text(subtree2, tvb, p - 1,
-                             1, "Capability length: Invalid");
-                        proto_tree_add_text(subtree2, tvb, p,
-                             clen, "Capability value: Unknown");
-                    }
-                    else {
-                        proto_tree_add_text(subtree2, tvb, p - 1,
+               ti = proto_tree_add_text(subtree, tvb, p - 4,
+                     2 + plen, "Capabilities Advertisement (%u bytes)",
+                    2 + plen);
+               subtree1 = proto_item_add_subtree(ti, ett_bgp_option);
+               proto_tree_add_text(subtree1, tvb, p - 4,
+                     1, "Parameter type: Capabilities (2)");
+               proto_tree_add_text(subtree1, tvb, p - 3,
+                     1, "Parameter length: %u %s", plen,
+                     (plen == 1) ? "byte" : "bytes");
+               p -= 2;
+
+               /* step through all of the capabilities */
+               while (p < cend) {
+                   ctype = tvb_get_guint8(tvb, p++);
+                   clen = tvb_get_guint8(tvb, p++);
+
+                   /* check the capability type */
+                   switch (ctype) {
+                   case BGP_CAPABILITY_RESERVED:
+                       ti = proto_tree_add_text(subtree1, tvb, p - 2,
+                             2 + clen, "Reserved capability (%u %s)", 2 + clen,
+                             (clen == 1) ? "byte" : "bytes");
+                       subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
+                       proto_tree_add_text(subtree2, tvb, p - 2,
+                             1, "Capability code: Reserved (0)");
+                       proto_tree_add_text(subtree2, tvb, p - 1,
                              1, "Capability length: %u %s", clen,
                              (clen == 1) ? "byte" : "bytes");
-                        ti = proto_tree_add_text(subtree2, tvb, p,
+                       if (clen != 0) {
+                           proto_tree_add_text(subtree2, tvb, p,
+                                 clen, "Capability value: Unknown");
+                       }
+                       p += clen;
+                       break;
+                   case BGP_CAPABILITY_MULTIPROTOCOL:
+                       ti = proto_tree_add_text(subtree1, tvb, p - 2,
+                             2 + clen,
+                             "Multiprotocol extensions capability (%u %s)",
+                             2 + clen, (clen == 1) ? "byte" : "bytes");
+                       subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
+                       proto_tree_add_text(subtree2, tvb, p - 2,
+                             1, "Capability code: Multiprotocol extensions (%d)",
+                             ctype);
+                       if (clen != 4) {
+                           proto_tree_add_text(subtree2, tvb, p - 1,
+                                 1, "Capability length: Invalid");
+                           proto_tree_add_text(subtree2, tvb, p,
+                                 clen, "Capability value: Unknown");
+                       }
+                       else {
+                           proto_tree_add_text(subtree2, tvb, p - 1,
+                                 1, "Capability length: %u %s", clen,
+                                 (clen == 1) ? "byte" : "bytes");
+                           ti = proto_tree_add_text(subtree2, tvb, p,
+                                 clen, "Capability value");
+                           subtree3 = proto_item_add_subtree(ti,
+                                       ett_bgp_option);
+                           /* AFI */
+                           i = tvb_get_ntohs(tvb, p);
+                           proto_tree_add_text(subtree3, tvb, p,
+                                 2, "Address family identifier: %s (%u)",
+                                 val_to_str(i, afn_vals, "Unknown"), i);
+                           p += 2;
+                           /* Reserved */
+                           proto_tree_add_text(subtree3, tvb, p,
+                                 1, "Reserved: 1 byte");
+                           p++;
+                           /* SAFI */
+                           i = tvb_get_guint8(tvb, p);
+                           proto_tree_add_text(subtree3, tvb, p,
+                                 1, "Subsequent address family identifier: %s (%u)",
+                                 val_to_str(i, bgpattr_nlri_safi,
+                                    i >= 128 ? "Vendor specific" : "Unknown"), i);
+                           p++;
+                       }
+                       break;
+                   case BGP_CAPABILITY_ROUTE_REFRESH_CISCO:
+                   case BGP_CAPABILITY_ROUTE_REFRESH:
+                       ti = proto_tree_add_text(subtree1, tvb, p - 2,
+                             2 + clen, "Route refresh capability (%u %s)", 2 + clen,
+                             (clen == 1) ? "byte" : "bytes");
+                       subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
+                       proto_tree_add_text(subtree2, tvb, p - 2,
+                             1, "Capability code: Route refresh (%d)", ctype);
+                       if (clen != 0) {
+                           proto_tree_add_text(subtree2, tvb, p,
+                                 clen, "Capability value: Invalid");
+                       }
+                       else {
+                           proto_tree_add_text(subtree2, tvb, p - 1,
+                                 1, "Capability length: %u %s", clen,
+                                 (clen == 1) ? "byte" : "bytes");
+                       }
+                       p += clen;
+                       break;
+                   case BGP_CAPABILITY_COOPERATIVE_ROUTE_FILTERING:
+                       ti = proto_tree_add_text(subtree1, tvb, p - 2,
+                             2 + clen,
+                             "Cooperative route filtering capability (%u %s)",
+                             2 + clen, (clen == 1) ? "byte" : "bytes");
+                       subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
+                       proto_tree_add_text(subtree2, tvb, p - 2,
+                             1, "Capability code: Cooperative route filtering (%d)",
+                             ctype);
+                       proto_tree_add_text(subtree2, tvb, p - 1,
+                            1, "Capability length: %u %s", clen,
+                            (clen == 1) ? "byte" : "bytes");
+                       ti = proto_tree_add_text(subtree2, tvb, p,
                              clen, "Capability value");
-                             subtree3 = proto_item_add_subtree(ti,
-                                        ett_bgp_option);
-                        /* AFI */
-                        i = tvb_get_ntohs(tvb, p);
-                        proto_tree_add_text(subtree3, tvb, p,
-                             2, "Address family identifier: %s (%u)",
-                             val_to_str(i, afnumber, "Unknown"), i);
-                        p += 2;
-                        /* Reserved */
-                        proto_tree_add_text(subtree3, tvb, p,
-                             1, "Reserved: 1 byte");
-                        p++;
-                        /* SAFI */
-                        i = tvb_get_guint8(tvb, p);
-                        proto_tree_add_text(subtree3, tvb, p,
-                             1, "Subsequent address family identifier: %s (%u)",
-                             val_to_str(i, bgpattr_nlri_safi,
-                                i >= 128 ? "Vendor specific" : "Unknown"), i);
-                        p++;
-                    }
-                    break;
-                case BGP_CAPABILITY_ROUTE_REFRESH_CISCO:
-                case BGP_CAPABILITY_ROUTE_REFRESH:
-                    ti = proto_tree_add_text(subtree, tvb, p - 4,
-                         2 + plen, "Route refresh capability (%u %s)", 2 + plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
-                    proto_tree_add_text(subtree2, tvb, p - 4,
-                         1, "Parameter type: Capabilities (2)");
-                    proto_tree_add_text(subtree2, tvb, p - 3,
-                         1, "Parameter length: %u %s", plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    proto_tree_add_text(subtree2, tvb, p - 2,
-                         1, "Capability code: Route refresh (%d)", ctype);
-                    if (clen != 0) {
-                        proto_tree_add_text(subtree2, tvb, p,
-                             clen, "Capability value: Invalid");
-                    }
-                    else {
-                        proto_tree_add_text(subtree2, tvb, p - 1,
+                       subtree3 = proto_item_add_subtree(ti, ett_bgp_option);
+                       /* AFI */
+                       i = tvb_get_ntohs(tvb, p);
+                       proto_tree_add_text(subtree3, tvb, p,
+                            2, "Address family identifier: %s (%u)",
+                             val_to_str(i, afn_vals, "Unknown"), i);
+                       p += 2;
+                       /* Reserved */
+                       proto_tree_add_text(subtree3, tvb, p, 
+                            1, "Reserved: 1 byte");
+                       p++;
+                       /* SAFI */
+                       i = tvb_get_guint8(tvb, p);
+                       proto_tree_add_text(subtree3, tvb, p,
+                            1, "Subsequent address family identifier: %s (%u)",
+                            val_to_str(i, bgpattr_nlri_safi,
+                            i >= 128 ? "Vendor specific" : "Unknown"), i);
+                       p++;
+                       /* Number of ORFs */
+                       orfnum = tvb_get_guint8(tvb, p);
+                       proto_tree_add_text(subtree3, tvb, p,
+                                           1, "Number of ORFs: %u", orfnum);
+                       p++;
+                       for (i=0; i<orfnum; i++) {
+                           /* ORF Type */
+                           orftype = tvb_get_guint8(tvb, p);
+                           proto_tree_add_text(subtree3, tvb, p,
+                               1, "ORF Type: %s (%u)",
+                               val_to_str(orftype, orf_type_vals,"Unknown"),
+                               orftype);
+                           p++;
+                           /* Send/Receive */
+                           orfsendrecv = tvb_get_guint8(tvb, p);
+                           proto_tree_add_text(subtree3, tvb, p,
+                               1, "Send/Receive: %s (%u)",
+                               val_to_str(orfsendrecv, orf_send_recv_vals, 
+                               "Uknown"), orfsendrecv);
+                           p++;
+                       }
+                       break;
+                   /* unknown capability */
+                   default:
+                       ti = proto_tree_add_text(subtree, tvb, p - 2,
+                             2 + clen, "Unknown capability (%u %s)", 2 + clen,
+                             (clen == 1) ? "byte" : "bytes");
+                       subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
+                       proto_tree_add_text(subtree2, tvb, p - 2,
+                             1, "Capability code: %s (%d)",
+                             ctype >= 128 ? "Private use" : "Unknown", ctype);
+                       proto_tree_add_text(subtree2, tvb, p - 1,
                              1, "Capability length: %u %s", clen,
                              (clen == 1) ? "byte" : "bytes");
-                    }
-                    p += clen;
-                    break;
-                /* unknown capability */
-                default:
-                    ti = proto_tree_add_text(subtree, tvb, p - 4,
-                         2 + plen, "Unknown capability (%u %s)", 2 + plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
-                    proto_tree_add_text(subtree2, tvb, p - 4,
-                         1, "Parameter type: Capabilities (2)");
-                    proto_tree_add_text(subtree2, tvb, p - 3,
-                         1, "Parameter length: %u %s", plen,
-                         (plen == 1) ? "byte" : "bytes");
-                    proto_tree_add_text(subtree2, tvb, p - 2,
-                         1, "Capability code: %s (%d)",
-                         ctype >= 128 ? "Private use" : "Unknown", ctype);
-                    proto_tree_add_text(subtree2, tvb, p - 1,
-                         1, "Capability length: %u %s", clen,
-                         (clen == 1) ? "byte" : "bytes");
-                    if (clen != 0) {
-                        proto_tree_add_text(subtree2, tvb, p,
-                             clen, "Capability value: Unknown");
-                    }
-                    p += clen;
-                    break;
-                }
+                       if (clen != 0) {
+                           proto_tree_add_text(subtree2, tvb, p,
+                                 clen, "Capability value: Unknown");
+                       }
+                       p += clen;
+                       break;
+                   }
+               }
                 break;
             default:
                 proto_tree_add_text(subtree, tvb, p - 2, 2 + plen,
@@ -773,10 +897,10 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                         "bytes");
                break;
            case BGPTYPE_AS_PATH:
-                /* (o + i + 3) =
-                   (o + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
                 /* must be freed by second switch!                         */
                 /* "alen * 6" (5 digits + space) should be a good estimate
                    of how long the AS path string could be                 */
@@ -883,10 +1007,10 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                if (alen % 4 != 0)
                    goto default_attribute_top;
 
-                /* (o + i + 3) =
-                   (o + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
                 /* must be freed by second switch!                          */
                 /* "alen * 12" (5 digits, a :, 5 digits + space ) should be
                    a good estimate of how long the communities string could
@@ -937,10 +1061,10 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                if (alen % 4 != 0)
                    goto default_attribute_top;
 
-                /* (o + i + 3) =
-                   (o + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
                 /* must be freed by second switch!                          */
                 /* "alen * 16" (12 digits, 3 dots + space ) should be
                    a good estimate of how long the cluster_list string could
@@ -969,25 +1093,27 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
            case BGPTYPE_EXTENDED_COMMUNITY:
                if (alen %8 != 0)
                    goto default_attribute_top;
-                q = o + i + aoff ;
-                end = o + i + aoff + alen ;
-                ext_com_str = malloc((alen / 8)*MAX_SIZE_OF_EXT_COM_NAMES) ;
-                if (ext_com_str == NULL) break ;
-                ext_com_str[0] = '\0' ;
+
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
+                ext_com_str = malloc((alen / 8)*MAX_SIZE_OF_EXT_COM_NAMES);
+                if (ext_com_str == NULL) break;
+                ext_com_str[0] = '\0';
                 while (q < end) {
-                        ext_com = tvb_get_ntohs(tvb,q) ;
+                        ext_com = tvb_get_ntohs(tvb, q);
                         snprintf(junk_buf, sizeof(junk_buf), "%s", val_to_str(ext_com,bgpext_com_type,"Unknown"));
-                        strncat(ext_com_str,junk_buf,sizeof(junk_buf));
-                        q = q + 8 ;
-                        if (q<end) strncat(ext_com_str,",",1);
+                        strncat(ext_com_str, junk_buf, sizeof(junk_buf));
+                        q = q + 8;
+                        if (q < end) strncat(ext_com_str, ",", 1);
                 }
                 ti = proto_tree_add_text(subtree,tvb,o+i,alen+aoff,
-                        "%s : %s (%u %s)",
+                        "%s: %s (%u %s)",
                         val_to_str(bgpa.bgpa_type,bgpattr_type,"Unknown"),
-                        ext_com_str,
-                        alen,
-                        (alen ==1 ) ? "byte" : "bytes");
-                free(ext_com_str) ;
+                        ext_com_str, alen + aoff,
+                        (alen + aoff == 1) ? "byte" : "bytes");
+                free(ext_com_str);
                 break;
 
            default:
@@ -1085,10 +1211,10 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                         "AS path: %s", as_path_str);
                as_paths_tree = proto_item_add_subtree(ti, ett_bgp_as_paths);
 
-                /* (o + i + 3) =
-                   (o + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
 
                 /* snarf each AS path tuple, we have to step through each one
                    again to make a separate subtree so we can't just reuse
@@ -1226,36 +1352,24 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                 communities_tree = proto_item_add_subtree(ti,
                         ett_bgp_communities);
 
-                /* (o + i + 3) =
-                   (o + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
 
                 /* snarf each community */
                 while (q < end) {
                     /* check for reserved values */
-                   if (tvb_get_ntohs(tvb, q) == FOURHEX0 || tvb_get_ntohs(tvb, q) == FOURHEXF) {
-                        /* check for well-known communities */
-                       if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT)
-                           proto_tree_add_text(communities_tree, tvb,
-                                   q - 3 + aoff, 4,
-                                   "Community: NO_EXPORT (0x%x)", tvb_get_ntohl(tvb, q));
-                       else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_ADVERTISE)
-                           proto_tree_add_text(communities_tree, tvb,
-                                   q - 3 + aoff, 4,
-                                   "Community: NO_ADVERTISE (0x%x)", pntohl(q));
-                       else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT_SUBCONFED)
-                           proto_tree_add_text(communities_tree, tvb,
-                                    q - 3 + aoff, 4,
-                                    "Community: NO_EXPORT_SUBCONFED (0x%x)",
-                                    tvb_get_ntohl(tvb, q));
-                        else
-                           proto_tree_add_text(communities_tree, tvb,
-                                    q - 3 + aoff, 4,
-                                    "Community (reserved): 0x%x", tvb_get_ntohl(tvb, q));
+                   guint32 community = tvb_get_ntohl(tvb, q);
+                   if ((community & 0xFFFF0000) == FOURHEX0 ||
+                        (community & 0xFFFF0000) == FOURHEXF) {
+                       proto_tree_add_text(communities_tree, tvb,
+                               q - 3 + aoff, 4,
+                               "Community: %s (0x%08x)",
+                               val_to_str(community, community_vals, "(reserved)"),
+                               community);
                     }
                     else {
-
                         ti = proto_tree_add_text(communities_tree, tvb,
                                 q - 3 + aoff, 4, "Community: %u:%u",
                                 tvb_get_ntohs(tvb, q), tvb_get_ntohs(tvb, q + 2));
@@ -1300,7 +1414,7 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                af = tvb_get_ntohs(tvb, o + i + aoff);
                proto_tree_add_text(subtree2, tvb, o + i + aoff, 2,
                    "Address family: %s (%u)",
-                   val_to_str(af, afnumber, "Unknown"), af);
+                   val_to_str(af, afn_vals, "Unknown"), af);
                 saf = tvb_get_guint8(tvb, o + i + aoff + 2) ;
                proto_tree_add_text(subtree2, tvb, o + i + aoff + 2, 1,
                    "Subsequent address family identifier: %s (%u)",
@@ -1363,7 +1477,7 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                af = tvb_get_ntohs(tvb, o + i + aoff);
                proto_tree_add_text(subtree2, tvb, o + i + aoff, 2,
                    "Address family: %s (%u)",
-                   val_to_str(af, afnumber, "Unknown"), af);
+                   val_to_str(af, afn_vals, "Unknown"), af);
                 saf = tvb_get_guint8(tvb, o + i + aoff + 2) ;
                proto_tree_add_text(subtree2, tvb, o + i + aoff + 2, 1,
                    "Subsequent address family identifier: %s (%u)",
@@ -1401,10 +1515,10 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                 cluster_list_tree = proto_item_add_subtree(ti,
                         ett_bgp_cluster_list);
 
-                /* (p + i + 3) =
-                   (p + current attribute + 3 bytes to first tuple) */
-                end = o + alen + i + 3;
-                q = o + i + 3;
+                /* (o + i + aoff) =
+                   (o + current attribute + aoff bytes to first tuple) */
+                q = o + i + aoff;
+                end = q + alen;
 
                 /* snarf each cluster identifier */
                 while (q < end) {
@@ -1431,27 +1545,47 @@ dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
                         subtree3 = proto_item_add_subtree(ti,ett_bgp_extended_communities) ;
 
                         while (q < end) {
-                                ext_com_str[0] = '\0' ;
-                                ext_com = tvb_get_ntohs(tvb,q) ;
-                                snprintf(junk_buf, sizeof(junk_buf), "%s", val_to_str(ext_com,bgpext_com_type,"Unknown"));
-                                strncat(ext_com_str,junk_buf,sizeof(junk_buf));
-                                switch (ext_com) {
-                                        case BGP_EXT_COM_RT_0:
-                                        case BGP_EXT_COM_RO_0:
-                                                snprintf(junk_buf, sizeof(junk_buf), ": %u%s%d",tvb_get_ntohs(tvb,q+2),":",tvb_get_ntohl(tvb,q+4));
-                                                break ;
-                                        case BGP_EXT_COM_RT_1:
-                                        case BGP_EXT_COM_RO_1:
-                                                tvb_memcpy(tvb,ipaddr,q+2,4);
-                                                snprintf(junk_buf, sizeof(junk_buf), ": %s%s%u",ip_to_str(ipaddr),":",tvb_get_ntohs(tvb,q+6));
-                                                break ;
-                                        default:
-                                                snprintf(junk_buf, sizeof(junk_buf), " ");
-                                                break ;
-                                        }
-                                strncat(ext_com_str,junk_buf,sizeof(junk_buf));
-                                proto_tree_add_text(subtree3,tvb,q,8, "%s",ext_com_str);
-                                q = q + 8 ;
+                         ext_com_str[0] = '\0' ;
+                         ext_com = tvb_get_ntohs(tvb,q) ;
+                         snprintf(junk_buf, sizeof(junk_buf), "%s", val_to_str(ext_com,bgpext_com_type,"Unknown"));
+                         strncat(ext_com_str,junk_buf,sizeof(junk_buf));
+                         switch (ext_com) {
+                         case BGP_EXT_COM_RT_0:
+                         case BGP_EXT_COM_RO_0:
+                           snprintf(junk_buf, sizeof(junk_buf), ": %u%s%d",tvb_get_ntohs(tvb,q+2),":",tvb_get_ntohl(tvb,q+4));
+                           break ;
+                         case BGP_EXT_COM_RT_1:
+                         case BGP_EXT_COM_RO_1:
+                           tvb_memcpy(tvb,ipaddr,q+2,4);
+                           snprintf(junk_buf, sizeof(junk_buf), ": %s%s%u",ip_to_str(ipaddr),":",tvb_get_ntohs(tvb,q+6));
+                           break;
+                         case BGP_EXT_COM_VPN_ORIGIN:
+                         case BGP_EXT_COM_OSPF_RID:
+                           tvb_memcpy(tvb,ipaddr,q+2,4);
+                           snprintf(junk_buf, sizeof(junk_buf), ": %s",ip_to_str(ipaddr));
+                           break;
+                         case BGP_EXT_COM_OSPF_RTYPE: 
+                           tvb_memcpy(tvb,ipaddr,q+2,4);
+                           snprintf(junk_buf, sizeof(junk_buf), ": Area:%s %s",ip_to_str(ipaddr),val_to_str(tvb_get_guint8(tvb,q+6),bgpext_ospf_rtype,"Unknown"));
+                               /* print OSPF Metric type if selected */
+                               /* always print E2 even if not external route -- receiving router should ignore */
+                           if ( (tvb_get_guint8(tvb,q+7)) & BGP_OSPF_RTYPE_METRIC_TYPE ) { 
+                             strcat(junk_buf," E2");
+                           } else if (tvb_get_guint8(tvb,q+6)==(BGP_OSPF_RTYPE_EXT ||BGP_OSPF_RTYPE_NSSA ) ) {
+                             strcat(junk_buf, " E1");
+                           }
+                           break;
+                         case BGP_EXT_COM_LINKBAND:
+                           tvb_memcpy(tvb,ipaddr,q+2,4); /* need to check on IEEE format on all platforms */
+                           snprintf(junk_buf, sizeof(junk_buf), ": %f bytes per second",(double)*ipaddr);
+                           break;
+                         default:
+                           snprintf(junk_buf, sizeof(junk_buf), " ");
+                           break ;
+                         }
+                         strncat(ext_com_str,junk_buf,sizeof(junk_buf));
+                         proto_tree_add_text(subtree3,tvb,q,8, "%s",ext_com_str);
+                         q = q + 8 ;
                         }
                         free(ext_com_str) ;
                 }
@@ -1540,15 +1674,15 @@ dissect_bgp_route_refresh(tvbuff_t *tvb, int offset, proto_tree *tree)
     i = tvb_get_ntohs(tvb, offset + BGP_HEADER_SIZE);
     proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE, 2, 
                         "Address family identifier: %s (%u)",
-                        val_to_str(i, afnumber, "Unknown"), i);
+                        val_to_str(i, afn_vals, "Unknown"), i);
     offset += 2;
     /* Reserved */
-    proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE + 2, 1, 
+    proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE, 1, 
                         "Reserved: 1 byte");
     offset++;
     /* SAFI */
-    i = tvb_get_guint8(tvb, offset);
-    proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE + 3, 1, 
+    i = tvb_get_guint8(tvb, offset + BGP_HEADER_SIZE);
+    proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE, 1, 
                         "Subsequent address family identifier: %s (%u)",
                         val_to_str(i, bgpattr_nlri_safi,
                         i >= 128 ? "Vendor specific" : "Unknown"),
@@ -1574,17 +1708,15 @@ dissect_bgp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     int           hlen;          /* BGP header length                */
     char          *typ;          /* BGP message type                 */
 
-    if (check_col(pinfo->fd, COL_PROTOCOL))
-       col_set_str(pinfo->fd, COL_PROTOCOL, "BGP");
-    if (check_col(pinfo->fd, COL_INFO))
-       col_clear(pinfo->fd, COL_INFO);
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "BGP");
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_clear(pinfo->cinfo, COL_INFO);
 
-    l = tvb_length(tvb);
+    l = tvb_reported_length(tvb);
     i = 0;
     found = -1;
     /* run through the TCP packet looking for BGP headers         */
-    /* this is done twice, but this way each message type can be 
-       printed in the COL_INFO field                              */
     while (i + BGP_HEADER_SIZE <= l) {
        tvb_memcpy(tvb, bgp.bgp_marker, i, BGP_HEADER_SIZE);
 
@@ -1596,44 +1728,42 @@ dissect_bgp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
        found++;
        hlen = ntohs(bgp.bgp_len);
+
+       /*
+        * Desegmentation check.
+        */
+       if (bgp_desegment) {
+           if (hlen > tvb_length_remaining(tvb, i) && pinfo->can_desegment) {
+               /*
+                * Not all of this packet is in the data we've been
+                * handed, but we can do reassembly on it.
+                *
+                * Tell the TCP dissector where the data for
+                * this message starts in the data it handed
+                * us, and how many more bytes we need, and
+                * return.
+                */
+               pinfo->desegment_offset = i;
+               pinfo->desegment_len = hlen - tvb_length_remaining(tvb, i);
+               return;
+           }
+       }
+
        typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
 
-       if (check_col(pinfo->fd, COL_INFO)) {
+       if (check_col(pinfo->cinfo, COL_INFO)) {
            if (found == 0)
-               col_add_fstr(pinfo->fd, COL_INFO, "%s", typ);
+               col_add_fstr(pinfo->cinfo, COL_INFO, "%s", typ);
            else
-               col_append_fstr(pinfo->fd, COL_INFO, ", %s", typ);
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", typ);
        }
 
-       i += hlen;
-    }
+       if (tree) {
+           ti = proto_tree_add_item(tree, proto_bgp, tvb, i, hlen, FALSE);
+           bgp_tree = proto_item_add_subtree(ti, ett_bgp);
 
-    if (tree) {
-        ti = proto_tree_add_item(tree, proto_bgp, tvb, 0,
-                                l, FALSE);
-       bgp_tree = proto_item_add_subtree(ti, ett_bgp);
-
-       i = 0;
-        /* now, run through the TCP packet again, this time dissect */
-        /* each message that we find */
-       while (i + BGP_HEADER_SIZE <= l) {
-           tvb_memcpy(tvb, bgp.bgp_marker, i, BGP_HEADER_SIZE);
-
-           /* look for bgp header */
-           if (memcmp(bgp.bgp_marker, marker, sizeof(marker)) != 0) {
-               i++;
-               continue;
-           }
+           ti = proto_tree_add_text(bgp_tree, tvb, i, hlen, "%s", typ);
 
-           hlen = ntohs(bgp.bgp_len);
-           typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
-           if (l < hlen) {
-               ti = proto_tree_add_text(bgp_tree, tvb, i,
-                     l, "%s (truncated)", typ);
-           } else {
-               ti = proto_tree_add_text(bgp_tree, tvb, i, hlen,
-                           "%s", typ);
-           }
            /* add a different tree for each message type */
            switch (bgp.bgp_type) {
            case BGP_OPEN:
@@ -1697,9 +1827,9 @@ dissect_bgp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            default:
                break;
            }
-
-           i += hlen;
        }
+
+       i += hlen;
     }
 }
 
@@ -1738,15 +1868,25 @@ proto_register_bgp(void)
       &ett_bgp_option,
       &ett_bgp_extended_communities
     };
+    module_t *bgp_module;
 
     proto_bgp = proto_register_protocol("Border Gateway Protocol",
                                        "BGP", "bgp");
     proto_register_field_array(proto_bgp, hf, array_length(hf));
     proto_register_subtree_array(ett, array_length(ett));
+
+    bgp_module = prefs_register_protocol(proto_bgp, NULL);
+    prefs_register_bool_preference(bgp_module, "desegment",
+      "Desegment all BGP messages spanning multiple TCP segments",
+      "Whether the BGP dissector should desegment all messages spanning multiple TCP segments",
+      &bgp_desegment);
 }
 
 void
 proto_reg_handoff_bgp(void)
 {
-    dissector_add("tcp.port", BGP_TCP_PORT, dissect_bgp, proto_bgp);
+    dissector_handle_t bgp_handle;
+
+    bgp_handle = create_dissector_handle(dissect_bgp, proto_bgp);
+    dissector_add("tcp.port", BGP_TCP_PORT, bgp_handle);
 }