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