Tvbuffified by Heikki Vatiainen.
[obnox/wireshark/wip.git] / packet-bgp.c
1 /* packet-bgp.c
2  * Routines for BGP packet dissection.
3  * Copyright 1999, Jun-ichiro itojun Hagino <itojun@itojun.org>
4  *
5  * $Id: packet-bgp.c,v 1.34 2001/04/17 21:25:13 guy Exp $
6  * 
7  * Supports:
8  * RFC1771 A Border Gateway Protocol 4 (BGP-4)
9  * RFC1965 Autonomous System Confederations for BGP 
10  * RFC1997 BGP Communities Attribute
11  * RFC2796 BGP Route Reflection An alternative to full mesh IBGP
12  * RFC2842 Capabilities Advertisement with BGP-4
13  * RFC2858 Multiprotocol Extensions for BGP-4
14  * RFC2918 Route Refresh Capability for BGP-4
15  *
16  * TODO:
17  * Destination Preference Attribute for BGP (work in progress)
18  * RFC1863 A BGP/IDRP Route Server alternative to a full mesh routing 
19  *
20  * Ethereal - Network traffic analyzer
21  * By Gerald Combs <gerald@zing.org>
22  * Copyright 1998 Gerald Combs
23  *
24  * This program is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU General Public License
26  * as published by the Free Software Foundation; either version 2
27  * of the License, or (at your option) any later version.
28  * 
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  * 
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #ifdef HAVE_SYS_TYPES_H
47 # include <sys/types.h>
48 #endif
49
50 #ifdef HAVE_NETINET_IN_H
51 # include <netinet/in.h>
52 #endif
53
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
56 #endif
57
58 #include <string.h>
59 #include <glib.h>
60
61 #ifdef NEED_SNPRINTF_H
62 # include "snprintf.h"
63 #endif
64
65 #include "packet.h"
66 #include "packet-bgp.h"
67 #include "packet-ipv6.h"
68
69 static const value_string bgptypevals[] = {
70     { BGP_OPEN, "OPEN Message" },
71     { BGP_UPDATE, "UPDATE Message" },
72     { BGP_NOTIFICATION, "NOTIFICATION Message" },
73     { BGP_KEEPALIVE, "KEEPALIVE Message" },
74     { BGP_ROUTE_REFRESH, "ROUTE-REFRESH Message" },
75     { 0, NULL },
76 };
77
78 static const value_string bgpnotify_major[] = {
79     { 1, "Message Header Error" },
80     { 2, "OPEN Message Error" },
81     { 3, "UPDATE Message Error" },
82     { 4, "Hold Timer Expired" },
83     { 5, "Finite State Machine Error" },
84     { 6, "Cease" },
85     { 0, NULL },
86 };
87
88 static const value_string bgpnotify_minor_1[] = {
89     { 1, "Connection Not Synchronized" },
90     { 2, "Bad Message Length" },
91     { 3, "Bad Message Type" },
92     { 0, NULL },
93 };
94
95 static const value_string bgpnotify_minor_2[] = {
96     { 1, "Unsupported Version Number" },
97     { 2, "Bad Peer AS" },
98     { 3, "Bad BGP Identifier" },
99     { 4, "Unsupported Optional Parameter" },
100     { 5, "Authentication Failure" },
101     { 6, "Unacceptable Hold Time" },
102     { 7, "Unsupported Capability" },
103     { 0, NULL },
104 };
105
106 static const value_string bgpnotify_minor_3[] = {
107     { 1, "Malformed Attribute List" },
108     { 2, "Unrecognized Well-known Attribute" },
109     { 3, "Missing Well-known Attribute" },
110     { 4, "Attribute Flags Error" },
111     { 5, "Attribute Length Error" },
112     { 6, "Invalid ORIGIN Attribute" },
113     { 7, "AS Routing Loop" },
114     { 8, "Invalid NEXT_HOP Attribute" },
115     { 9, "Optional Attribute Error" },
116     { 10, "Invalid Network Field" },
117     { 11, "Malformed AS_PATH" },
118     { 0, NULL },
119 };
120
121 static const value_string *bgpnotify_minor[] = {
122     NULL, bgpnotify_minor_1, bgpnotify_minor_2, bgpnotify_minor_3,
123 };
124
125 static const value_string bgpattr_origin[] = {
126     { 0, "IGP" },
127     { 1, "EGP" },
128     { 2, "INCOMPLETE" },
129     { 0, NULL },
130 };
131
132 static const value_string as_segment_type[] = {
133     { 1, "AS_SET" },
134     { 2, "AS_SEQUENCE" },
135 /* RFC1965 has the wrong values, corrected in  */
136 /* draft-ietf-idr-bgp-confed-rfc1965bis-01.txt */
137     { 4, "AS_CONFED_SET" },
138     { 3, "AS_CONFED_SEQUENCE" },
139     { 0, NULL },
140 };
141
142 static const value_string bgpattr_type[] = {
143     { BGPTYPE_ORIGIN, "ORIGIN" },
144     { BGPTYPE_AS_PATH, "AS_PATH" },
145     { BGPTYPE_NEXT_HOP, "NEXT_HOP" },
146     { BGPTYPE_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" },
147     { BGPTYPE_LOCAL_PREF, "LOCAL_PREF" },
148     { BGPTYPE_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" },
149     { BGPTYPE_AGGREGATOR, "AGGREGATOR" },
150     { BGPTYPE_COMMUNITIES, "COMMUNITIES" },
151     { BGPTYPE_ORIGINATOR_ID, "ORIGINATOR_ID" },
152     { BGPTYPE_CLUSTER_LIST, "CLUSTER_LIST" },
153     { BGPTYPE_MP_REACH_NLRI, "MP_REACH_NLRI" },
154     { BGPTYPE_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" },
155     { 0, NULL },
156 };
157
158 /* Subsequent address family identifier, RFC2283 section 7 */
159 static const value_string bgpattr_nlri_safi[] = {
160     { 0, "Reserved" },
161     { 1, "Unicast" },
162     { 2, "Multicast" },
163     { 3, "Unicast+Multicast" },
164     { 0, NULL },
165 };
166
167 static const value_string afnumber[] = {
168     { 0, "Reserved" },
169     { AFNUM_INET, "IPv4" },
170     { AFNUM_INET6, "IPv6" },
171     { AFNUM_NSAP, "NSAP" },
172     { AFNUM_HDLC, "HDLC" },
173     { AFNUM_BBN1822, "BBN 1822" },
174     { AFNUM_802, "802" },
175     { AFNUM_E163, "E.163" },
176     { AFNUM_E164, "E.164" },
177     { AFNUM_F69, "F.69" },
178     { AFNUM_X121, "X.121" },
179     { AFNUM_IPX, "IPX" },
180     { AFNUM_ATALK, "Appletalk" },
181     { AFNUM_DECNET, "Decnet IV" },
182     { AFNUM_BANYAN, "Banyan Vines" },
183     { AFNUM_E164NSAP, "E.164 with NSAP subaddress" },
184     { 65535, "Reserved" },
185     { 0, NULL },
186 };
187
188 static int proto_bgp = -1;
189 static int hf_bgp_type = -1;
190
191 static gint ett_bgp = -1;
192 static gint ett_bgp_unfeas = -1;
193 static gint ett_bgp_attrs = -1;
194 static gint ett_bgp_attr = -1;
195 static gint ett_bgp_attr_flags = -1;
196 static gint ett_bgp_mp_reach_nlri = -1;
197 static gint ett_bgp_mp_unreach_nlri = -1;
198 static gint ett_bgp_nlri = -1;
199 static gint ett_bgp_open = -1;
200 static gint ett_bgp_update = -1;
201 static gint ett_bgp_notification = -1;
202 static gint ett_bgp_route_refresh = -1; /* ROUTE-REFRESH message tree */
203 static gint ett_bgp_as_paths = -1;
204 static gint ett_bgp_communities = -1;
205 static gint ett_bgp_cluster_list = -1;  /* cluster list tree          */
206 static gint ett_bgp_options = -1;       /* optional parameters tree   */
207 static gint ett_bgp_option = -1;        /* an optional parameter tree */
208
209 /*
210  * Decode an IPv4 prefix.
211  */
212 static int
213 decode_prefix4(tvbuff_t *tvb, gint offset, char *buf, int buflen)
214 {
215     guint8 addr[4];   /* IP address                         */
216     int    plen;      /* prefix length                      */
217     int    length;    /* number of octets needed for prefix */
218
219     /* snarf length */
220     plen = tvb_get_guint8(tvb, offset);
221     if (plen < 0 || 32 < plen)
222         return -1;
223     length = (plen + 7) / 8;
224
225     /* snarf prefix */
226     memset(addr, 0, sizeof(addr));
227     tvb_memcpy(tvb, addr, offset + 1, length);
228     if (plen % 8)
229         addr[length - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
230
231     /* hand back a formatted string */
232     snprintf(buf, buflen, "%s/%d", ip_to_str(addr), plen);
233     return(1 + length);
234 }
235
236 /*
237  * Decode an IPv6 prefix.
238  */
239 static int
240 decode_prefix6(tvbuff_t *tvb, gint offset, char *buf, int buflen)
241 {
242     struct e_in6_addr addr;     /* IPv6 address                       */
243     int               plen;     /* prefix length                      */
244     int               length;   /* number of octets needed for prefix */
245
246     /* snarf length */
247     plen = tvb_get_guint8(tvb, offset);
248     if (plen < 0 || 128 < plen)
249         return -1;
250     length = (plen + 7) / 8;
251
252     /* snarf prefix */
253     memset(&addr, 0, sizeof(addr));
254     tvb_memcpy(tvb, (guint8 *)&addr, offset + 1, length);
255     if (plen % 8)
256         addr.s6_addr[length - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
257
258     /* hand back a formatted string */
259     snprintf(buf, buflen, "%s/%d", ip6_to_str(&addr), plen);
260     return(1 + length);
261 }
262
263 /*
264  * Dissect a BGP OPEN message.
265  */
266 static void
267 dissect_bgp_open(tvbuff_t *tvb, int offset, proto_tree *tree)
268 {
269     struct bgp_open bgpo;      /* BGP OPEN message      */
270     int             hlen;      /* message length        */
271     u_int           i;         /* tmp                   */
272     int             ptype;     /* parameter type        */
273     int             plen;      /* parameter length      */
274     int             ctype;     /* capability type       */
275     int             clen;      /* capability length     */
276     int             ostart;    /* options start         */
277     int             oend;      /* options end           */
278     int             p;         /* tvb offset counter    */
279     proto_item      *ti;       /* tree item             */
280     proto_tree      *subtree;  /* subtree for options   */
281     proto_tree      *subtree2; /* subtree for an option */
282     proto_tree      *subtree3; /* subtree for an option */
283
284     /* snarf OPEN message */
285     tvb_memcpy(tvb, bgpo.bgpo_marker, offset, BGP_MIN_OPEN_MSG_SIZE);
286     hlen = ntohs(bgpo.bgpo_len);
287
288     proto_tree_add_text(tree, tvb,
289         offset + offsetof(struct bgp_open, bgpo_version), 1,
290         "Version: %u", bgpo.bgpo_version);
291     proto_tree_add_text(tree, tvb,
292         offset + offsetof(struct bgp_open, bgpo_myas), 2,
293         "My AS: %u", ntohs(bgpo.bgpo_myas));
294     proto_tree_add_text(tree, tvb,
295         offset + offsetof(struct bgp_open, bgpo_holdtime), 2,
296         "Hold time: %u", ntohs(bgpo.bgpo_holdtime));
297     proto_tree_add_text(tree, tvb,
298         offset + offsetof(struct bgp_open, bgpo_id), 4,
299         "BGP identifier: %s", ip_to_str((guint8 *)&bgpo.bgpo_id));
300     proto_tree_add_text(tree, tvb,
301         offset + offsetof(struct bgp_open, bgpo_optlen), 1,
302         "Optional parameters length: %u %s", bgpo.bgpo_optlen,
303         (bgpo.bgpo_optlen == 1) ? "byte" : "bytes");
304
305     /* optional parameters */
306     if (bgpo.bgpo_optlen > 0) {
307         /* add a subtree and setup some offsets */
308         ostart = offset + BGP_MIN_OPEN_MSG_SIZE;
309         ti = proto_tree_add_text(tree, tvb, ostart, bgpo.bgpo_optlen, 
310              "Optional parameters");
311         subtree = proto_item_add_subtree(ti, ett_bgp_options);
312         p = offset + ostart;
313         oend = p + bgpo.bgpo_optlen;
314
315         /* step through all of the optional parameters */
316         while (p < oend) {
317
318             /* grab the type and length */
319             ptype = tvb_get_guint8(tvb, p++);
320             plen = tvb_get_guint8(tvb, p++);
321         
322             /* check the type */ 
323             switch (ptype) {
324             case BGP_OPTION_AUTHENTICATION:
325                 proto_tree_add_text(subtree, tvb, p - 2, 2 + plen,
326                     "Authentication information (%u %s)", plen,
327                     (plen == 1) ? "byte" : "bytes");
328                 break;
329             case BGP_OPTION_CAPABILITY:
330                 /* grab the capability code */
331                 ctype = tvb_get_guint8(tvb, p++);
332                 clen = tvb_get_guint8(tvb, p++);
333
334                 /* check the capability type */
335                 switch (ctype) {
336                 case BGP_CAPABILITY_RESERVED:
337                     ti = proto_tree_add_text(subtree, tvb, p - 4, 
338                          2 + plen, "Reserved capability (%u %s)", 2 + plen,
339                          (plen == 1) ? "byte" : "bytes");
340                     subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
341                     proto_tree_add_text(subtree2, tvb, p - 4, 
342                          1, "Parameter type: Capabilities (2)");
343                     proto_tree_add_text(subtree2, tvb, p - 3, 
344                          1, "Parameter length: %u %s", plen, 
345                          (plen == 1) ? "byte" : "bytes");
346                     proto_tree_add_text(subtree2, tvb, p - 2, 
347                          1, "Capability code: Reserved (0)");
348                     proto_tree_add_text(subtree2, tvb, p - 1, 
349                          1, "Capability length: %u %s", clen, 
350                          (clen == 1) ? "byte" : "bytes");
351                     if (clen != 0) {
352                         proto_tree_add_text(subtree2, tvb, p, 
353                              clen, "Capability value: Unknown");
354                     }
355                     p += clen;
356                     break;
357                 case BGP_CAPABILITY_MULTIPROTOCOL:
358                     ti = proto_tree_add_text(subtree, tvb, p - 4, 
359                          2 + plen, 
360                          "Multiprotocol extensions capability (%u %s)",  
361                          2 + plen, (plen == 1) ? "byte" : "bytes");
362                     subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
363                     proto_tree_add_text(subtree2, tvb, p - 4, 
364                          1, "Parameter type: Capabilities (2)");
365                     proto_tree_add_text(subtree2, tvb, p - 3, 
366                          1, "Parameter length: %u %s", plen, 
367                          (plen == 1) ? "byte" : "bytes");
368                     proto_tree_add_text(subtree2, tvb, p - 2, 
369                          1, "Capability code: Multiprotocol extensions (%d)", 
370                          ctype);
371                     if (clen != 4) {
372                         proto_tree_add_text(subtree2, tvb, p - 1, 
373                              1, "Capability length: Invalid");
374                         proto_tree_add_text(subtree2, tvb, p, 
375                              clen, "Capability value: Unknown");
376                     }
377                     else {
378                         proto_tree_add_text(subtree2, tvb, p - 1, 
379                              1, "Capability length: %u %s", clen, 
380                              (clen == 1) ? "byte" : "bytes");
381                         ti = proto_tree_add_text(subtree2, tvb, p, 
382                              clen, "Capability value");
383                              subtree3 = proto_item_add_subtree(ti, 
384                                         ett_bgp_option);
385                         /* AFI */
386                         i = tvb_get_ntohs(tvb, p);
387                         proto_tree_add_text(subtree3, tvb, p, 
388                              2, "Address family identifier: %s (%u)",
389                              val_to_str(i, afnumber, "Unknown"), i);
390                         p += 2;
391                         /* Reserved */
392                         proto_tree_add_text(subtree3, tvb, p, 
393                              1, "Reserved: 1 byte");
394                         p++;
395                         /* SAFI */
396                         i = tvb_get_guint8(tvb, p);
397                         proto_tree_add_text(subtree3, tvb, p, 
398                              1, "Subsequent address family identifier: %s (%u)",
399                              val_to_str(i, bgpattr_nlri_safi, 
400                              i >= 128 ? "Vendor specific" : "Unknown"), i);
401                         p++;
402                     }
403                     break;
404                 case BGP_CAPABILITY_ROUTE_REFRESH:
405                     ti = proto_tree_add_text(subtree, tvb, p - 4, 
406                          2 + plen, "Route refresh capability (%u %s)", 2 + plen,
407                          (plen == 1) ? "byte" : "bytes");
408                     subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
409                     proto_tree_add_text(subtree2, tvb, p - 4, 
410                          1, "Parameter type: Capabilities (2)");
411                     proto_tree_add_text(subtree2, tvb, p - 3, 
412                          1, "Parameter length: %u %s", plen, 
413                          (plen == 1) ? "byte" : "bytes");
414                     proto_tree_add_text(subtree2, tvb, p - 2, 
415                          1, "Capability code: Route refresh (%d)", ctype);
416                     if (clen != 0) {
417                         proto_tree_add_text(subtree2, tvb, p, 
418                              clen, "Capability value: Invalid");
419                     }
420                     else {
421                         proto_tree_add_text(subtree2, tvb, p - 1, 
422                              1, "Capability length: %u %s", clen, 
423                              (clen == 1) ? "byte" : "bytes");
424                     }
425                     p += clen;
426                     break;
427                 /* unknown capability */
428                 default:
429                     ti = proto_tree_add_text(subtree, tvb, p - 4, 
430                          2 + plen, "Unknown capability (%u %s)", 2 + plen,
431                          (plen == 1) ? "byte" : "bytes");
432                     subtree2 = proto_item_add_subtree(ti, ett_bgp_option);
433                     proto_tree_add_text(subtree2, tvb, p - 4, 
434                          1, "Parameter type: Capabilities (2)");
435                     proto_tree_add_text(subtree2, tvb, p - 3, 
436                          1, "Parameter length: %u %s", plen, 
437                          (plen == 1) ? "byte" : "bytes");
438                     proto_tree_add_text(subtree2, tvb, p - 2, 
439                          1, "Capability code: %s (%d)",
440                          ctype >= 128 ? "Private use" : "Unknown", ctype);
441                     proto_tree_add_text(subtree2, tvb, p - 1, 
442                          1, "Capability length: %u %s", clen, 
443                          (clen == 1) ? "byte" : "bytes");
444                     if (clen != 0) {
445                         proto_tree_add_text(subtree2, tvb, p, 
446                              clen, "Capability value: Unknown");
447                     }
448                     p += clen;
449                     break;
450                 }
451                 break;
452             default:
453                 proto_tree_add_text(subtree, tvb, p - 2, 2 + plen,
454                     "Unknown optional parameter");
455                 break;
456             }
457         }
458     }
459 }
460
461 /*
462  * Dissect a BGP UPDATE message.
463  */
464 static void
465 dissect_bgp_update(tvbuff_t *tvb, int offset, proto_tree *tree)
466  {
467     struct bgp_attr bgpa;                       /* path attributes          */
468     int             hlen;                       /* message length           */
469     gint            o;                          /* packet offset            */
470     gint            q;                          /* tmp                      */
471     gint            end;                        /* message end              */
472     int             len;                        /* tmp                      */
473     proto_item      *ti;                        /* tree item                */
474     proto_tree      *subtree;                   /* subtree for attibutes    */ 
475     proto_tree      *subtree2;                  /* subtree for attibutes    */ 
476     proto_tree      *subtree3;                  /* subtree for attibutes    */
477     proto_tree      *as_paths_tree;             /* subtree for AS_PATHs     */
478     proto_tree      *as_path_tree;              /* subtree for AS_PATH      */
479     proto_tree      *communities_tree;          /* subtree for COMMUNITIES  */
480     proto_tree      *community_tree;            /* subtree for a community  */
481     proto_tree      *cluster_list_tree;         /* subtree for CLUSTER_LIST */
482     int             i, j;                       /* tmp                      */
483     guint8          length;                     /* AS_PATH length           */
484     guint8          type;                       /* AS_PATH type             */
485     char            *as_path_str = NULL;        /* AS_PATH string           */
486     char            *communities_str = NULL;    /* COMMUNITIES string       */
487     char            *cluster_list_str = NULL;   /* CLUSTER_LIST string      */
488     char            junk_buf[256];              /* tmp                      */
489     guint8          ipaddr[4];                  /* IPv4 address             */
490     struct e_in6_addr ip6addr;                  /* IPv6 address             */
491
492     hlen = tvb_get_ntohs(tvb, offset + BGP_MARKER_SIZE);
493     o = offset + BGP_HEADER_SIZE;
494
495     /* check for withdrawals */
496     len = tvb_get_ntohs(tvb, o);
497     proto_tree_add_text(tree, tvb, o, 2, 
498         "Unfeasible routes length: %u %s", len, (len == 1) ? "byte" : "bytes");
499     o += 2;
500
501     /* parse unfeasible prefixes */
502     if (len > 0) {
503         ti = proto_tree_add_text(tree, tvb, o, len, "Withdrawn routes:");
504         subtree = proto_item_add_subtree(ti, ett_bgp_unfeas);
505
506         /* parse each prefixes */
507         end = o + len;
508         while (o < end) {
509             i = decode_prefix4(tvb, o, junk_buf, sizeof(junk_buf));
510             proto_tree_add_text(subtree, tvb, o, i, "%s", junk_buf);
511             o += i;
512         }
513     }
514     else {
515         o += len;
516     }
517
518     /* check for advertisements */
519     len = tvb_get_ntohs(tvb, o);
520     proto_tree_add_text(tree, tvb, o, 2, "Total path attribute length: %u %s", 
521             len, (len == 1) ? "byte" : "bytes");
522
523     /* path attributes */
524     if (len > 0) {
525         ti = proto_tree_add_text(tree, tvb, o + 2, len, "Path attributes");
526         subtree = proto_item_add_subtree(ti, ett_bgp_attrs);
527         i = 2;
528         while (i < len) {
529             int alen, aoff;
530             char *msg;
531             guint16 af;
532             int off, snpa;
533
534             tvb_memcpy(tvb, (guint8 *)&bgpa, o + i, sizeof(bgpa));
535             /* check for the Extended Length bit */
536             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) {
537                 alen = tvb_get_ntohs(tvb, o + i + sizeof(bgpa));
538                 aoff = sizeof(bgpa) + 2;
539             } else {
540                 alen = tvb_get_guint8(tvb, o + i + sizeof(bgpa));
541                 aoff = sizeof(bgpa) + 1;
542             }
543
544             /* This is kind of ugly - similar code appears twice, but it 
545                helps browsing attrs.                                      */
546             /* the first switch prints things in the title of the subtree */
547             switch (bgpa.bgpa_type) {
548             case BGPTYPE_ORIGIN:
549                 if (alen != 1)
550                     goto default_attribute_top;
551                 msg = val_to_str(tvb_get_guint8(tvb, o + i + aoff), bgpattr_origin, "Unknown");
552                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
553                         "%s: %s (%u %s)",
554                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
555                         msg, alen + aoff, (alen + aoff == 1) ? "byte" : 
556                         "bytes");
557                 break;
558             case BGPTYPE_AS_PATH:
559                 /* (o + i + 3) =
560                    (o + current attribute + 3 bytes to first tuple) */ 
561                 end = o + alen + i + 3;
562                 q = o + i + 3;
563                 /* must be freed by second switch!                         */
564                 /* "alen * 6" (5 digits + space) should be a good estimate
565                    of how long the AS path string could be                 */
566                 as_path_str = malloc((alen + 1) * 6);
567                 if (as_path_str == NULL) break;
568                 as_path_str[0] = '\0';
569    
570                 /* snarf each AS path */
571                 while (q < end) {
572                     type = tvb_get_guint8(tvb, q++);
573                     if (type == AS_SET) {
574                         snprintf(as_path_str, 2, "{");
575                     }
576                     else if (type == AS_CONFED_SET) {
577                         snprintf(as_path_str, 2, "[");
578                     }
579                     else if (type == AS_CONFED_SEQUENCE) {
580                         snprintf(as_path_str, 2, "(");
581                     }
582                     length = tvb_get_guint8(tvb, q++);
583
584                     /* snarf each value in path */
585                     for (j = 0; j < length; j++) {
586                         snprintf(junk_buf, sizeof(junk_buf), "%u%s", tvb_get_ntohs(tvb, q),
587                                 (type == AS_SET || type == AS_CONFED_SET) 
588                                 ? ", " : " ");
589                         strncat(as_path_str, junk_buf, sizeof(junk_buf));
590                         q += 2;
591                     }
592                    
593                     /* cleanup end of string */
594                     if (type == AS_SET) {
595                         as_path_str[strlen(as_path_str) - 2] = '}';
596                     }
597                     else if (type == AS_CONFED_SET) {
598                         as_path_str[strlen(as_path_str) - 2] = ']';
599                     }
600                     else if (type == AS_CONFED_SEQUENCE) {
601                         as_path_str[strlen(as_path_str) - 1] = ')';
602                     }
603                     else {
604                         as_path_str[strlen(as_path_str) - 1] = '\0';
605                     }
606                 }
607
608                 /* check for empty AS_PATH */
609                 if (alen == 0)
610                     strncpy(as_path_str, "empty", 6);
611
612                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
613                         "%s: %s (%u %s)",
614                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
615                         as_path_str, alen + aoff,
616                         (alen + aoff == 1) ? "byte" : "bytes");
617                 break;
618             case BGPTYPE_NEXT_HOP:
619                 if (alen != 4)
620                     goto default_attribute_top;
621                 tvb_memcpy(tvb, ipaddr, o + i + aoff, 4);
622                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
623                         "%s: %s (%u %s)",
624                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
625                         ip_to_str(ipaddr), alen + aoff, (alen + aoff == 1)
626                         ? "byte" : "bytes");
627                 break;
628             case BGPTYPE_MULTI_EXIT_DISC:
629                 if (alen != 4)
630                     goto default_attribute_top;
631                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
632                         "%s: %u (%u %s)",
633                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
634                         tvb_get_ntohl(tvb, o + i + aoff), alen + aoff,
635                         (alen + aoff == 1) ? "byte" : "bytes");
636                 break;
637             case BGPTYPE_LOCAL_PREF:
638                 if (alen != 4)
639                     goto default_attribute_top;
640                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
641                         "%s: %u (%u %s)",
642                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
643                         tvb_get_ntohl(tvb, o + i + aoff), alen + aoff,
644                         (alen + aoff == 1) ? "byte" : "bytes");
645                 break;
646             case BGPTYPE_ATOMIC_AGGREGATE:
647                 if (alen != 0) 
648                     goto default_attribute_top;
649                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
650                         "%s (%u %s)",
651                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
652                         alen + aoff, (alen + aoff == 1) ? "byte" : "bytes");
653                 break;
654             case BGPTYPE_AGGREGATOR:
655                 if (alen != 6) 
656                     goto default_attribute_top;
657                 tvb_memcpy(tvb, ipaddr, o + i + aoff + 2, 4);
658                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
659                         "%s: AS: %u origin: %s (%u %s)",
660                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
661                         tvb_get_ntohs(tvb, o + i + aoff),
662                         ip_to_str(ipaddr), alen + aoff, 
663                         (alen + aoff == 1) ? "byte" : "bytes");
664                 break;
665             case BGPTYPE_COMMUNITIES:
666                 if (alen % 4 != 0)
667                     goto default_attribute_top;
668
669                 /* (o + i + 3) =
670                    (o + current attribute + 3 bytes to first tuple) */ 
671                 end = o + alen + i + 3;
672                 q = o + i + 3;
673                 /* must be freed by second switch!                          */
674                 /* "alen * 12" (5 digits, a :, 5 digits + space ) should be 
675                    a good estimate of how long the communities string could 
676                    be                                                       */
677                 communities_str = malloc((alen + 1) * 12);
678                 if (communities_str == NULL) break;
679                 communities_str[0] = '\0';
680                 memset(junk_buf, 0, sizeof(junk_buf)); 
681
682                 /* snarf each community */
683                 while (q < end) {
684                     /* check for well-known communities */
685                     if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT)
686                         strncpy(junk_buf, "NO_EXPORT ", 10);
687                     else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_ADVERTISE)
688                         strncpy(junk_buf, "NO_ADVERTISE ", 13);
689                     else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT_SUBCONFED)
690                         strncpy(junk_buf, "NO_EXPORT_SUBCONFED ", 20);
691                     else {
692                         snprintf(junk_buf, sizeof(junk_buf), "%u:%u ",
693                                 tvb_get_ntohs(tvb, q), 
694                                 tvb_get_ntohs(tvb, q + 2));
695                     }
696                     q += 4; 
697  
698                     strncat(communities_str, junk_buf, sizeof(junk_buf));
699                 }
700                 /* cleanup end of string */
701                 communities_str[strlen(communities_str) - 1] = '\0';
702
703                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
704                         "%s: %s (%u %s)",
705                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
706                         communities_str, alen + aoff,
707                         (alen + aoff == 1) ? "byte" : "bytes");
708                 break;
709             case BGPTYPE_ORIGINATOR_ID:
710                 if (alen != 4)
711                     goto default_attribute_top;
712                 tvb_memcpy(tvb, ipaddr, o + i + aoff, 4);
713                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
714                         "%s: %s (%u %s)",
715                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
716                         ip_to_str(ipaddr), alen + aoff, (alen + aoff == 1)
717                         ? "byte" : "bytes");
718                 break;
719             case BGPTYPE_CLUSTER_LIST:
720                 if (alen % 4 != 0)
721                     goto default_attribute_top;
722
723                 /* (o + i + 3) =
724                    (o + current attribute + 3 bytes to first tuple) */ 
725                 end = o + alen + i + 3;
726                 q = o + i + 3;
727                 /* must be freed by second switch!                          */
728                 /* "alen * 16" (12 digits, 3 dots + space ) should be 
729                    a good estimate of how long the cluster_list string could 
730                    be                                                       */
731                 cluster_list_str = malloc((alen + 1) * 16);
732                 if (cluster_list_str == NULL) break;
733                 cluster_list_str[0] = '\0';
734                 memset(junk_buf, 0, sizeof(junk_buf)); 
735
736                 /* snarf each cluster list */
737                 tvb_memcpy(tvb, ipaddr, q, 4);
738                 while (q < end) {
739                     snprintf(junk_buf, sizeof(junk_buf), "%s ", ip_to_str(ipaddr));
740                     strncat(cluster_list_str, junk_buf, sizeof(junk_buf));
741                     q += 4; 
742                 }
743                 /* cleanup end of string */
744                 cluster_list_str[strlen(cluster_list_str) - 1] = '\0';
745
746                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
747                         "%s: %s (%u %s)",
748                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
749                         cluster_list_str, alen + aoff,
750                         (alen + aoff == 1) ? "byte" : "bytes");
751                 break;
752             default:
753             default_attribute_top:
754                 ti = proto_tree_add_text(subtree, tvb, o + i, alen + aoff,
755                         "%s (%u %s)",
756                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
757                         alen + aoff, (alen + aoff == 1) ? "byte" : "bytes");
758             } /* end of first switch */
759             subtree2 = proto_item_add_subtree(ti, ett_bgp_attr);
760
761             /* figure out flags */
762             junk_buf[0] = '\0';
763             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_OPTIONAL) {
764                  strncat(junk_buf, "Optional, ", 10);
765             }
766             else {
767                  strncat(junk_buf, "Well-known, ", 12);
768             }
769             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_TRANSITIVE) {
770                  strncat(junk_buf, "Transitive, ", 12);
771             }
772             else {
773                  strncat(junk_buf, "Non-transitive, ", 16);
774             }
775             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_PARTIAL) {
776                  strncat(junk_buf, "Partial, ", 9);
777             }
778             else {
779                  strncat(junk_buf, "Complete, ", 10);
780             }
781             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) {
782                  strncat(junk_buf, "Extended Length, ", 17);
783             }
784             /* stomp last ", " */
785             j = strlen(junk_buf);
786             junk_buf[j - 2] = '\0';
787             ti = proto_tree_add_text(subtree2, tvb,
788                     o + i + offsetof(struct bgp_attr, bgpa_flags), 1,
789                     "Flags: 0x%02x (%s)", bgpa.bgpa_flags, junk_buf);
790             subtree3 = proto_item_add_subtree(ti, ett_bgp_attr_flags);
791
792             /* add flag bitfield subtrees */
793             proto_tree_add_text(subtree3, tvb,
794                     o + i + offsetof(struct bgp_attr, bgpa_flags), 1,
795                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
796                         BGP_ATTR_FLAG_OPTIONAL, 8, "Optional", "Well-known"));
797             proto_tree_add_text(subtree3, tvb,
798                     o + i + offsetof(struct bgp_attr, bgpa_flags), 1,
799                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
800                         BGP_ATTR_FLAG_TRANSITIVE, 8, "Transitive", 
801                         "Non-transitive"));
802             proto_tree_add_text(subtree3, tvb,
803                     o + i + offsetof(struct bgp_attr, bgpa_flags), 1,
804                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
805                         BGP_ATTR_FLAG_PARTIAL, 8, "Partial", "Complete"));
806             proto_tree_add_text(subtree3, tvb,
807                     o + i + offsetof(struct bgp_attr, bgpa_flags), 1,
808                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
809                         BGP_ATTR_FLAG_EXTENDED_LENGTH, 8, "Extended length", 
810                         "Regular length"));
811
812             proto_tree_add_text(subtree2, tvb,
813                     o + i + offsetof(struct bgp_attr, bgpa_type), 1,
814                     "Type code: %s (%u)",
815                     val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
816                     bgpa.bgpa_type);
817             
818             proto_tree_add_text(subtree2, tvb, o + i + sizeof(bgpa), 
819                     aoff - sizeof(bgpa), "Length: %d %s", alen, 
820                     (alen == 1) ? "byte" : "bytes");
821
822             /* the second switch prints things in the actual subtree of each 
823                attribute                                                     */ 
824             switch (bgpa.bgpa_type) {
825             case BGPTYPE_ORIGIN:
826                 if (alen != 1) {
827                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
828                             "Origin (invalid): %u %s", alen,
829                              (alen == 1) ? "byte" : "bytes");
830                 } else {
831                     msg = val_to_str(tvb_get_guint8(tvb, o + i + aoff), bgpattr_origin, "Unknown");
832                     proto_tree_add_text(subtree2, tvb, o + i + aoff, 1,
833                             "Origin: %s (%u)", msg, tvb_get_guint8(tvb, o + i + aoff));
834                 }
835                 break;
836             case BGPTYPE_AS_PATH:
837                 /* check for empty AS_PATH */
838                 if (alen == 0) {
839                     free(as_path_str);
840                     break;
841                 }
842
843                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
844                         "AS path: %s", as_path_str);
845                 as_paths_tree = proto_item_add_subtree(ti, ett_bgp_as_paths);
846
847                 /* (o + i + 3) =
848                    (o + current attribute + 3 bytes to first tuple) */ 
849                 end = o + alen + i + 3;
850                 q = o + i + 3;
851    
852                 /* snarf each AS path tuple, we have to step through each one
853                    again to make a separate subtree so we can't just reuse
854                    as_path_str from above */
855                 while (q < end) {
856                     as_path_str[0] = '\0';
857                     type = tvb_get_guint8(tvb, q++);
858                     if (type == AS_SET) {
859                         snprintf(as_path_str, 2, "{");
860                     }
861                     else if (type == AS_CONFED_SET) {
862                         snprintf(as_path_str, 2, "[");
863                     }
864                     else if (type == AS_CONFED_SEQUENCE) {
865                         snprintf(as_path_str, 2, "(");
866                     }
867                     length = tvb_get_guint8(tvb, q++);
868
869                     /* snarf each value in path, we're just going to reuse 
870                        as_path_str since we already have it malloced       */
871                     for (j = 0; j < length; j++) {
872                         snprintf(junk_buf, sizeof(junk_buf), "%u%s", tvb_get_ntohs(tvb, q),
873                                 (type == AS_SET || type == AS_CONFED_SET) 
874                                 ? ", " : " ");
875                         strncat(as_path_str, junk_buf, sizeof(junk_buf));
876                         q += 2;
877                     }
878
879                     /* cleanup end of string */
880                     if (type == AS_SET) {
881                         as_path_str[strlen(as_path_str) - 2] = '}';
882                     }
883                     else if (type == AS_CONFED_SET) {
884                         as_path_str[strlen(as_path_str) - 2] = ']';
885                     }
886                     else if (type == AS_CONFED_SEQUENCE) {
887                         as_path_str[strlen(as_path_str) - 1] = ')';
888                     }
889                     else {
890                         as_path_str[strlen(as_path_str) - 1] = '\0';
891                     }
892
893                     /* length here means number of ASs, ie length * 2 bytes */
894                     ti = proto_tree_add_text(as_paths_tree, tvb, 
895                             q - length * 2 - 2,
896                             length * 2 + 2, "AS path segment: %s", as_path_str);
897                     as_path_tree = proto_item_add_subtree(ti, ett_bgp_as_paths);
898                     proto_tree_add_text(as_path_tree, tvb, q - length * 2 - 2,
899                             1, "Path segment type: %s (%u)",
900                             val_to_str(type, as_segment_type, "Unknown"), type);
901                     proto_tree_add_text(as_path_tree, tvb, q - length * 2 - 1, 
902                             1, "Path segment length: %u %s", length,
903                             (length == 1) ? "AS" : "ASs");
904
905                     /* backup and reprint path segment value(s) only */
906                     q -= 2 * length;
907                     as_path_str[0] = '\0';
908                     for (j = 0; j < length; j++) {
909                         snprintf(junk_buf, sizeof(junk_buf), "%u ", tvb_get_ntohs(tvb, q));
910                         strncat(as_path_str, junk_buf, sizeof(junk_buf));
911                         q += 2;
912                     }
913                     as_path_str[strlen(as_path_str) - 1] = '\0';
914
915                     proto_tree_add_text(as_path_tree, tvb, q - length * 2, 
916                             length * 2, "Path segment value: %s", as_path_str);
917                 }
918
919                 free(as_path_str);
920                 break;
921             case BGPTYPE_NEXT_HOP:
922                 if (alen != 4) {
923                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
924                             "Next hop (invalid): %u %s", alen,
925                             (alen == 1) ? "byte" : "bytes");
926                 } else {
927                     tvb_memcpy(tvb, ipaddr, o + i + aoff, 4);
928                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
929                             "Next hop: %s", ip_to_str(ipaddr));
930                 }
931                 break;
932             case BGPTYPE_MULTI_EXIT_DISC:
933                 if (alen != 4) {
934                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
935                             "Multiple exit discriminator (invalid): %u %s",
936                             alen, (alen == 1) ? "byte" : "bytes");
937                 } else {
938                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
939                             "Multiple exit discriminator: %u",
940                             tvb_get_ntohl(tvb, o + i + aoff));
941                 }
942                 break;
943             case BGPTYPE_LOCAL_PREF:
944                 if (alen != 4) {
945                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
946                             "Local preference (invalid): %u %s", alen,
947                              (alen == 1) ? "byte" : "bytes");
948                 } else {
949                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
950                             "Local preference: %u", tvb_get_ntohl(tvb, o + i + aoff));
951                 }
952                 break;
953             case BGPTYPE_ATOMIC_AGGREGATE:
954                 if (alen != 0) {
955                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
956                             "Atomic aggregate (invalid): %u %s", alen,
957                             (alen == 1) ? "byte" : "bytes");    
958                 }
959                 break;
960             case BGPTYPE_AGGREGATOR:
961                 if (alen != 6) {
962                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
963                             "Aggregator (invalid): %u %s", alen,
964                             (alen == 1) ? "byte" : "bytes");
965                 } else {
966                     proto_tree_add_text(subtree2, tvb, o + i + aoff, 2,
967                             "Aggregator AS: %u", tvb_get_ntohs(tvb, o + i + aoff));
968                     tvb_memcpy(tvb, ipaddr, o + i + aoff + 2, 4);
969                     proto_tree_add_text(subtree2, tvb, o + i + aoff + 2, 4,
970                             "Aggregator origin: %s",
971                             ip_to_str(ipaddr));
972                 }
973                 break;
974             case BGPTYPE_COMMUNITIES:
975                 if (alen % 4 != 0) {
976                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen, 
977                             "Communities (invalid): %u %s", alen,
978                             (alen == 1) ? "byte" : "bytes");
979                     free(communities_str);
980                     break;
981                 }
982
983                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
984                         "Communities: %s", communities_str);
985                 communities_tree = proto_item_add_subtree(ti, 
986                         ett_bgp_communities);
987
988                 /* (o + i + 3) =
989                    (o + current attribute + 3 bytes to first tuple) */
990                 end = o + alen + i + 3;
991                 q = o + i + 3;
992
993                 /* snarf each community */
994                 while (q < end) {
995                     /* check for reserved values */
996                     if (tvb_get_ntohs(tvb, q) == FOURHEX0 || tvb_get_ntohs(tvb, q) == FOURHEXF) {
997                         /* check for well-known communities */
998                         if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT)
999                             proto_tree_add_text(communities_tree, tvb, 
1000                                    q - 3 + aoff, 4, 
1001                                    "Community: NO_EXPORT (0x%x)", tvb_get_ntohl(tvb, q));
1002                         else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_ADVERTISE)
1003                             proto_tree_add_text(communities_tree, tvb, 
1004                                    q - 3 + aoff, 4, 
1005                                    "Community: NO_ADVERTISE (0x%x)", pntohl(q));
1006                         else if (tvb_get_ntohl(tvb, q) == BGP_COMM_NO_EXPORT_SUBCONFED)
1007                             proto_tree_add_text(communities_tree, tvb, 
1008                                     q - 3 + aoff, 4, 
1009                                     "Community: NO_EXPORT_SUBCONFED (0x%x)",
1010                                     tvb_get_ntohl(tvb, q));
1011                         else
1012                             proto_tree_add_text(communities_tree, tvb, 
1013                                     q - 3 + aoff, 4, 
1014                                     "Community (reserved): 0x%x", tvb_get_ntohl(tvb, q));
1015                     }
1016                     else {
1017
1018                         ti = proto_tree_add_text(communities_tree, tvb,
1019                                 q - 3 + aoff, 4, "Community: %u:%u", 
1020                                 tvb_get_ntohs(tvb, q), tvb_get_ntohs(tvb, q + 2));
1021                         community_tree = proto_item_add_subtree(ti, 
1022                             ett_bgp_communities);
1023                         proto_tree_add_text(community_tree, tvb, q - 3 + aoff,
1024                                 2, "Community AS: %u", tvb_get_ntohs(tvb, q));
1025                         proto_tree_add_text(community_tree, tvb, q - 1 + aoff, 
1026                                 2, "Community value: %u", tvb_get_ntohs(tvb, q + 2));
1027                     }
1028
1029                     q += 4;
1030                 }
1031
1032                 free(communities_str);
1033                 break;
1034             case BGPTYPE_ORIGINATOR_ID:
1035                 if (alen != 4) {
1036                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
1037                             "Originator identifier (invalid): %u %s", alen,
1038                             (alen == 1) ? "byte" : "bytes");
1039                 } else {
1040                     tvb_memcpy(tvb, ipaddr, o + i + aoff, 4);
1041                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
1042                             "Originator identifier: %s",
1043                             ip_to_str(ipaddr));
1044                 }
1045                 break;
1046             case BGPTYPE_MP_REACH_NLRI:
1047                 af = tvb_get_ntohs(tvb, o + i + aoff);
1048                 proto_tree_add_text(subtree2, tvb, o + i + aoff, 2,
1049                     "Address family: %s (%u)",
1050                     val_to_str(af, afnumber, "Unknown"), af);
1051                 proto_tree_add_text(subtree2, tvb, o + i + aoff + 2, 1,
1052                     "Subsequent address family identifier: %s (%u)",
1053                     val_to_str(tvb_get_guint8(tvb, o + i + aoff + 2), bgpattr_nlri_safi,
1054                         tvb_get_guint8(tvb, o + i + aoff + 2) >= 128 ? "Vendor specific" : "Unknown"),
1055                     tvb_get_guint8(tvb, o + i + aoff + 2));
1056                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff + 3, 1,
1057                         "Next hop network address (%d %s)",
1058                         tvb_get_guint8(tvb, o + i + aoff + 3),
1059                         (tvb_get_guint8(tvb, o + i + aoff + 3) == 1) ? "byte" : "bytes");
1060                 if (af == AFNUM_INET || af == AFNUM_INET6) {
1061                     int j, advance;
1062                     const char *s;
1063
1064                     subtree3 = proto_item_add_subtree(ti, 
1065                             ett_bgp_mp_reach_nlri);
1066
1067                     j = 0;
1068                     while (j < tvb_get_guint8(tvb, o + i + aoff + 3)) {
1069                         if (af == AFNUM_INET)
1070                             advance = 4;
1071                         else if (af == AFNUM_INET6)
1072                             advance = 16;
1073                         else
1074                             break;
1075                         if (j + advance > tvb_get_guint8(tvb, o + i + aoff + 3))
1076                             break;
1077
1078                         if (af == AFNUM_INET) {
1079                             tvb_memcpy(tvb, ipaddr, o + i + aoff + 4 + j, 4);
1080                             s = ip_to_str(ipaddr);
1081                         } else {
1082                             tvb_memcpy(tvb, ip6addr.u6_addr.u6_addr8,
1083                                        o + i + aoff + 4 + j, sizeof ip6addr);
1084                             s = ip6_to_str(&ip6addr);
1085                         }
1086                         proto_tree_add_text(subtree3, tvb,
1087                             o + i + aoff + 4 + j, advance,
1088                             "Next hop: %s", s);
1089                         j += advance;
1090                     }
1091                 }
1092
1093                 alen -= tvb_get_guint8(tvb, o + i + aoff + 3) + 4;
1094                 aoff += tvb_get_guint8(tvb, o + i + aoff + 3) + 4;
1095                 off = 0;
1096                 snpa = tvb_get_guint8(tvb, o + i + aoff);
1097                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff, 1,
1098                         "Subnetwork points of attachment: %u", snpa);
1099                 off++;
1100                 if (snpa)
1101                     subtree3 = proto_item_add_subtree(ti, 
1102                             ett_bgp_mp_reach_nlri);
1103                 for (/*nothing*/; snpa > 0; snpa--) {
1104                     proto_tree_add_text(subtree3, tvb, o + i + aoff + off, 1,
1105                         "SNPA length: %u", tvb_get_guint8(tvb, o + i + aoff + off));
1106                     off++;
1107                     proto_tree_add_text(subtree3, tvb, o + i + aoff + off,
1108                         tvb_get_guint8(tvb, o + i + aoff + off - 1),
1109                         "SNPA (%u %s)", tvb_get_guint8(tvb, o + i + aoff + off - 1),
1110                         (tvb_get_guint8(tvb, o + i + aoff + off - 1) == 1) ? "byte" : "bytes");
1111                     off += tvb_get_guint8(tvb, o + i + aoff + off - 1);
1112                 }
1113
1114                 alen -= off;
1115                 aoff += off;
1116                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
1117                         "Network layer reachability information (%u %s)",
1118                         alen, (alen == 1) ? "byte" : "bytes");
1119                 if (alen)
1120                     subtree3 = proto_item_add_subtree(ti, 
1121                             ett_bgp_mp_unreach_nlri);
1122                 while (alen > 0) {
1123                     int advance;
1124                     char buf[256];
1125
1126                     if (af == AFNUM_INET) {
1127                         advance = decode_prefix4(tvb, o + i + aoff, buf,
1128                             sizeof(buf));
1129                     } else if (af == AFNUM_INET6) {
1130                         advance = decode_prefix6(tvb, o + i + aoff, buf,
1131                             sizeof(buf));
1132                     } else
1133                         break;
1134                     if (advance < 0)
1135                         break;
1136                     if (alen < advance)
1137                         break;
1138                     proto_tree_add_text(subtree3, tvb, o + i + aoff, advance,
1139                         "Network layer reachability information: %s", buf);
1140
1141                     alen -= advance;
1142                     aoff += advance;
1143                 }
1144
1145                 break;
1146             case BGPTYPE_MP_UNREACH_NLRI:
1147                 af = tvb_get_ntohs(tvb, o + i + aoff);  
1148                 proto_tree_add_text(subtree2, tvb, o + i + aoff, 2,
1149                     "Address family: %s (%u)",
1150                     val_to_str(af, afnumber, "Unknown"), af);
1151                 proto_tree_add_text(subtree2, tvb, o + i + aoff + 2, 1,
1152                     "Subsequent address family identifier: %s (%u)",
1153                     val_to_str(tvb_get_guint8(tvb, o + i + aoff + 2), bgpattr_nlri_safi,
1154                         tvb_get_guint8(tvb, o + i + aoff + 2) >= 128 ? "Vendor specific" : "Unknown"),
1155                     tvb_get_guint8(tvb, o + i + aoff + 2));
1156                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff + 3,
1157                         alen - 3, "Withdrawn routes (%u %s)", alen - 3,
1158                         (alen - 3 == 1) ? "byte" : "bytes");
1159
1160                 alen -= 3;
1161                 aoff += 3;
1162                 if (alen > 0)
1163                     subtree3 = proto_item_add_subtree(ti, 
1164                             ett_bgp_mp_unreach_nlri);
1165                 while (alen > 0) {
1166                     int advance;
1167                     char buf[256];
1168
1169                     if (af == AFNUM_INET) {
1170                         advance = decode_prefix4(tvb, o + i + aoff, buf,
1171                             sizeof(buf));
1172                     } else if (af == AFNUM_INET6) {
1173                         advance = decode_prefix6(tvb, o + i + aoff, buf,
1174                             sizeof(buf));
1175                     } else
1176                         break;
1177                     if (advance < 0)
1178                         break;
1179                     if (alen < advance)
1180                         break;
1181                     proto_tree_add_text(subtree3, tvb, o + i + aoff, advance,
1182                         "Withdrawn route: %s", buf);
1183
1184                     alen -= advance;
1185                     aoff += advance;
1186                 }
1187
1188                 break;
1189             case BGPTYPE_CLUSTER_LIST:
1190                 if (alen % 4 != 0) {
1191                     proto_tree_add_text(subtree2, tvb, o + i + aoff, alen, 
1192                             "Cluster list (invalid): %u %s", alen,
1193                             (alen == 1) ? "byte" : "bytes");
1194                     free(cluster_list_str);
1195                     break;
1196                 }
1197
1198                 ti = proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
1199                         "Cluster list: %s", cluster_list_str);
1200                 cluster_list_tree = proto_item_add_subtree(ti, 
1201                         ett_bgp_cluster_list);
1202
1203                 /* (p + i + 3) =
1204                    (p + current attribute + 3 bytes to first tuple) */
1205                 end = o + alen + i + 3;
1206                 q = o + i + 3;
1207
1208                 /* snarf each cluster identifier */
1209                 while (q < end) {
1210                     tvb_memcpy(tvb, ipaddr, q, 4);
1211                     ti = proto_tree_add_text(cluster_list_tree, tvb,
1212                             q - 3 + aoff, 4, "Cluster identifier: %s", 
1213                             ip_to_str(ipaddr));
1214
1215                     q += 4;
1216                 }
1217
1218                 free(cluster_list_str);
1219                 break;
1220             default:
1221                 proto_tree_add_text(subtree2, tvb, o + i + aoff, alen,
1222                         "Unknown (%d %s)", alen, (alen == 1) ? "byte" : 
1223                         "bytes");
1224                 break;
1225             } /* end of second switch */
1226
1227             i += alen + aoff;
1228         }
1229         o += 2 + len;
1230
1231         /* NLRI */
1232         len = offset + hlen - o;
1233
1234         /* parse prefixes */
1235         if (len > 0) {
1236            ti = proto_tree_add_text(tree, tvb, o, len,
1237                    "Network layer reachability information: %u %s", len,
1238                    (len == 1) ? "byte" : "bytes");
1239             subtree = proto_item_add_subtree(ti, ett_bgp_nlri);
1240             end = o + len;
1241             while (o < end) {
1242                 i = decode_prefix4(tvb, o, junk_buf, sizeof(junk_buf));
1243                 proto_tree_add_text(subtree, tvb, o, i, "%s", junk_buf);
1244                 o += i;
1245             }
1246         }
1247     }
1248 }
1249
1250 /*
1251  * Dissect a BGP NOTIFICATION message.
1252  */
1253 static void
1254 dissect_bgp_notification(tvbuff_t *tvb, int offset, proto_tree *tree)
1255 {
1256     struct bgp_notification bgpn;   /* BGP NOTIFICATION message */
1257     int                     hlen;   /* message length           */
1258     char                    *p;     /* string pointer           */
1259
1260     /* snarf message */
1261     tvb_memcpy(tvb, bgpn.bgpn_marker, offset, BGP_MIN_NOTIFICATION_MSG_SIZE);
1262     hlen = ntohs(bgpn.bgpn_len);
1263
1264     /* print error code */
1265     proto_tree_add_text(tree, tvb,
1266         offset + offsetof(struct bgp_notification, bgpn_major), 1,
1267         "Error code: %s (%u)",
1268         val_to_str(bgpn.bgpn_major, bgpnotify_major, "Unknown"),
1269         bgpn.bgpn_major);
1270
1271     /* print error subcode */
1272     if (bgpn.bgpn_major < array_length(bgpnotify_minor)
1273      && bgpnotify_minor[bgpn.bgpn_major] != NULL) {
1274         p = val_to_str(bgpn.bgpn_minor, bgpnotify_minor[bgpn.bgpn_major],
1275             "Unknown");
1276     } else if (bgpn.bgpn_minor == 0)
1277         p = "Unspecified";
1278     else
1279         p = "Unknown";
1280     proto_tree_add_text(tree, tvb,
1281         offset + offsetof(struct bgp_notification, bgpn_minor), 1,
1282         "Error subcode: %s (%u)", p, bgpn.bgpn_minor);
1283
1284     /* only print if there is optional data */
1285     if (hlen > BGP_MIN_NOTIFICATION_MSG_SIZE) {
1286         proto_tree_add_text(tree, tvb, offset + BGP_MIN_NOTIFICATION_MSG_SIZE,
1287             hlen - BGP_MIN_NOTIFICATION_MSG_SIZE, "Data");
1288     }
1289 }
1290
1291 /*
1292  * Dissect a BGP ROUTE-REFRESH message.
1293  */
1294 static void
1295 dissect_bgp_route_refresh(tvbuff_t *tvb, int offset, proto_tree *tree)
1296 {
1297     u_int        i;    /* tmp            */
1298
1299     /* AFI */
1300     i = tvb_get_ntohs(tvb, offset + BGP_HEADER_SIZE);
1301     proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE, 2, 
1302                         "Address family identifier: %s (%u)",
1303                         val_to_str(i, afnumber, "Unknown"), i);
1304     offset += 2;
1305     /* Reserved */
1306     proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE + 2, 1, 
1307                         "Reserved: 1 byte");
1308     offset++;
1309     /* SAFI */
1310     i = tvb_get_guint8(tvb, offset);
1311     proto_tree_add_text(tree, tvb, offset + BGP_HEADER_SIZE + 3, 1, 
1312                         "Subsequent address family identifier: %s (%u)",
1313                         val_to_str(i, bgpattr_nlri_safi,
1314                         i >= 128 ? "Vendor specific" : "Unknown"), 
1315                         i);
1316 }
1317
1318 /*
1319  * Dissect a BGP packet.
1320  */
1321 static void
1322 dissect_bgp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1323 {
1324     proto_item    *ti;           /* tree item                        */
1325     proto_tree    *bgp_tree;     /* BGP packet tree                  */
1326     proto_tree    *bgp1_tree;    /* BGP message tree                 */
1327     int           l, i;          /* tmp                              */
1328     int           found;         /* number of BGP messages in packet */
1329     static u_char marker[] = {   /* BGP message marker               */
1330         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1331         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1332     };
1333     struct bgp    bgp;           /* BGP header                       */
1334     int           hlen;          /* BGP header length                */
1335     char          *typ;          /* BGP message type                 */
1336
1337     pinfo->current_proto = "BGP";
1338     if (check_col(pinfo->fd, COL_PROTOCOL))
1339         col_set_str(pinfo->fd, COL_PROTOCOL, "BGP");
1340     if (check_col(pinfo->fd, COL_INFO))
1341         col_clear(pinfo->fd, COL_INFO);
1342
1343     l = tvb_length(tvb);
1344     i = 0;
1345     found = -1;
1346     /* run through the TCP packet looking for BGP headers         */
1347     /* this is done twice, but this way each message type can be 
1348        printed in the COL_INFO field                              */
1349     while (i + BGP_HEADER_SIZE <= l) {
1350         tvb_memcpy(tvb, bgp.bgp_marker, i, BGP_HEADER_SIZE);
1351
1352         /* look for bgp header */
1353         if (memcmp(bgp.bgp_marker, marker, sizeof(marker)) != 0) {
1354             i++;
1355             continue;
1356         }
1357
1358         found++;
1359         hlen = ntohs(bgp.bgp_len);
1360         typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
1361
1362         if (check_col(pinfo->fd, COL_INFO)) {
1363             if (found == 0) 
1364                 col_add_fstr(pinfo->fd, COL_INFO, "%s", typ);
1365             else
1366                 col_append_fstr(pinfo->fd, COL_INFO, ", %s", typ);
1367         }
1368
1369         i += hlen;
1370     }
1371
1372     if (tree) {
1373         ti = proto_tree_add_item(tree, proto_bgp, tvb, 0, 
1374                                  l, FALSE);
1375         bgp_tree = proto_item_add_subtree(ti, ett_bgp);
1376
1377         i = 0;
1378         /* now, run through the TCP packet again, this time dissect */
1379         /* each message that we find */
1380         while (i + BGP_HEADER_SIZE <= l) {
1381             tvb_memcpy(tvb, bgp.bgp_marker, i, BGP_HEADER_SIZE);
1382
1383             /* look for bgp header */
1384             if (memcmp(bgp.bgp_marker, marker, sizeof(marker)) != 0) {
1385                 i++;
1386                 continue;
1387             }
1388
1389             hlen = ntohs(bgp.bgp_len);
1390             typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
1391             if (l < hlen) {
1392                 ti = proto_tree_add_text(bgp_tree, tvb, i, 
1393                      l, "%s (truncated)", typ);
1394             } else {
1395                 ti = proto_tree_add_text(bgp_tree, tvb, i, hlen,
1396                             "%s", typ);
1397             }
1398             /* add a different tree for each message type */
1399             switch (bgp.bgp_type) {
1400             case BGP_OPEN:
1401                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_open);
1402                 break;
1403             case BGP_UPDATE:
1404                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_update);
1405                 break;
1406             case BGP_NOTIFICATION:
1407                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_notification);
1408                 break;
1409             case BGP_KEEPALIVE:
1410                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp);
1411                 break;
1412             case BGP_ROUTE_REFRESH:
1413                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_route_refresh);
1414                 break;
1415             default:
1416                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp);
1417                 break;
1418             }
1419
1420             proto_tree_add_text(bgp1_tree, tvb, i, BGP_MARKER_SIZE,
1421                 "Marker: 16 bytes");
1422                             
1423             if (hlen < BGP_HEADER_SIZE || hlen > BGP_MAX_PACKET_SIZE) {
1424                 proto_tree_add_text(bgp1_tree, tvb,
1425                     i + offsetof(struct bgp, bgp_len), 2,
1426                     "Length (invalid): %u %s", hlen, 
1427                     (hlen == 1) ? "byte" : "bytes");
1428             } else {
1429                 proto_tree_add_text(bgp1_tree, tvb,
1430                     i + offsetof(struct bgp, bgp_len), 2,
1431                     "Length: %u %s", hlen, 
1432                     (hlen == 1) ? "byte" : "bytes");
1433             }
1434
1435             proto_tree_add_uint_format(bgp1_tree, hf_bgp_type, tvb, 
1436                                        i + offsetof(struct bgp, bgp_type), 1,
1437                                        bgp.bgp_type,
1438                                        "Type: %s (%u)", typ, bgp.bgp_type);
1439
1440             switch (bgp.bgp_type) {
1441             case BGP_OPEN:
1442                 dissect_bgp_open(tvb, i, bgp1_tree);
1443                 break;
1444             case BGP_UPDATE:
1445                 dissect_bgp_update(tvb, i, bgp1_tree);
1446                 break;
1447             case BGP_NOTIFICATION:
1448                 dissect_bgp_notification(tvb, i, bgp1_tree);
1449                 break;
1450             case BGP_KEEPALIVE:
1451                 /* no data in KEEPALIVE messages */
1452                 break;
1453             case BGP_ROUTE_REFRESH:
1454                 dissect_bgp_route_refresh(tvb, i, bgp1_tree);
1455                 break;
1456             default:
1457                 break;
1458             }
1459
1460             i += hlen;
1461         }
1462     }
1463 }
1464
1465 /*
1466  * Register ourselves.
1467  */
1468 void
1469 proto_register_bgp(void)
1470 {
1471
1472     static hf_register_info hf[] = {
1473       { &hf_bgp_type,
1474         { "BGP message type", "bgp.type", FT_UINT8, BASE_HEX, 
1475           VALS(bgptypevals), 0x0, "BGP message type" }},
1476     };
1477
1478     static gint *ett[] = {
1479       &ett_bgp,
1480       &ett_bgp_unfeas,
1481       &ett_bgp_attrs,
1482       &ett_bgp_attr,
1483       &ett_bgp_attr_flags,
1484       &ett_bgp_mp_reach_nlri,
1485       &ett_bgp_mp_unreach_nlri,
1486       &ett_bgp_nlri,
1487       &ett_bgp_open,
1488       &ett_bgp_update,
1489       &ett_bgp_notification,
1490       &ett_bgp_route_refresh,
1491       &ett_bgp_as_paths,
1492       &ett_bgp_communities,
1493       &ett_bgp_cluster_list,
1494       &ett_bgp_options,
1495       &ett_bgp_option,
1496     };
1497
1498     proto_bgp = proto_register_protocol("Border Gateway Protocol",
1499                                         "BGP", "bgp");
1500     proto_register_field_array(proto_bgp, hf, array_length(hf));
1501     proto_register_subtree_array(ett, array_length(ett));
1502 }
1503
1504 void
1505 proto_reg_handoff_bgp(void)
1506 {
1507     dissector_add("tcp.port", BGP_TCP_PORT, dissect_bgp, proto_bgp);
1508 }