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