e617020e97cd916dad24c7626ad70f783c04f441
[obnox/wireshark/wip.git] / packet-icmpv6.c
1 /* packet-icmpv6.c
2  * Routines for ICMPv6 packet disassembly
3  *
4  * $Id: packet-icmpv6.c,v 1.36 2001/01/23 02:49:55 gerald Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * MobileIPv6 support added by Tomislav Borosa <tomislav.borosa@siemens.hr>
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 #include <stdio.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include <stdlib.h>
38 #include <string.h>
39
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 #endif
43
44 #ifdef HAVE_NETINET_IN_H
45 # include <netinet/in.h>
46 #endif
47
48 #include <glib.h>
49
50 #ifdef NEED_SNPRINTF_H
51 # include "snprintf.h"
52 #endif
53
54 #include "packet.h"
55 #include "packet-ipv6.h"
56 #include "packet-ip.h"
57 #include "packet-dns.h"
58 #include "in_cksum.h"
59 #include "resolv.h"
60
61 #ifndef offsetof
62 #define offsetof(type, member)  ((size_t)(&((type *)0)->member))
63 #endif
64
65 static int proto_icmpv6 = -1;
66 static int hf_icmpv6_type = -1;
67 static int hf_icmpv6_code = -1;
68 static int hf_icmpv6_checksum = -1;
69
70 static gint ett_icmpv6 = -1;
71 static gint ett_icmpv6opt = -1;
72 static gint ett_icmpv6flag = -1;
73 static gint ett_nodeinfo_flag = -1;
74 static gint ett_nodeinfo_subject4 = -1;
75 static gint ett_nodeinfo_subject6 = -1;
76 static gint ett_nodeinfo_node4 = -1;
77 static gint ett_nodeinfo_node6 = -1;
78 static gint ett_nodeinfo_nodebitmap = -1;
79 static gint ett_nodeinfo_nodedns = -1;
80
81 static const value_string names_nodeinfo_qtype[] = {
82     { NI_QTYPE_NOOP,            "NOOP" },
83     { NI_QTYPE_SUPTYPES,        "Supported query types" },
84     { NI_QTYPE_DNSNAME,         "DNS name" },
85     { NI_QTYPE_NODEADDR,        "Node addresses" },
86     { NI_QTYPE_IPV4ADDR,        "IPv4 node addresses" },
87     { 0,                        NULL }
88 };
89
90 static const value_string names_rrenum_matchcode[] = {
91     { RPM_PCO_ADD,              "Add" },
92     { RPM_PCO_CHANGE,           "Change" },
93     { RPM_PCO_SETGLOBAL,        "Set Global" },
94     { 0,                        NULL }
95 };
96
97 static void
98 dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
99 {
100     proto_tree *icmp6opt_tree, *field_tree;
101         proto_item *ti, *tf;
102     struct nd_opt_hdr *opt;
103     int len;
104     char *typename;
105
106     if (!tree)
107         return;
108
109 again:
110     if (!IS_DATA_IN_FRAME(offset))
111         return;
112
113     opt = (struct nd_opt_hdr *)&pd[offset];
114     len = opt->nd_opt_len << 3;
115
116     /* !!! specify length */
117     ti = proto_tree_add_text(tree, NullTVB, offset, len, "ICMPv6 options");
118     icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
119
120     switch (opt->nd_opt_type) {
121     case ND_OPT_SOURCE_LINKADDR:
122         typename = "Source link-layer address";
123         break;
124     case ND_OPT_TARGET_LINKADDR:
125         typename = "Target link-layer address";
126         break;
127     case ND_OPT_PREFIX_INFORMATION:
128         typename = "Prefix information";
129         break;
130     case ND_OPT_REDIRECTED_HEADER:
131         typename = "Redirected header";
132         break;
133     case ND_OPT_MTU:
134         typename = "MTU";
135         break;
136     case ND_OPT_ADVERTISEMENT_INTERVAL:
137         typename = "Advertisement Interval";
138         break;
139     case ND_OPT_HOME_AGENT_INFORMATION:
140         typename = "Home Agent Information";
141         break;
142     default:
143
144         typename = "Unknown";
145         break;
146     }
147
148     proto_tree_add_text(icmp6opt_tree, NullTVB,
149         offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
150         "Type: 0x%02x (%s)", opt->nd_opt_type, typename);
151     proto_tree_add_text(icmp6opt_tree, NullTVB,
152         offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
153         "Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
154
155     /* decode... */
156     switch (opt->nd_opt_type) {
157     case ND_OPT_SOURCE_LINKADDR:
158     case ND_OPT_TARGET_LINKADDR:
159       {
160         char *t;
161         const char *p;
162         int len, i;
163         len = (opt->nd_opt_len << 3) - sizeof(*opt);
164         t = (char *)malloc(len * 3);
165         memset(t, 0, len * 3);
166         p = &pd[offset + sizeof(*opt)];
167         for (i = 0; i < len; i++) {
168             if (i)
169                 t[i * 3 - 1] = ':';
170             sprintf(&t[i * 3], "%02x", p[i] & 0xff);
171         }
172         proto_tree_add_text(icmp6opt_tree, NullTVB,
173             offset + sizeof(*opt), len, "Link-layer address: %s", t);
174         break;
175       }
176     case ND_OPT_PREFIX_INFORMATION:
177       {
178         struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
179         int flagoff;
180         proto_tree_add_text(icmp6opt_tree, NullTVB,
181             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
182             1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
183
184         flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
185         tf = proto_tree_add_text(icmp6opt_tree, NullTVB, flagoff, 1, "Flags: 0x%02x",
186             pntohl(&pi->nd_opt_pi_flags_reserved));
187         field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
188         proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
189             decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
190                     0x80, 8, "Onlink", "Not onlink"));
191         proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
192             decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
193                     0x40, 8, "Auto", "Not auto"));
194   /* BT INSERT BEGIN */
195         proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
196             decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
197                     0x20, 8, "Router Address", "Not router address"));
198   /* BT INSERT END */
199         proto_tree_add_text(icmp6opt_tree, NullTVB,
200             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
201             4, "Valid lifetime: 0x%08x",
202             pntohl(&pi->nd_opt_pi_valid_time));
203         proto_tree_add_text(icmp6opt_tree, NullTVB,
204             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
205             4, "Preferred lifetime: 0x%08x",
206             pntohl(&pi->nd_opt_pi_preferred_time));
207         proto_tree_add_text(icmp6opt_tree, NullTVB,
208             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
209             16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
210         break;
211       }
212     case ND_OPT_REDIRECTED_HEADER:
213         proto_tree_add_text(icmp6opt_tree, NullTVB,
214             offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
215         /* tiny sanity check */
216         if ((pd[offset + 8] & 0xf0) == 0x60)
217             dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
218         else
219             old_dissect_data(pd, offset + 8, fd, icmp6opt_tree);
220         break;
221     case ND_OPT_MTU:
222       {
223         struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
224         proto_tree_add_text(icmp6opt_tree, NullTVB,
225             offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
226             "MTU: %d", pntohl(&pi->nd_opt_mtu_mtu));
227         break;
228       }
229         /* BT INSERT BEGIN */
230     case ND_OPT_ADVERTISEMENT_INTERVAL:
231       {
232         struct nd_opt_adv_int *pi = (struct nd_opt_adv_int *)opt;
233         proto_tree_add_text(icmp6opt_tree, NullTVB,
234             offset + offsetof(struct nd_opt_adv_int, nd_opt_adv_int_advint), 4,
235             "Advertisement Interval: %d", pntohl(&pi->nd_opt_adv_int_advint));
236         break;
237       }
238     case ND_OPT_HOME_AGENT_INFORMATION:
239       {
240         struct nd_opt_ha_info *pi = (struct nd_opt_ha_info *)opt;
241         proto_tree_add_text(icmp6opt_tree, NullTVB,
242             offset + offsetof(struct nd_opt_ha_info, nd_opt_ha_info_ha_pref),
243             2, "Home Agent Preference: %d",
244             pntohs(&pi->nd_opt_ha_info_ha_pref));
245         proto_tree_add_text(icmp6opt_tree, NullTVB,
246             offset + offsetof(struct nd_opt_ha_info, nd_opt_ha_info_ha_life),
247             2, "Home Agent Lifetime: %d",
248             pntohs(&pi->nd_opt_ha_info_ha_life));
249         break;
250       }
251         /* BT INSERT END */
252     }
253
254     if (opt->nd_opt_len == 0) {
255         proto_tree_add_text(icmp6opt_tree, NullTVB,
256                             offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
257                             "Invalid option length: %d",
258                             opt->nd_opt_len);
259         return;
260     }
261
262     offset += (opt->nd_opt_len << 3);
263     goto again;
264 }
265
266 /*
267  * draft-ietf-ipngwg-icmp-name-lookups-06.txt
268  * Note that the packet format was changed several times in the past.
269  */
270
271 static const char *
272 bitrange0(v, s, buf, buflen)
273         guint32 v;
274         int s;
275         char *buf;
276         int buflen;
277 {
278         guint32 v0;
279         char *p, *ep;
280         int off;
281         int i, l;
282
283         if (buflen < 1)
284                 return NULL;
285         if (buflen == 1) {
286                 buf[0] = '\0';
287                 return NULL;
288         }
289
290         v0 = v;
291         p = buf;
292         ep = buf + buflen - 1;
293         memset(buf, 0, buflen);
294         off = 0;
295         while (off < 32) {
296                 /* shift till we have 0x01 */
297                 if ((v & 0x01) == 0) {
298                         switch (v & 0x0f) {
299                         case 0x00:
300                                 v >>= 4; off += 4; continue;
301                         case 0x08:
302                                 v >>= 3; off += 3; continue;
303                         case 0x04: case 0x0c:
304                                 v >>= 2; off += 2; continue;
305                         default:
306                                 v >>= 1; off += 1; continue;
307                         }
308                 }
309
310                 /* we have 0x01 with us */
311                 for (i = 0; i < 32 - off; i++) {
312                         if ((v & (0x01 << i)) == 0)
313                                 break;
314                 }
315                 if (i == 1)
316                         l = snprintf(p, ep - p, ",%d", s + off);
317                 else {
318                         l = snprintf(p, ep - p, ",%d-%d", s + off,
319                             s + off + i - 1);
320                 }
321                 if (l > ep - p) {
322                         buf[0] = '\0';
323                         return NULL;
324                 }
325                 v >>= i; off += i;
326         }
327
328         return buf;
329 }
330
331 static const char *
332 bitrange(u_char *p, int l, int s)
333 {
334     static char buf[1024];
335     char *q, *eq;
336     int i;
337
338     memset(buf, 0, sizeof(buf));
339     q = buf;
340     eq = buf + sizeof(buf) - 1;
341     for (i = 0; i < l; i++) {
342         if (bitrange0(pntohl(p + i * 4), s + i * 4, q, eq - q) == NULL) {
343             if (q != buf && q + 5 < buf + sizeof(buf))
344                 strncpy(q, ",...", 5);
345             return buf;
346         }
347     }
348
349     return buf + 1;
350 }
351
352 static void
353 dissect_nodeinfo(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
354 {
355     proto_tree *field_tree;
356         proto_item *tf;
357     struct icmp6_nodeinfo *ni;
358     int off;
359     int i, n, l;
360     guint16 flags;
361     u_char *p;
362     char dname[MAXDNAME];
363
364     ni = (struct icmp6_nodeinfo *)&pd[offset];
365
366     /* flags */
367     flags = pntohs(&ni->ni_flags);
368     tf = proto_tree_add_text(tree, NullTVB,
369         offset + offsetof(struct icmp6_nodeinfo, ni_flags),
370         sizeof(ni->ni_flags), "Flags: 0x%04x", flags);
371     field_tree = proto_item_add_subtree(tf, ett_nodeinfo_flag);
372     switch (pntohs(&ni->ni_qtype)) {
373     case NI_QTYPE_SUPTYPES:
374         if (ni->ni_type == ICMP6_NI_QUERY) {
375             proto_tree_add_text(field_tree, NullTVB,
376                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
377                 sizeof(ni->ni_flags), "%s",
378                 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
379                     "Compressed reply supported",
380                     "No compressed reply support"));
381         } else {
382             proto_tree_add_text(field_tree, NullTVB,
383                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
384                 sizeof(ni->ni_flags), "%s",
385                 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
386                     "Compressed", "Not compressed"));
387         }
388         break;
389     case NI_QTYPE_DNSNAME:
390         if (ni->ni_type == ICMP6_NI_REPLY) {
391             proto_tree_add_text(field_tree, NullTVB,
392                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
393                 sizeof(ni->ni_flags), "%s",
394                 decode_boolean_bitfield(flags, NI_FQDN_FLAG_VALIDTTL, sizeof(flags) * 8,
395                     "Valid TTL field", "Meaningless TTL field"));
396         }
397         break;
398     case NI_QTYPE_NODEADDR:
399         proto_tree_add_text(field_tree, NullTVB,
400             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
401             sizeof(ni->ni_flags), "%s",
402             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_GLOBAL, sizeof(flags) * 8,
403                 "Global address",
404                 "Not global address"));
405         proto_tree_add_text(field_tree, NullTVB,
406             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
407             sizeof(ni->ni_flags), "%s",
408             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_SITELOCAL, sizeof(flags) * 8,
409                 "Site-local address",
410                 "Not site-local address"));
411         proto_tree_add_text(field_tree, NullTVB,
412             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
413             sizeof(ni->ni_flags), "%s",
414             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_LINKLOCAL, sizeof(flags) * 8,
415                 "Link-local address",
416                 "Not link-local address"));
417         proto_tree_add_text(field_tree, NullTVB,
418             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
419             sizeof(ni->ni_flags), "%s",
420             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_COMPAT, sizeof(flags) * 8,
421                 "IPv4 compatible/mapped address",
422                 "Not IPv4 compatible/mapped address"));
423         /* fall through */
424     case NI_QTYPE_IPV4ADDR:
425         proto_tree_add_text(field_tree, NullTVB,
426             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
427             sizeof(ni->ni_flags), "%s",
428             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_ALL, sizeof(flags) * 8,
429                 "All unicast address",
430                 "Unicast addresses on the queried interface"));
431         proto_tree_add_text(field_tree, NullTVB,
432             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
433             sizeof(ni->ni_flags), "%s",
434             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_TRUNCATE, sizeof(flags) * 8,
435                 "Truncated", "Not truncated"));
436         break;
437     }
438
439     /* nonce */
440     proto_tree_add_text(tree, NullTVB,
441         offset + offsetof(struct icmp6_nodeinfo, icmp6_ni_nonce[0]),
442         sizeof(ni->icmp6_ni_nonce), "Nonce: 0x%08x%08x",
443         pntohl(&ni->icmp6_ni_nonce[0]), pntohl(&ni->icmp6_ni_nonce[4]));
444
445     /* offset for "the rest of data" */
446     off = sizeof(*ni);
447
448     /* rest of data */
449     if (!IS_DATA_IN_FRAME(offset + sizeof(*ni)))
450         goto nodata;
451     if (ni->ni_type == ICMP6_NI_QUERY) {
452         switch (ni->ni_code) {
453         case ICMP6_NI_SUBJ_IPV6:
454             n = pi.captured_len - (offset + sizeof(*ni));
455             n /= sizeof(struct e_in6_addr);
456             tf = proto_tree_add_text(tree, NullTVB,
457                 offset + sizeof(*ni), END_OF_FRAME, "IPv6 subject addresses");
458             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject6);
459             p = (u_char *)(ni + 1);
460             for (i = 0; i < n; i++) {
461                 proto_tree_add_text(field_tree, NullTVB,
462                     p - pd, sizeof(struct e_in6_addr),
463                     "%s", ip6_to_str((struct e_in6_addr *)p));
464                 p += sizeof(struct e_in6_addr);
465             }
466             off = pi.captured_len - offset;
467             break;
468         case ICMP6_NI_SUBJ_FQDN:
469             l = get_dns_name(pd, offset + sizeof(*ni), offset + sizeof(*ni),
470                 dname, sizeof(dname));
471             if (IS_DATA_IN_FRAME(offset + sizeof(*ni) + l) &&
472                 pd[offset + sizeof(*ni) + l] == 0) {
473                 l++;
474                 proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
475                     "DNS label: %s (truncated)", dname);
476             } else {
477                 proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
478                     "DNS label: %s", dname);
479             }
480             off = offset + sizeof(*ni) + l;
481             break;
482         case ICMP6_NI_SUBJ_IPV4:
483             n = pi.captured_len - (offset + sizeof(*ni));
484             n /= sizeof(guint32);
485             tf = proto_tree_add_text(tree, NullTVB,
486                 offset + sizeof(*ni), END_OF_FRAME, "IPv4 subject addresses");
487             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject4);
488             p = (u_char *)(ni + 1);
489             for (i = 0; i < n; i++) {
490                 proto_tree_add_text(field_tree, NullTVB,
491                     p - pd, sizeof(guint32), "%s", ip_to_str(p));
492                 p += sizeof(guint32);
493             }
494             off = pi.captured_len - offset;
495             break;
496         }
497     } else {
498         switch (pntohs(&ni->ni_qtype)) {
499         case NI_QTYPE_NOOP:
500             break;
501         case NI_QTYPE_SUPTYPES:
502             p = (u_char *)(ni + 1);
503             tf = proto_tree_add_text(tree, NullTVB,
504                 offset + sizeof(*ni), END_OF_FRAME,
505                 "Supported type bitmap%s",
506                 (flags & 0x0001) ? ", compressed" : "");
507             field_tree = proto_item_add_subtree(tf,
508                 ett_nodeinfo_nodebitmap);
509             n = 0;
510             while (IS_DATA_IN_FRAME(p - pd)) {
511                 if ((flags & 0x0001) == 0) {
512                     l = pi.captured_len - (offset + sizeof(*ni));
513                     l /= sizeof(guint32);
514                     i = 0;
515                 } else {
516                     if (!IS_DATA_IN_FRAME(p + sizeof(guint32) - 1 - pd))
517                         break;
518                     l = pntohs(p);
519                     i = pntohs(p + sizeof(guint16));    /*skip*/
520                 }
521                 if (n + l * 32 > (1 << 16))
522                     break;
523                 if (n + (l + i) * 32 > (1 << 16))
524                     break;
525                 if ((flags & 0x0001) == 0) {
526                     proto_tree_add_text(field_tree, NullTVB, p - pd,
527                         l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
528                         bitrange(p, l, n));
529                     p += l * 4;
530                 } else {
531                     proto_tree_add_text(field_tree, NullTVB, p - pd,
532                         4 + l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
533                         bitrange(p + 4, l, n));
534                     p += (4 + l * 4);
535                 }
536                 n += l * 32 + i * 32;
537             }
538             off = pi.captured_len - offset;
539             break;
540         case NI_QTYPE_DNSNAME:
541             proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni),
542                 sizeof(gint32), "TTL: %d", (gint32)pntohl(ni + 1));
543             tf = proto_tree_add_text(tree, NullTVB,
544                 offset + sizeof(*ni) + sizeof(guint32), END_OF_FRAME,
545                 "DNS labels");
546             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_nodedns);
547             n = pi.captured_len;
548             i = offset + sizeof(*ni) + sizeof(guint32);
549             while (i < pi.captured_len) {
550                 l = get_dns_name(pd, i, offset + sizeof(*ni), dname,
551                     sizeof(dname));
552                 if (IS_DATA_IN_FRAME(i + l) && pd[i + l] == 0) {
553                     l++;
554                     proto_tree_add_text(field_tree, NullTVB, i, l,
555                         "DNS label: %s (truncated)", dname);
556                 } else {
557                     proto_tree_add_text(field_tree, NullTVB, i, l,
558                         "DNS label: %s", dname);
559                 }
560                 i += l;
561             }
562             off = pi.captured_len - offset;
563             break;
564         case NI_QTYPE_NODEADDR:
565             n = pi.captured_len - (offset + sizeof(*ni));
566             n /= sizeof(struct e_in6_addr) + sizeof(gint32);;
567             tf = proto_tree_add_text(tree, NullTVB,
568                 offset + sizeof(*ni), END_OF_FRAME, "IPv6 node addresses");
569             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node6);
570             p = (u_char *)(ni + 1);
571             for (i = 0; i < n; i++) {
572                 proto_tree_add_text(field_tree, NullTVB,
573                     p - pd, sizeof(struct e_in6_addr) + sizeof(gint32),
574                     "%s (TTL %d)", ip6_to_str((struct e_in6_addr *)p),
575                     (gint32)pntohl(p + sizeof(struct e_in6_addr)));
576                 p += sizeof(struct e_in6_addr) + sizeof(gint32);
577             }
578             off = pi.captured_len - offset;
579             break;
580         case NI_QTYPE_IPV4ADDR:
581             n = pi.captured_len - (offset + sizeof(*ni));
582             n /= sizeof(guint32);
583             tf = proto_tree_add_text(tree, NullTVB,
584                 offset + sizeof(*ni), END_OF_FRAME, "IPv4 node addresses");
585             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node4);
586             p = (u_char *)(ni + 1);
587             for (i = 0; i < n; i++) {
588                 proto_tree_add_text(field_tree, NullTVB,
589                     p - pd, sizeof(guint32), "%s", ip_to_str(p));
590                 p += sizeof(guint32);
591             }
592             off = pi.captured_len - offset;
593             break;
594         }
595     }
596 nodata:;
597
598     /* the rest of data */
599     old_dissect_data(pd, offset + off, fd, tree);
600 }
601
602 static void
603 dissect_rrenum(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
604 {
605     proto_tree *field_tree, *opt_tree;
606         proto_item *tf;
607     struct icmp6_router_renum *rr = (struct icmp6_router_renum *)&pd[offset];
608     struct rr_pco_match *match;
609     struct rr_pco_use *use;
610     int flagoff, off, l;
611
612     proto_tree_add_text(tree, NullTVB,
613         offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
614         "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
615     proto_tree_add_text(tree, NullTVB,
616         offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
617         "Segment number: 0x%02x", rr->rr_segnum);
618
619     flagoff = offset + offsetof(struct icmp6_router_renum, rr_flags);
620     tf = proto_tree_add_text(tree, NullTVB, flagoff, 1,
621         "Flags: 0x%02x", pd[flagoff]);
622     field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
623     proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
624         decode_boolean_bitfield(pd[flagoff], 0x80, 8,
625             "Test command", "Not test command"));
626     proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
627         decode_boolean_bitfield(pd[flagoff], 0x40, 8,
628             "Result requested", "Result not requested"));
629     proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
630         decode_boolean_bitfield(pd[flagoff], 0x20, 8,
631             "All interfaces", "Not all interfaces"));
632     proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
633         decode_boolean_bitfield(pd[flagoff], 0x10, 8,
634             "Site specific", "Not site specific"));
635     proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
636         decode_boolean_bitfield(pd[flagoff], 0x08, 8,
637             "Processed previously", "Complete result"));
638
639     proto_tree_add_text(tree, NullTVB,
640         offset + offsetof(struct icmp6_router_renum, rr_maxdelay), 2,
641         "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
642     old_dissect_data(pd, offset + sizeof(*rr), fd, tree);       /*XXX*/
643
644     if (rr->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
645         off = offset + sizeof(*rr);
646         match = (struct rr_pco_match *)&pd[off];
647         tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*match),
648             "Match-Prefix: %s/%u (%u-%u)", ip6_to_str(&match->rpm_prefix),
649             match->rpm_matchlen, match->rpm_minlen, match->rpm_maxlen);
650         opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
651         proto_tree_add_text(opt_tree, NullTVB,
652             off + offsetof(struct rr_pco_match, rpm_code),
653             sizeof(match->rpm_code), "OpCode: %s (%u)",
654             val_to_str(match->rpm_code, names_rrenum_matchcode, "Unknown"), 
655             match->rpm_code);
656         proto_tree_add_text(opt_tree, NullTVB,
657             off + offsetof(struct rr_pco_match, rpm_len),
658             sizeof(match->rpm_len), "OpLength: %u (%u octets)",
659             match->rpm_len, match->rpm_len * 8);
660         proto_tree_add_text(opt_tree, NullTVB,
661             off + offsetof(struct rr_pco_match, rpm_ordinal),
662             sizeof(match->rpm_ordinal), "Ordinal: %u", match->rpm_ordinal);
663         proto_tree_add_text(opt_tree, NullTVB,
664             off + offsetof(struct rr_pco_match, rpm_matchlen),
665             sizeof(match->rpm_matchlen), "MatchLen: %u", match->rpm_matchlen);
666         proto_tree_add_text(opt_tree, NullTVB,
667             off + offsetof(struct rr_pco_match, rpm_minlen),
668             sizeof(match->rpm_minlen), "MinLen: %u", match->rpm_minlen);
669         proto_tree_add_text(opt_tree, NullTVB,
670             off + offsetof(struct rr_pco_match, rpm_maxlen),
671             sizeof(match->rpm_maxlen), "MaxLen: %u", match->rpm_maxlen);
672         proto_tree_add_text(opt_tree, NullTVB,
673             off + offsetof(struct rr_pco_match, rpm_prefix),
674             sizeof(match->rpm_prefix), "MatchPrefix: %s",
675             ip6_to_str(&match->rpm_prefix));
676
677         off += sizeof(*match);
678         use = (struct rr_pco_use *)&pd[off];
679         for (l = match->rpm_len * 8 - sizeof(*match);
680              l >= sizeof(*use); l -= sizeof(*use), off += sizeof(*use)) {
681             tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*use),
682                 "Use-Prefix: %s/%u (keep %u)", ip6_to_str(&use->rpu_prefix),
683                 use->rpu_uselen, use->rpu_keeplen);
684             opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
685             proto_tree_add_text(opt_tree, NullTVB,
686                 off + offsetof(struct rr_pco_use, rpu_uselen),
687                 sizeof(use->rpu_uselen), "UseLen: %u", use->rpu_uselen);
688             proto_tree_add_text(opt_tree, NullTVB,
689                 off + offsetof(struct rr_pco_use, rpu_keeplen),
690                 sizeof(use->rpu_keeplen), "KeepLen: %u", use->rpu_keeplen);
691             tf = proto_tree_add_text(opt_tree, NullTVB,
692                 flagoff = off + offsetof(struct rr_pco_use, rpu_ramask),
693                 sizeof(use->rpu_ramask), "FlagMask: 0x%x", use->rpu_ramask);
694             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
695             proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
696                 decode_boolean_bitfield(pd[flagoff],
697                     ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
698                     "Onlink", "Not onlink"));
699             proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
700                 decode_boolean_bitfield(pd[flagoff],
701                     ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8,
702                     "Auto", "Not auto"));
703             tf = proto_tree_add_text(opt_tree, NullTVB,
704                 flagoff = off + offsetof(struct rr_pco_use, rpu_raflags),
705                 sizeof(use->rpu_raflags), "RAFlags: 0x%x", use->rpu_raflags);
706             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
707             proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
708                 decode_boolean_bitfield(pd[flagoff],
709                     ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
710                     "Onlink", "Not onlink"));
711             proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
712                 decode_boolean_bitfield(pd[flagoff],
713                     ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8, "Auto", "Not auto"));
714             if (pntohl(&use->rpu_vltime) == 0xffffffff)
715                 proto_tree_add_text(opt_tree, NullTVB,
716                     off + offsetof(struct rr_pco_use, rpu_vltime),
717                     sizeof(use->rpu_vltime), "Valid Lifetime: infinity");
718             else
719                 proto_tree_add_text(opt_tree, NullTVB,
720                     off + offsetof(struct rr_pco_use, rpu_vltime),
721                     sizeof(use->rpu_vltime), "Valid Lifetime: %u",
722                     pntohl(&use->rpu_vltime));
723             if (pntohl(&use->rpu_pltime) == 0xffffffff)
724                 proto_tree_add_text(opt_tree, NullTVB,
725                     off + offsetof(struct rr_pco_use, rpu_pltime),
726                     sizeof(use->rpu_pltime), "Preferred Lifetime: infinity");
727             else
728                 proto_tree_add_text(opt_tree, NullTVB,
729                     off + offsetof(struct rr_pco_use, rpu_pltime),
730                     sizeof(use->rpu_pltime), "Preferred Lifetime: %u",
731                     pntohl(&use->rpu_pltime));
732             tf = proto_tree_add_text(opt_tree, NullTVB,
733                 flagoff = off + offsetof(struct rr_pco_use, rpu_flags),
734                 sizeof(use->rpu_flags), "Flags: 0x%08x",
735                 pntohl(&use->rpu_flags));
736             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
737             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
738                 decode_boolean_bitfield(pd[flagoff],
739                     ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, 32,
740                     "Decrement valid lifetime", "No decrement valid lifetime"));
741             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
742                 decode_boolean_bitfield(pd[flagoff],
743                     ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, 32,
744                     "Decrement preferred lifetime",
745                     "No decrement preferred lifetime"));
746             proto_tree_add_text(opt_tree, NullTVB,
747                 off + offsetof(struct rr_pco_use, rpu_prefix),
748                 sizeof(use->rpu_prefix), "UsePrefix: %s",
749                 ip6_to_str(&use->rpu_prefix));
750         }
751
752     }
753 }
754
755 static void
756 dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
757 {
758     proto_tree *icmp6_tree, *field_tree;
759     proto_item *ti, *tf = NULL;
760     struct icmp6_hdr *dp;
761     struct icmp6_nodeinfo *ni = NULL;
762     char *codename, *typename;
763     char *colcodename, *coltypename;
764     int len;
765     guint length, reported_length;
766     vec_t cksum_vec[4];
767     guint32 phdr[2];
768     guint16 cksum, computed_cksum;
769
770     dp = (struct icmp6_hdr *)&pd[offset];
771     codename = typename = colcodename = coltypename = "Unknown";
772     len = sizeof(*dp);
773     switch (dp->icmp6_type) {
774     case ICMP6_DST_UNREACH:
775         typename = coltypename = "Unreachable";
776         switch (dp->icmp6_code) {
777         case ICMP6_DST_UNREACH_NOROUTE:
778             codename = colcodename = "Route unreachable";
779             break;
780         case ICMP6_DST_UNREACH_ADMIN:
781             codename = colcodename = "Administratively prohibited";
782             break;
783         case ICMP6_DST_UNREACH_NOTNEIGHBOR:
784             codename = colcodename = "Not a neighbor";
785             break;
786         case ICMP6_DST_UNREACH_ADDR:
787             codename = colcodename = "Address unreachable";
788             break;
789         case ICMP6_DST_UNREACH_NOPORT:
790             codename = colcodename = "Port unreachable";
791             break;
792         }
793         break;
794     case ICMP6_PACKET_TOO_BIG:
795         typename = coltypename = "Too big";
796         codename = colcodename = NULL;
797         break;
798     case ICMP6_TIME_EXCEEDED:
799         typename = coltypename = "Time exceeded";
800         switch (dp->icmp6_code) {
801         case ICMP6_TIME_EXCEED_TRANSIT:
802             codename = colcodename = "In-transit";
803             break;
804         case ICMP6_TIME_EXCEED_REASSEMBLY:
805             codename = colcodename = "Reassembly";
806             break;
807         }
808         break;
809     case ICMP6_PARAM_PROB:
810         typename = coltypename = "Parameter problem";
811         switch (dp->icmp6_code) {
812         case ICMP6_PARAMPROB_HEADER:
813             codename = colcodename = "Header";
814             break;
815         case ICMP6_PARAMPROB_NEXTHEADER:
816             codename = colcodename = "Next header";
817             break;
818         case ICMP6_PARAMPROB_OPTION:
819             codename = colcodename = "Option";
820             break;
821         }
822         break;
823     case ICMP6_ECHO_REQUEST:
824         typename = coltypename = "Echo request";
825         codename = colcodename = NULL;
826         break;
827     case ICMP6_ECHO_REPLY:
828         typename = coltypename = "Echo reply";
829         codename = colcodename = NULL;
830         break;
831     case ICMP6_MEMBERSHIP_QUERY:
832         typename = coltypename = "Multicast listener query";
833         codename = colcodename = NULL;
834         break;
835     case ICMP6_MEMBERSHIP_REPORT:
836         typename = coltypename = "Multicast listener report";
837         codename = colcodename = NULL;
838         break;
839     case ICMP6_MEMBERSHIP_REDUCTION:
840         typename = coltypename = "Multicast listener done";
841         codename = colcodename = NULL;
842         break;
843     case ND_ROUTER_SOLICIT:
844         typename = coltypename = "Router solicitation";
845         codename = colcodename = NULL;
846         len = sizeof(struct nd_router_solicit);
847         break;
848     case ND_ROUTER_ADVERT:
849         typename = coltypename = "Router advertisement";
850         codename = colcodename = NULL;
851         len = sizeof(struct nd_router_advert);
852         break;
853     case ND_NEIGHBOR_SOLICIT:
854         typename = coltypename = "Neighbor solicitation";
855         codename = colcodename = NULL;
856         len = sizeof(struct nd_neighbor_solicit);
857         break;
858     case ND_NEIGHBOR_ADVERT:
859         typename = coltypename = "Neighbor advertisement";
860         codename = colcodename = NULL;
861         len = sizeof(struct nd_neighbor_advert);
862         break;
863     case ND_REDIRECT:
864         typename = coltypename = "Redirect";
865         codename = colcodename = NULL;
866         len = sizeof(struct nd_redirect);
867         break;
868     case ICMP6_ROUTER_RENUMBERING:
869         typename = coltypename = "Router renumbering";
870         switch (dp->icmp6_code) {
871         case ICMP6_ROUTER_RENUMBERING_COMMAND:
872             codename = colcodename = "Command";
873             break;
874         case ICMP6_ROUTER_RENUMBERING_RESULT:
875             codename = colcodename = "Result";
876             break;
877         case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
878             codename = colcodename = "Sequence number reset";
879             break;
880         }
881         len = sizeof(struct icmp6_router_renum);
882         break;
883     case ICMP6_NI_QUERY:
884     case ICMP6_NI_REPLY:
885         ni = (struct icmp6_nodeinfo *)dp;
886         if (ni->ni_type == ICMP6_NI_QUERY) {
887             typename = coltypename = "Node information query";
888             switch (ni->ni_code) {
889             case ICMP6_NI_SUBJ_IPV6:
890                 codename = "Query subject = IPv6 addresses";
891                 break;
892             case ICMP6_NI_SUBJ_FQDN:
893                 if (IS_DATA_IN_FRAME(offset + sizeof(*ni)))
894                     codename = "Query subject = DNS name";
895                 else
896                     codename = "Query subject = empty";
897                 break;
898             case ICMP6_NI_SUBJ_IPV4:
899                 codename = "Query subject = IPv4 addresses";
900                 break;
901             }
902         } else {
903             typename = coltypename = "Node information reply";
904             switch (ni->ni_code) {
905             case ICMP6_NI_SUCCESS:
906                 codename = "Successful";
907                 break;
908             case ICMP6_NI_REFUSED:
909                 codename = "Refused";
910                 break;
911             case ICMP6_NI_UNKNOWN:
912                 codename = "Unknown query type";
913                 break;
914             }
915         }
916         colcodename = val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
917             "Unknown");
918         len = sizeof(struct icmp6_nodeinfo);
919         break;
920     }
921
922     if (check_col(fd, COL_PROTOCOL))
923         col_set_str(fd, COL_PROTOCOL, "ICMPv6");
924     if (check_col(fd, COL_INFO)) {
925         char typebuf[256], codebuf[256];
926
927         if (coltypename && strcmp(coltypename, "Unknown") == 0) {
928             snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
929                 dp->icmp6_type);
930             coltypename = typebuf;
931         }
932         if (colcodename && strcmp(colcodename, "Unknown") == 0) {
933             snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
934                 dp->icmp6_code);
935             colcodename = codebuf;
936         }
937         if (colcodename) {
938             col_add_fstr(fd, COL_INFO, "%s (%s)", coltypename, colcodename);
939         } else {
940             col_add_fstr(fd, COL_INFO, "%s", coltypename);
941         }
942     }
943
944     if (tree) {
945         /* !!! specify length */
946         ti = proto_tree_add_item(tree, proto_icmpv6, NullTVB, offset, len, FALSE);
947         icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
948
949         proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_type, NullTVB,
950             offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
951             dp->icmp6_type,
952             "Type: 0x%02x (%s)", dp->icmp6_type, typename);
953         if (codename) {
954             proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
955                 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
956                 dp->icmp6_code,
957                 "Code: 0x%02x (%s)", dp->icmp6_code, codename);
958         } else {
959             proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
960                 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
961                 dp->icmp6_code,
962                 "Code: 0x%02x", dp->icmp6_code);
963         }
964         cksum = (guint16)htons(dp->icmp6_cksum);
965         length = pi.captured_len - offset;
966         reported_length = pi.len - offset;
967         if (!pi.fragmented && length >= reported_length) {
968             /* The packet isn't part of a fragmented datagram and isn't
969                truncated, so we can checksum it. */
970
971             /* Set up the fields of the pseudo-header. */
972             cksum_vec[0].ptr = pi.src.data;
973             cksum_vec[0].len = pi.src.len;
974             cksum_vec[1].ptr = pi.dst.data;
975             cksum_vec[1].len = pi.dst.len;
976             cksum_vec[2].ptr = (const guint8 *)&phdr;
977             phdr[0] = htonl(reported_length);
978             phdr[1] = htonl(IP_PROTO_ICMPV6);
979             cksum_vec[2].len = 8;
980             cksum_vec[3].ptr = &pd[offset];
981             cksum_vec[3].len = reported_length;
982             computed_cksum = in_cksum(cksum_vec, 4);
983             if (computed_cksum == 0) {
984                 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
985                         NullTVB,
986                         offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
987                         cksum,
988                         "Checksum: 0x%04x (correct)", cksum);
989             } else {
990                 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
991                         NullTVB,
992                         offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
993                         cksum,
994                         "Checksum: 0x%04x (incorrect, should be 0x%04x)",
995                         cksum, in_cksum_shouldbe(cksum, computed_cksum));
996             }
997         } else {
998             proto_tree_add_uint(icmp6_tree, hf_icmpv6_checksum, NullTVB,
999                 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
1000                 cksum);
1001         }
1002
1003         /* decode... */
1004         switch (dp->icmp6_type) {
1005         case ICMP6_DST_UNREACH:
1006         case ICMP6_TIME_EXCEEDED:
1007             /* tiny sanity check */
1008             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
1009                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
1010             } else {
1011                 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1012             }
1013             break;
1014         case ICMP6_PACKET_TOO_BIG:
1015             proto_tree_add_text(icmp6_tree, NullTVB,
1016                 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
1017                 "MTU: %d", pntohl(&dp->icmp6_mtu));
1018             /* tiny sanity check */
1019             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
1020                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
1021             } else {
1022                 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1023             }
1024             break;
1025         case ICMP6_PARAM_PROB:
1026             proto_tree_add_text(icmp6_tree, NullTVB,
1027                 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
1028                 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
1029             /* tiny sanity check */
1030             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
1031                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
1032             } else {
1033                 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1034             }
1035             break;
1036         case ICMP6_ECHO_REQUEST:
1037         case ICMP6_ECHO_REPLY:
1038             proto_tree_add_text(icmp6_tree, NullTVB,
1039                 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
1040                 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
1041             proto_tree_add_text(icmp6_tree, NullTVB,
1042                 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
1043                 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
1044             old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1045             break;
1046         case ICMP6_MEMBERSHIP_QUERY:
1047         case ICMP6_MEMBERSHIP_REPORT:
1048         case ICMP6_MEMBERSHIP_REDUCTION:
1049             proto_tree_add_text(icmp6_tree, NullTVB,
1050                 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
1051                 "Maximum response delay: %d",
1052                 (guint16)ntohs(dp->icmp6_maxdelay));
1053             proto_tree_add_text(icmp6_tree, NullTVB, offset + sizeof(*dp), 16,
1054                 "Multicast Address: %s",
1055                 ip6_to_str((struct e_in6_addr *)(dp + 1)));
1056             break;
1057         case ND_ROUTER_SOLICIT:
1058             dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
1059             break;
1060         case ND_ROUTER_ADVERT:
1061           {
1062             struct nd_router_advert *ra = (struct nd_router_advert *)dp;
1063             int flagoff;
1064             guint32 ra_flags;
1065
1066             proto_tree_add_text(icmp6_tree, NullTVB,
1067                 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
1068                 1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
1069
1070             flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
1071             ra_flags = pntohl(&pd[flagoff]);
1072             tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", ra_flags);
1073             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1074             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1075                 decode_boolean_bitfield(ra_flags,
1076                         0x80000000, 32, "Managed", "Not managed"));
1077             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1078                 decode_boolean_bitfield(ra_flags,
1079                         0x40000000, 32, "Other", "Not other"));
1080     /* BT INSERT BEGIN */
1081             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1082                 decode_boolean_bitfield(ra_flags,
1083                         0x20000000, 32, "Home Agent", "Not Home Agent"));               
1084     /* BT INSERT END */
1085             proto_tree_add_text(icmp6_tree, NullTVB,
1086                 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
1087                 2, "Router lifetime: %d",
1088                 (guint16)ntohs(ra->nd_ra_router_lifetime));
1089             proto_tree_add_text(icmp6_tree, NullTVB,
1090                 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
1091                 "Reachable time: %d", pntohl(&ra->nd_ra_reachable));
1092             proto_tree_add_text(icmp6_tree, NullTVB,
1093                 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
1094                 "Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
1095             dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
1096             break;
1097           }
1098         case ND_NEIGHBOR_SOLICIT:
1099           {
1100             struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
1101
1102             proto_tree_add_text(icmp6_tree, NullTVB,
1103                         offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
1104 #ifdef INET6
1105                         "Target: %s (%s)",
1106                         get_hostname6(&ns->nd_ns_target),
1107 #else
1108                         "Target: %s",
1109 #endif
1110                         ip6_to_str(&ns->nd_ns_target));
1111
1112             dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
1113             break;
1114           }
1115         case ND_NEIGHBOR_ADVERT:
1116           {
1117             int flagoff, targetoff;
1118             guint32 na_flags;
1119                 struct e_in6_addr *na_target_p;
1120
1121             flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
1122             na_flags = pntohl(&pd[flagoff]);
1123
1124             tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", na_flags);
1125             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1126             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1127                 decode_boolean_bitfield(na_flags,
1128                         ND_NA_FLAG_ROUTER, 32, "Router", "Not router"));
1129             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1130                 decode_boolean_bitfield(na_flags,
1131                         ND_NA_FLAG_SOLICITED, 32, "Solicited", "Not adverted"));
1132             proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1133                 decode_boolean_bitfield(na_flags,
1134                         ND_NA_FLAG_OVERRIDE, 32, "Override", "Not override"));
1135
1136                 targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
1137             na_target_p = (struct e_in6_addr*) &pd[targetoff];
1138             proto_tree_add_text(icmp6_tree, NullTVB, targetoff, 16,
1139 #ifdef INET6
1140                         "Target: %s (%s)",
1141                         get_hostname6(na_target_p),
1142 #else
1143                         "Target: %s",
1144 #endif
1145                         ip6_to_str(na_target_p));
1146
1147             dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
1148             break;
1149           }
1150         case ND_REDIRECT:
1151           {
1152             struct nd_redirect *rd = (struct nd_redirect *)dp;
1153
1154             proto_tree_add_text(icmp6_tree, NullTVB,
1155                         offset + offsetof(struct nd_redirect, nd_rd_target), 16,
1156 #ifdef INET6
1157                         "Target: %s (%s)",
1158                         get_hostname6(&rd->nd_rd_target),
1159 #else
1160                         "Target: %s",
1161 #endif
1162                         ip6_to_str(&rd->nd_rd_target));
1163
1164             proto_tree_add_text(icmp6_tree, NullTVB,
1165                         offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
1166 #ifdef INET6
1167                         "Destination: %s (%s)",
1168                         get_hostname6(&rd->nd_rd_dst),
1169 #else
1170                         "Destination: %s",
1171 #endif
1172                         ip6_to_str(&rd->nd_rd_dst));
1173
1174             dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
1175             break;
1176           }
1177         case ICMP6_ROUTER_RENUMBERING:
1178             dissect_rrenum(pd, offset, fd, icmp6_tree);
1179             break;
1180         case ICMP6_NI_QUERY:
1181         case ICMP6_NI_REPLY:
1182             ni = (struct icmp6_nodeinfo *)dp;
1183             proto_tree_add_text(icmp6_tree, NullTVB,
1184                 offset + offsetof(struct icmp6_nodeinfo, ni_qtype),
1185                 sizeof(ni->ni_qtype),
1186                 "Query type: 0x%04x (%s)", pntohs(&ni->ni_qtype),
1187                 val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
1188                 "Unknown"));
1189             dissect_nodeinfo(pd, offset, fd, icmp6_tree);
1190             break;
1191         default:
1192             old_dissect_data(pd, offset + sizeof(*dp), fd, tree);
1193             break;
1194         }
1195     }
1196 }
1197
1198 void
1199 proto_register_icmpv6(void)
1200 {
1201   static hf_register_info hf[] = {
1202     { &hf_icmpv6_type,
1203       { "Type",           "icmpv6.type",        FT_UINT8,  BASE_HEX, NULL, 0x0,
1204         "" }},
1205     { &hf_icmpv6_code,
1206       { "Code",           "icmpv6.code",        FT_UINT8,  BASE_HEX, NULL, 0x0,
1207         "" }},
1208     { &hf_icmpv6_checksum,
1209       { "Checksum",       "icmpv6.checksum",    FT_UINT16, BASE_HEX, NULL, 0x0,
1210         "" }}
1211   };
1212   static gint *ett[] = {
1213     &ett_icmpv6,
1214     &ett_icmpv6opt,
1215     &ett_icmpv6flag,
1216     &ett_nodeinfo_flag,
1217     &ett_nodeinfo_subject4,
1218     &ett_nodeinfo_subject6,
1219     &ett_nodeinfo_node4,
1220     &ett_nodeinfo_node6,
1221     &ett_nodeinfo_nodebitmap,
1222     &ett_nodeinfo_nodedns,
1223   };
1224
1225   proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
1226                                          "ICMPv6", "icmpv6");
1227   proto_register_field_array(proto_icmpv6, hf, array_length(hf));
1228   proto_register_subtree_array(ett, array_length(ett));
1229 }
1230
1231 void
1232 proto_reg_handoff_icmpv6(void)
1233 {
1234   old_dissector_add("ip.proto", IP_PROTO_ICMPV6, dissect_icmpv6, proto_icmpv6);
1235 }