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