additions to tns dissector - sns and connect started
[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
6  * 
7  * Supports:
8  * RFC1771 A Border Gateway Protocol 4 (BGP-4)
9  * RFC1997 BGP Communities Attribute
10  * RFC2283 Multiprotocol Extensions for BGP-4
11  *
12  * TODO:
13  * RFC1966 BGP Route Reflection An alternative to full mesh IBGP
14  * RFC1863 A BGP/IDRP Route Server alternative to a full mesh routing 
15  * RFC1965 Autonomous System Confederations for BGP 
16  * Destination Preference Attribute for BGP (work in progress)
17  *
18  * Ethereal - Network traffic analyzer
19  * By Gerald Combs <gerald@unicom.net>
20  * Copyright 1998 Gerald Combs
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  * 
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  * 
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #include <stdio.h>
42 #include <stdlib.h>
43
44 #ifdef HAVE_SYS_TYPES_H
45 # include <sys/types.h>
46 #endif
47
48 #ifdef HAVE_NETINET_IN_H
49 # include <netinet/in.h>
50 #endif
51
52 #ifdef NEED_SNPRINTF_H
53 # ifdef HAVE_STDARG_H
54 #  include <stdarg.h>
55 # else
56 #  include <varargs.h>
57 # endif
58 # include "snprintf.h"
59 #endif
60
61 #include <string.h>
62 #include <glib.h>
63 #include "packet.h"
64 #include "packet-bgp.h"
65 #include "packet-ipv6.h"
66 #include <arpa/inet.h>
67
68 static const value_string bgptypevals[] = {
69     { BGP_OPEN, "OPEN Message" },
70     { BGP_UPDATE, "UPDATE Message" },
71     { BGP_NOTIFICATION, "NOTIFICATION Message" },
72     { BGP_KEEPALIVE, "KEEPALIVE Message" },
73     { 0, NULL },
74 };
75
76 static const value_string bgpnotify_major[] = {
77     { 1, "Message Header Error" },
78     { 2, "OPEN Message Error" },
79     { 3, "UPDATE Message Error" },
80     { 4, "Hold Timer Expired" },
81     { 5, "Finite State Machine Error" },
82     { 6, "Cease" },
83     { 0, NULL },
84 };
85
86 static const value_string bgpnotify_minor_1[] = {
87     { 1, "Connection Not Synchronized" },
88     { 2, "Bad Message Length" },
89     { 3, "Bad Message Type" },
90     { 0, NULL },
91 };
92
93 static const value_string bgpnotify_minor_2[] = {
94     { 1, "Unsupported Version Number" },
95     { 2, "Bad Peer AS" },
96     { 3, "Bad BGP Identifier" },
97     { 4, "Unsupported Optional Parameter" },
98     { 5, "Authentication Failure" },
99     { 6, "Unacceptable Hold Time" },
100     { 0, NULL },
101 };
102
103 static const value_string bgpnotify_minor_3[] = {
104     { 1, "Malformed Attribute List" },
105     { 2, "Unrecognized Well-known Attribute" },
106     { 3, "Missing Well-known Attribute" },
107     { 4, "Attribute Flags Error" },
108     { 5, "Attribute Length Error" },
109     { 6, "Invalid ORIGIN Attribute" },
110     { 7, "AS Routing Loop" },
111     { 8, "Invalid NEXT_HOP Attribute" },
112     { 9, "Optional Attribute Error" },
113     { 10, "Invalid Network Field" },
114     { 11, "Malformed AS_PATH" },
115     { 0, NULL },
116 };
117
118 static const value_string *bgpnotify_minor[] = {
119     NULL, bgpnotify_minor_1, bgpnotify_minor_2, bgpnotify_minor_3,
120 };
121
122 static const value_string bgpattr_origin[] = {
123     { 0, "IGP" },
124     { 1, "EGP" },
125     { 2, "INCOMPLETE" },
126     { 0, NULL },
127 };
128
129 static const value_string as_segment_type[] = {
130     { 1, "AS_SET" },
131     { 2, "AS_SEQUENCE" },
132     { 3, "AS_CONFED_SET" },
133     { 4, "AS_CONFED_SEQUENCE" },
134     { 0, NULL },
135 };
136
137 static const value_string bgpattr_type[] = {
138     { BGPTYPE_ORIGIN, "ORIGIN" },
139     { BGPTYPE_AS_PATH, "AS_PATH" },
140     { BGPTYPE_NEXT_HOP, "NEXT_HOP" },
141     { BGPTYPE_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" },
142     { BGPTYPE_LOCAL_PREF, "LOCAL_PREF" },
143     { BGPTYPE_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" },
144     { BGPTYPE_AGGREGATOR, "AGGREGATOR" },
145     { BGPTYPE_COMMUNITIES, "COMMUNITIES" },
146     { BGPTYPE_ORIGINATOR_ID, "ORIGINATOR_ID" },
147     { BGPTYPE_CLUSTER_LIST, "CLUSTER_LIST" },
148     { BGPTYPE_MP_REACH_NLRI, "MP_REACH_NLRI" },
149     { BGPTYPE_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" },
150     { 0, NULL },
151 };
152
153 /* Subsequent address family identifier, RFC2283 section 7 */
154 static const value_string bgpattr_nlri_safi[] = {
155     { 0, "Reserved" },
156     { 1, "Unicast" },
157     { 2, "Multicast" },
158     { 3, "Unicast+Multicast" },
159     { 0, NULL },
160 };
161
162 static const value_string afnumber[] = {
163     { 0, "Reserved" },
164     { AFNUM_INET, "IPv4" },
165     { AFNUM_INET6, "IPv6" },
166     { AFNUM_NSAP, "NSAP" },
167     { AFNUM_HDLC, "HDLC" },
168     { AFNUM_BBN1822, "BBN 1822" },
169     { AFNUM_802, "802" },
170     { AFNUM_E163, "E.163" },
171     { AFNUM_E164, "E.164" },
172     { AFNUM_F69, "F.69" },
173     { AFNUM_X121, "X.121" },
174     { AFNUM_IPX, "IPX" },
175     { AFNUM_ATALK, "Appletalk" },
176     { AFNUM_DECNET, "Decnet IV" },
177     { AFNUM_BANYAN, "Banyan Vines" },
178     { AFNUM_E164NSAP, "E.164 with NSAP subaddress" },
179     { 65535, "Reserved" },
180     { 0, NULL },
181 };
182
183 static int proto_bgp = -1;
184
185 static gint ett_bgp = -1;
186 static gint ett_bgp_unfeas = -1;
187 static gint ett_bgp_attrs = -1;
188 static gint ett_bgp_attr = -1;
189 static gint ett_bgp_attr_flags = -1;
190 static gint ett_bgp_mp_reach_nlri = -1;
191 static gint ett_bgp_mp_unreach_nlri = -1;
192 static gint ett_bgp_nlri = -1;
193 static gint ett_bgp_open = -1;
194 static gint ett_bgp_update = -1;
195 static gint ett_bgp_notification = -1;
196 static gint ett_bgp_as_paths = -1;
197
198 /*
199  * Decode an IPv4 prefix.
200  */
201 static int
202 decode_prefix4(const u_char *pd, char *buf, int buflen)
203 {
204     guint8 addr[4];   /* IP address                         */
205     int    plen;      /* prefix length                      */
206     int    length;    /* number of octets needed for prefix */
207
208     /* snarf length */
209     plen = pd[0];
210     if (plen < 0 || 32 < plen)
211         return -1;
212     length = (plen + 7) / 8;
213
214     /* snarf prefix */
215     memset(addr, 0, sizeof(addr));
216     memcpy(addr, &pd[1], length);
217     if (plen % 8)
218         addr[length - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
219
220     /* hand back a formatted string */
221     snprintf(buf, buflen, "%s/%d", ip_to_str(addr), plen);
222     return(1 + length);
223 }
224
225 /*
226  * Decode an IPv6 prefix.
227  */
228 static int
229 decode_prefix6(const u_char *pd, char *buf, int buflen)
230 {
231     struct e_in6_addr addr;     /* IPv6 address                       */
232     int               plen;     /* prefix length                      */
233     int               length;   /* number of octets needed for prefix */
234
235     /* snarf length */
236     plen = pd[0];
237     if (plen < 0 || 128 < plen)
238         return -1;
239     length = (plen + 7) / 8;
240
241     /* snarf prefix */
242     memset(&addr, 0, sizeof(addr));
243     memcpy(&addr, &pd[1], length);
244     if (plen % 8)
245         addr.s6_addr[length - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
246
247     /* hand back a formatted string */
248     snprintf(buf, buflen, "%s/%d", ip6_to_str(&addr), plen);
249     return(1 + length);
250 }
251
252 /*
253  * Dissect a BGP OPEN message.
254  */
255 static void
256 dissect_bgp_open(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
257 {
258     struct bgp_open bgpo;   /* BGP OPEN message   */
259     int             hlen;   /* message length     */
260
261     /* snarf OPEN message */
262     memcpy(&bgpo, &pd[offset], sizeof(bgpo));
263     hlen = ntohs(bgpo.bgpo_len);
264
265     proto_tree_add_text(tree,
266         offset + offsetof(struct bgp_open, bgpo_version), 1,
267         "Version: %u", bgpo.bgpo_version);
268     proto_tree_add_text(tree,
269         offset + offsetof(struct bgp_open, bgpo_myas), 2,
270         "My AS: %u", ntohs(bgpo.bgpo_myas));
271     proto_tree_add_text(tree,
272         offset + offsetof(struct bgp_open, bgpo_holdtime), 2,
273         "Hold time: %u", ntohs(bgpo.bgpo_holdtime));
274     proto_tree_add_text(tree,
275         offset + offsetof(struct bgp_open, bgpo_id), 4,
276         "BGP identifier: %s", ip_to_str((guint8 *)&bgpo.bgpo_id));
277     proto_tree_add_text(tree,
278         offset + offsetof(struct bgp_open, bgpo_optlen), 1,
279         "Optional parameters length: %u %s", bgpo.bgpo_optlen,
280         (bgpo.bgpo_optlen == 1) ? "byte" : "bytes");
281
282     if (hlen > sizeof(struct bgp_open)) {
283         proto_tree_add_text(tree,
284             offset + sizeof(struct bgp_open), hlen - sizeof(struct bgp_open),
285             "Optional parameters");
286     }
287 }
288
289 /*
290  * Dissect a BGP UPDATE message.
291  */
292 static void
293 dissect_bgp_update(const u_char *pd, int offset, frame_data *fd,
294     proto_tree *tree)
295 {
296     struct bgp bgp;                        /* BGP header            */
297     struct bgp_attr bgpa;                  /* path attributes       */
298     int             hlen;                  /* message length        */
299     const u_char    *p;                    /* packet offset pointer */
300     const u_char    *q;                    /* tmp                   */
301     const u_char    *end;                  /* message end           */
302     int             len;                   /* tmp                   */
303     proto_item      *ti;                   /* tree item             */
304     proto_tree      *subtree;              /* subtree for attibutes */ 
305     proto_tree      *subtree2;             /* subtree for attibutes */ 
306     proto_tree      *subtree3;             /* subtree for attibutes */
307     proto_tree      *as_paths_tree;        /* subtree for AS_PATHs  */
308     proto_tree      *as_path_tree;         /* subtree for AS_PATH   */
309     int             i, j;                  /* tmp                   */
310     guint8          length;                /* AS_PATH length        */
311     guint8          type;                  /* AS_PATH type          */
312     char            *as_path_str = NULL;   /* AS_PATH string        */
313     char            junk_buf[256];         /* tmp                   */
314
315
316     /* snarf UPDATE message */
317     memcpy(&bgp, &pd[offset], sizeof(bgp));
318     hlen = ntohs(bgp.bgp_len);
319     p = &pd[offset + BGP_HEADER_SIZE];  /*XXX*/
320
321     /* check for withdrawals */
322     len = ntohs(*(guint16 *)p);
323     proto_tree_add_text(tree, p - pd, 2, 
324         "Unfeasible routes length: %u %s", len, (len == 1) ? "byte" : "bytes");
325     p += 2;
326
327     /* parse unfeasible prefixes */
328     if (len > 0) {
329         ti = proto_tree_add_text(tree, p - pd, len, "Withdrawn routes:");
330         subtree = proto_item_add_subtree(ti, ett_bgp_unfeas);
331
332         /* parse each prefixes */
333         end = p + len;
334         while (p < end) {
335             i = decode_prefix4(p, junk_buf, sizeof(junk_buf));
336             proto_tree_add_text(subtree, p - pd, i, "%s", junk_buf);
337             p += i;
338         }
339     }
340     else {
341         p += len;
342     }
343
344     /* check for advertisements */
345     len = ntohs(*(guint16 *)p);
346     proto_tree_add_text(tree, p - pd, 2, "Total path attribute length: %u %s", 
347             len, (len == 1) ? "byte" : "bytes");
348
349     /* path attributes */
350 /* --- move --- */
351     if (len > 0) {
352         ti = proto_tree_add_text(tree, p - pd + 2, len, "Path attributes");
353         subtree = proto_item_add_subtree(ti, ett_bgp_attrs);
354         i = 2;
355         while (i < len) {
356             int alen, aoff;
357             char *msg;
358             guint16 af;
359             int off, snpa;
360
361             memcpy(&bgpa, &p[i], sizeof(bgpa));
362             /* check for the Extended Length bit */
363             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) {
364                 alen = ntohs(*(guint16 *)&p[i + sizeof(bgpa)]);
365                 aoff = sizeof(bgpa) + 2;
366             } else {
367                 alen = p[i + sizeof(bgpa)];
368                 aoff = sizeof(bgpa) + 1;
369             }
370             
371             /* This is kind of ugly - similar code appears twice, but it 
372                helps browsing attrs.                                      */
373             /* the first switch prints things in the title of the subtree */
374             switch (bgpa.bgpa_type) {
375             case BGPTYPE_ORIGIN:
376                 if (alen != 1)
377                     goto default_attribute_top;
378                 msg = val_to_str(p[i + aoff], bgpattr_origin, "Unknown");
379                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
380                         "%s: %s (%u %s)",
381                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
382                         msg, alen + aoff, (alen + aoff == 1) ? "byte" : 
383                         "bytes");
384                 break;
385             case BGPTYPE_AS_PATH:
386                 /* (p + i + 3) =
387                    (p + current attribute + 3 bytes to first tuple) */ 
388                 end = p + alen + i + 3;
389                 q = p + i + 3;
390                 /* must be freed by second switch!                         */
391                 /* "alen * 6" (5 digits + space) should be a good estimate
392                    of how long the AS path string could be                 */
393                 as_path_str = malloc(alen * 6);
394                 if (as_path_str == NULL) break;
395                 as_path_str[0] = '\0';
396    
397                 /* snarf each AS path */
398                 while (q < end) {
399                     type = *q++;
400                     if (type == AS_SET) {
401                         snprintf(as_path_str, 2, "{");
402                     }
403                     length = *q++;
404
405                     /* ignore confederation types until we support them */
406                     if (type == AS_CONFED_SET || type == AS_CONFED_SEQUENCE) {
407                         q += length;
408                         break;
409                     }
410
411                     /* snarf each value in path */
412                     for (j = 0; j < length; j++) {
413                         snprintf(junk_buf, sizeof(junk_buf), "%u%s", pntohs(q), 
414                                 (type == AS_SET) ? ", " : " ");
415                         strncat(as_path_str, junk_buf, sizeof(junk_buf));
416                         q += 2;
417                     }
418                    
419                     /* cleanup end of string */
420                     if (type == AS_SET) {
421                         as_path_str[strlen(as_path_str) - 2] = '}';
422                     }
423                     else {
424                         as_path_str[strlen(as_path_str) - 1] = '\0';
425                     }
426                 }
427
428                 if (as_path_str[0] == '\0')
429                     goto default_attribute_top;
430                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
431                         "%s: %s (%u %s)",
432                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
433                         as_path_str, alen + aoff, (alen + aoff == 1) ? "byte" :
434                         "bytes");
435                 break;
436             case BGPTYPE_NEXT_HOP:
437                 if (alen != 4)
438                     goto default_attribute_top;
439                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
440                         "%s: %s (%u %s)",
441                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
442                         ip_to_str(&p[i + aoff]), alen + aoff, (alen + aoff == 1)
443                         ? "byte" : "bytes");
444                 break;
445             case BGPTYPE_MULTI_EXIT_DISC:
446                 if (alen != 4)
447                     goto default_attribute_top;
448                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
449                         "%s: %u (%u %s)",
450                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
451                         ntohl(*(guint32 *)&p[i + aoff]), alen + aoff, 
452                         (alen + aoff == 1) ? "byte" : "bytes");
453                 break;
454             case BGPTYPE_LOCAL_PREF:
455                 if (alen != 4)
456                     goto default_attribute_top;
457                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
458                         "%s: %u (%u %s)",
459                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
460                         ntohl(*(guint32 *)&p[i + aoff]), alen + aoff,
461                         (alen + aoff == 1) ? "byte" : "bytes");
462                 break;
463             case BGPTYPE_ATOMIC_AGGREGATE:
464                 if (alen != 0) 
465                     goto default_attribute_top;
466                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
467                         "%s (%u %s)",
468                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
469                         alen + aoff, (alen + aoff == 1) ? "byte" : "bytes");
470                 break;
471             case BGPTYPE_AGGREGATOR:
472                 if (alen != 6) 
473                     goto default_attribute_top;
474                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
475                         "%s: AS: %u origin: %s (%u %s)",
476                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
477                         ntohs(*(guint16 *)&p[i + aoff]),
478                         ip_to_str(&p[i + aoff + 2]), alen + aoff, 
479                         (alen + aoff == 1) ? "byte" : "bytes");
480                 break;
481             case BGPTYPE_COMMUNITIES:
482                 if (alen != 4)
483                     goto default_attribute_top;
484
485                 /* check for well-known communities */
486                 if (ntohl(*(guint32 *)&p[i + aoff]) == BGP_COMM_NO_EXPORT)
487                     strncpy(junk_buf, "NO_EXPORT", 10);
488                 else if (ntohl(*(guint32 *)&p[i + aoff]) == 
489                         BGP_COMM_NO_ADVERTISE)
490                     strncpy(junk_buf, "NO_ADVERTISE", 13);
491                 else if (ntohl(*(guint32 *)&p[i + aoff]) == 
492                         BGP_COMM_NO_EXPORT_SUBCONFED)
493                     strncpy(junk_buf, "NO_EXPORT_SUBCONFED", 20);
494                 else {
495                     snprintf(junk_buf, sizeof(junk_buf), "%u:%u",
496                             ntohs(*(guint16 *)&p[i + aoff]), 
497                             ntohs(*(guint16 *)&p[i + aoff + 2]));
498                 }
499
500                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
501                         "%s: %s (%u %s)",
502                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
503                         junk_buf, alen + aoff,
504                         (alen + aoff == 1) ? "byte" : "bytes");
505                 break;
506             case BGPTYPE_ORIGINATOR_ID:
507                 if (alen != 4)
508                     goto default_attribute_top;
509                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
510                         "%s: %s (%u %s)",
511                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
512                         ip_to_str(&p[i + aoff]), alen + aoff, (alen + aoff == 1)
513                         ? "byte" : "bytes");
514                 break;
515             case BGPTYPE_CLUSTER_LIST:
516             default:
517             default_attribute_top:
518                 ti = proto_tree_add_text(subtree, p - pd + i, alen + aoff,
519                         "%s (%u %s)",
520                         val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
521                         alen + aoff, (alen + aoff == 1) ? "byte" : "bytes");
522             }
523             subtree2 = proto_item_add_subtree(ti, ett_bgp_attr);
524
525             /* figure out flags */
526             junk_buf[0] = '\0';
527             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_OPTIONAL) {
528                  strncat(junk_buf, "Optional, ", 10);
529             }
530             else {
531                  strncat(junk_buf, "Well-known, ", 12);
532             }
533             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_TRANSITIVE) {
534                  strncat(junk_buf, "Transitive, ", 12);
535             }
536             else {
537                  strncat(junk_buf, "Non-transitive, ", 16);
538             }
539             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_PARTIAL) {
540                  strncat(junk_buf, "Partial, ", 9);
541             }
542             else {
543                  strncat(junk_buf, "Complete, ", 10);
544             }
545             if (bgpa.bgpa_flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) {
546                  strncat(junk_buf, "Extended Length, ", 17);
547             }
548             /* stomp last ", " */
549             j = strlen(junk_buf);
550             junk_buf[j - 2] = '\0';
551             ti = proto_tree_add_text(subtree2,
552                     p - pd + i + offsetof(struct bgp_attr, bgpa_flags), 1,
553                     "Flags: 0x%02x (%s)", bgpa.bgpa_flags, junk_buf);
554             subtree3 = proto_item_add_subtree(ti, ett_bgp_attr_flags);
555
556             /* add flag bitfield subtrees */
557             proto_tree_add_text(subtree3,
558                     p - pd + i + offsetof(struct bgp_attr, bgpa_flags), 1,
559                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
560                         BGP_ATTR_FLAG_OPTIONAL, 8, "Optional", "Well-known"));
561             proto_tree_add_text(subtree3,
562                     p - pd + i + offsetof(struct bgp_attr, bgpa_flags), 1,
563                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
564                         BGP_ATTR_FLAG_TRANSITIVE, 8, "Transitive", 
565                         "Non-transitive"));
566             proto_tree_add_text(subtree3,
567                     p - pd + i + offsetof(struct bgp_attr, bgpa_flags), 1,
568                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
569                         BGP_ATTR_FLAG_PARTIAL, 8, "Partial", "Complete"));
570             proto_tree_add_text(subtree3,
571                     p - pd + i + offsetof(struct bgp_attr, bgpa_flags), 1,
572                     "%s", decode_boolean_bitfield(bgpa.bgpa_flags,
573                         BGP_ATTR_FLAG_EXTENDED_LENGTH, 8, "Extended length", 
574                         "Regular length"));
575
576             proto_tree_add_text(subtree2,
577                     p - pd + i + offsetof(struct bgp_attr, bgpa_type), 1,
578                     "Type code: %s (%u)",
579                     val_to_str(bgpa.bgpa_type, bgpattr_type, "Unknown"),
580                     bgpa.bgpa_type);
581             
582             proto_tree_add_text(subtree2, p - pd + i + sizeof(bgpa), 
583                     aoff - sizeof(bgpa), "Length: %d %s", alen, 
584                     (alen == 1) ? "byte" : "bytes");
585
586             /* the second switch prints things in the actual subtree of each 
587                attribute                                                     */ 
588             switch (bgpa.bgpa_type) {
589             case BGPTYPE_ORIGIN:
590                 if (alen != 1) {
591                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
592                             "Origin (invalid): %u %s", alen,
593                              (alen == 1) ? "byte" : "bytes");
594                 } else {
595                     msg = val_to_str(p[i + aoff], bgpattr_origin, "Unknown");
596                     proto_tree_add_text(subtree2, p - pd + i + aoff, 1,
597                             "Origin: %s (%u)", msg, p[i + aoff]);
598                 }
599                 break;
600             case BGPTYPE_AS_PATH:
601                 ti = proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
602                         "AS path: %s", as_path_str);
603                 as_paths_tree = proto_item_add_subtree(ti, ett_bgp_as_paths);
604
605                 /* (p + i + 3) =
606                    (p + current attribute + 3 bytes to first tuple) */ 
607                 end = p + alen + i + 3;
608                 q = p + i + 3;
609    
610                 /* snarf each AS path tuple */
611                 while (q < end) {
612                     as_path_str[0] = '\0';
613                     type = *q++;
614                     if (type == AS_SET) {
615                         snprintf(as_path_str, 2, "{");
616                     }
617                     length = *q++;
618
619                     /* ignore confederation types until we support them */
620                     if (type == AS_CONFED_SET || type == AS_CONFED_SEQUENCE) {
621                         q += length;
622                         break;
623                     }
624
625                     /* snarf each value in path, we're just going to reuse 
626                        as_path_str since we already have it malloced       */
627                     for (j = 0; j < length; j++) {
628                         snprintf(junk_buf, sizeof(junk_buf), "%u%s", pntohs(q),
629                                 (type == AS_SET) ? ", " : " ");
630                         strncat(as_path_str, junk_buf, sizeof(junk_buf));
631                         q += 2;
632                     }
633
634                     /* cleanup end of string */
635                     if (type == AS_SET) {
636                         as_path_str[strlen(as_path_str) - 2] = '}';
637                     }
638                     else {
639                         as_path_str[strlen(as_path_str) - 1] = '\0';
640                     }
641
642                     /* length here means number of ASs, ie length * 2 bytes */
643                     ti = proto_tree_add_text(as_paths_tree, 
644                             q - pd - length * 2 - 2,
645                             length * 2 + 2, "AS path segment: %s", as_path_str);
646                     as_path_tree = proto_item_add_subtree(ti, ett_bgp_as_paths);
647                     proto_tree_add_text(as_path_tree, q - pd - length * 2 - 2,
648                             1, "Path segment type: %s (%u)",
649                             val_to_str(type, as_segment_type, "Unknown"), type);
650                     proto_tree_add_text(as_path_tree, q - pd - length * 2 - 1, 
651                             1, "Path segment length: %u %s", length,
652                             (length == 1) ? "AS" : "ASs");
653                     proto_tree_add_text(as_path_tree, q - pd - length * 2, 
654                             length * 2, "Path segment value: %s", as_path_str);
655                 }
656
657                 free(as_path_str);
658                 break;
659             case BGPTYPE_NEXT_HOP:
660                 if (alen != 4) {
661                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
662                             "Next hop (invalid): %u %s", alen,
663                             (alen == 1) ? "byte" : "bytes");
664                 } else {
665                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
666                             "Next hop: %s", ip_to_str(&p[i + aoff]));
667                 }
668                 break;
669             case BGPTYPE_MULTI_EXIT_DISC:
670                 if (alen != 4) {
671                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
672                             "Multi exit discriminator (invalid): %u %s",
673                             alen, (alen == 1) ? "byte" : "bytes");
674                 } else {
675                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
676                             "Multi exit discriminator: %u",
677                             ntohl(*(guint32 *)&p[i + aoff]));
678                 }
679                 break;
680             case BGPTYPE_LOCAL_PREF:
681                 if (alen != 4) {
682                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
683                             "Local preference (invalid): %u %s", alen,
684                              (alen == 1) ? "byte" : "bytes");
685                 } else {
686                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
687                             "Local preference: %u",
688                             ntohl(*(guint32 *)&p[i + aoff]));
689                 }
690                 break;
691             case BGPTYPE_ATOMIC_AGGREGATE:
692                 if (alen != 0) {
693                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
694                             "Atomic aggregate (invalid): %u %s", alen,
695                             (alen == 1) ? "byte" : "bytes");    
696                 }
697                 break;
698             case BGPTYPE_AGGREGATOR:
699                 if (alen != 6) {
700                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
701                             "Aggregator (invalid): %u %s", alen,
702                             (alen == 1) ? "byte" : "bytes");
703                 } else {
704                     proto_tree_add_text(subtree2, p - pd + i + aoff, 2,
705                             "Aggregator AS: %u",
706                             ntohs(*(guint16 *)&p[i + aoff]));
707                     proto_tree_add_text(subtree2, p - pd + i + aoff + 2, 4,
708                             "Aggregator origin: %s",
709                             ip_to_str(&p[i + aoff + 2]));
710                 }
711                 break;
712             case BGPTYPE_COMMUNITIES:
713                 if (alen != 4) {
714                     proto_tree_add_text(subtree2, p - pd + i + aoff, 4, 
715                             "Communities (invalid): %u %s", alen,
716                             (alen == 1) ? "byte" : "bytes");
717                 }
718                 /* check for reserved values */
719                 else if (ntohs(*(guint16 *)&p[i + aoff]) == FOURHEX0 ||
720                         ntohs(*(guint16 *)&p[i + aoff]) == FOURHEXF) {
721                     /* check for well-known communities */
722                     if (ntohl(*(guint32 *)&p[i + aoff]) == BGP_COMM_NO_EXPORT)
723                         proto_tree_add_text(subtree2, p - pd + i + aoff, 4, 
724                                 "Communities: NO_EXPORT (0x%x)", 
725                                 ntohl(*(guint32 *)&p[i + aoff]));
726                     else if (ntohl(*(guint32 *)&p[i + aoff]) == 
727                             BGP_COMM_NO_ADVERTISE)
728                         proto_tree_add_text(subtree2, p - pd + i + aoff, 4, 
729                                 "Communities: NO_ADVERTISE (0x%x)",
730                                 ntohl(*(guint32 *)&p[i + aoff]));
731                     else if (ntohl(*(guint32 *)&p[i + aoff]) == 
732                             BGP_COMM_NO_EXPORT_SUBCONFED)
733                         proto_tree_add_text(subtree2, p - pd + i + aoff, 4, 
734                                 "Communities: NO_EXPORT_SUBCONFED (0x%x)",
735                                 ntohl(*(guint32 *)&p[i + aoff]));
736                     else {
737                         proto_tree_add_text(subtree2, p - pd + i + aoff, 4, 
738                                 "Communities (reserved): 0x%x",
739                                 ntohl(*(guint32 *)&p[i + aoff]));
740                     }
741                 }
742                 else {
743                     proto_tree_add_text(subtree2, p - pd + i + aoff, 2, 
744                             "Communities AS: %u", 
745                             ntohs(*(guint16 *)&p[i + aoff]));
746                     proto_tree_add_text(subtree2, p - pd + i + aoff + 2, 2, 
747                             "Communities value: %u", 
748                             ntohs(*(guint16 *)&p[i + aoff + 2]));
749                 }
750                 break;
751             case BGPTYPE_ORIGINATOR_ID:
752                 if (alen != 4) {
753                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
754                             "Originator identifier (invalid): %u %s", alen,
755                             (alen == 1) ? "byte" : "bytes");
756                 } else {
757                     proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
758                             "Originator identifier: %s",
759                             ip_to_str(&p[i + aoff]));
760                 }
761                 break;
762             case BGPTYPE_MP_REACH_NLRI:
763                 af = ntohs(*(guint16 *)&p[i + aoff]);
764                 proto_tree_add_text(subtree2, p - pd + i + aoff, 2,
765                     "Address family: %s (%u)",
766                     val_to_str(af, afnumber, "Unknown"), af);
767                 proto_tree_add_text(subtree2, p - pd + i + aoff + 2, 1,
768                     "Subsequent address family identifier: %s (%u)",
769                     val_to_str(p[i + aoff + 2], bgpattr_nlri_safi,
770                         p[i + aoff + 2] >= 128 ? "Vendor specific" : "Unknown"),
771                     p[i + aoff + 2]);
772                 ti = proto_tree_add_text(subtree2, p - pd + i + aoff + 3, 1,
773                         "Next hop network address (%d %s)",
774                         p[i + aoff + 3], (p[i + aoff + 3] == 1) ? "byte" : 
775                         "bytes");
776                 if (af == AFNUM_INET || af == AFNUM_INET6) {
777                     int j, advance;
778                     const char *s;
779
780                     subtree3 = proto_item_add_subtree(ti, ett_bgp_mp_reach_nlri);
781
782                     j = 0;
783                     while (j < p[i + aoff + 3]) {
784                         if (af == AFNUM_INET)
785                             advance = 4;
786                         else if (af == AFNUM_INET6)
787                             advance = 16;
788                         else
789                             break;
790                         if (j + advance > p[i + aoff + 3])
791                             break;
792
793                         if (af == AFNUM_INET)
794                             s = ip_to_str(&p[i + aoff + 4 + j]);
795                         else {
796                             s = ip6_to_str((struct e_in6_addr *)
797                                 &p[i + aoff + 4 + j]);
798                         }
799                         proto_tree_add_text(subtree3,
800                             p - pd + i + aoff + 4 + j, advance,
801                             "Next hop: %s", s);
802                         j += advance;
803                     }
804                 }
805
806                 alen -= (p[i + aoff + 3] + 4);
807                 aoff += (p[i + aoff + 3] + 4);
808                 off = 0;
809                 snpa = p[i + aoff];
810                 ti = proto_tree_add_text(subtree2, p - pd + i + aoff, 1,
811                         "Subnetwork points of attachment: %u", snpa);
812                 off++;
813                 if (snpa)
814                     subtree3 = proto_item_add_subtree(ti, ett_bgp_mp_reach_nlri);
815                 for (/*nothing*/; snpa > 0; snpa--) {
816                     proto_tree_add_text(subtree3, p - pd + i + aoff + off, 1,
817                         "SNPA length: ", p[i + aoff + off]);
818                     off++;
819                     proto_tree_add_text(subtree3, p - pd + i + aoff + off,
820                         p[i + aoff + off - 1],
821                         "SNPA (%u %s)", p[i + aoff + off - 1],
822                         (p[i + aoff + off - 1] == 1) ? "byte" : "bytes");
823                     off += p[i + aoff + off - 1];
824                 }
825
826                 alen -= off;
827                 aoff += off;
828                 ti = proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
829                         "Network layer reachability information (%u %s)",
830                         alen, (alen == 1) ? "byte" : "bytes");
831                 if (alen)
832                     subtree3 = proto_item_add_subtree(ti, ett_bgp_mp_unreach_nlri);
833                 while (alen > 0) {
834                     int advance;
835                     char buf[256];
836
837                     if (af == AFNUM_INET) {
838                         advance = decode_prefix4(&p[i + aoff], buf,
839                             sizeof(buf));
840                     } else if (af == AFNUM_INET6) {
841                         advance = decode_prefix6(&p[i + aoff], buf,
842                             sizeof(buf));
843                     } else
844                         break;
845                     if (advance < 0)
846                         break;
847                     if (alen < advance)
848                         break;
849                     proto_tree_add_text(subtree3, p - pd + i + aoff, advance,
850                         "Network layer reachability information: %s", buf);
851
852                     alen -= advance;
853                     aoff += advance;
854                 }
855
856                 break;
857             case BGPTYPE_MP_UNREACH_NLRI:
858                 af = ntohs(*(guint16 *)&p[i + aoff]);
859                 proto_tree_add_text(subtree2, p - pd + i + aoff, 2,
860                     "Address family: %s (%u)",
861                     val_to_str(af, afnumber, "Unknown"), af);
862                 proto_tree_add_text(subtree2, p - pd + i + aoff + 2, 1,
863                     "Subsequent address family identifier: %s (%u)",
864                     val_to_str(p[i + aoff + 2], bgpattr_nlri_safi,
865                         p[i + aoff + 2] >= 128 ? "Vendor specific" : "Unknown"),
866                     p[i + aoff + 2]);
867                 ti = proto_tree_add_text(subtree2, p - pd + i + aoff + 3,
868                         alen - 3, "Withdrawn routes (%u %s)", alen - 3,
869                         (alen - 3 == 1) ? "byte" : "bytes");
870
871                 alen -= 3;
872                 aoff += 3;
873                 if (alen > 0)
874                     subtree3 = proto_item_add_subtree(ti, ett_bgp_mp_unreach_nlri);
875                 while (alen > 0) {
876                     int advance;
877                     char buf[256];
878
879                     if (af == AFNUM_INET) {
880                         advance = decode_prefix4(&p[i + aoff], buf,
881                             sizeof(buf));
882                     } else if (af == AFNUM_INET6) {
883                         advance = decode_prefix6(&p[i + aoff], buf,
884                             sizeof(buf));
885                     } else
886                         break;
887                     if (advance < 0)
888                         break;
889                     if (alen < advance)
890                         break;
891                     proto_tree_add_text(subtree3, p - pd + i + aoff, advance,
892                         "Withdrawn route: %s", buf);
893
894                     alen -= advance;
895                     aoff += advance;
896                 }
897
898                 break;
899             case BGPTYPE_CLUSTER_LIST:
900             default:
901                 proto_tree_add_text(subtree2, p - pd + i + aoff, alen,
902                         "Unknown (%d %s)", alen, (alen == 1) ? "byte" : 
903                         "bytes");
904                 break;
905             }
906
907             i += alen + aoff;
908         }
909 /* --- move --- */
910         p += 2 + len;
911
912         /* NLRI */
913         len = hlen - (p - &pd[offset]);
914         ti = proto_tree_add_text(tree, p - pd, len,
915                 "Network layer reachability information: %u %s", len,
916                 (len == 1) ? "byte" : "bytes");
917
918         /* parse prefixes */
919         if (len > 0) {
920             subtree = proto_item_add_subtree(ti, ett_bgp_nlri);
921             end = p + len;
922             while (p < end) {
923                 i = decode_prefix4(p, junk_buf, sizeof(junk_buf));
924                 proto_tree_add_text(subtree, p - pd, i, "%s", junk_buf);
925                 p += i;
926             }
927         }
928     }
929 }
930
931 /*
932  * Dissect a BGP NOTIFICATION message.
933  */
934 static void
935 dissect_bgp_notification(const u_char *pd, int offset, frame_data *fd,
936     proto_tree *tree)
937 {
938     struct bgp_notification bgpn;   /* BGP NOTIFICATION message */
939     int                     hlen;   /* message length           */
940     char                    *p;     /* string pointer           */
941
942     /* snarf message */
943     memcpy(&bgpn, &pd[offset], sizeof(bgpn));
944     hlen = ntohs(bgpn.bgpn_len);
945
946     /* print error code */
947     proto_tree_add_text(tree,
948         offset + offsetof(struct bgp_notification, bgpn_major), 1,
949         "Error code: %s (%u)",
950         val_to_str(bgpn.bgpn_major, bgpnotify_major, "Unknown"),
951         bgpn.bgpn_major);
952
953     /* print error subcode */
954     if (bgpn.bgpn_major < array_length(bgpnotify_minor)
955      && bgpnotify_minor[bgpn.bgpn_major] != NULL) {
956         p = val_to_str(bgpn.bgpn_minor, bgpnotify_minor[bgpn.bgpn_major],
957             "Unknown");
958     } else if (bgpn.bgpn_minor == 0)
959         p = "Unspecified";
960     else
961         p = "Unknown";
962     proto_tree_add_text(tree,
963         offset + offsetof(struct bgp_notification, bgpn_minor), 1,
964         "Error subcode: %s (%u)", p, bgpn.bgpn_minor);
965
966     /* only print if there is optional data */
967     if (hlen > BGP_MIN_NOTIFICATION_MSG_SIZE) {
968         proto_tree_add_text(tree, offset + BGP_MIN_NOTIFICATION_MSG_SIZE,
969             hlen - BGP_MIN_NOTIFICATION_MSG_SIZE, "Data");
970     }
971 }
972
973 /*
974  * Dissect a BGP packet.
975  */
976 void
977 dissect_bgp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
978 {
979     proto_item    *ti;           /* tree item                        */
980     proto_tree    *bgp_tree;     /* BGP packet tree                  */
981     proto_tree    *bgp1_tree;    /* BGP message tree                 */
982     const u_char  *p;            /* packet offset pointer            */
983     int           l, i;          /* tmp                              */
984     int           found;         /* number of BGP messages in packet */
985     static u_char marker[] = {   /* BGP message marker               */
986         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
987         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
988     };
989     struct bgp    bgp;           /* BGP header                       */
990     int           hlen;          /* BGP header length                */
991     char          *typ;          /* BGP message type                 */
992
993     if (check_col(fd, COL_PROTOCOL))
994         col_add_str(fd, COL_PROTOCOL, "BGP");
995
996     p = &pd[offset];
997     l = END_OF_FRAME;
998     i = 0;
999     found = -1;
1000     /* run through the TCP packet looking for BGP headers         */
1001     /* this is done twice, but this way each message type can be 
1002        printed in the COL_INFO field                              */
1003     while (i < l) {
1004         /* look for bgp header */
1005         if (p[i] != 0xff) {
1006             i++;
1007             continue;
1008         }
1009         CHECK_SIZE(i, sizeof(marker), l);
1010         if (memcmp(&p[i], marker, sizeof(marker)) != 0) {
1011             i++;
1012             continue;
1013         }
1014
1015         memcpy(&bgp, &p[i], sizeof(bgp));
1016         found++;
1017         hlen = ntohs(bgp.bgp_len);
1018         typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
1019
1020         if (check_col(fd, COL_INFO)) {
1021             if (found == 0) 
1022                 col_add_fstr(fd, COL_INFO, "%s", typ);
1023             else
1024                 col_append_fstr(fd, COL_INFO, ", %s", typ);
1025         }
1026
1027         i += hlen;
1028     }
1029
1030     if (tree) {
1031         ti = proto_tree_add_text(tree, offset, END_OF_FRAME,
1032                     "Border Gateway Protocol");
1033         bgp_tree = proto_item_add_subtree(ti, ett_bgp);
1034
1035         p = &pd[offset];
1036         l = END_OF_FRAME;
1037         i = 0;
1038         /* now, run through the TCP packet again, this time dissect */
1039         /* each message that we find */
1040         while (i < l) {
1041             /* look for bgp header */
1042             if (p[i] != 0xff) {
1043                 i++;
1044                 continue;
1045             }
1046             CHECK_SIZE(i, sizeof(marker), l);
1047             if (memcmp(&p[i], marker, sizeof(marker)) != 0) {
1048                 i++;
1049                 continue;
1050             }
1051
1052             memcpy(&bgp, &p[i], sizeof(bgp));
1053             hlen = ntohs(bgp.bgp_len);
1054             typ = val_to_str(bgp.bgp_type, bgptypevals, "Unknown Message");
1055             if (END_OF_FRAME < hlen) {
1056                 ti = proto_tree_add_text(bgp_tree, offset + i, END_OF_FRAME,
1057                             "%s (truncated)", typ);
1058             } else {
1059                 ti = proto_tree_add_text(bgp_tree, offset + i, hlen,
1060                             "%s", typ);
1061             }
1062             /* add a different tree for each message type */
1063             switch (bgp.bgp_type) {
1064             case BGP_OPEN:
1065                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_open);
1066                 break;
1067             case BGP_UPDATE:
1068                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_update);
1069                 break;
1070             case BGP_NOTIFICATION:
1071                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp_notification);
1072                 break;
1073             case BGP_KEEPALIVE:
1074                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp);
1075                 break;
1076             default:
1077                 bgp1_tree = proto_item_add_subtree(ti, ett_bgp);
1078                 break;
1079             }
1080
1081             proto_tree_add_text(bgp1_tree, offset + i, BGP_MARKER_SIZE,
1082                 "Marker", NULL);
1083                             
1084             if (hlen < BGP_HEADER_SIZE || hlen > BGP_MAX_PACKET_SIZE) {
1085                 proto_tree_add_text(bgp1_tree,
1086                     offset + i + offsetof(struct bgp, bgp_len), 2,
1087                     "Length (invalid): %u %s", hlen, 
1088                     (hlen == 1) ? "byte" : "bytes");
1089             } else {
1090                 proto_tree_add_text(bgp1_tree,
1091                     offset + i + offsetof(struct bgp, bgp_len), 2,
1092                     "Length: %u %s", hlen, 
1093                     (hlen == 1) ? "byte" : "bytes");
1094             }
1095
1096             proto_tree_add_text(bgp1_tree,
1097                 offset + i + offsetof(struct bgp, bgp_type), 1,
1098                 "Type: %s (%u)", typ, bgp.bgp_type);
1099
1100             CHECK_SIZE(i, hlen, l);
1101
1102             /* handle each message type */
1103             switch (bgp.bgp_type) {
1104             case BGP_OPEN:
1105                 dissect_bgp_open(pd, offset + i, fd, bgp1_tree);
1106                 break;
1107             case BGP_UPDATE:
1108                 dissect_bgp_update(pd, offset + i, fd, bgp1_tree);
1109                 break;
1110             case BGP_NOTIFICATION:
1111                 dissect_bgp_notification(pd, offset + i, fd, bgp1_tree);
1112                 break;
1113             case BGP_KEEPALIVE:
1114                 /* no data in KEEPALIVE messages */
1115                 break;
1116             default:
1117                 break;
1118             }
1119
1120             i += hlen;
1121         }
1122     }
1123 }
1124
1125 /*
1126  * Register ourselves.
1127  */
1128 void
1129 proto_register_bgp(void)
1130 {
1131     static gint *ett[] = {
1132       &ett_bgp,
1133       &ett_bgp_unfeas,
1134       &ett_bgp_attrs,
1135       &ett_bgp_attr,
1136       &ett_bgp_attr_flags,
1137       &ett_bgp_mp_reach_nlri,
1138       &ett_bgp_mp_unreach_nlri,
1139       &ett_bgp_nlri,
1140       &ett_bgp_open,
1141       &ett_bgp_update,
1142       &ett_bgp_notification,
1143       &ett_bgp_as_paths,
1144     };
1145
1146     proto_bgp = proto_register_protocol("Border Gateway Protocol", "bgp");
1147     proto_register_subtree_array(ett, array_length(ett));
1148 }