Updates from Ed Warnicke.
[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.19 2000/11/18 10:38:25 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  * 
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H 
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38
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 "packet-ip.h"
48 #include "packet-ipv6.h"
49
50 #ifndef offsetof
51 #define offsetof(type, member)  ((size_t)(&((type *)0)->member))
52 #endif
53
54 struct pim {
55         guint8  pim_typever;
56 #define PIM_TYPE(x)     ((x) & 0x0f)
57 #define PIM_VER(x)      (((x) & 0xf0) >> 4)
58         guint8  pim_rsv;        /* Reserved */
59         guint16 pim_cksum;      /* IP style check sum */
60 };
61
62 enum pimv2_addrtype {
63         pimv2_unicast, pimv2_group, pimv2_source
64 };
65
66 static int proto_pim = -1;
67 static int hf_pim_version = -1;
68 static int hf_pim_type = -1;
69 static int hf_pim_cksum = -1;
70
71 static gint ett_pim = -1;
72
73 static dissector_handle_t ip_handle;
74
75 static const char *
76 dissect_pim_addr(const u_char *bp, const u_char *ep, enum pimv2_addrtype at,
77         int *advance) {
78     static char buf[512];
79     int af;
80     int len = 0;
81
82     if (bp >= ep)
83         return NULL;
84
85     switch (bp[0]) {
86     case 1:
87         af = 4;
88         break;
89     case 2:
90         af = 6;
91         break;
92     default:
93         return NULL;
94     }
95
96     if (bp[1] != 0)
97         return NULL;
98
99     switch (at) {
100     case pimv2_unicast:
101         if (af == 4) {
102             len = 4;
103             if (bp + 2 + len > ep)
104                 return NULL;
105             (void)snprintf(buf, sizeof(buf), "%s", ip_to_str(bp + 2));
106         }
107         else if (af == 6) {
108             len = 16;
109             if (bp + 2 + len > ep)
110                 return NULL;
111             (void)snprintf(buf, sizeof(buf), "%s",
112                 ip6_to_str((struct e_in6_addr *)(bp + 2)));
113         }
114         if (advance)
115             *advance = 2 + len;
116         break;
117
118     case pimv2_group:
119         if (af == 4) {
120             len = 4;
121             if (bp + 4 + len > ep)
122                 return NULL;
123             (void)snprintf(buf, sizeof(buf), "%s/%u", ip_to_str(bp + 4), bp[3]);
124         }
125         else if (af == 6) {
126             len = 16;
127             if (bp + 4 + len > ep)
128                 return NULL;
129             (void)snprintf(buf, sizeof(buf), "%s/%u",
130                 ip6_to_str((struct e_in6_addr *)(bp + 4)), bp[3]);
131         }
132         if (advance)
133             *advance = 4 + len;
134         break;
135     case pimv2_source:
136         if (af == 4) {
137             len = 4;
138             if (bp + 4 + len > ep)
139                 return NULL;
140             (void)snprintf(buf, sizeof(buf), "%s/%u", ip_to_str(bp + 4), bp[3]);
141         }
142         else if (af == 6) {
143             len = 16;
144             if (bp + 4 + len > ep)
145                 return NULL;
146             (void)snprintf(buf, sizeof(buf), "%s/%u",
147                 ip6_to_str((struct e_in6_addr *)(bp + 4)), bp[3]);
148         }
149         if (bp[2]) {
150             (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
151                 " (%s%s%s)",
152                 bp[2] & 0x04 ? "S" : "",
153                 bp[2] & 0x02 ? "W" : "",
154                 bp[2] & 0x01 ? "R" : "");
155         }
156         if (advance)
157             *advance = 4 + len;
158         break;
159     default:
160         return NULL;
161     }
162
163     return buf;
164 }
165
166 static void 
167 dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
168     struct pim pim;
169     static const value_string type1vals[] = {
170         { 0, "Query" },
171         { 1, "Register" },
172         { 2, "Register-stop" },
173         { 3, "Join/Prune" },
174         { 4, "RP-Reachable" },
175         { 5, "Assert" },
176         { 6, "Graft" },
177         { 7, "Graft-Ack" },
178         { 8, "Mode" },
179         { 0, NULL },
180     };
181     static const value_string type2vals[] = {
182         { 0, "Hello" },
183         { 1, "Register" },
184         { 2, "Register-stop" },
185         { 3, "Join/Prune" },
186         { 4, "Bootstrap" },
187         { 5, "Assert" },
188         { 6, "Graft" },
189         { 7, "Graft-Ack" },
190         { 8, "Candidate-RP-Advertisement" },
191         { 0, NULL },
192     };
193     char *typestr;
194     proto_tree *pim_tree = NULL;
195         proto_item *ti; 
196     proto_tree *pimopt_tree = NULL;
197         proto_item *tiopt; 
198
199     OLD_CHECK_DISPLAY_AS_DATA(proto_pim, pd, offset, fd, tree);
200
201     /* avoid alignment problem */
202     memcpy(&pim, &pd[offset], sizeof(pim));
203
204     switch (PIM_VER(pim.pim_typever)) {
205     case 1:
206         typestr = val_to_str(PIM_TYPE(pim.pim_typever), type1vals, "Unknown");
207         break;
208     case 2:
209         typestr = val_to_str(PIM_TYPE(pim.pim_typever), type2vals, "Unknown");
210         break;
211     default:
212         typestr = "Unknown";
213         break;
214     }
215
216     if (check_col(fd, COL_PROTOCOL)) {
217         col_add_fstr(fd, COL_PROTOCOL, "PIM version %d",
218             PIM_VER(pim.pim_typever));
219     }
220     if (check_col(fd, COL_INFO))
221         col_add_fstr(fd, COL_INFO, "%s", typestr); 
222
223     if (tree) {
224         ti = proto_tree_add_item(tree, proto_pim, NullTVB, offset, END_OF_FRAME, FALSE);
225         pim_tree = proto_item_add_subtree(ti, ett_pim);
226
227         proto_tree_add_uint(pim_tree, hf_pim_version, NullTVB, offset, 1,
228             PIM_VER(pim.pim_typever)); 
229         proto_tree_add_uint_format(pim_tree, hf_pim_type, NullTVB, offset, 1,
230             PIM_TYPE(pim.pim_typever),
231             "Type: %s (%u)", typestr, PIM_TYPE(pim.pim_typever)); 
232
233         proto_tree_add_uint(pim_tree, hf_pim_cksum, NullTVB,
234             offset + offsetof(struct pim, pim_cksum), sizeof(pim.pim_cksum),
235             ntohs(pim.pim_cksum));
236
237         if (sizeof(struct pim) < END_OF_FRAME) {
238             tiopt = proto_tree_add_text(pim_tree, NullTVB,
239                 offset + sizeof(struct pim), END_OF_FRAME,
240                 "PIM parameters");
241             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
242         } else
243             goto done;
244
245         if (PIM_VER(pim.pim_typever) != 2)
246             goto done;
247
248         /* version 2 decoder */
249         switch (PIM_TYPE(pim.pim_typever)) {
250         case 0: /*hello*/
251           {
252             guint16 *w;
253             w = (guint16 *)&pd[offset + sizeof(struct pim)];
254             while ((guint8 *)w < &pd[offset + END_OF_FRAME]) {
255                 if (pntohs(&w[0]) == 1 && pntohs(&w[1]) == 2
256                  && (guint8 *)&w[3] <= &pd[offset + END_OF_FRAME]) {
257                     proto_tree_add_text(pimopt_tree, NullTVB, (guint8 *)w - pd, 6,
258                         "Holdtime: %u%s", pntohs(&w[2]),
259                         pntohs(&w[2]) == 0xffff ? " (infty)" : "");
260                     w += 3;
261                 } else
262                     break;
263             }
264             break;
265           }
266
267         case 1: /* register */
268           {
269             const guint8 *ip;
270             proto_tree *flag_tree = NULL;
271                 proto_item *tiflag; 
272             int flagoff;
273
274             flagoff = offset + sizeof(struct pim);
275             tiflag = proto_tree_add_text(pimopt_tree, NullTVB, flagoff, 4,
276                 "Flags: 0x%08x", pntohl(&pd[flagoff]));
277             flag_tree = proto_item_add_subtree(tiflag, ett_pim);
278             proto_tree_add_text(flag_tree, NullTVB, flagoff, 1, "%s",
279                 decode_boolean_bitfield(pd[flagoff], 0x80000000, 32,
280                     "Border", "Not border"));
281             proto_tree_add_text(flag_tree, NullTVB, flagoff, 1, "%s",
282                 decode_boolean_bitfield(pd[flagoff], 0x40000000, 32,
283                     "Null-Register", "Not Null-Register"));
284
285             ip = &pd[flagoff + sizeof(guint32)];
286             switch((*ip & 0xf0) >> 4) {
287             case 4:     /* IPv4 */
288 #if 0
289                     old_call_dissector(ip_handle, pd, ip - pd, fd, tree);
290 #else
291                     old_call_dissector(ip_handle, pd, ip - pd, fd, pimopt_tree);
292 #endif
293                     break;
294             case 6:     /* IPv6 */
295 #if 0
296                     dissect_ipv6(pd, ip - pd, fd, tree);
297 #else
298                     dissect_ipv6(pd, ip - pd, fd, pimopt_tree);
299 #endif
300                     break;
301             default:
302                     proto_tree_add_text(pimopt_tree, NullTVB,
303                         ip - pd, END_OF_FRAME,
304                         "Unknown IP version %d", (*ip & 0xf0) >> 4);
305                     break;
306             }
307             break;
308           }
309
310         case 2: /* register-stop */
311           {
312             int advance;
313             const guint8 *ep;
314             const char *s;
315
316             ep = &pd[offset + END_OF_FRAME];
317             offset += sizeof(struct pim);
318             s = dissect_pim_addr(&pd[offset], ep, pimv2_group, &advance);
319             if (s == NULL)
320                 break;
321             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "Group: %s", s);
322             offset += advance;
323             s = dissect_pim_addr(&pd[offset], ep, pimv2_unicast, &advance);
324             if (s == NULL)
325                 break;
326             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "Source: %s", s);
327             break;
328           }
329
330         case 3: /* join/prune */
331         case 6: /* graft */
332         case 7: /* graft-ack */
333           {
334             int advance;
335             const guint8 *ep;
336             int off;
337             const char *s;
338             int ngroup, i, njoin, nprune, j;
339             proto_tree *grouptree = NULL;
340                 proto_item *tigroup; 
341             proto_tree *subtree = NULL;
342                 proto_item *tisub; 
343
344             ep = &pd[offset + END_OF_FRAME];
345             offset += sizeof(struct pim);
346             if (PIM_TYPE(pim.pim_typever) != 7) {
347                 /* not graft-ack */
348                 s = dissect_pim_addr(&pd[offset], ep, pimv2_unicast, &advance);
349                 if (s == NULL)
350                     break;
351                 proto_tree_add_text(pimopt_tree, NullTVB, offset, advance,
352                     "Upstream-neighbor: %s", s);
353                 offset += advance;
354             }
355
356             if (&pd[offset + 2] > ep)
357                 break;
358             ngroup = pd[offset + 1];
359             proto_tree_add_text(pimopt_tree, NullTVB, offset + 1, 1,
360                 "Groups: %u", pd[offset + 1]);
361             offset += 2;
362
363             if (&pd[offset + 2] > ep)
364                 break;
365             if (PIM_TYPE(pim.pim_typever) != 7) {
366                 /* not graft-ack */
367                 proto_tree_add_text(pimopt_tree, NullTVB, offset, 2,
368                     "Holdtime: %u%s", pntohs(&pd[offset]),
369                     pntohs(&pd[offset]) == 0xffff ? " (infty)" : "");
370             }
371             offset += 2;
372
373             for (i = 0; i < ngroup; i++) {
374                 if (&pd[offset] >= ep)
375                     goto breakbreak3;
376
377                 s = dissect_pim_addr(&pd[offset], ep, pimv2_group, &advance);
378                 if (s == NULL)
379                     goto breakbreak3;
380                 tigroup = proto_tree_add_text(pimopt_tree, NullTVB, offset, advance,
381                     "Group %d: %s", i, s);
382                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
383                 offset += advance;
384
385                 if (&pd[offset + 4] > ep)
386                     goto breakbreak3;
387                 njoin = pntohs(&pd[offset]);
388                 nprune = pntohs(&pd[offset + 2]);
389
390                 tisub = proto_tree_add_text(grouptree, NullTVB, offset, 2,
391                     "Join: %d", njoin);
392                 subtree = proto_item_add_subtree(tisub, ett_pim);
393                 off = offset + 4;
394                 for (j = 0; j < nprune; j++) {
395                     s = dissect_pim_addr(&pd[off], ep, pimv2_source,
396                         &advance);
397                     if (s == NULL)
398                         goto breakbreak3;
399                     proto_tree_add_text(subtree, NullTVB, off, advance,
400                         "IP address: %s", s);
401                     off += advance;
402                 }
403
404                 tisub = proto_tree_add_text(grouptree, NullTVB, offset + 2, 2,
405                     "Prune: %d", nprune);
406                 subtree = proto_item_add_subtree(tisub, ett_pim);
407                 for (j = 0; j < nprune; j++) {
408                     s = dissect_pim_addr(&pd[off], ep, pimv2_source,
409                         &advance);
410                     if (s == NULL)
411                         goto breakbreak3;
412                     proto_tree_add_text(subtree, NullTVB, off, advance,
413                         "IP address: %s", s);
414                     off += advance;
415                 }
416             }
417     breakbreak3:
418             break;
419           }
420
421         case 4: /* bootstrap */
422           {
423             const char *s;
424             int advance;
425             int i, j;
426             int frpcnt;
427             proto_tree *grouptree = NULL;
428                 proto_item *tigroup; 
429
430             offset += sizeof(struct pim);
431
432             if (END_OF_FRAME < 2)
433                 break;
434             proto_tree_add_text(pimopt_tree, NullTVB, offset, 2,
435                 "Fragment tag: 0x%04x", pntohs(&pd[offset]));
436             offset += 2;
437
438             if (END_OF_FRAME < 2)
439                 break;
440             proto_tree_add_text(pimopt_tree, NullTVB, offset, 1,
441                 "Hash mask len: %u", pd[offset]);
442             proto_tree_add_text(pimopt_tree, NullTVB, offset + 1, 1,
443                 "BSR priority: %u", pd[offset + 1]);
444             offset += 2;
445
446             s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
447                 pimv2_unicast, &advance);
448             if (s == NULL)
449                 break;
450             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "BSR: %s", s);
451             offset += advance;
452
453             for (i = 0; END_OF_FRAME > 0; i++) {
454                 s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
455                     pimv2_group, &advance);
456                 if (s == NULL)
457                     goto breakbreak4;
458                 tigroup = proto_tree_add_text(pimopt_tree, NullTVB, offset, advance,
459                     "Group %d: %s", i, s);
460                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
461                 offset += advance;
462
463                 if (END_OF_FRAME < 2)
464                     goto breakbreak4;
465                 proto_tree_add_text(grouptree, NullTVB, offset, 1,
466                     "RP count: %u", pd[offset]);
467                 proto_tree_add_text(grouptree, NullTVB, offset + 1, 1,
468                     "FRP count: %u", pd[offset + 1]);
469                 frpcnt = pd[offset + 1];
470                 offset += 4;
471
472                 for (j = 0; j < frpcnt && END_OF_FRAME > 0; j++) {
473                     s = dissect_pim_addr(&pd[offset],
474                         &pd[offset + END_OF_FRAME], pimv2_unicast, &advance);
475                     if (s == NULL)
476                         goto breakbreak4;
477                     proto_tree_add_text(grouptree, NullTVB, offset, advance,
478                         "RP %d: %s", j, s);
479                     offset += advance;
480
481                     if (END_OF_FRAME < 4)
482                         goto breakbreak4;
483                     proto_tree_add_text(grouptree, NullTVB, offset, 2,
484                         "Holdtime: %u%s", pntohs(&pd[offset]),
485                         pntohs(&pd[offset]) == 0xffff ? " (infty)" : "");
486                     proto_tree_add_text(grouptree, NullTVB, offset + 3, 1,
487                         "Priority: %u", pd[offset + 3]);
488
489                     offset += 4;
490                 }
491             }
492
493     breakbreak4:
494             break;
495           }
496
497         case 5: /* assert */
498           {
499             const char *s;
500             int advance;
501
502             offset += sizeof(struct pim);
503
504             s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
505                 pimv2_group, &advance);
506             if (s == NULL)
507                 break;
508             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "Group: %s", s);
509             offset += advance;
510
511             s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
512                 pimv2_unicast, &advance);
513             if (s == NULL)
514                 break;
515             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "Source: %s", s);
516             offset += advance;
517
518             if (END_OF_FRAME < 4)
519                 break;
520             proto_tree_add_text(pimopt_tree, NullTVB, offset, 1, "%s",
521                 decode_boolean_bitfield(pd[offset], 0x80, 8,
522                     "RP Tree", "Not RP Tree"));
523             proto_tree_add_text(pimopt_tree, NullTVB, offset, 4, "Preference: %u",
524                 pntohl(&pd[offset]) & 0x7fffffff);
525             offset += 4;
526
527             if (END_OF_FRAME < 4)
528                 break;
529             proto_tree_add_text(pimopt_tree, NullTVB, offset, 4, "Metric: %u",
530                 pntohl(&pd[offset]));
531
532             break;
533           }
534
535         case 8: /* Candidate-RP-Advertisement */
536           {
537             const char *s;
538             int advance;
539             int pfxcnt;
540             int i;
541
542             offset += sizeof(struct pim);
543             if (END_OF_FRAME < 4)
544                 break;
545             pfxcnt = pd[offset];
546             proto_tree_add_text(pimopt_tree, NullTVB, offset, 1,
547                 "Prefix-count: %u", pd[offset]);
548             proto_tree_add_text(pimopt_tree, NullTVB, offset + 1, 1,
549                 "Priority: %u", pd[offset + 1]);
550             proto_tree_add_text(pimopt_tree, NullTVB, offset + 2, 2,
551                 "Holdtime: %u%s", pntohs(&pd[offset + 2]),
552                 pntohs(&pd[offset + 2]) == 0xffff ? " (infty)" : "");
553             offset += 4;
554
555             if (END_OF_FRAME <= 0)
556                 break;
557             s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
558                 pimv2_unicast, &advance);
559             if (s == NULL)
560                 break;
561             proto_tree_add_text(pimopt_tree, NullTVB, offset, advance, "RP: %s", s);
562             offset += advance;
563
564             for (i = 0; i < pfxcnt && END_OF_FRAME > 0; i++) {
565                 s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
566                     pimv2_group, &advance);
567                 if (s == NULL)
568                     goto breakbreak8;
569                 proto_tree_add_text(pimopt_tree, NullTVB, offset, advance,
570                     "Group %d: %s", i, s);
571                 offset += advance;
572             }
573     breakbreak8:
574             break;
575           }
576
577         default:
578             break;
579         }
580     }
581 done:;
582 }
583
584 void
585 proto_register_pim(void)
586 {
587     static hf_register_info hf[] = {
588       { &hf_pim_version,
589         { "Version",            "pim.version",
590                                 FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
591       { &hf_pim_type,
592         { "Type",                       "pim.type",
593                                 FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
594       { &hf_pim_cksum,
595         { "Checksum",           "pim.cksum",
596                                 FT_UINT16, BASE_HEX, NULL, 0x0, "" }},
597     };
598     static gint *ett[] = {
599         &ett_pim,
600     };
601
602     proto_pim = proto_register_protocol("Protocol Independent Multicast",
603         "pim");
604     proto_register_field_array(proto_pim, hf, array_length(hf));
605     proto_register_subtree_array(ett, array_length(ett));
606 }
607
608 void
609 proto_reg_handoff_pim(void)
610 {
611     old_dissector_add("ip.proto", IP_PROTO_PIM, dissect_pim);
612
613     /*
614      * Get a handle for the IP dissector.
615      */
616     ip_handle = find_dissector("ip");
617 }