From Tobias Witek:
[obnox/wireshark/wip.git] / epan / dissectors / 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$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stddef.h>  /* For offsetof */
31 #include <string.h>
32 #include <glib.h>
33
34 #include <epan/packet.h>
35 #include <epan/ipproto.h>
36 #include <epan/afn.h>
37 #include "packet-ipv6.h"
38 #include <epan/in_cksum.h>
39 #include "packet-pim.h"
40
41 #define PIM_TYPE(x)     ((x) & 0x0f)
42 #define PIM_VER(x)      (((x) & 0xf0) >> 4)
43
44 enum pimv2_addrtype {
45         pimv2_unicast, pimv2_group, pimv2_source
46 };
47
48 static int proto_pim = -1;
49 static int hf_pim_version = -1;
50 static int hf_pim_type = -1;
51 static int hf_pim_code = -1;
52 static int hf_pim_cksum = -1;
53
54 static gint ett_pim = -1;
55
56 static dissector_handle_t ip_handle;
57 static dissector_handle_t ipv6_handle;
58
59 /*
60  * For PIM v1, see the PDF slides at
61  *
62  *      http://www.mbone.de/training/Module3.pdf
63  *
64  * Is it documented anywhere else?
65  */
66 static const char *
67 dissect_pimv1_addr(tvbuff_t *tvb, int offset) {
68     guint16 flags_masklen;
69
70     flags_masklen = tvb_get_ntohs(tvb, offset);
71     if (flags_masklen & 0x0180) {
72         return ep_strdup_printf("(%s%s%s) ",
73             flags_masklen & 0x0100 ? "S" : "",
74             flags_masklen & 0x0080 ? "W" : "",
75             flags_masklen & 0x0040 ? "R" : "");
76     } else {
77         return ep_strdup_printf("%s/%u",
78             ip_to_str(tvb_get_ptr(tvb, offset + 2, 4)), flags_masklen & 0x3f);
79     }
80 }
81
82 static const value_string type1vals[] = {
83     { 0, "Query" },
84     { 1, "Register" },
85     { 2, "Register-stop" },
86     { 3, "Join/Prune" },
87     { 4, "RP-Reachable" },
88     { 5, "Assert" },
89     { 6, "Graft" },
90     { 7, "Graft-Ack" },
91     { 8, "Mode" },
92     { 0, NULL },
93 };
94
95 /* This function is only called from the IGMP dissector */
96 int
97 dissect_pimv1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
98               int offset) {
99     guint8 pim_type;
100     guint8 pim_ver;
101     guint length, pim_length;
102     guint16 pim_cksum, computed_cksum;
103     vec_t cksum_vec[1];
104     proto_tree *pim_tree = NULL;
105     proto_item *ti;
106     proto_tree *pimopt_tree = NULL;
107     proto_item *tiopt;
108
109     if (!proto_is_protocol_enabled(find_protocol_by_id(proto_pim))) {
110         /*
111          * We are not enabled; skip entire packet to be nice to the
112          * IGMP layer (so clicking on IGMP will display the data).
113          */
114         return offset+tvb_length_remaining(tvb, offset);
115     }
116
117     col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIMv1");
118     col_clear(pinfo->cinfo, COL_INFO);
119
120     if (tree) {
121         ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
122         pim_tree = proto_item_add_subtree(ti, ett_pim);
123
124         /* Put IGMP type, 0x14, into the tree */
125         proto_tree_add_text(pim_tree, tvb, offset, 1,
126             "Type: PIM (0x14)");
127     }
128     offset += 1;
129
130     pim_type = tvb_get_guint8(tvb, offset);
131     if (check_col(pinfo->cinfo, COL_INFO))
132         col_add_str(pinfo->cinfo, COL_INFO,
133             val_to_str(pim_type, type1vals, "Unknown (%u)"));
134
135     if (tree) {
136         proto_tree_add_uint(pim_tree, hf_pim_code, tvb, offset, 1, pim_type);
137     }
138     offset += 1;
139
140     pim_cksum = tvb_get_ntohs(tvb, offset);
141     pim_ver = PIM_VER(tvb_get_guint8(tvb, offset + 2));
142     if (pim_ver != 1) {
143         /*
144          * Not PIMv1 - what gives?
145          */
146         if (tree) {
147             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
148                     offset, 2, pim_cksum);
149         }
150         offset += 2;
151         if (tree)
152             proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
153         return offset+tvb_length_remaining(tvb, offset);
154     }
155
156     /*
157      * Well, it's PIM v1, so we can check whether this is a
158      * Register message, and thus can figure out how much to
159      * checksum and whether to make the columns read-only.
160      */
161     length = tvb_length(tvb);
162     if (pim_type == 1) {
163         /*
164          * Register message - the PIM header is 8 bytes long.
165          * Also set the columns non-writable. Otherwise the IPv4 or
166          * IPv6 dissector for the encapsulated packet that caused
167          * this register will overwrite the PIM info in the columns.
168          */
169         pim_length = 8;
170         col_set_writable(pinfo->cinfo, FALSE);
171     } else {
172         /*
173          * Other message - checksum the entire packet.
174          */
175         pim_length = tvb_reported_length(tvb);
176     }
177
178     if (tree) {
179         if (!pinfo->fragmented && length >= pim_length) {
180             /*
181              * The packet isn't part of a fragmented datagram and isn't
182              * truncated, so we can checksum it.
183              */
184             cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
185             cksum_vec[0].len = pim_length;
186             computed_cksum = in_cksum(&cksum_vec[0], 1);
187             if (computed_cksum == 0) {
188                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
189                             offset, 2, pim_cksum,
190                             "Checksum: 0x%04x [correct]",
191                             pim_cksum);
192             } else {
193                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
194                             offset, 2, pim_cksum,
195                             "Checksum: 0x%04x [incorrect, should be 0x%04x]",
196                             pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
197             }
198         } else {
199             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
200                     offset, 2, pim_cksum);
201         }
202     }
203     offset += 2;
204
205     if (tree)
206         proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
207     offset += 1;
208
209     offset += 3;        /* skip reserved stuff */
210
211     if (tree) {
212         if (tvb_reported_length_remaining(tvb, offset) > 0) {
213             tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
214                     "PIM parameters");
215             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
216         } else
217             goto done;
218
219         /* version 1 decoder */
220         switch (pim_type) {
221         case 0: /* query */
222           {
223             guint8 mode;
224             guint16 holdtime;
225             static const value_string pimv1_modevals[] = {
226                 { 0, "Dense" },
227                 { 1, "Sparse" },
228                 { 2, "Sparse-Dense" },
229                 { 0, NULL },
230             };
231
232             mode = tvb_get_guint8(tvb, offset) >> 4;
233             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
234                 "Mode: %s",
235                 val_to_str(mode, pimv1_modevals, "Unknown (%u)"));
236             offset += 2;
237             holdtime = tvb_get_ntohs(tvb, offset);
238             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
239                 "Holdtime: %u%s", holdtime,
240                 holdtime == 0xffff ? " (infty)" : "");
241             offset += 2;
242             break;
243           }
244
245         case 1: /* register */
246           {
247             guint8 v_hl;
248             tvbuff_t *next_tvb;
249
250             /*
251              * The rest of the packet is a multicast data packet.
252              */
253             next_tvb = tvb_new_subset_remaining(tvb, offset);
254
255             /*
256              * It's an IP packet - determine whether it's IPv4 or IPv6.
257              */
258             v_hl = tvb_get_guint8(tvb, offset);
259             switch((v_hl & 0xf0) >> 4) {
260             case 0:     /* Null-Register dummy header.
261                          * Has the same address family as the encapsulating PIM packet,
262                          * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
263                          */
264                     if (pinfo->src.type == AT_IPv4) {
265                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
266                                                 "IPv4 dummy header");
267                             proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
268                                                 "Source: %s",
269                                                 ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
270                             proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
271                                                 "Group: %s",
272                                                 ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
273                     } else if (pinfo->src.type == AT_IPv6) {
274                             struct ip6_hdr ip6_hdr;
275                             tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
276                                        sizeof ip6_hdr);
277                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
278                                                 "IPv6 dummy header");
279                             proto_tree_add_text(pimopt_tree, tvb,
280                                                 offset + offsetof(struct ip6_hdr, ip6_src), 16,
281                                                 "Source: %s",
282                                                 ip6_to_str(&ip6_hdr.ip6_src));
283                             proto_tree_add_text(pimopt_tree, tvb,
284                                                 offset + offsetof(struct ip6_hdr, ip6_dst), 16,
285                                                 "Group: %s",
286                                                 ip6_to_str(&ip6_hdr.ip6_dst));
287                     } else
288                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
289                                                 "Dummy header for an unknown protocol");
290                     break;
291             case 4:     /* IPv4 */
292 #if 0
293                     call_dissector(ip_handle, next_tvb, pinfo, tree);
294 #else
295                     call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
296 #endif
297                     break;
298             case 6:     /* IPv6 */
299 #if 0
300                     call_dissector(ipv6_handle, next_tvb, pinfo, tree);
301 #else
302                     call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
303 #endif
304                     break;
305             default:
306                     proto_tree_add_text(pimopt_tree, tvb, offset, -1,
307                         "Unknown IP version %d", (v_hl & 0xf0) >> 4);
308                     break;
309             }
310             break;
311           }
312
313         case 2: /* register-stop */
314           {
315             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
316                 "Group: %s",
317                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
318             offset += 4;
319             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
320                 "Source: %s",
321                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
322             offset += 4;
323             break;
324           }
325
326         case 3: /* join/prune */
327         case 6: /* graft */
328         case 7: /* graft-ack */
329           {
330             int off;
331             const char *s;
332             int ngroup, i, njoin, nprune, j;
333             guint16 holdtime;
334             guint8 mask_len;
335             guint8 adr_len;
336             proto_tree *grouptree = NULL;
337             proto_item *tigroup;
338             proto_tree *subtree = NULL;
339             proto_item *tisub;
340
341             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
342                 "Upstream-neighbor: %s",
343                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
344             offset += 4;
345
346             offset += 2;        /* skip reserved stuff */
347
348             holdtime = tvb_get_ntohs(tvb, offset);
349             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
350                 "Holdtime: %u%s", holdtime,
351                 holdtime == 0xffff ? " (infty)" : "");
352             offset += 2;
353
354             offset += 1;        /* skip reserved stuff */
355
356             mask_len = tvb_get_guint8(tvb, offset);
357             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
358                 "Mask length: %u", mask_len);
359             offset += 1;
360
361             adr_len = tvb_get_guint8(tvb, offset);
362             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
363                 "Address length: %u", adr_len);
364             offset += 1;
365
366             ngroup = tvb_get_guint8(tvb, offset);
367             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
368                 "Groups: %u", ngroup);
369             offset += 1;
370
371             for (i = 0; i < ngroup; i++) {
372                 /*
373                  * XXX - does the group address have the length "adr_len"
374                  * and the group mask the length "mask_len"?
375                  */
376                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
377                     "Group %d: %s", i,
378                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
379                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
380                 offset += 4;
381
382                 proto_tree_add_text(grouptree, tvb, offset, 4,
383                     "Group %d Mask: %s", i,
384                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
385                 offset += 4;
386
387                 njoin = tvb_get_ntohs(tvb, offset);
388                 nprune = tvb_get_ntohs(tvb, offset + 2);
389
390                 tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
391                     "Join: %d", njoin);
392                 subtree = proto_item_add_subtree(tisub, ett_pim);
393                 off = offset + 4;
394                 for (j = 0; j < njoin; j++) {
395                     s = dissect_pimv1_addr(tvb, off);
396                     proto_tree_add_text(subtree, tvb, off, 6,
397                         "IP address: %s", s);
398                     off += 6;
399                 }
400
401                 tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
402                     "Prune: %d", nprune);
403                 subtree = proto_item_add_subtree(tisub, ett_pim);
404                 for (j = 0; j < nprune; j++) {
405                     s = dissect_pimv1_addr(tvb, off);
406                     proto_tree_add_text(subtree, tvb, off, 6,
407                         "IP address: %s", s);
408                     off += 6;
409                 }
410                 offset = off;
411             }
412             break;
413           }
414
415         case 4: /* rp-reachability */
416           {
417             guint16 holdtime;
418
419             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
420                 "Group Address: %s",
421                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
422             offset += 4;
423
424             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
425                 "Group Mask: %s",
426                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
427             offset += 4;
428
429             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
430                 "RP Address: %s",
431                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
432             offset += 4;
433
434             offset += 2;        /* skip reserved stuff */
435
436             holdtime = tvb_get_ntohs(tvb, offset);
437             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
438                 "Holdtime: %u%s", holdtime,
439                 holdtime == 0xffff ? " (infty)" : "");
440             offset += 2;
441             break;
442           }
443
444         case 5: /* assert */
445           {
446             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
447                 "Group Address: %s",
448                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
449             offset += 4;
450
451             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
452                 "Group Mask: %s",
453                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
454             offset += 4;
455
456             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
457                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
458                     "RP Tree", "Not RP Tree"));
459             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
460                 tvb_get_ntohl(tvb, offset) & 0x7fffffff);
461             offset += 4;
462
463             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
464                 tvb_get_ntohl(tvb, offset));
465
466             break;
467           }
468
469         default:
470             break;
471         }
472     }
473 done:;
474
475     return offset+tvb_length_remaining(tvb, offset);
476 }
477
478 static const char *
479 dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
480         int *advance) {
481     emem_strbuf_t *strbuf;
482     guint8 af;
483     guint8 et;
484     guint8 flags;
485     guint8 mask_len;
486     int len = 0;
487
488     af = tvb_get_guint8(tvb, offset);
489     if (af != AFNUM_INET && af != AFNUM_INET6) {
490         /*
491          * We don't handle the other formats, and addresses don't include
492          * a length field, so we can't even show them as raw bytes.
493          */
494         return NULL;
495     }
496
497     et = tvb_get_guint8(tvb, offset + 1);
498     if (et != 0) {
499         /*
500          * The only defined encoding type is 0, for the native encoding;
501          * again, as addresses don't include a length field, we can't
502          * even show addresses with a different encoding type as raw
503          * bytes.
504          */
505         return NULL;
506     }
507
508     strbuf = ep_strbuf_new_label(NULL);
509     switch (at) {
510     case pimv2_unicast:
511         switch (af) {
512         case AFNUM_INET:
513             len = 4;
514             ep_strbuf_printf(strbuf, "%s", ip_to_str(tvb_get_ptr(tvb, offset + 2, len)));
515             break;
516
517         case AFNUM_INET6:
518             len = 16;
519             ep_strbuf_printf(strbuf, "%s", ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 2, len)));
520             break;
521         }
522         if (advance)
523             *advance = 2 + len;
524         break;
525
526     case pimv2_group:
527         mask_len = tvb_get_guint8(tvb, offset + 3);
528         switch (af) {
529         case AFNUM_INET:
530             len = 4;
531             ep_strbuf_printf(strbuf, "%s/%u",
532                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
533             break;
534
535         case AFNUM_INET6:
536             len = 16;
537             ep_strbuf_printf(strbuf, "%s/%u",
538                 ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
539             break;
540         }
541         if (advance)
542             *advance = 4 + len;
543         break;
544
545     case pimv2_source:
546         flags = tvb_get_guint8(tvb, offset + 2);
547         mask_len = tvb_get_guint8(tvb, offset + 3);
548         switch (af) {
549         case AFNUM_INET:
550             len = 4;
551             ep_strbuf_printf(strbuf, "%s/%u",
552                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
553             break;
554
555         case AFNUM_INET6:
556             len = 16;
557             ep_strbuf_printf(strbuf, "%s/%u",
558                 ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
559             break;
560         }
561         if (flags) {
562             ep_strbuf_append_printf(strbuf,
563                 " (%s%s%s)",
564                 flags & 0x04 ? "S" : "",
565                 flags & 0x02 ? "W" : "",
566                 flags & 0x01 ? "R" : "");
567         }
568         if (advance)
569             *advance = 4 + len;
570         break;
571     default:
572         return NULL;
573     }
574
575     return strbuf->str;
576 }
577
578 static const value_string type2vals[] = {
579     { 0, "Hello" },
580     { 1, "Register" },
581     { 2, "Register-stop" },
582     { 3, "Join/Prune" },
583     { 4, "Bootstrap" },
584     { 5, "Assert" },
585     { 6, "Graft" },
586     { 7, "Graft-Ack" },
587     { 8, "Candidate-RP-Advertisement" },
588     { 9, "State-Refresh" },
589     { 0, NULL }
590 };
591
592 /*
593  * For PIM v2, see RFC 4601, RFC 3973 and draft-ietf-pim-sm-v2-new-03
594  * (when PIM is run over IPv6, the rules for computing the PIM checksum
595  * from the draft in question, not from RFC 2362, should be used).
596  */
597 static void
598 dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
599     int offset = 0;
600     guint8 pim_typever;
601     guint length, pim_length;
602     guint16 pim_cksum, computed_cksum;
603     vec_t cksum_vec[4];
604     guint32 phdr[2];
605     const char *typestr;
606     proto_tree *pim_tree = NULL;
607     proto_item *ti;
608     proto_tree *pimopt_tree = NULL;
609     proto_item *tiopt;
610
611     col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIM");
612     col_clear(pinfo->cinfo, COL_INFO);
613
614     pim_typever = tvb_get_guint8(tvb, 0);
615
616     switch (PIM_VER(pim_typever)) {
617     case 2:
618         typestr = val_to_str(PIM_TYPE(pim_typever), type2vals, "Unknown (%u)");
619         break;
620     case 1:     /* PIMv1 - we should never see this */
621     default:
622         typestr = "Unknown";
623         break;
624     }
625
626     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
627         col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "PIMv%d",
628             PIM_VER(pim_typever));
629     }
630     if (check_col(pinfo->cinfo, COL_INFO))
631         col_add_str(pinfo->cinfo, COL_INFO, typestr);
632
633     if (tree) {
634         ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
635         pim_tree = proto_item_add_subtree(ti, ett_pim);
636
637         proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1,
638             PIM_VER(pim_typever));
639         proto_tree_add_uint(pim_tree, hf_pim_type, tvb, offset, 1,
640             PIM_TYPE(pim_typever));
641
642         pim_cksum = tvb_get_ntohs(tvb, offset + 2);
643         length = tvb_length(tvb);
644         if (PIM_VER(pim_typever) == 2) {
645             /*
646              * Well, it's PIM v2, so we can check whether this is a Register
647              * message, and thus can figure out how much to checksum and
648              * whether to make the columns read-only.
649              */
650             if (PIM_TYPE(pim_typever) == 1) {
651                 /*
652                  * Register message - the PIM header is 8 bytes long.
653                  * Also set the columns non-writable. Otherwise the IPv4 or
654                  * IPv6 dissector for the encapsulated packet that caused
655                  * this register will overwrite the PIM info in the columns.
656                  */
657                 pim_length = 8;
658                 col_set_writable(pinfo->cinfo, FALSE);
659             } else {
660                 /*
661                  * Other message - checksum the entire packet.
662                  */
663                 pim_length = tvb_reported_length(tvb);
664             }
665         } else {
666             /*
667              * We don't know what type of message this is, so say that
668              * the length is 0, to force it not to be checksummed.
669              */
670             pim_length = 0;
671         }
672         if (!pinfo->fragmented && length >= pim_length) {
673             /*
674              * The packet isn't part of a fragmented datagram and isn't
675              * truncated, so we can checksum it.
676              */
677
678             switch (pinfo->src.type) {
679             case AT_IPv4:
680                 cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
681                 cksum_vec[0].len = pim_length;
682                 computed_cksum = in_cksum(&cksum_vec[0], 1);
683                 break;
684             case AT_IPv6:
685                 /* Set up the fields of the pseudo-header. */
686                 cksum_vec[0].ptr = pinfo->src.data;
687                 cksum_vec[0].len = pinfo->src.len;
688                 cksum_vec[1].ptr = pinfo->dst.data;
689                 cksum_vec[1].len = pinfo->dst.len;
690                 cksum_vec[2].ptr = (const guint8 *)&phdr;
691                 phdr[0] = g_htonl(pim_length);
692                 phdr[1] = g_htonl(IP_PROTO_PIM);
693                 cksum_vec[2].len = 8;
694                 cksum_vec[3].ptr = tvb_get_ptr(tvb, 0, pim_length);
695                 cksum_vec[3].len = pim_length;
696                 computed_cksum = in_cksum(&cksum_vec[0], 4);
697                 break;
698             default:
699                 /* PIM is available for IPv4 and IPv6 right now */
700                 computed_cksum = 0;     /* squelch GCC complaints */
701                 DISSECTOR_ASSERT_NOT_REACHED();
702                 break;
703             }
704
705             if (computed_cksum == 0) {
706                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
707                         offset + 2, 2, pim_cksum,
708                         "Checksum: 0x%04x [correct]",
709                         pim_cksum);
710             } else {
711                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
712                         offset + 2, 2, pim_cksum,
713                         "Checksum: 0x%04x [incorrect, should be 0x%04x]",
714                         pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
715             }
716         } else {
717             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
718                 offset + 2, 2, pim_cksum);
719         }
720
721         offset += 4;
722
723         if (tvb_reported_length_remaining(tvb, offset) > 0) {
724             tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
725                 "PIM parameters");
726             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
727         } else
728             goto done;
729
730         if (PIM_VER(pim_typever) != 2)
731             goto done;
732
733         /* version 2 decoder */
734         switch (PIM_TYPE(pim_typever)) {
735         case 0: /*hello*/
736           {
737             while (tvb_reported_length_remaining(tvb, offset) >= 2) {
738                 guint16 hello_opt, opt_len;
739                 guint16 holdtime;
740                 guint16 lan_delay;
741                 guint16 override_interval;
742                 guint32 priority;
743                 guint32 opt_value = 0;
744
745                 hello_opt = tvb_get_ntohs(tvb, offset);
746                 opt_len = tvb_get_ntohs(tvb, offset + 2);
747
748                 if(opt_len == 2)
749                         opt_value = tvb_get_ntohs(tvb, offset + 4);
750                 if(opt_len == 4)
751                         opt_value = tvb_get_ntohl(tvb, offset + 4);
752
753                 switch(hello_opt) {
754                 case 1: /* holdtime */
755                         holdtime = opt_value;
756                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
757                                             "Holdtime (%u): %us %s", hello_opt, holdtime,
758                                             holdtime == 0xffff ? " (infty)" : "");
759                         break;
760                 case 2: /* LAN prune delay
761                          *
762                          * 0                   1                   2                   3
763                          * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
764                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
765                          * |            Type = 2           |           Length = 4          |
766                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
767                          * |T|       LAN Prune Delay       |       Override Interval       |
768                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
769                          */
770                 {
771                         proto_tree *sub_tree = NULL;
772                         proto_item *landelay_option;
773
774                         landelay_option = proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
775                                                         "LAN Prune Delay (%u)", hello_opt);
776                         sub_tree = proto_item_add_subtree(landelay_option, ett_pim);
777
778                         lan_delay = (opt_value & 0x7fff0000) >> 16;
779                         override_interval = opt_value & 0x0000ffff;
780                         proto_tree_add_text(sub_tree, tvb, offset + 4, 1,
781                                             "T bit is %s",
782                                             (opt_value & 0x80000000) ? "set" : "not set");
783                         proto_tree_add_text(sub_tree, tvb, offset + 4, 2,
784                                             "LAN Delay: %ums", lan_delay);
785                         proto_tree_add_text(sub_tree, tvb, offset + 6, 2,
786                                             "Override Interval: %ums", override_interval);
787                         break;
788                 }
789                 case 19: /* priority */
790                         priority = opt_value;
791                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
792                                         "DR Priority (%u): %u", hello_opt, priority);
793                         break;
794                 case 20: /* generation ID */
795                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
796                                             "Generation ID (%u): %d", hello_opt, opt_value);
797                         break;
798
799                 case 24: /* address list */
800                 case 65001: /* address list (old implementations) */
801                 {
802                         int i;
803                         proto_tree *sub_tree = NULL;
804                         proto_item *addrlist_option;
805
806                         addrlist_option = proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
807                                             "%sAddress List (%u)",
808                                             hello_opt == 65001 ? "old " : "",
809                                             hello_opt);
810                         sub_tree = proto_item_add_subtree(addrlist_option, ett_pim);
811
812                         for (i = offset + 4; i < offset + 4 + opt_len; ) {
813                                 int advance;
814                                 const char *s;
815
816                                 s = dissect_pim_addr(tvb, i, pimv2_unicast, &advance);
817                                 if (s == NULL)
818                                         break;
819                                 proto_tree_add_text(sub_tree, tvb, offset, 
820                                                     advance, "Address: %s", s);
821                                 i += advance;
822                         }
823                         break;
824                 }
825                 default:
826                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
827                                             "Unknown option (%u), length: %u, value: 0x%x",
828                                             hello_opt, opt_len, opt_value);
829                         break;
830                 }
831                 offset += 4 + opt_len;
832             }
833             break;
834           }
835
836         case 1: /* register */
837           {
838             guint32 flags;
839             guint8 v_hl;
840             tvbuff_t *next_tvb;
841             proto_tree *flag_tree = NULL;
842             proto_item *tiflag;
843
844             flags = tvb_get_ntohl(tvb, offset);
845             tiflag = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
846                 "Flags: 0x%08x", flags);
847             flag_tree = proto_item_add_subtree(tiflag, ett_pim);
848             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
849                 decode_boolean_bitfield(flags, 0x80000000, 32,
850                     "Border", "Not border"));
851             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
852                 decode_boolean_bitfield(flags, 0x40000000, 32,
853                     "Null-Register", "Not Null-Register"));
854             offset += 4;
855
856             /*
857              * The rest of the packet is a multicast data packet.
858              */
859             next_tvb = tvb_new_subset_remaining(tvb, offset);
860
861             /*
862              * It's an IP packet - determine whether it's IPv4 or IPv6.
863              */
864             v_hl = tvb_get_guint8(tvb, offset);
865             switch((v_hl & 0xf0) >> 4) {
866             case 0:     /* Null-Register dummy header.
867                          * Has the same address family as the encapsulating PIM packet,
868                          * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
869                          */
870                     if (pinfo->src.type == AT_IPv4) {
871                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
872                                                 "IPv4 dummy header");
873                             proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
874                                                 "Source: %s",
875                                                 ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
876                             proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
877                                                 "Group: %s",
878                                                 ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
879                     } else if (pinfo->src.type == AT_IPv6) {
880                             struct ip6_hdr ip6_hdr;
881                             tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
882                                        sizeof ip6_hdr);
883                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
884                                                 "IPv6 dummy header");
885                             proto_tree_add_text(pimopt_tree, tvb,
886                                                 offset + offsetof(struct ip6_hdr, ip6_src), 16,
887                                                 "Source: %s",
888                                                 ip6_to_str(&ip6_hdr.ip6_src));
889                             proto_tree_add_text(pimopt_tree, tvb,
890                                                 offset + offsetof(struct ip6_hdr, ip6_dst), 16,
891                                                 "Group: %s",
892                                                 ip6_to_str(&ip6_hdr.ip6_dst));
893                     } else
894                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
895                                                 "Dummy header for an unknown protocol");
896                     break;
897             case 4:     /* IPv4 */
898 #if 0
899                     call_dissector(ip_handle, next_tvb, pinfo, tree);
900 #else
901                     call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
902 #endif
903                     break;
904             case 6:     /* IPv6 */
905 #if 0
906                     call_dissector(ipv6_handle, next_tvb, pinfo, tree);
907 #else
908                     call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
909 #endif
910                     break;
911             default:
912                     proto_tree_add_text(pimopt_tree, tvb, offset, -1,
913                         "Unknown IP version %d", (v_hl & 0xf0) >> 4);
914                     break;
915             }
916             break;
917           }
918
919         case 2: /* register-stop */
920           {
921             int advance;
922             const char *s;
923
924             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
925             if (s == NULL)
926                 break;
927             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
928             offset += advance;
929             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
930             if (s == NULL)
931                 break;
932             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
933             break;
934           }
935
936         case 3: /* join/prune */
937         case 6: /* graft */
938         case 7: /* graft-ack */
939           {
940             int advance;
941             int off;
942             const char *s;
943             int ngroup, i, njoin, nprune, j;
944             guint16 holdtime;
945             proto_tree *grouptree = NULL;
946             proto_item *tigroup;
947             proto_tree *subtree = NULL;
948             proto_item *tisub;
949
950             if (PIM_TYPE(pim_typever) != 7) {
951                 /* not graft-ack */
952                 s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
953                 if (s == NULL)
954                     break;
955                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
956                     "Upstream-neighbor: %s", s);
957                 offset += advance;
958             }
959
960             offset += 1;        /* skip reserved field */
961
962             ngroup = tvb_get_guint8(tvb, offset);
963             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
964                 "Groups: %u", ngroup);
965             offset += 1;
966
967             if (PIM_TYPE(pim_typever) != 7)     {
968                 /* not graft-ack */
969                 holdtime = tvb_get_ntohs(tvb, offset);
970                 proto_tree_add_text(pimopt_tree, tvb, offset, 2,
971                     "Holdtime: %u%s", holdtime,
972                     holdtime == 0xffff ? " (infty)" : "");
973             }
974             offset += 2;
975
976             for (i = 0; i < ngroup; i++) {
977                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
978                 if (s == NULL)
979                     goto breakbreak3;
980                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
981                     "Group %d: %s", i, s);
982                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
983                 offset += advance;
984
985                 njoin = tvb_get_ntohs(tvb, offset);
986                 nprune = tvb_get_ntohs(tvb, offset + 2);
987
988                 tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
989                     "Join: %d", njoin);
990                 subtree = proto_item_add_subtree(tisub, ett_pim);
991                 off = offset + 4;
992                 for (j = 0; j < njoin; j++) {
993                     s = dissect_pim_addr(tvb, off, pimv2_source,
994                         &advance);
995                     if (s == NULL)
996                         goto breakbreak3;
997                     proto_tree_add_text(subtree, tvb, off, advance,
998                         "IP address: %s", s);
999                     off += advance;
1000                 }
1001
1002                 tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
1003                     "Prune: %d", nprune);
1004                 subtree = proto_item_add_subtree(tisub, ett_pim);
1005                 for (j = 0; j < nprune; j++) {
1006                     s = dissect_pim_addr(tvb, off, pimv2_source,
1007                         &advance);
1008                     if (s == NULL)
1009                         goto breakbreak3;
1010                     proto_tree_add_text(subtree, tvb, off, advance,
1011                         "IP address: %s", s);
1012                     off += advance;
1013                 }
1014                 offset = off;
1015             }
1016     breakbreak3:
1017             break;
1018           }
1019
1020         case 4: /* bootstrap */
1021           {
1022             const char *s;
1023             int advance;
1024             int i, j;
1025             int frpcnt;
1026             guint16 holdtime;
1027             proto_tree *grouptree = NULL;
1028             proto_item *tigroup;
1029
1030             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
1031                 "Fragment tag: 0x%04x", tvb_get_ntohs(tvb, offset));
1032             offset += 2;
1033
1034             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1035                 "Hash mask len: %u", tvb_get_guint8(tvb, offset));
1036             offset += 1;
1037             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1038                 "BSR priority: %u", tvb_get_guint8(tvb, offset));
1039             offset += 1;
1040
1041             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1042             if (s == NULL)
1043                 break;
1044             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "BSR: %s", s);
1045             offset += advance;
1046
1047             for (i = 0; tvb_reported_length_remaining(tvb, offset) > 0; i++) {
1048                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1049                 if (s == NULL)
1050                     goto breakbreak4;
1051                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1052                     "Group %d: %s", i, s);
1053                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
1054                 offset += advance;
1055
1056                 proto_tree_add_text(grouptree, tvb, offset, 1,
1057                     "RP count: %u", tvb_get_guint8(tvb, offset));
1058                 offset += 1;
1059                 frpcnt = tvb_get_guint8(tvb, offset);
1060                 proto_tree_add_text(grouptree, tvb, offset, 1,
1061                     "FRP count: %u", frpcnt);
1062                 offset += 3;
1063
1064                 for (j = 0; j < frpcnt; j++) {
1065                     s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1066                     if (s == NULL)
1067                         goto breakbreak4;
1068                     proto_tree_add_text(grouptree, tvb, offset, advance,
1069                         "RP %d: %s", j, s);
1070                     offset += advance;
1071
1072                     holdtime = tvb_get_ntohs(tvb, offset);
1073                     proto_tree_add_text(grouptree, tvb, offset, 2,
1074                         "Holdtime: %u%s", holdtime,
1075                         holdtime == 0xffff ? " (infty)" : "");
1076                     offset += 2;
1077                     proto_tree_add_text(grouptree, tvb, offset, 1,
1078                         "Priority: %u", tvb_get_guint8(tvb, offset));
1079                     offset += 2;        /* also skips reserved field */
1080                 }
1081             }
1082
1083     breakbreak4:
1084             break;
1085           }
1086
1087         case 5: /* assert */
1088           {
1089             const char *s;
1090             int advance;
1091
1092             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1093             if (s == NULL)
1094                 break;
1095             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
1096             offset += advance;
1097
1098             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1099             if (s == NULL)
1100                 break;
1101             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
1102             offset += advance;
1103
1104             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
1105                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
1106                     "RP Tree", "Not RP Tree"));
1107             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
1108                 tvb_get_ntohl(tvb, offset) & 0x7fffffff);
1109             offset += 4;
1110
1111             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
1112                 tvb_get_ntohl(tvb, offset));
1113
1114             break;
1115           }
1116
1117         case 8: /* Candidate-RP-Advertisement */
1118           {
1119             const char *s;
1120             int advance;
1121             int pfxcnt;
1122             guint16 holdtime;
1123             int i;
1124
1125             pfxcnt = tvb_get_guint8(tvb, offset);
1126             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1127                 "Prefix-count: %u", pfxcnt);
1128             offset += 1;
1129             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1130                 "Priority: %u", tvb_get_guint8(tvb, offset));
1131             offset += 1;
1132             holdtime = tvb_get_ntohs(tvb, offset);
1133             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
1134                 "Holdtime: %u%s", holdtime,
1135                 holdtime == 0xffff ? " (infty)" : "");
1136             offset += 2;
1137
1138             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1139             if (s == NULL)
1140                 break;
1141             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "RP: %s", s);
1142             offset += advance;
1143
1144             for (i = 0; i < pfxcnt; i++) {
1145                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1146                 if (s == NULL)
1147                     goto breakbreak8;
1148                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1149                     "Group %d: %s", i, s);
1150                 offset += advance;
1151             }
1152     breakbreak8:
1153             break;
1154           }
1155
1156         case 9: /* State-Refresh */
1157           {
1158             const char *s;
1159             int advance;
1160
1161             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1162             if (s == NULL)
1163                 break;
1164             proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1165                 "Group: %s", s);
1166             offset += advance;
1167
1168             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1169             if (s == NULL)
1170                 break;
1171             proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1172                 "Source: %s", s);
1173             offset += advance;
1174
1175             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1176             if (s == NULL)
1177                 break;
1178             proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1179                 "Originator: %s", s);
1180             offset += advance;
1181
1182             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Rendezvous Point Tree %s",
1183                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 1, 1,
1184                     "set", "clear"));
1185             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
1186                 "Metric Preference: %u", tvb_get_ntohl(tvb, offset) & 0x7FFFFFFF);
1187             offset += 4;
1188
1189             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
1190                 "Metric: %u", tvb_get_ntohl(tvb, offset));
1191             offset += 4;
1192
1193             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1194                 "Masklen: %u", tvb_get_guint8(tvb, offset));
1195             offset += 1;
1196
1197             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1198                 "TTL: %u", tvb_get_guint8(tvb, offset));
1199             offset += 1;
1200
1201             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Prune indicator %s",
1202                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
1203                     "set", "clear"));
1204             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Prune now %s",
1205                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x40, 8,
1206                     "set", "clear"));
1207             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Assert override %s",
1208                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x20, 8,
1209                     "set", "clear"));
1210             offset += 1;
1211
1212             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1213                 "Interval: %u", tvb_get_guint8(tvb, offset));
1214             offset += 1;
1215
1216             break;
1217           }
1218
1219         default:
1220             break;
1221         }
1222     }
1223 done:;
1224 }
1225
1226 void
1227 proto_register_pim(void)
1228 {
1229     static hf_register_info hf[] = {
1230       { &hf_pim_version,
1231         { "Version",            "pim.version",
1232                                 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1233       { &hf_pim_type,
1234         { "Type",               "pim.type",
1235                                 FT_UINT8, BASE_DEC, VALS(type2vals), 0x0, NULL, HFILL }},
1236       { &hf_pim_code,
1237         { "Code",               "pim.code",
1238                                 FT_UINT8, BASE_DEC, VALS(type1vals), 0x0, NULL, HFILL }},
1239       { &hf_pim_cksum,
1240         { "Checksum",           "pim.cksum",
1241                                 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1242     };
1243     static gint *ett[] = {
1244         &ett_pim,
1245     };
1246
1247     proto_pim = proto_register_protocol("Protocol Independent Multicast",
1248         "PIM", "pim");
1249     proto_register_field_array(proto_pim, hf, array_length(hf));
1250     proto_register_subtree_array(ett, array_length(ett));
1251 }
1252
1253 void
1254 proto_reg_handoff_pim(void)
1255 {
1256     dissector_handle_t pim_handle;
1257
1258     pim_handle = create_dissector_handle(dissect_pim, proto_pim);
1259     dissector_add("ip.proto", IP_PROTO_PIM, pim_handle);
1260
1261     /*
1262      * Get handles for the IPv4 and IPv6 dissectors.
1263      */
1264     ip_handle = find_dissector("ip");
1265     ipv6_handle = find_dissector("ipv6");
1266 }