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