Move the declarations of IP protocol numbers to "ipproto.h" from
[obnox/wireshark/wip.git] / packet-pim.c
1 /* packet-pim.c
2  * Routines for PIM disassembly
3  * (c) Copyright Jun-ichiro itojun Hagino <itojun@itojun.org>
4  *
5  * $Id: packet-pim.c,v 1.27 2001/04/23 17:51:33 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  * 
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H 
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38
39 #include <string.h>
40 #include <glib.h>
41
42 #ifdef NEED_SNPRINTF_H
43 # include "snprintf.h"
44 #endif
45
46 #include "packet.h"
47 #include "ipproto.h"
48 #include "in_cksum.h"
49
50 #define PIM_TYPE(x)     ((x) & 0x0f)
51 #define PIM_VER(x)      (((x) & 0xf0) >> 4)
52
53 enum pimv2_addrtype {
54         pimv2_unicast, pimv2_group, pimv2_source
55 };
56
57 static int proto_pim = -1;
58 static int hf_pim_version = -1;
59 static int hf_pim_type = -1;
60 static int hf_pim_cksum = -1;
61
62 static gint ett_pim = -1;
63
64 static dissector_handle_t ip_handle;
65 static dissector_handle_t ipv6_handle;
66
67 /*
68  * Address family values.
69  */
70 #define PIM_AF_RESERVED         0
71 #define PIM_AF_IP               1       /* IPv4 */
72 #define PIM_AF_IPV6             2       /* IPv6 */
73 #define PIM_AF_NSAP             3       /* NSAP */
74 #define PIM_AF_HDLC             4       /* HDLC (8-bit multidrop) */
75 #define PIM_AF_BBN_1822         5       /* BBN 1822 */
76 #define PIM_AF_802              6       /* 802 (D/I/X Ethernet, 802.x, FDDI) */
77 #define PIM_AF_E_163            7       /* E.163 */
78 #define PIM_AF_E_164            8       /* E.164 (SMDS, Frame Relay, ATM) */
79 #define PIM_AF_F_69             9       /* F.69 (Telex) */
80 #define PIM_AF_X_121            10      /* X.121 (X.25, Frame Relay) */
81 #define PIM_AF_IPX              11      /* IPX */
82 #define PIM_AF_ATALK            12      /* Appletalk */
83 #define PIM_AF_DECNET_IV        13      /* DECnet Phase IV */
84 #define PIM_AF_VINES            14      /* Banyan Vines */
85 #define PIM_AF_E_164_NSAP       15      /* E.164 with NSAP format subaddress */
86
87 static const char *
88 dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
89         int *advance) {
90     static char buf[512];
91     guint8 af;
92     guint8 et;
93     guint8 flags;
94     guint8 mask_len;
95     int len = 0;
96
97     af = tvb_get_guint8(tvb, offset);
98     if (af != PIM_AF_IP && af != PIM_AF_IPV6) {
99         /*
100          * We don't handle the other formats, and addresses don't include
101          * a length field, so we can't even show them as raw bytes.
102          */
103         return NULL;
104     }
105
106     et = tvb_get_guint8(tvb, offset + 1);
107     if (et != 0) {
108         /*
109          * The only defined encoding type is 0, for the native encoding;
110          * again, as addresses don't include a length field, we can't
111          * even show addresses with a different encoding type as raw
112          * bytes.
113          */
114         return NULL;
115     }
116
117     switch (at) {
118     case pimv2_unicast:
119         switch (af) {
120         case PIM_AF_IP:
121             len = 4;
122             (void)snprintf(buf, sizeof(buf), "%s",
123                 ip_to_str(tvb_get_ptr(tvb, offset + 2, len)));
124             break;
125
126         case PIM_AF_IPV6:
127             len = 16;
128             (void)snprintf(buf, sizeof(buf), "%s",
129                 ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 2, len)));
130             break;
131         }
132         if (advance)
133             *advance = 2 + len;
134         break;
135
136     case pimv2_group:
137         mask_len = tvb_get_guint8(tvb, offset + 3);
138         switch (af) {
139         case PIM_AF_IP:
140             len = 4;
141             (void)snprintf(buf, sizeof(buf), "%s/%u",
142                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
143             break;
144
145         case PIM_AF_IPV6:
146             len = 16;
147             (void)snprintf(buf, sizeof(buf), "%s/%u",
148                 ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
149             break;
150         }
151         if (advance)
152             *advance = 4 + len;
153         break;
154
155     case pimv2_source:
156         flags = tvb_get_guint8(tvb, offset + 2);
157         mask_len = tvb_get_guint8(tvb, offset + 3);
158         switch (af) {
159         case PIM_AF_IP:
160             len = 4;
161             (void)snprintf(buf, sizeof(buf), "%s/%u",
162                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
163             break;
164
165         case PIM_AF_IPV6:
166             len = 16;
167             (void)snprintf(buf, sizeof(buf), "%s/%u",
168                 ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
169             break;
170         }
171         if (flags) {
172             (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
173                 " (%s%s%s)",
174                 flags & 0x04 ? "S" : "",
175                 flags & 0x02 ? "W" : "",
176                 flags & 0x01 ? "R" : "");
177         }
178         if (advance)
179             *advance = 4 + len;
180         break;
181     default:
182         return NULL;
183     }
184
185     return buf;
186 }
187
188 static void 
189 dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
190     int offset = 0;
191     guint8 pim_typever;
192     guint length, pim_length;
193     guint16 pim_cksum, computed_cksum;
194     vec_t cksum_vec[1];
195     static const value_string type1vals[] = {
196         { 0, "Query" },
197         { 1, "Register" },
198         { 2, "Register-stop" },
199         { 3, "Join/Prune" },
200         { 4, "RP-Reachable" },
201         { 5, "Assert" },
202         { 6, "Graft" },
203         { 7, "Graft-Ack" },
204         { 8, "Mode" },
205         { 0, NULL },
206     };
207     static const value_string type2vals[] = {
208         { 0, "Hello" },
209         { 1, "Register" },
210         { 2, "Register-stop" },
211         { 3, "Join/Prune" },
212         { 4, "Bootstrap" },
213         { 5, "Assert" },
214         { 6, "Graft" },
215         { 7, "Graft-Ack" },
216         { 8, "Candidate-RP-Advertisement" },
217         { 0, NULL },
218     };
219     char *typestr;
220     proto_tree *pim_tree = NULL;
221     proto_item *ti; 
222     proto_tree *pimopt_tree = NULL;
223     proto_item *tiopt; 
224
225     if (check_col(pinfo->fd, COL_PROTOCOL))
226         col_set_str(pinfo->fd, COL_PROTOCOL, "PIM");
227     if (check_col(pinfo->fd, COL_INFO))
228         col_clear(pinfo->fd, COL_INFO);
229
230     pim_typever = tvb_get_guint8(tvb, 0);
231
232     switch (PIM_VER(pim_typever)) {
233     case 1:
234         typestr = val_to_str(PIM_TYPE(pim_typever), type1vals, "Unknown");
235         break;
236     case 2:
237         typestr = val_to_str(PIM_TYPE(pim_typever), type2vals, "Unknown");
238         break;
239     default:
240         typestr = "Unknown";
241         break;
242     }
243
244     if (check_col(pinfo->fd, COL_PROTOCOL)) {
245         col_add_fstr(pinfo->fd, COL_PROTOCOL, "PIM version %d",
246             PIM_VER(pim_typever));
247     }
248     if (check_col(pinfo->fd, COL_INFO))
249         col_add_fstr(pinfo->fd, COL_INFO, "%s", typestr); 
250
251     if (tree) {
252         ti = proto_tree_add_item(tree, proto_pim, tvb, offset,
253             tvb_length_remaining(tvb, offset), FALSE);
254         pim_tree = proto_item_add_subtree(ti, ett_pim);
255
256         proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1,
257             PIM_VER(pim_typever)); 
258         proto_tree_add_uint_format(pim_tree, hf_pim_type, tvb, offset, 1,
259             PIM_TYPE(pim_typever),
260             "Type: %s (%u)", typestr, PIM_TYPE(pim_typever)); 
261
262         pim_cksum = tvb_get_ntohs(tvb, offset + 2);
263         length = tvb_length(tvb);
264         if (PIM_VER(pim_typever) == 2) {
265             /*
266              * Well, it's PIM v2, so we can check whether this is a Register
267              * mesage, and thus can figure out how much to checksum.
268              */
269             if (PIM_TYPE(pim_typever) == 1) {
270                 /*
271                  * Register message - the PIM header is 8 bytes long.
272                  */
273                 pim_length = 8;
274             } else {
275                 /*
276                  * Other message - checksum the entire packet.
277                  */
278                 pim_length = tvb_reported_length(tvb);
279             }
280         } else {
281             /*
282              * We don't know what type of message this is, so say that
283              * the length is 0, to force it not to be checksummed.
284              */
285             pim_length = 0;
286         }
287         if (!pinfo->fragmented && length >= pim_length) {
288             /*
289              * The packet isn't part of a fragmented datagram and isn't
290              * truncated, so we can checksum it.
291              */
292             cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
293             cksum_vec[0].len = pim_length;
294             computed_cksum = in_cksum(&cksum_vec[0], 1);
295             if (computed_cksum == 0) {
296                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
297                         offset + 2, 2, pim_cksum,
298                         "Checksum: 0x%04x (correct)",
299                         pim_cksum);
300             } else {
301                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
302                         offset + 2, 2, pim_cksum,
303                         "Checksum: 0x%04x (incorrect, should be 0x%04x)",
304                         pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
305             }
306         } else {
307             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
308                 offset + 2, 2, pim_cksum);
309         }
310
311         offset += 4;
312
313         if (tvb_reported_length_remaining(tvb, offset) > 0) {
314             tiopt = proto_tree_add_text(pim_tree, tvb, offset,
315                 tvb_length_remaining(tvb, offset), "PIM parameters");
316             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
317         } else
318             goto done;
319
320         if (PIM_VER(pim_typever) != 2)
321             goto done;
322
323         /* version 2 decoder */
324         switch (PIM_TYPE(pim_typever)) {
325         case 0: /*hello*/
326           {
327             while (tvb_reported_length_remaining(tvb, offset) >= 2) {
328                 if (tvb_get_ntohs(tvb, offset) == 1 &&
329                     tvb_get_ntohs(tvb, offset + 2) == 2) {
330                     guint16 holdtime;
331
332                     holdtime = tvb_get_ntohs(tvb, offset + 4);
333                     proto_tree_add_text(pimopt_tree, tvb, offset, 6,
334                         "Holdtime: %u%s", holdtime,
335                         holdtime == 0xffff ? " (infty)" : "");
336                     offset += 6;
337                 } else
338                     break;
339             }
340             break;
341           }
342
343         case 1: /* register */
344           {
345             guint32 flags;
346             guint8 v_hl;
347             tvbuff_t *next_tvb;
348             proto_tree *flag_tree = NULL;
349             proto_item *tiflag; 
350
351             flags = tvb_get_ntohl(tvb, offset);
352             tiflag = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
353                 "Flags: 0x%08x", flags);
354             flag_tree = proto_item_add_subtree(tiflag, ett_pim);
355             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
356                 decode_boolean_bitfield(flags, 0x80000000, 32,
357                     "Border", "Not border"));
358             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
359                 decode_boolean_bitfield(flags, 0x40000000, 32,
360                     "Null-Register", "Not Null-Register"));
361             offset += 4;
362             
363             /*
364              * The rest of the packet is a multicast data packet.
365              */
366             next_tvb = tvb_new_subset(tvb, offset, -1, -1);
367
368             /*
369              * It's an IP packet - determine whether it's IPv4 or IPv6.
370              */
371             v_hl = tvb_get_guint8(tvb, offset);
372             switch((v_hl & 0xf0) >> 4) {
373             case 4:     /* IPv4 */
374 #if 0
375                     call_dissector(ip_handle, next_tvb, pinfo, tree);
376 #else
377                     call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
378 #endif
379                     break;
380             case 6:     /* IPv6 */
381 #if 0
382                     call_dissector(ipv6_handle, next_tvb, pinfo, tree);
383 #else
384                     call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
385 #endif
386                     break;
387             default:
388                     proto_tree_add_text(pimopt_tree, tvb,
389                         offset, tvb_length_remaining(tvb, offset),
390                         "Unknown IP version %d", (v_hl & 0xf0) >> 4);
391                     break;
392             }
393             break;
394           }
395
396         case 2: /* register-stop */
397           {
398             int advance;
399             const char *s;
400
401             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
402             if (s == NULL)
403                 break;
404             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
405             offset += advance;
406             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
407             if (s == NULL)
408                 break;
409             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
410             break;
411           }
412
413         case 3: /* join/prune */
414         case 6: /* graft */
415         case 7: /* graft-ack */
416           {
417             int advance;
418             int off;
419             const char *s;
420             int ngroup, i, njoin, nprune, j;
421             guint16 holdtime;
422             proto_tree *grouptree = NULL;
423             proto_item *tigroup; 
424             proto_tree *subtree = NULL;
425             proto_item *tisub; 
426
427             if (PIM_TYPE(pim_typever) != 7) {
428                 /* not graft-ack */
429                 s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
430                 if (s == NULL)
431                     break;
432                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
433                     "Upstream-neighbor: %s", s);
434                 offset += advance;
435             }
436
437             offset += 1;        /* skip reserved field */
438
439             ngroup = tvb_get_guint8(tvb, offset);
440             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
441                 "Groups: %u", ngroup);
442             offset += 1;
443
444             if (PIM_TYPE(pim_typever) != 7)     {
445                 /* not graft-ack */
446                 holdtime = tvb_get_ntohs(tvb, offset);
447                 proto_tree_add_text(pimopt_tree, tvb, offset, 2,
448                     "Holdtime: %u%s", holdtime,
449                     holdtime == 0xffff ? " (infty)" : "");
450             }
451             offset += 2;
452
453             for (i = 0; i < ngroup; i++) {
454                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
455                 if (s == NULL)
456                     goto breakbreak3;
457                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
458                     "Group %d: %s", i, s);
459                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
460                 offset += advance;
461
462                 njoin = tvb_get_ntohs(tvb, offset);
463                 nprune = tvb_get_ntohs(tvb, offset + 2);
464
465                 tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
466                     "Join: %d", njoin);
467                 subtree = proto_item_add_subtree(tisub, ett_pim);
468                 off = offset + 4;
469                 for (j = 0; j < nprune; j++) {
470                     s = dissect_pim_addr(tvb, off, pimv2_source,
471                         &advance);
472                     if (s == NULL)
473                         goto breakbreak3;
474                     proto_tree_add_text(subtree, tvb, off, advance,
475                         "IP address: %s", s);
476                     off += advance;
477                 }
478
479                 tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
480                     "Prune: %d", nprune);
481                 subtree = proto_item_add_subtree(tisub, ett_pim);
482                 for (j = 0; j < nprune; j++) {
483                     s = dissect_pim_addr(tvb, off, pimv2_source,
484                         &advance);
485                     if (s == NULL)
486                         goto breakbreak3;
487                     proto_tree_add_text(subtree, tvb, off, advance,
488                         "IP address: %s", s);
489                     off += advance;
490                 }
491             }
492     breakbreak3:
493             break;
494           }
495
496         case 4: /* bootstrap */
497           {
498             const char *s;
499             int advance;
500             int i, j;
501             int frpcnt;
502             guint16 holdtime;
503             proto_tree *grouptree = NULL;
504             proto_item *tigroup; 
505
506             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
507                 "Fragment tag: 0x%04x", tvb_get_ntohs(tvb, offset));
508             offset += 2;
509
510             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
511                 "Hash mask len: %u", tvb_get_guint8(tvb, offset));
512             offset += 1;
513             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
514                 "BSR priority: %u", tvb_get_guint8(tvb, offset));
515             offset += 1;
516
517             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
518             if (s == NULL)
519                 break;
520             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "BSR: %s", s);
521             offset += advance;
522
523             for (i = 0; tvb_reported_length_remaining(tvb, offset) > 0; i++) {
524                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
525                 if (s == NULL)
526                     goto breakbreak4;
527                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
528                     "Group %d: %s", i, s);
529                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
530                 offset += advance;
531
532                 proto_tree_add_text(grouptree, tvb, offset, 1,
533                     "RP count: %u", tvb_get_guint8(tvb, offset));
534                 offset += 1;
535                 frpcnt = tvb_get_guint8(tvb, offset);
536                 proto_tree_add_text(grouptree, tvb, offset, 1,
537                     "FRP count: %u", frpcnt);
538                 offset += 3;
539
540                 for (j = 0; j < frpcnt; j++) {
541                     s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
542                     if (s == NULL)
543                         goto breakbreak4;
544                     proto_tree_add_text(grouptree, tvb, offset, advance,
545                         "RP %d: %s", j, s);
546                     offset += advance;
547
548                     holdtime = tvb_get_ntohs(tvb, offset);
549                     proto_tree_add_text(grouptree, tvb, offset, 2,
550                         "Holdtime: %u%s", holdtime,
551                         holdtime == 0xffff ? " (infty)" : "");
552                     offset += 2;
553                     proto_tree_add_text(grouptree, tvb, offset, 1,
554                         "Priority: %u", tvb_get_guint8(tvb, offset));
555                     offset += 2;        /* also skips reserved field */
556                 }
557             }
558
559     breakbreak4:
560             break;
561           }
562
563         case 5: /* assert */
564           {
565             const char *s;
566             int advance;
567
568             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
569             if (s == NULL)
570                 break;
571             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
572             offset += advance;
573
574             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
575             if (s == NULL)
576                 break;
577             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
578             offset += advance;
579
580             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
581                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
582                     "RP Tree", "Not RP Tree"));
583             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
584                 tvb_get_ntohl(tvb, offset) & 0x7fffffff);
585             offset += 4;
586
587             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
588                 tvb_get_ntohl(tvb, offset));
589
590             break;
591           }
592
593         case 8: /* Candidate-RP-Advertisement */
594           {
595             const char *s;
596             int advance;
597             int pfxcnt;
598             guint16 holdtime;
599             int i;
600
601             pfxcnt = tvb_get_guint8(tvb, offset);
602             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
603                 "Prefix-count: %u", pfxcnt);
604             offset += 1;
605             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
606                 "Priority: %u", tvb_get_guint8(tvb, offset));
607             offset += 1;
608             holdtime = tvb_get_ntohs(tvb, offset);
609             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
610                 "Holdtime: %u%s", holdtime,
611                 holdtime == 0xffff ? " (infty)" : "");
612             offset += 2;
613
614             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
615             if (s == NULL)
616                 break;
617             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "RP: %s", s);
618             offset += advance;
619
620             for (i = 0; i < pfxcnt; i++) {
621                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
622                 if (s == NULL)
623                     goto breakbreak8;
624                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
625                     "Group %d: %s", i, s);
626                 offset += advance;
627             }
628     breakbreak8:
629             break;
630           }
631
632         default:
633             break;
634         }
635     }
636 done:;
637 }
638
639 void
640 proto_register_pim(void)
641 {
642     static hf_register_info hf[] = {
643       { &hf_pim_version,
644         { "Version",            "pim.version",
645                                 FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
646       { &hf_pim_type,
647         { "Type",                       "pim.type",
648                                 FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
649       { &hf_pim_cksum,
650         { "Checksum",           "pim.cksum",
651                                 FT_UINT16, BASE_HEX, NULL, 0x0, "" }},
652     };
653     static gint *ett[] = {
654         &ett_pim,
655     };
656
657     proto_pim = proto_register_protocol("Protocol Independent Multicast",
658         "PIM", "pim");
659     proto_register_field_array(proto_pim, hf, array_length(hf));
660     proto_register_subtree_array(ett, array_length(ett));
661 }
662
663 void
664 proto_reg_handoff_pim(void)
665 {
666     dissector_add("ip.proto", IP_PROTO_PIM, dissect_pim, proto_pim);
667
668     /*
669      * Get handles for the IPv4 and IPv6 dissectors.
670      */
671     ip_handle = find_dissector("ip");
672     ipv6_handle = find_dissector("ipv6");
673 }