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