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