More signed-vs-unsigned changes from Joerg Mayer.
[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.42 2001/04/27 01:27:37 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-dns.h"
57 #include "in_cksum.h"
58 #include "resolv.h"
59 #include "ipproto.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     unsigned int j;
369     int i, n, l, p;
370     guint16 flags;
371     char dname[MAXDNAME];
372     guint8 ipaddr[4];
373     const u_char *pd;
374     int top_level_offset;
375
376     ni = &icmp6_nodeinfo;
377     tvb_memcpy(tvb, (guint8 *)ni, offset, sizeof *ni);
378     /* flags */
379     flags = pntohs(&ni->ni_flags);
380     tf = proto_tree_add_text(tree, tvb,
381         offset + offsetof(struct icmp6_nodeinfo, ni_flags),
382         sizeof(ni->ni_flags), "Flags: 0x%04x", flags);
383     field_tree = proto_item_add_subtree(tf, ett_nodeinfo_flag);
384     switch (pntohs(&ni->ni_qtype)) {
385     case NI_QTYPE_SUPTYPES:
386         if (ni->ni_type == ICMP6_NI_QUERY) {
387             proto_tree_add_text(field_tree, tvb,
388                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
389                 sizeof(ni->ni_flags), "%s",
390                 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
391                     "Compressed reply supported",
392                     "No compressed reply support"));
393         } else {
394             proto_tree_add_text(field_tree, tvb,
395                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
396                 sizeof(ni->ni_flags), "%s",
397                 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
398                     "Compressed", "Not compressed"));
399         }
400         break;
401     case NI_QTYPE_DNSNAME:
402         if (ni->ni_type == ICMP6_NI_REPLY) {
403             proto_tree_add_text(field_tree, tvb,
404                 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
405                 sizeof(ni->ni_flags), "%s",
406                 decode_boolean_bitfield(flags, NI_FQDN_FLAG_VALIDTTL, sizeof(flags) * 8,
407                     "Valid TTL field", "Meaningless TTL field"));
408         }
409         break;
410     case NI_QTYPE_NODEADDR:
411         proto_tree_add_text(field_tree, tvb,
412             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
413             sizeof(ni->ni_flags), "%s",
414             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_GLOBAL, sizeof(flags) * 8,
415                 "Global address",
416                 "Not global address"));
417         proto_tree_add_text(field_tree, tvb,
418             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
419             sizeof(ni->ni_flags), "%s",
420             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_SITELOCAL, sizeof(flags) * 8,
421                 "Site-local address",
422                 "Not site-local address"));
423         proto_tree_add_text(field_tree, tvb,
424             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
425             sizeof(ni->ni_flags), "%s",
426             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_LINKLOCAL, sizeof(flags) * 8,
427                 "Link-local address",
428                 "Not link-local address"));
429         proto_tree_add_text(field_tree, tvb,
430             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
431             sizeof(ni->ni_flags), "%s",
432             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_COMPAT, sizeof(flags) * 8,
433                 "IPv4 compatible/mapped address",
434                 "Not IPv4 compatible/mapped address"));
435         /* fall through */
436     case NI_QTYPE_IPV4ADDR:
437         proto_tree_add_text(field_tree, tvb,
438             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
439             sizeof(ni->ni_flags), "%s",
440             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_ALL, sizeof(flags) * 8,
441                 "All unicast address",
442                 "Unicast addresses on the queried interface"));
443         proto_tree_add_text(field_tree, tvb,
444             offset + offsetof(struct icmp6_nodeinfo, ni_flags),
445             sizeof(ni->ni_flags), "%s",
446             decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_TRUNCATE, sizeof(flags) * 8,
447                 "Truncated", "Not truncated"));
448         break;
449     }
450
451     /* nonce */
452     proto_tree_add_text(tree, tvb,
453         offset + offsetof(struct icmp6_nodeinfo, icmp6_ni_nonce[0]),
454         sizeof(ni->icmp6_ni_nonce), "Nonce: 0x%08x%08x",
455         pntohl(&ni->icmp6_ni_nonce[0]), pntohl(&ni->icmp6_ni_nonce[4]));
456
457     /* offset for "the rest of data" */
458     off = sizeof(*ni);
459
460     /* rest of data */
461     if (!tvb_bytes_exist(tvb, offset, sizeof(*ni)))
462         goto nodata;
463     if (ni->ni_type == ICMP6_NI_QUERY) {
464         switch (ni->ni_code) {
465         case ICMP6_NI_SUBJ_IPV6:
466             n = tvb_length_remaining(tvb, offset + sizeof(*ni));
467             n /= sizeof(struct e_in6_addr);
468             tf = proto_tree_add_text(tree, tvb,
469                 offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv6 subject addresses");
470             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject6);
471             p = offset + sizeof *ni;
472             for (i = 0; i < n; i++) {
473                 struct e_in6_addr e_in6_addr;
474                 tvb_memcpy(tvb, (guint8 *)&e_in6_addr, p, sizeof e_in6_addr);
475                 proto_tree_add_text(field_tree, tvb,
476                     p, sizeof(struct e_in6_addr),
477                     "%s", ip6_to_str(&e_in6_addr));
478                 p += sizeof(struct e_in6_addr);
479             }
480             off = tvb_length_remaining(tvb, offset);
481             break;
482         case ICMP6_NI_SUBJ_FQDN:
483             /* XXXX - clean this up when packet-dns.c has been tvbuffified */
484             tvb_compat(tvb, &pd, &top_level_offset);
485             l = get_dns_name(pd, top_level_offset + offset + sizeof(*ni),
486                 top_level_offset + offset + sizeof(*ni),
487                 dname, sizeof(dname));
488             if (tvb_bytes_exist(tvb, offset + sizeof(*ni) + l, 1) &&
489                 tvb_get_guint8(tvb, offset + sizeof(*ni) + l) == 0) {
490                 l++;
491                 proto_tree_add_text(tree, tvb, offset + sizeof(*ni), l,
492                     "DNS label: %s (truncated)", dname);
493             } else {
494                 proto_tree_add_text(tree, tvb, offset + sizeof(*ni), l,
495                     "DNS label: %s", dname);
496             }
497             off = tvb_length_remaining(tvb, offset + sizeof(*ni) + l);
498             break;
499         case ICMP6_NI_SUBJ_IPV4:
500             n = tvb_length_remaining(tvb, offset + sizeof(*ni));
501             n /= sizeof(guint32);
502             tf = proto_tree_add_text(tree, tvb,
503                 offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv4 subject addresses");
504             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject4);
505             p = offset + sizeof *ni;
506             for (i = 0; i < n; i++) {
507                 tvb_memcpy(tvb, ipaddr, p, 4);                    
508                 proto_tree_add_text(field_tree, tvb,
509                     p, sizeof(guint32), "%s", ip_to_str(ipaddr));
510                 p += sizeof(guint32);
511             }
512             off = tvb_length_remaining(tvb, offset);
513             break;
514         }
515     } else {
516         switch (pntohs(&ni->ni_qtype)) {
517         case NI_QTYPE_NOOP:
518             break;
519         case NI_QTYPE_SUPTYPES:
520             p = offset + sizeof *ni;
521             tf = proto_tree_add_text(tree, tvb,
522                 offset + sizeof(*ni), tvb_length_remaining(tvb, p),
523                 "Supported type bitmap%s",
524                 (flags & 0x0001) ? ", compressed" : "");
525             field_tree = proto_item_add_subtree(tf,
526                 ett_nodeinfo_nodebitmap);
527             n = 0;
528             while (tvb_bytes_exist(tvb, p, sizeof(guint32))) { /* XXXX Check what? */
529                 if ((flags & 0x0001) == 0) {
530                     l = tvb_length_remaining(tvb, offset + sizeof(*ni));
531                     l /= sizeof(guint32);
532                     i = 0;
533                 } else {
534                     l = tvb_get_ntohs(tvb, p);
535                     i = tvb_get_ntohs(tvb, p + sizeof(guint16));        /*skip*/
536                 }
537                 if (n + l * 32 > (1 << 16))
538                     break;
539                 if (n + (l + i) * 32 > (1 << 16))
540                     break;
541                 if ((flags & 0x0001) == 0) {
542                     proto_tree_add_text(field_tree, tvb, p,
543                         l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
544                         bitrange(tvb, p, l, n));
545                     p += l * 4;
546                 } else {
547                     proto_tree_add_text(field_tree, tvb, p,
548                         4 + l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
549                         bitrange(tvb, p + 4, l, n));
550                     p += (4 + l * 4);
551                 }
552                 n += l * 32 + i * 32;
553             }
554             off = tvb_length_remaining(tvb, offset);
555             break;
556         case NI_QTYPE_DNSNAME:
557             proto_tree_add_text(tree, tvb, offset + sizeof(*ni),
558                 sizeof(gint32), "TTL: %d", (gint32)tvb_get_ntohl(tvb, offset + sizeof *ni));
559             tf = proto_tree_add_text(tree, tvb,
560                 offset + sizeof(*ni) + sizeof(guint32),
561                 tvb_length_remaining(tvb, offset),
562                 "DNS labels");
563             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_nodedns);
564             /* XXXX - clean this up when packet-dns.c has been tvbuffified */
565             tvb_compat(tvb, &pd, &top_level_offset);
566             j = offset + sizeof (*ni) + sizeof(guint32);
567             while (j < tvb_length(tvb)) {
568                 l = get_dns_name(pd, top_level_offset + j,
569                    top_level_offset + offset + sizeof (*ni) + sizeof(guint32),
570                    dname,sizeof(dname));
571                 if (tvb_bytes_exist(tvb, top_level_offset + j + l, 1) &&
572                     tvb_get_guint8(tvb, top_level_offset + j + l) == 0) {
573                     l++;
574                     proto_tree_add_text(field_tree, tvb, j, l,
575                         "DNS label: %s (truncated)", dname);
576                 } else {
577                     proto_tree_add_text(field_tree, tvb, j, l,
578                         "DNS label: %s", dname);
579                 }
580                 j += l;
581             }
582             off = tvb_length_remaining(tvb, offset);
583             break;
584         case NI_QTYPE_NODEADDR:
585             n = tvb_length_remaining(tvb, offset + sizeof(*ni));
586             n /= sizeof(gint32) + sizeof(struct e_in6_addr);
587             tf = proto_tree_add_text(tree, tvb,
588                 offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv6 node addresses");
589             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node6);
590             p = offset + sizeof (*ni);
591             for (i = 0; i < n; i++) {
592                 struct e_in6_addr e_in6_addr;
593                 gint32 ttl;
594                 ttl = (gint32)tvb_get_ntohl(tvb, p);
595                 tvb_memcpy(tvb, (guint8 *)&e_in6_addr, p + sizeof ttl, sizeof e_in6_addr);
596                 proto_tree_add_text(field_tree, tvb,
597                     p, sizeof(struct e_in6_addr) + sizeof(gint32),
598                     "%s (TTL %d)", ip6_to_str(&e_in6_addr), ttl);
599                 p += sizeof(struct e_in6_addr) + sizeof(gint32);
600             }
601             off = tvb_length_remaining(tvb, offset);
602             break;
603         case NI_QTYPE_IPV4ADDR:
604             n = tvb_length_remaining(tvb, offset + sizeof(*ni));
605             n /= sizeof(gint32) + sizeof(guint32);
606             tf = proto_tree_add_text(tree, tvb,
607                 offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv4 node addresses");
608             field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node4);
609             p = offset + sizeof *ni;
610             for (i = 0; i < n; i++) {
611                 tvb_memcpy(tvb, ipaddr, sizeof(gint32) + p, 4);
612                 proto_tree_add_text(field_tree, tvb,
613                     p, sizeof(guint32), "%s (TTL %d)", ip_to_str(ipaddr), tvb_get_ntohl(tvb, p));
614                 p += sizeof(gint32) + sizeof(guint32);
615             }
616             off = tvb_length_remaining(tvb, offset);
617             break;
618         }
619     }
620 nodata:;
621
622     /* the rest of data */
623     dissect_data(tvb_new_subset(tvb, offset + off, -1, -1), 0, pinfo, tree);
624 }
625
626 static void
627 dissect_rrenum(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
628 {
629     proto_tree *field_tree, *opt_tree;
630         proto_item *tf;
631     struct icmp6_router_renum icmp6_router_renum, *rr;
632     struct rr_pco_match rr_pco_match, *match;
633     struct rr_pco_use rr_pco_use, *use;
634     int flagoff, off;
635     unsigned int l;
636     guint8 flags;
637
638     rr = &icmp6_router_renum;
639     tvb_memcpy(tvb, (guint8 *)rr, offset, sizeof *rr);
640     proto_tree_add_text(tree, tvb,
641         offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
642         "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
643     proto_tree_add_text(tree, tvb,
644         offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
645         "Segment number: 0x%02x", rr->rr_segnum);
646
647     flagoff = offset + offsetof(struct icmp6_router_renum, rr_flags);
648     flags = tvb_get_guint8(tvb, flagoff);
649     tf = proto_tree_add_text(tree, tvb, flagoff, 1,
650         "Flags: 0x%02x", flags);
651     field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
652     proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
653         decode_boolean_bitfield(flags, 0x80, 8,
654             "Test command", "Not test command"));
655     proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
656         decode_boolean_bitfield(flags, 0x40, 8,
657             "Result requested", "Result not requested"));
658     proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
659         decode_boolean_bitfield(flags, 0x20, 8,
660             "All interfaces", "Not all interfaces"));
661     proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
662         decode_boolean_bitfield(flags, 0x10, 8,
663             "Site specific", "Not site specific"));
664     proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
665         decode_boolean_bitfield(flags, 0x08, 8,
666             "Processed previously", "Complete result"));
667
668     proto_tree_add_text(tree, tvb,
669         offset + offsetof(struct icmp6_router_renum, rr_maxdelay), 2,
670         "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
671     dissect_data(tvb_new_subset(tvb, offset + sizeof(*rr), -1, -1), 0, pinfo, tree);    /*XXX*/
672
673     if (rr->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
674         off = offset + sizeof(*rr);
675         match = &rr_pco_match;
676         tvb_memcpy(tvb, (guint8 *)match, off, sizeof *match);
677         tf = proto_tree_add_text(tree, tvb, off, sizeof(*match),
678             "Match-Prefix: %s/%u (%u-%u)", ip6_to_str(&match->rpm_prefix),
679             match->rpm_matchlen, match->rpm_minlen, match->rpm_maxlen);
680         opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
681         proto_tree_add_text(opt_tree, tvb,
682             off + offsetof(struct rr_pco_match, rpm_code),
683             sizeof(match->rpm_code), "OpCode: %s (%u)",
684             val_to_str(match->rpm_code, names_rrenum_matchcode, "Unknown"), 
685             match->rpm_code);
686         proto_tree_add_text(opt_tree, tvb,
687             off + offsetof(struct rr_pco_match, rpm_len),
688             sizeof(match->rpm_len), "OpLength: %u (%u octets)",
689             match->rpm_len, match->rpm_len * 8);
690         proto_tree_add_text(opt_tree, tvb,
691             off + offsetof(struct rr_pco_match, rpm_ordinal),
692             sizeof(match->rpm_ordinal), "Ordinal: %u", match->rpm_ordinal);
693         proto_tree_add_text(opt_tree, tvb,
694             off + offsetof(struct rr_pco_match, rpm_matchlen),
695             sizeof(match->rpm_matchlen), "MatchLen: %u", match->rpm_matchlen);
696         proto_tree_add_text(opt_tree, tvb,
697             off + offsetof(struct rr_pco_match, rpm_minlen),
698             sizeof(match->rpm_minlen), "MinLen: %u", match->rpm_minlen);
699         proto_tree_add_text(opt_tree, tvb,
700             off + offsetof(struct rr_pco_match, rpm_maxlen),
701             sizeof(match->rpm_maxlen), "MaxLen: %u", match->rpm_maxlen);
702         proto_tree_add_text(opt_tree, tvb,
703             off + offsetof(struct rr_pco_match, rpm_prefix),
704             sizeof(match->rpm_prefix), "MatchPrefix: %s",
705             ip6_to_str(&match->rpm_prefix));
706
707         off += sizeof(*match);
708         use = &rr_pco_use;
709         for (l = match->rpm_len * 8 - sizeof(*match);
710              l >= sizeof(*use); l -= sizeof(*use), off += sizeof(*use)) {
711             tvb_memcpy(tvb, (guint8 *)use, off, sizeof *use);
712             tf = proto_tree_add_text(tree, tvb, off, sizeof(*use),
713                 "Use-Prefix: %s/%u (keep %u)", ip6_to_str(&use->rpu_prefix),
714                 use->rpu_uselen, use->rpu_keeplen);
715             opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
716             proto_tree_add_text(opt_tree, tvb,
717                 off + offsetof(struct rr_pco_use, rpu_uselen),
718                 sizeof(use->rpu_uselen), "UseLen: %u", use->rpu_uselen);
719             proto_tree_add_text(opt_tree, tvb,
720                 off + offsetof(struct rr_pco_use, rpu_keeplen),
721                 sizeof(use->rpu_keeplen), "KeepLen: %u", use->rpu_keeplen);
722             tf = proto_tree_add_text(opt_tree, tvb,
723                 flagoff = off + offsetof(struct rr_pco_use, rpu_ramask),
724                 sizeof(use->rpu_ramask), "FlagMask: 0x%x", use->rpu_ramask);
725             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
726             flags = tvb_get_guint8(tvb, flagoff);
727             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
728                 decode_boolean_bitfield(flags,
729                     ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
730                     "Onlink", "Not onlink"));
731             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
732                 decode_boolean_bitfield(flags,
733                     ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8,
734                     "Auto", "Not auto"));
735             tf = proto_tree_add_text(opt_tree, tvb,
736                 flagoff = off + offsetof(struct rr_pco_use, rpu_raflags),
737                 sizeof(use->rpu_raflags), "RAFlags: 0x%x", use->rpu_raflags);
738             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
739             flags = tvb_get_guint8(tvb, flagoff);
740             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
741                 decode_boolean_bitfield(flags,
742                     ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
743                     "Onlink", "Not onlink"));
744             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
745                 decode_boolean_bitfield(flags,
746                     ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8, "Auto", "Not auto"));
747             if (pntohl(&use->rpu_vltime) == 0xffffffff)
748                 proto_tree_add_text(opt_tree, tvb,
749                     off + offsetof(struct rr_pco_use, rpu_vltime),
750                     sizeof(use->rpu_vltime), "Valid Lifetime: infinity");
751             else
752                 proto_tree_add_text(opt_tree, tvb,
753                     off + offsetof(struct rr_pco_use, rpu_vltime),
754                     sizeof(use->rpu_vltime), "Valid Lifetime: %u",
755                     pntohl(&use->rpu_vltime));
756             if (pntohl(&use->rpu_pltime) == 0xffffffff)
757                 proto_tree_add_text(opt_tree, tvb,
758                     off + offsetof(struct rr_pco_use, rpu_pltime),
759                     sizeof(use->rpu_pltime), "Preferred Lifetime: infinity");
760             else
761                 proto_tree_add_text(opt_tree, tvb,
762                     off + offsetof(struct rr_pco_use, rpu_pltime),
763                     sizeof(use->rpu_pltime), "Preferred Lifetime: %u",
764                     pntohl(&use->rpu_pltime));
765             tf = proto_tree_add_text(opt_tree, tvb,
766                 flagoff = off + offsetof(struct rr_pco_use, rpu_flags),
767                 sizeof(use->rpu_flags), "Flags: 0x%08x",
768                 pntohl(&use->rpu_flags));
769             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
770             flags = tvb_get_guint8(tvb, flagoff);
771             proto_tree_add_text(field_tree, tvb, flagoff, 4, "%s",
772                 decode_boolean_bitfield(flags,
773                     ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, 32,
774                     "Decrement valid lifetime", "No decrement valid lifetime"));
775             proto_tree_add_text(field_tree, tvb, flagoff, 4, "%s",
776                 decode_boolean_bitfield(flags,
777                     ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, 32,
778                     "Decrement preferred lifetime",
779                     "No decrement preferred lifetime"));
780             proto_tree_add_text(opt_tree, tvb,
781                 off + offsetof(struct rr_pco_use, rpu_prefix),
782                 sizeof(use->rpu_prefix), "UsePrefix: %s",
783                 ip6_to_str(&use->rpu_prefix));
784         }
785
786     }
787 }
788
789 static void
790 dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
791 {
792     proto_tree *icmp6_tree, *field_tree;
793     proto_item *ti, *tf = NULL;
794     struct icmp6_hdr icmp6_hdr, *dp;
795     struct icmp6_nodeinfo *ni = NULL;
796     char *codename, *typename;
797     char *colcodename, *coltypename;
798     int len;
799     guint length, reported_length;
800     vec_t cksum_vec[4];
801     guint32 phdr[2];
802     guint16 cksum, computed_cksum;
803     int offset;
804     tvbuff_t *next_tvb;
805
806     offset = 0;
807     tvb_memcpy(tvb, (guint8 *)&icmp6_hdr, offset, sizeof icmp6_hdr);
808     dp = &icmp6_hdr;
809     codename = typename = colcodename = coltypename = "Unknown";
810     len = sizeof(*dp);
811     switch (dp->icmp6_type) {
812     case ICMP6_DST_UNREACH:
813         typename = coltypename = "Unreachable";
814         switch (dp->icmp6_code) {
815         case ICMP6_DST_UNREACH_NOROUTE:
816             codename = colcodename = "Route unreachable";
817             break;
818         case ICMP6_DST_UNREACH_ADMIN:
819             codename = colcodename = "Administratively prohibited";
820             break;
821         case ICMP6_DST_UNREACH_NOTNEIGHBOR:
822             codename = colcodename = "Not a neighbor";
823             break;
824         case ICMP6_DST_UNREACH_ADDR:
825             codename = colcodename = "Address unreachable";
826             break;
827         case ICMP6_DST_UNREACH_NOPORT:
828             codename = colcodename = "Port unreachable";
829             break;
830         }
831         break;
832     case ICMP6_PACKET_TOO_BIG:
833         typename = coltypename = "Too big";
834         codename = colcodename = NULL;
835         break;
836     case ICMP6_TIME_EXCEEDED:
837         typename = coltypename = "Time exceeded";
838         switch (dp->icmp6_code) {
839         case ICMP6_TIME_EXCEED_TRANSIT:
840             codename = colcodename = "In-transit";
841             break;
842         case ICMP6_TIME_EXCEED_REASSEMBLY:
843             codename = colcodename = "Reassembly";
844             break;
845         }
846         break;
847     case ICMP6_PARAM_PROB:
848         typename = coltypename = "Parameter problem";
849         switch (dp->icmp6_code) {
850         case ICMP6_PARAMPROB_HEADER:
851             codename = colcodename = "Header";
852             break;
853         case ICMP6_PARAMPROB_NEXTHEADER:
854             codename = colcodename = "Next header";
855             break;
856         case ICMP6_PARAMPROB_OPTION:
857             codename = colcodename = "Option";
858             break;
859         }
860         break;
861     case ICMP6_ECHO_REQUEST:
862         typename = coltypename = "Echo request";
863         codename = colcodename = NULL;
864         break;
865     case ICMP6_ECHO_REPLY:
866         typename = coltypename = "Echo reply";
867         codename = colcodename = NULL;
868         break;
869     case ICMP6_MEMBERSHIP_QUERY:
870         typename = coltypename = "Multicast listener query";
871         codename = colcodename = NULL;
872         break;
873     case ICMP6_MEMBERSHIP_REPORT:
874         typename = coltypename = "Multicast listener report";
875         codename = colcodename = NULL;
876         break;
877     case ICMP6_MEMBERSHIP_REDUCTION:
878         typename = coltypename = "Multicast listener done";
879         codename = colcodename = NULL;
880         break;
881     case ND_ROUTER_SOLICIT:
882         typename = coltypename = "Router solicitation";
883         codename = colcodename = NULL;
884         len = sizeof(struct nd_router_solicit);
885         break;
886     case ND_ROUTER_ADVERT:
887         typename = coltypename = "Router advertisement";
888         codename = colcodename = NULL;
889         len = sizeof(struct nd_router_advert);
890         break;
891     case ND_NEIGHBOR_SOLICIT:
892         typename = coltypename = "Neighbor solicitation";
893         codename = colcodename = NULL;
894         len = sizeof(struct nd_neighbor_solicit);
895         break;
896     case ND_NEIGHBOR_ADVERT:
897         typename = coltypename = "Neighbor advertisement";
898         codename = colcodename = NULL;
899         len = sizeof(struct nd_neighbor_advert);
900         break;
901     case ND_REDIRECT:
902         typename = coltypename = "Redirect";
903         codename = colcodename = NULL;
904         len = sizeof(struct nd_redirect);
905         break;
906     case ICMP6_ROUTER_RENUMBERING:
907         typename = coltypename = "Router renumbering";
908         switch (dp->icmp6_code) {
909         case ICMP6_ROUTER_RENUMBERING_COMMAND:
910             codename = colcodename = "Command";
911             break;
912         case ICMP6_ROUTER_RENUMBERING_RESULT:
913             codename = colcodename = "Result";
914             break;
915         case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
916             codename = colcodename = "Sequence number reset";
917             break;
918         }
919         len = sizeof(struct icmp6_router_renum);
920         break;
921     case ICMP6_NI_QUERY:
922     case ICMP6_NI_REPLY:
923         ni = (struct icmp6_nodeinfo *)dp;
924         if (ni->ni_type == ICMP6_NI_QUERY) {
925             typename = coltypename = "Node information query";
926             switch (ni->ni_code) {
927             case ICMP6_NI_SUBJ_IPV6:
928                 codename = "Query subject = IPv6 addresses";
929                 break;
930             case ICMP6_NI_SUBJ_FQDN:
931                 if (tvb_bytes_exist(tvb, offset, sizeof(*ni)))
932                     codename = "Query subject = DNS name";
933                 else
934                     codename = "Query subject = empty";
935                 break;
936             case ICMP6_NI_SUBJ_IPV4:
937                 codename = "Query subject = IPv4 addresses";
938                 break;
939             }
940         } else {
941             typename = coltypename = "Node information reply";
942             switch (ni->ni_code) {
943             case ICMP6_NI_SUCCESS:
944                 codename = "Successful";
945                 break;
946             case ICMP6_NI_REFUSED:
947                 codename = "Refused";
948                 break;
949             case ICMP6_NI_UNKNOWN:
950                 codename = "Unknown query type";
951                 break;
952             }
953         }
954         colcodename = val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
955             "Unknown");
956         len = sizeof(struct icmp6_nodeinfo);
957         break;
958     }
959
960     if (check_col(pinfo->fd, COL_PROTOCOL))
961         col_set_str(pinfo->fd, COL_PROTOCOL, "ICMPv6");
962     if (check_col(pinfo->fd, COL_INFO)) {
963         char typebuf[256], codebuf[256];
964
965         if (coltypename && strcmp(coltypename, "Unknown") == 0) {
966             snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
967                 dp->icmp6_type);
968             coltypename = typebuf;
969         }
970         if (colcodename && strcmp(colcodename, "Unknown") == 0) {
971             snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
972                 dp->icmp6_code);
973             colcodename = codebuf;
974         }
975         if (colcodename) {
976             col_add_fstr(pinfo->fd, COL_INFO, "%s (%s)", coltypename, colcodename);
977         } else {
978             col_add_fstr(pinfo->fd, COL_INFO, "%s", coltypename);
979         }
980     }
981
982     if (tree) {
983         /* !!! specify length */
984         ti = proto_tree_add_item(tree, proto_icmpv6, tvb, offset, len, FALSE);
985         icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
986
987         proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_type, tvb,
988             offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
989             dp->icmp6_type,
990             "Type: %u (%s)", dp->icmp6_type, typename);
991         if (codename) {
992             proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, tvb,
993                 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
994                 dp->icmp6_code,
995                 "Code: %u (%s)", dp->icmp6_code, codename);
996         } else {
997             proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, tvb,
998                 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
999                 dp->icmp6_code,
1000                 "Code: %u", dp->icmp6_code);
1001         }
1002         cksum = (guint16)htons(dp->icmp6_cksum);
1003         length = tvb_length(tvb);
1004         reported_length = tvb_reported_length(tvb);
1005         if (!pinfo->fragmented && length >= reported_length) {
1006             /* The packet isn't part of a fragmented datagram and isn't
1007                truncated, so we can checksum it. */
1008
1009             /* Set up the fields of the pseudo-header. */
1010             cksum_vec[0].ptr = pinfo->src.data;
1011             cksum_vec[0].len = pinfo->src.len;
1012             cksum_vec[1].ptr = pinfo->dst.data;
1013             cksum_vec[1].len = pinfo->dst.len;
1014             cksum_vec[2].ptr = (const guint8 *)&phdr;
1015             phdr[0] = htonl(tvb_reported_length(tvb));
1016             phdr[1] = htonl(IP_PROTO_ICMPV6);
1017             cksum_vec[2].len = 8;
1018             cksum_vec[3].len = tvb_reported_length(tvb);
1019             cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, cksum_vec[3].len);
1020             computed_cksum = in_cksum(cksum_vec, 4);
1021             if (computed_cksum == 0) {
1022                 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
1023                         tvb,
1024                         offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
1025                         cksum,
1026                         "Checksum: 0x%04x (correct)", cksum);
1027             } else {
1028                 proto_tree_add_boolean_hidden(icmp6_tree, hf_icmpv6_checksum_bad,
1029                         tvb,
1030                         offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
1031                         TRUE);
1032                 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
1033                         tvb,
1034                         offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
1035                         cksum,
1036                         "Checksum: 0x%04x (incorrect, should be 0x%04x)",
1037                         cksum, in_cksum_shouldbe(cksum, computed_cksum));
1038             }
1039         } else {
1040             proto_tree_add_uint(icmp6_tree, hf_icmpv6_checksum, tvb,
1041                 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
1042                 cksum);
1043         }
1044
1045         /* decode... */
1046         next_tvb = tvb_new_subset(tvb, offset + sizeof(*dp), -1, -1);
1047         switch (dp->icmp6_type) {
1048         case ICMP6_DST_UNREACH:
1049         case ICMP6_TIME_EXCEEDED:
1050             /* tiny sanity check */
1051             if ((tvb_get_guint8(tvb, offset + sizeof(*dp)) & 0xf0) == 0x60) {
1052                 /* The invoking packet is an IPv6 datagram; dissect it.
1053
1054                    Set the columns non-writable, so that the packet list
1055                    shows this as an ICMPv6 packet, not as the type of packet
1056                    for which the ICMPv6 packet was generated. */
1057                 col_set_writable(pinfo->fd, FALSE);
1058
1059                 call_dissector(ipv6_handle, next_tvb, pinfo, icmp6_tree);
1060             } else {
1061                 dissect_data(next_tvb, 0, pinfo, icmp6_tree);
1062             }
1063             break;
1064         case ICMP6_PACKET_TOO_BIG:
1065             proto_tree_add_text(icmp6_tree, tvb,
1066                 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
1067                 "MTU: %u", pntohl(&dp->icmp6_mtu));
1068             /* tiny sanity check */
1069             if ((tvb_get_guint8(tvb, offset + sizeof(*dp)) & 0xf0) == 0x60) {
1070                 /* The invoking packet is an IPv6 datagram; dissect it.
1071
1072                    Set the columns non-writable, so that the packet list
1073                    shows this as an ICMPv6 packet, not as the type of packet
1074                    for which the ICMPv6 packet was generated. */
1075                 col_set_writable(pinfo->fd, FALSE);
1076
1077                 call_dissector(ipv6_handle, next_tvb, pinfo, icmp6_tree);
1078             } else {
1079                 dissect_data(next_tvb, 0, pinfo, icmp6_tree);
1080             }
1081             break;
1082         case ICMP6_PARAM_PROB:
1083             proto_tree_add_text(icmp6_tree, tvb,
1084                 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
1085                 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
1086             /* tiny sanity check */
1087             if ((tvb_get_guint8(tvb, offset + sizeof(*dp)) & 0xf0) == 0x60) {
1088                 /* The invoking packet is an IPv6 datagram; dissect it.
1089
1090                    Set the columns non-writable, so that the packet list
1091                    shows this as an ICMPv6 packet, not as the type of packet
1092                    for which the ICMPv6 packet was generated. */
1093                 col_set_writable(pinfo->fd, FALSE);
1094
1095                 call_dissector(ipv6_handle, next_tvb, pinfo, icmp6_tree);
1096             } else {
1097                 dissect_data(next_tvb, 0, pinfo, icmp6_tree);
1098             }
1099             break;
1100         case ICMP6_ECHO_REQUEST:
1101         case ICMP6_ECHO_REPLY:
1102             proto_tree_add_text(icmp6_tree, tvb,
1103                 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
1104                 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
1105             proto_tree_add_text(icmp6_tree, tvb,
1106                 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
1107                 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
1108             dissect_data(next_tvb, 0, pinfo, icmp6_tree);
1109             break;
1110         case ICMP6_MEMBERSHIP_QUERY:
1111         case ICMP6_MEMBERSHIP_REPORT:
1112         case ICMP6_MEMBERSHIP_REDUCTION:
1113             proto_tree_add_text(icmp6_tree, tvb,
1114                 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
1115                 "Maximum response delay: %u",
1116                 (guint16)ntohs(dp->icmp6_maxdelay));
1117             proto_tree_add_text(icmp6_tree, tvb, offset + sizeof(*dp), 16,
1118                 "Multicast Address: %s",
1119                 ip6_to_str((struct e_in6_addr *)(tvb_get_ptr(tvb, offset + sizeof *dp, sizeof (struct e_in6_addr)))));
1120             break;
1121         case ND_ROUTER_SOLICIT:
1122             dissect_icmpv6opt(tvb, offset + sizeof(*dp), pinfo, icmp6_tree);
1123             break;
1124         case ND_ROUTER_ADVERT:
1125           {
1126             struct nd_router_advert nd_router_advert, *ra;
1127             int flagoff;
1128             guint32 ra_flags;
1129
1130             ra = &nd_router_advert;
1131             tvb_memcpy(tvb, (guint8 *)ra, offset, sizeof *ra);
1132             proto_tree_add_text(icmp6_tree, tvb,
1133                 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
1134                 1, "Cur hop limit: %u", ra->nd_ra_curhoplimit);
1135
1136             flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
1137             ra_flags = tvb_get_guint8(tvb, flagoff);
1138             tf = proto_tree_add_text(icmp6_tree, tvb, flagoff, 1, "Flags: 0x%02x", ra_flags);
1139             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1140             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
1141                 decode_boolean_bitfield(ra_flags,
1142                         0x80, 8, "Managed", "Not managed"));
1143             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
1144                 decode_boolean_bitfield(ra_flags,
1145                         0x40, 8, "Other", "Not other"));
1146     /* BT INSERT BEGIN */
1147             proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
1148                 decode_boolean_bitfield(ra_flags,
1149                         0x20, 8, "Home Agent", "Not Home Agent"));              
1150     /* BT INSERT END */
1151             proto_tree_add_text(icmp6_tree, tvb,
1152                 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
1153                 2, "Router lifetime: %u",
1154                 (guint16)ntohs(ra->nd_ra_router_lifetime));
1155             proto_tree_add_text(icmp6_tree, tvb,
1156                 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
1157                 "Reachable time: %u", pntohl(&ra->nd_ra_reachable));
1158             proto_tree_add_text(icmp6_tree, tvb,
1159                 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
1160                 "Retrans time: %u", pntohl(&ra->nd_ra_retransmit));
1161             dissect_icmpv6opt(tvb, offset + sizeof(struct nd_router_advert), pinfo, icmp6_tree);
1162             break;
1163           }
1164         case ND_NEIGHBOR_SOLICIT:
1165           {
1166             struct nd_neighbor_solicit nd_neighbor_solicit, *ns;
1167
1168             ns = &nd_neighbor_solicit;
1169             tvb_memcpy(tvb, (guint8 *)ns, offset, sizeof *ns);
1170             proto_tree_add_text(icmp6_tree, tvb,
1171                         offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
1172 #ifdef INET6
1173                         "Target: %s (%s)",
1174                         get_hostname6(&ns->nd_ns_target),
1175 #else
1176                         "Target: %s",
1177 #endif
1178                         ip6_to_str(&ns->nd_ns_target));
1179
1180             dissect_icmpv6opt(tvb, offset + sizeof(*ns), pinfo, icmp6_tree);
1181             break;
1182           }
1183         case ND_NEIGHBOR_ADVERT:
1184           {
1185             int flagoff, targetoff;
1186             guint32 na_flags;
1187             struct e_in6_addr na_target;
1188
1189             flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
1190             na_flags = tvb_get_ntohl(tvb, flagoff);
1191
1192             tf = proto_tree_add_text(icmp6_tree, tvb, flagoff, 4, "Flags: 0x%08x", na_flags);
1193             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1194             proto_tree_add_text(field_tree, tvb, flagoff, 4, "%s",
1195                 decode_boolean_bitfield(na_flags,
1196                         ND_NA_FLAG_ROUTER, 32, "Router", "Not router"));
1197             proto_tree_add_text(field_tree, tvb, flagoff, 4, "%s",
1198                 decode_boolean_bitfield(na_flags,
1199                         ND_NA_FLAG_SOLICITED, 32, "Solicited", "Not adverted"));
1200             proto_tree_add_text(field_tree, tvb, flagoff, 4, "%s",
1201                 decode_boolean_bitfield(na_flags,
1202                         ND_NA_FLAG_OVERRIDE, 32, "Override", "Not override"));
1203
1204             targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
1205             tvb_memcpy(tvb, (guint8 *)&na_target, targetoff, sizeof na_target);
1206             proto_tree_add_text(icmp6_tree, tvb, targetoff, 16,
1207 #ifdef INET6
1208                         "Target: %s (%s)",
1209                         get_hostname6(&na_target),
1210 #else
1211                         "Target: %s",
1212 #endif
1213                         ip6_to_str(&na_target));
1214
1215             dissect_icmpv6opt(tvb, offset + sizeof(struct nd_neighbor_advert), pinfo, icmp6_tree);
1216             break;
1217           }
1218         case ND_REDIRECT:
1219           {
1220             struct nd_redirect nd_redirect, *rd;
1221
1222             rd = &nd_redirect;
1223             tvb_memcpy(tvb, (guint8 *)rd, offset, sizeof *rd);
1224             proto_tree_add_text(icmp6_tree, tvb,
1225                         offset + offsetof(struct nd_redirect, nd_rd_target), 16,
1226 #ifdef INET6
1227                         "Target: %s (%s)",
1228                         get_hostname6(&rd->nd_rd_target),
1229 #else
1230                         "Target: %s",
1231 #endif
1232                         ip6_to_str(&rd->nd_rd_target));
1233
1234             proto_tree_add_text(icmp6_tree, tvb,
1235                         offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
1236 #ifdef INET6
1237                         "Destination: %s (%s)",
1238                         get_hostname6(&rd->nd_rd_dst),
1239 #else
1240                         "Destination: %s",
1241 #endif
1242                         ip6_to_str(&rd->nd_rd_dst));
1243
1244             dissect_icmpv6opt(tvb, offset + sizeof(*rd), pinfo, icmp6_tree);
1245             break;
1246           }
1247         case ICMP6_ROUTER_RENUMBERING:
1248             dissect_rrenum(tvb, offset, pinfo, icmp6_tree);
1249             break;
1250         case ICMP6_NI_QUERY:
1251         case ICMP6_NI_REPLY:
1252             ni = (struct icmp6_nodeinfo *)dp;
1253             proto_tree_add_text(icmp6_tree, tvb,
1254                 offset + offsetof(struct icmp6_nodeinfo, ni_qtype),
1255                 sizeof(ni->ni_qtype),
1256                 "Query type: 0x%04x (%s)", pntohs(&ni->ni_qtype),
1257                 val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
1258                 "Unknown"));
1259             dissect_nodeinfo(tvb, offset, pinfo, icmp6_tree);
1260             break;
1261         default:
1262             dissect_data(next_tvb, 0, pinfo, tree);
1263             break;
1264         }
1265     }
1266 }
1267
1268 void
1269 proto_register_icmpv6(void)
1270 {
1271   static hf_register_info hf[] = {
1272     { &hf_icmpv6_type,
1273       { "Type",           "icmpv6.type",        FT_UINT8,  BASE_DEC, NULL, 0x0,
1274         "" }},
1275     { &hf_icmpv6_code,
1276       { "Code",           "icmpv6.code",        FT_UINT8,  BASE_DEC, NULL, 0x0,
1277         "" }},
1278     { &hf_icmpv6_checksum,
1279       { "Checksum",       "icmpv6.checksum",    FT_UINT16, BASE_HEX, NULL, 0x0,
1280         "" }},
1281     { &hf_icmpv6_checksum_bad,
1282       { "Bad Checksum",   "icmpv6.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1283         "" }},
1284   };
1285   static gint *ett[] = {
1286     &ett_icmpv6,
1287     &ett_icmpv6opt,
1288     &ett_icmpv6flag,
1289     &ett_nodeinfo_flag,
1290     &ett_nodeinfo_subject4,
1291     &ett_nodeinfo_subject6,
1292     &ett_nodeinfo_node4,
1293     &ett_nodeinfo_node6,
1294     &ett_nodeinfo_nodebitmap,
1295     &ett_nodeinfo_nodedns,
1296   };
1297
1298   proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
1299                                          "ICMPv6", "icmpv6");
1300   proto_register_field_array(proto_icmpv6, hf, array_length(hf));
1301   proto_register_subtree_array(ett, array_length(ett));
1302 }
1303
1304 void
1305 proto_reg_handoff_icmpv6(void)
1306 {
1307   dissector_add("ip.proto", IP_PROTO_ICMPV6, dissect_icmpv6, proto_icmpv6);
1308
1309   /*
1310    * Get a handle for the IPv6 dissector.
1311    */
1312   ipv6_handle = find_dissector("ipv6");
1313 }