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