From Ronnie Sahlberg:
[obnox/wireshark/wip.git] / packet-bgp.c
index e72aecdf90e01662fee40690309cf981b899106c..ab6a6f0e426db898c0fd17cca0471651e11461d0 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for BGP packet dissection.
  * Copyright 1999, Jun-ichiro itojun Hagino <itojun@itojun.org>
  *
- * $Id: packet-bgp.c,v 1.45 2001/07/09 11:08:39 guy Exp $
+ * $Id: packet-bgp.c,v 1.53 2002/01/21 07:36:32 guy Exp $
  *
  * Supports:
  * RFC1771 A Border Gateway Protocol 4 (BGP-4)
 # 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" },
@@ -185,27 +187,6 @@ static const value_string bgpext_ospf_rtype[] = {
 /* 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" },
@@ -217,6 +198,21 @@ static const value_string bgpattr_nlri_safi[] = {
     { 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
 
@@ -243,6 +239,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.
  */
@@ -502,13 +502,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);
@@ -557,127 +562,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,
@@ -789,10 +845,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                 */
@@ -899,10 +955,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
@@ -953,10 +1009,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
@@ -985,25 +1041,28 @@ 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)",
                         val_to_str(bgpa.bgpa_type,bgpattr_type,"Unknown"),
                         ext_com_str,
                         alen,
-                        (alen ==) ? "byte" : "bytes");
-                free(ext_com_str) ;
+                        (alen == 1) ? "byte" : "bytes");
+                free(ext_com_str);
                 break;
 
            default:
@@ -1101,10 +1160,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
@@ -1242,10 +1301,10 @@ 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) {
@@ -1316,7 +1375,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)",
@@ -1379,7 +1438,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)",
@@ -1417,10 +1476,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) {
@@ -1576,15 +1635,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"),
@@ -1610,17 +1669,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);
 
@@ -1632,44 +1689,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:
@@ -1733,9 +1788,9 @@ dissect_bgp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            default:
                break;
            }
-
-           i += hlen;
        }
+
+       i += hlen;
     }
 }
 
@@ -1774,15 +1829,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);
 }