2 * Routines for ICMPv6 packet disassembly
4 * $Id: packet-icmpv6.c,v 1.33 2001/01/03 06:55:28 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39 #ifdef HAVE_SYS_TYPES_H
40 # include <sys/types.h>
43 #ifdef HAVE_NETINET_IN_H
44 # include <netinet/in.h>
49 #ifdef NEED_SNPRINTF_H
50 # include "snprintf.h"
54 #include "packet-ipv6.h"
55 #include "packet-ip.h"
56 #include "packet-dns.h"
61 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
64 static int proto_icmpv6 = -1;
65 static int hf_icmpv6_type = -1;
66 static int hf_icmpv6_code = -1;
67 static int hf_icmpv6_checksum = -1;
69 static gint ett_icmpv6 = -1;
70 static gint ett_icmpv6opt = -1;
71 static gint ett_icmpv6flag = -1;
72 static gint ett_nodeinfo_flag = -1;
73 static gint ett_nodeinfo_subject4 = -1;
74 static gint ett_nodeinfo_subject6 = -1;
75 static gint ett_nodeinfo_node4 = -1;
76 static gint ett_nodeinfo_node6 = -1;
77 static gint ett_nodeinfo_nodebitmap = -1;
78 static gint ett_nodeinfo_nodedns = -1;
80 static const value_string names_nodeinfo_qtype[] = {
81 { NI_QTYPE_NOOP, "NOOP" },
82 { NI_QTYPE_SUPTYPES, "Supported query types" },
83 { NI_QTYPE_DNSNAME, "DNS name" },
84 { NI_QTYPE_NODEADDR, "Node addresses" },
85 { NI_QTYPE_IPV4ADDR, "IPv4 node addresses" },
89 static const value_string names_rrenum_matchcode[] = {
90 { RPM_PCO_ADD, "Add" },
91 { RPM_PCO_CHANGE, "Change" },
92 { RPM_PCO_SETGLOBAL, "Set Global" },
97 dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
99 proto_tree *icmp6opt_tree, *field_tree;
101 struct nd_opt_hdr *opt;
109 if (!IS_DATA_IN_FRAME(offset))
112 opt = (struct nd_opt_hdr *)&pd[offset];
113 len = opt->nd_opt_len << 3;
115 /* !!! specify length */
116 ti = proto_tree_add_text(tree, NullTVB, offset, len, "ICMPv6 options");
117 icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
119 switch (opt->nd_opt_type) {
120 case ND_OPT_SOURCE_LINKADDR:
121 typename = "Source link-layer address";
123 case ND_OPT_TARGET_LINKADDR:
124 typename = "Target link-layer address";
126 case ND_OPT_PREFIX_INFORMATION:
127 typename = "Prefix information";
129 case ND_OPT_REDIRECTED_HEADER:
130 typename = "Redirected header";
136 typename = "Unknown";
140 proto_tree_add_text(icmp6opt_tree, NullTVB,
141 offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
142 "Type: 0x%02x (%s)", opt->nd_opt_type, typename);
143 proto_tree_add_text(icmp6opt_tree, NullTVB,
144 offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
145 "Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
148 switch (opt->nd_opt_type) {
149 case ND_OPT_SOURCE_LINKADDR:
150 case ND_OPT_TARGET_LINKADDR:
155 len = (opt->nd_opt_len << 3) - sizeof(*opt);
156 t = (char *)malloc(len * 3);
157 memset(t, 0, len * 3);
158 p = &pd[offset + sizeof(*opt)];
159 for (i = 0; i < len; i++) {
162 sprintf(&t[i * 3], "%02x", p[i] & 0xff);
164 proto_tree_add_text(icmp6opt_tree, NullTVB,
165 offset + sizeof(*opt), len, "Link-layer address: %s", t);
168 case ND_OPT_PREFIX_INFORMATION:
170 struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
172 proto_tree_add_text(icmp6opt_tree, NullTVB,
173 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
174 1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
176 flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
177 tf = proto_tree_add_text(icmp6opt_tree, NullTVB, flagoff, 1, "Flags: 0x%02x",
178 pntohl(&pi->nd_opt_pi_flags_reserved));
179 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
180 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
181 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
182 0x80, 8, "Onlink", "Not onlink"));
183 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
184 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
185 0x40, 8, "Auto", "Not auto"));
187 proto_tree_add_text(icmp6opt_tree, NullTVB,
188 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
189 4, "Valid lifetime: 0x%08x",
190 pntohl(&pi->nd_opt_pi_valid_time));
191 proto_tree_add_text(icmp6opt_tree, NullTVB,
192 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
193 4, "Preferred lifetime: 0x%08x",
194 pntohl(&pi->nd_opt_pi_preferred_time));
195 proto_tree_add_text(icmp6opt_tree, NullTVB,
196 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
197 16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
200 case ND_OPT_REDIRECTED_HEADER:
201 proto_tree_add_text(icmp6opt_tree, NullTVB,
202 offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
203 /* tiny sanity check */
204 if ((pd[offset + 8] & 0xf0) == 0x60)
205 dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
207 old_dissect_data(pd, offset + 8, fd, icmp6opt_tree);
211 struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
212 proto_tree_add_text(icmp6opt_tree, NullTVB,
213 offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
214 "MTU: %d", pntohl(&pi->nd_opt_mtu_mtu));
219 if (opt->nd_opt_len == 0) {
220 proto_tree_add_text(icmp6opt_tree, NullTVB,
221 offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
222 "Invalid option length: %d",
227 offset += (opt->nd_opt_len << 3);
232 * draft-ietf-ipngwg-icmp-name-lookups-06.txt
233 * Note that the packet format was changed several times in the past.
237 bitrange0(v, s, buf, buflen)
257 ep = buf + buflen - 1;
258 memset(buf, 0, buflen);
261 /* shift till we have 0x01 */
262 if ((v & 0x01) == 0) {
265 v >>= 4; off += 4; continue;
267 v >>= 3; off += 3; continue;
268 case 0x04: case 0x0c:
269 v >>= 2; off += 2; continue;
271 v >>= 1; off += 1; continue;
275 /* we have 0x01 with us */
276 for (i = 0; i < 32 - off; i++) {
277 if ((v & (0x01 << i)) == 0)
281 l = snprintf(p, ep - p, ",%d", s + off);
283 l = snprintf(p, ep - p, ",%d-%d", s + off,
297 bitrange(u_char *p, int l, int s)
299 static char buf[1024];
303 memset(buf, 0, sizeof(buf));
305 eq = buf + sizeof(buf) - 1;
306 for (i = 0; i < l; i++) {
307 if (bitrange0(pntohl(p + i * 4), s + i * 4, q, eq - q) == NULL) {
308 if (q != buf && q + 5 < buf + sizeof(buf))
309 strncpy(q, ",...", 5);
318 dissect_nodeinfo(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
320 proto_tree *field_tree;
322 struct icmp6_nodeinfo *ni;
327 char dname[MAXDNAME];
329 ni = (struct icmp6_nodeinfo *)&pd[offset];
332 flags = pntohs(&ni->ni_flags);
333 tf = proto_tree_add_text(tree, NullTVB,
334 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
335 sizeof(ni->ni_flags), "Flags: 0x%04x", flags);
336 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_flag);
337 switch (pntohs(&ni->ni_qtype)) {
338 case NI_QTYPE_SUPTYPES:
339 if (ni->ni_type == ICMP6_NI_QUERY) {
340 proto_tree_add_text(field_tree, NullTVB,
341 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
342 sizeof(ni->ni_flags), "%s",
343 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
344 "Compressed reply supported",
345 "No compressed reply support"));
347 proto_tree_add_text(field_tree, NullTVB,
348 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
349 sizeof(ni->ni_flags), "%s",
350 decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
351 "Compressed", "Not compressed"));
354 case NI_QTYPE_DNSNAME:
355 if (ni->ni_type == ICMP6_NI_REPLY) {
356 proto_tree_add_text(field_tree, NullTVB,
357 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
358 sizeof(ni->ni_flags), "%s",
359 decode_boolean_bitfield(flags, NI_FQDN_FLAG_VALIDTTL, sizeof(flags) * 8,
360 "Valid TTL field", "Meaningless TTL field"));
363 case NI_QTYPE_NODEADDR:
364 proto_tree_add_text(field_tree, NullTVB,
365 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
366 sizeof(ni->ni_flags), "%s",
367 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_GLOBAL, sizeof(flags) * 8,
369 "Not global address"));
370 proto_tree_add_text(field_tree, NullTVB,
371 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
372 sizeof(ni->ni_flags), "%s",
373 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_SITELOCAL, sizeof(flags) * 8,
374 "Site-local address",
375 "Not site-local address"));
376 proto_tree_add_text(field_tree, NullTVB,
377 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
378 sizeof(ni->ni_flags), "%s",
379 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_LINKLOCAL, sizeof(flags) * 8,
380 "Link-local address",
381 "Not link-local address"));
382 proto_tree_add_text(field_tree, NullTVB,
383 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
384 sizeof(ni->ni_flags), "%s",
385 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_COMPAT, sizeof(flags) * 8,
386 "IPv4 compatible/mapped address",
387 "Not IPv4 compatible/mapped address"));
389 case NI_QTYPE_IPV4ADDR:
390 proto_tree_add_text(field_tree, NullTVB,
391 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
392 sizeof(ni->ni_flags), "%s",
393 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_ALL, sizeof(flags) * 8,
394 "All unicast address",
395 "Unicast addresses on the queried interface"));
396 proto_tree_add_text(field_tree, NullTVB,
397 offset + offsetof(struct icmp6_nodeinfo, ni_flags),
398 sizeof(ni->ni_flags), "%s",
399 decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_TRUNCATE, sizeof(flags) * 8,
400 "Truncated", "Not truncated"));
405 proto_tree_add_text(tree, NullTVB,
406 offset + offsetof(struct icmp6_nodeinfo, icmp6_ni_nonce[0]),
407 sizeof(ni->icmp6_ni_nonce), "Nonce: 0x%08x%08x",
408 pntohl(&ni->icmp6_ni_nonce[0]), pntohl(&ni->icmp6_ni_nonce[4]));
410 /* offset for "the rest of data" */
414 if (!IS_DATA_IN_FRAME(offset + sizeof(*ni)))
416 if (ni->ni_type == ICMP6_NI_QUERY) {
417 switch (ni->ni_code) {
418 case ICMP6_NI_SUBJ_IPV6:
419 n = pi.captured_len - (offset + sizeof(*ni));
420 n /= sizeof(struct e_in6_addr);
421 tf = proto_tree_add_text(tree, NullTVB,
422 offset + sizeof(*ni), END_OF_FRAME, "IPv6 subject addresses");
423 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject6);
424 p = (u_char *)(ni + 1);
425 for (i = 0; i < n; i++) {
426 proto_tree_add_text(field_tree, NullTVB,
427 p - pd, sizeof(struct e_in6_addr),
428 "%s", ip6_to_str((struct e_in6_addr *)p));
429 p += sizeof(struct e_in6_addr);
431 off = pi.captured_len - offset;
433 case ICMP6_NI_SUBJ_FQDN:
434 l = get_dns_name(pd, offset + sizeof(*ni), offset + sizeof(*ni),
435 dname, sizeof(dname));
436 if (IS_DATA_IN_FRAME(offset + sizeof(*ni) + l) &&
437 pd[offset + sizeof(*ni) + l] == 0) {
439 proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
440 "DNS label: %s (truncated)", dname);
442 proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
443 "DNS label: %s", dname);
445 off = offset + sizeof(*ni) + l;
447 case ICMP6_NI_SUBJ_IPV4:
448 n = pi.captured_len - (offset + sizeof(*ni));
449 n /= sizeof(guint32);
450 tf = proto_tree_add_text(tree, NullTVB,
451 offset + sizeof(*ni), END_OF_FRAME, "IPv4 subject addresses");
452 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject4);
453 p = (u_char *)(ni + 1);
454 for (i = 0; i < n; i++) {
455 proto_tree_add_text(field_tree, NullTVB,
456 p - pd, sizeof(guint32), "%s", ip_to_str(p));
457 p += sizeof(guint32);
459 off = pi.captured_len - offset;
463 switch (pntohs(&ni->ni_qtype)) {
466 case NI_QTYPE_SUPTYPES:
467 p = (u_char *)(ni + 1);
468 tf = proto_tree_add_text(tree, NullTVB,
469 offset + sizeof(*ni), END_OF_FRAME,
470 "Supported type bitmap%s",
471 (flags & 0x0001) ? ", compressed" : "");
472 field_tree = proto_item_add_subtree(tf,
473 ett_nodeinfo_nodebitmap);
475 while (IS_DATA_IN_FRAME(p - pd)) {
476 if ((flags & 0x0001) == 0) {
477 l = pi.captured_len - (offset + sizeof(*ni));
478 l /= sizeof(guint32);
481 if (!IS_DATA_IN_FRAME(p + sizeof(guint32) - 1 - pd))
484 i = pntohs(p + sizeof(guint16)); /*skip*/
486 if (n + l * 32 > (1 << 16))
488 if (n + (l + i) * 32 > (1 << 16))
490 if ((flags & 0x0001) == 0) {
491 proto_tree_add_text(field_tree, NullTVB, p - pd,
492 l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
496 proto_tree_add_text(field_tree, NullTVB, p - pd,
497 4 + l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
498 bitrange(p + 4, l, n));
501 n += l * 32 + i * 32;
503 off = pi.captured_len - offset;
505 case NI_QTYPE_DNSNAME:
506 proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni),
507 sizeof(gint32), "TTL: %d", (gint32)pntohl(ni + 1));
508 tf = proto_tree_add_text(tree, NullTVB,
509 offset + sizeof(*ni) + sizeof(guint32), END_OF_FRAME,
511 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_nodedns);
513 i = offset + sizeof(*ni) + sizeof(guint32);
514 while (i < pi.captured_len) {
515 l = get_dns_name(pd, i, offset + sizeof(*ni), dname,
517 if (IS_DATA_IN_FRAME(i + l) && pd[i + l] == 0) {
519 proto_tree_add_text(field_tree, NullTVB, i, l,
520 "DNS label: %s (truncated)", dname);
522 proto_tree_add_text(field_tree, NullTVB, i, l,
523 "DNS label: %s", dname);
527 off = pi.captured_len - offset;
529 case NI_QTYPE_NODEADDR:
530 n = pi.captured_len - (offset + sizeof(*ni));
531 n /= sizeof(struct e_in6_addr) + sizeof(gint32);;
532 tf = proto_tree_add_text(tree, NullTVB,
533 offset + sizeof(*ni), END_OF_FRAME, "IPv6 node addresses");
534 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node6);
535 p = (u_char *)(ni + 1);
536 for (i = 0; i < n; i++) {
537 proto_tree_add_text(field_tree, NullTVB,
538 p - pd, sizeof(struct e_in6_addr) + sizeof(gint32),
539 "%s (TTL %d)", ip6_to_str((struct e_in6_addr *)p),
540 (gint32)pntohl(p + sizeof(struct e_in6_addr)));
541 p += sizeof(struct e_in6_addr) + sizeof(gint32);
543 off = pi.captured_len - offset;
545 case NI_QTYPE_IPV4ADDR:
546 n = pi.captured_len - (offset + sizeof(*ni));
547 n /= sizeof(guint32);
548 tf = proto_tree_add_text(tree, NullTVB,
549 offset + sizeof(*ni), END_OF_FRAME, "IPv4 node addresses");
550 field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node4);
551 p = (u_char *)(ni + 1);
552 for (i = 0; i < n; i++) {
553 proto_tree_add_text(field_tree, NullTVB,
554 p - pd, sizeof(guint32), "%s", ip_to_str(p));
555 p += sizeof(guint32);
557 off = pi.captured_len - offset;
563 /* the rest of data */
564 old_dissect_data(pd, offset + off, fd, tree);
568 dissect_rrenum(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
570 proto_tree *field_tree, *opt_tree;
572 struct icmp6_router_renum *rr = (struct icmp6_router_renum *)&pd[offset];
573 struct rr_pco_match *match;
574 struct rr_pco_use *use;
577 proto_tree_add_text(tree, NullTVB,
578 offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
579 "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
580 proto_tree_add_text(tree, NullTVB,
581 offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
582 "Segment number: 0x%02x", rr->rr_segnum);
584 flagoff = offset + offsetof(struct icmp6_router_renum, rr_flags);
585 tf = proto_tree_add_text(tree, NullTVB, flagoff, 1,
586 "Flags: 0x%02x", pd[flagoff]);
587 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
588 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
589 decode_boolean_bitfield(pd[flagoff], 0x80, 8,
590 "Test command", "Not test command"));
591 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
592 decode_boolean_bitfield(pd[flagoff], 0x40, 8,
593 "Result requested", "Result not requested"));
594 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
595 decode_boolean_bitfield(pd[flagoff], 0x20, 8,
596 "All interfaces", "Not all interfaces"));
597 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
598 decode_boolean_bitfield(pd[flagoff], 0x10, 8,
599 "Site specific", "Not site specific"));
600 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
601 decode_boolean_bitfield(pd[flagoff], 0x08, 8,
602 "Processed previously", "Complete result"));
604 proto_tree_add_text(tree, NullTVB,
605 offset + offsetof(struct icmp6_router_renum, rr_maxdelay), 2,
606 "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
607 old_dissect_data(pd, offset + sizeof(*rr), fd, tree); /*XXX*/
609 if (rr->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
610 off = offset + sizeof(*rr);
611 match = (struct rr_pco_match *)&pd[off];
612 tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*match),
613 "Match-Prefix: %s/%u (%u-%u)", ip6_to_str(&match->rpm_prefix),
614 match->rpm_matchlen, match->rpm_minlen, match->rpm_maxlen);
615 opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
616 proto_tree_add_text(opt_tree, NullTVB,
617 off + offsetof(struct rr_pco_match, rpm_code),
618 sizeof(match->rpm_code), "OpCode: %s (%u)",
619 val_to_str(match->rpm_code, names_rrenum_matchcode, "Unknown"),
621 proto_tree_add_text(opt_tree, NullTVB,
622 off + offsetof(struct rr_pco_match, rpm_len),
623 sizeof(match->rpm_len), "OpLength: %u (%u octets)",
624 match->rpm_len, match->rpm_len * 8);
625 proto_tree_add_text(opt_tree, NullTVB,
626 off + offsetof(struct rr_pco_match, rpm_ordinal),
627 sizeof(match->rpm_ordinal), "Ordinal: %u", match->rpm_ordinal);
628 proto_tree_add_text(opt_tree, NullTVB,
629 off + offsetof(struct rr_pco_match, rpm_matchlen),
630 sizeof(match->rpm_matchlen), "MatchLen: %u", match->rpm_matchlen);
631 proto_tree_add_text(opt_tree, NullTVB,
632 off + offsetof(struct rr_pco_match, rpm_minlen),
633 sizeof(match->rpm_minlen), "MinLen: %u", match->rpm_minlen);
634 proto_tree_add_text(opt_tree, NullTVB,
635 off + offsetof(struct rr_pco_match, rpm_maxlen),
636 sizeof(match->rpm_maxlen), "MaxLen: %u", match->rpm_maxlen);
637 proto_tree_add_text(opt_tree, NullTVB,
638 off + offsetof(struct rr_pco_match, rpm_prefix),
639 sizeof(match->rpm_prefix), "MatchPrefix: %s",
640 ip6_to_str(&match->rpm_prefix));
642 off += sizeof(*match);
643 use = (struct rr_pco_use *)&pd[off];
644 for (l = match->rpm_len * 8 - sizeof(*match);
645 l >= sizeof(*use); l -= sizeof(*use), off += sizeof(*use)) {
646 tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*use),
647 "Use-Prefix: %s/%u (keep %u)", ip6_to_str(&use->rpu_prefix),
648 use->rpu_uselen, use->rpu_keeplen);
649 opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
650 proto_tree_add_text(opt_tree, NullTVB,
651 off + offsetof(struct rr_pco_use, rpu_uselen),
652 sizeof(use->rpu_uselen), "UseLen: %u", use->rpu_uselen);
653 proto_tree_add_text(opt_tree, NullTVB,
654 off + offsetof(struct rr_pco_use, rpu_keeplen),
655 sizeof(use->rpu_keeplen), "KeepLen: %u", use->rpu_keeplen);
656 tf = proto_tree_add_text(opt_tree, NullTVB,
657 flagoff = off + offsetof(struct rr_pco_use, rpu_ramask),
658 sizeof(use->rpu_ramask), "FlagMask: 0x%x", use->rpu_ramask);
659 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
660 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
661 decode_boolean_bitfield(pd[flagoff],
662 ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
663 "Onlink", "Not onlink"));
664 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
665 decode_boolean_bitfield(pd[flagoff],
666 ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8,
667 "Auto", "Not auto"));
668 tf = proto_tree_add_text(opt_tree, NullTVB,
669 flagoff = off + offsetof(struct rr_pco_use, rpu_raflags),
670 sizeof(use->rpu_raflags), "RAFlags: 0x%x", use->rpu_raflags);
671 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
672 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
673 decode_boolean_bitfield(pd[flagoff],
674 ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
675 "Onlink", "Not onlink"));
676 proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
677 decode_boolean_bitfield(pd[flagoff],
678 ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8, "Auto", "Not auto"));
679 if (pntohl(&use->rpu_vltime) == 0xffffffff)
680 proto_tree_add_text(opt_tree, NullTVB,
681 off + offsetof(struct rr_pco_use, rpu_vltime),
682 sizeof(use->rpu_vltime), "Valid Lifetime: infinity");
684 proto_tree_add_text(opt_tree, NullTVB,
685 off + offsetof(struct rr_pco_use, rpu_vltime),
686 sizeof(use->rpu_vltime), "Valid Lifetime: %u",
687 pntohl(&use->rpu_vltime));
688 if (pntohl(&use->rpu_pltime) == 0xffffffff)
689 proto_tree_add_text(opt_tree, NullTVB,
690 off + offsetof(struct rr_pco_use, rpu_pltime),
691 sizeof(use->rpu_pltime), "Preferred Lifetime: infinity");
693 proto_tree_add_text(opt_tree, NullTVB,
694 off + offsetof(struct rr_pco_use, rpu_pltime),
695 sizeof(use->rpu_pltime), "Preferred Lifetime: %u",
696 pntohl(&use->rpu_pltime));
697 tf = proto_tree_add_text(opt_tree, NullTVB,
698 flagoff = off + offsetof(struct rr_pco_use, rpu_flags),
699 sizeof(use->rpu_flags), "Flags: 0x%08x",
700 pntohl(&use->rpu_flags));
701 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
702 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
703 decode_boolean_bitfield(pd[flagoff],
704 ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, 32,
705 "Decrement valid lifetime", "No decrement valid lifetime"));
706 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
707 decode_boolean_bitfield(pd[flagoff],
708 ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, 32,
709 "Decrement preferred lifetime",
710 "No decrement preferred lifetime"));
711 proto_tree_add_text(opt_tree, NullTVB,
712 off + offsetof(struct rr_pco_use, rpu_prefix),
713 sizeof(use->rpu_prefix), "UsePrefix: %s",
714 ip6_to_str(&use->rpu_prefix));
721 dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
723 proto_tree *icmp6_tree, *field_tree;
724 proto_item *ti, *tf = NULL;
725 struct icmp6_hdr *dp;
726 struct icmp6_nodeinfo *ni = NULL;
727 char *codename, *typename;
728 char *colcodename, *coltypename;
730 guint length, reported_length;
733 guint16 cksum, computed_cksum;
735 OLD_CHECK_DISPLAY_AS_DATA(proto_icmpv6, pd, offset, fd, tree);
737 dp = (struct icmp6_hdr *)&pd[offset];
738 codename = typename = colcodename = coltypename = "Unknown";
740 switch (dp->icmp6_type) {
741 case ICMP6_DST_UNREACH:
742 typename = coltypename = "Unreachable";
743 switch (dp->icmp6_code) {
744 case ICMP6_DST_UNREACH_NOROUTE:
745 codename = colcodename = "Route unreachable";
747 case ICMP6_DST_UNREACH_ADMIN:
748 codename = colcodename = "Administratively prohibited";
750 case ICMP6_DST_UNREACH_NOTNEIGHBOR:
751 codename = colcodename = "Not a neighbor";
753 case ICMP6_DST_UNREACH_ADDR:
754 codename = colcodename = "Address unreachable";
756 case ICMP6_DST_UNREACH_NOPORT:
757 codename = colcodename = "Port unreachable";
761 case ICMP6_PACKET_TOO_BIG:
762 typename = coltypename = "Too big";
763 codename = colcodename = NULL;
765 case ICMP6_TIME_EXCEEDED:
766 typename = coltypename = "Time exceeded";
767 switch (dp->icmp6_code) {
768 case ICMP6_TIME_EXCEED_TRANSIT:
769 codename = colcodename = "In-transit";
771 case ICMP6_TIME_EXCEED_REASSEMBLY:
772 codename = colcodename = "Reassembly";
776 case ICMP6_PARAM_PROB:
777 typename = coltypename = "Parameter problem";
778 switch (dp->icmp6_code) {
779 case ICMP6_PARAMPROB_HEADER:
780 codename = colcodename = "Header";
782 case ICMP6_PARAMPROB_NEXTHEADER:
783 codename = colcodename = "Next header";
785 case ICMP6_PARAMPROB_OPTION:
786 codename = colcodename = "Option";
790 case ICMP6_ECHO_REQUEST:
791 typename = coltypename = "Echo request";
792 codename = colcodename = NULL;
794 case ICMP6_ECHO_REPLY:
795 typename = coltypename = "Echo reply";
796 codename = colcodename = NULL;
798 case ICMP6_MEMBERSHIP_QUERY:
799 typename = coltypename = "Multicast listener query";
800 codename = colcodename = NULL;
802 case ICMP6_MEMBERSHIP_REPORT:
803 typename = coltypename = "Multicast listener report";
804 codename = colcodename = NULL;
806 case ICMP6_MEMBERSHIP_REDUCTION:
807 typename = coltypename = "Multicast listener done";
808 codename = colcodename = NULL;
810 case ND_ROUTER_SOLICIT:
811 typename = coltypename = "Router solicitation";
812 codename = colcodename = NULL;
813 len = sizeof(struct nd_router_solicit);
815 case ND_ROUTER_ADVERT:
816 typename = coltypename = "Router advertisement";
817 codename = colcodename = NULL;
818 len = sizeof(struct nd_router_advert);
820 case ND_NEIGHBOR_SOLICIT:
821 typename = coltypename = "Neighbor solicitation";
822 codename = colcodename = NULL;
823 len = sizeof(struct nd_neighbor_solicit);
825 case ND_NEIGHBOR_ADVERT:
826 typename = coltypename = "Neighbor advertisement";
827 codename = colcodename = NULL;
828 len = sizeof(struct nd_neighbor_advert);
831 typename = coltypename = "Redirect";
832 codename = colcodename = NULL;
833 len = sizeof(struct nd_redirect);
835 case ICMP6_ROUTER_RENUMBERING:
836 typename = coltypename = "Router renumbering";
837 switch (dp->icmp6_code) {
838 case ICMP6_ROUTER_RENUMBERING_COMMAND:
839 codename = colcodename = "Command";
841 case ICMP6_ROUTER_RENUMBERING_RESULT:
842 codename = colcodename = "Result";
844 case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
845 codename = colcodename = "Sequence number reset";
848 len = sizeof(struct icmp6_router_renum);
852 ni = (struct icmp6_nodeinfo *)dp;
853 if (ni->ni_type == ICMP6_NI_QUERY) {
854 typename = coltypename = "Node information query";
855 switch (ni->ni_code) {
856 case ICMP6_NI_SUBJ_IPV6:
857 codename = "Query subject = IPv6 addresses";
859 case ICMP6_NI_SUBJ_FQDN:
860 if (IS_DATA_IN_FRAME(offset + sizeof(*ni)))
861 codename = "Query subject = DNS name";
863 codename = "Query subject = empty";
865 case ICMP6_NI_SUBJ_IPV4:
866 codename = "Query subject = IPv4 addresses";
870 typename = coltypename = "Node information reply";
871 switch (ni->ni_code) {
872 case ICMP6_NI_SUCCESS:
873 codename = "Successful";
875 case ICMP6_NI_REFUSED:
876 codename = "Refused";
878 case ICMP6_NI_UNKNOWN:
879 codename = "Unknown query type";
883 colcodename = val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
885 len = sizeof(struct icmp6_nodeinfo);
889 if (check_col(fd, COL_PROTOCOL))
890 col_set_str(fd, COL_PROTOCOL, "ICMPv6");
891 if (check_col(fd, COL_INFO)) {
892 char typebuf[256], codebuf[256];
894 if (coltypename && strcmp(coltypename, "Unknown") == 0) {
895 snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
897 coltypename = typebuf;
899 if (colcodename && strcmp(colcodename, "Unknown") == 0) {
900 snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
902 colcodename = codebuf;
905 col_add_fstr(fd, COL_INFO, "%s (%s)", coltypename, colcodename);
907 col_add_fstr(fd, COL_INFO, "%s", coltypename);
912 /* !!! specify length */
913 ti = proto_tree_add_item(tree, proto_icmpv6, NullTVB, offset, len, FALSE);
914 icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
916 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_type, NullTVB,
917 offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
919 "Type: 0x%02x (%s)", dp->icmp6_type, typename);
921 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
922 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
924 "Code: 0x%02x (%s)", dp->icmp6_code, codename);
926 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
927 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
929 "Code: 0x%02x", dp->icmp6_code);
931 cksum = (guint16)htons(dp->icmp6_cksum);
932 length = pi.captured_len - offset;
933 reported_length = pi.len - offset;
934 if (!pi.fragmented && length >= reported_length) {
935 /* The packet isn't part of a fragmented datagram and isn't
936 truncated, so we can checksum it. */
938 /* Set up the fields of the pseudo-header. */
939 cksum_vec[0].ptr = pi.src.data;
940 cksum_vec[0].len = pi.src.len;
941 cksum_vec[1].ptr = pi.dst.data;
942 cksum_vec[1].len = pi.dst.len;
943 cksum_vec[2].ptr = (const guint8 *)&phdr;
944 phdr[0] = htonl(reported_length);
945 phdr[1] = htonl(IP_PROTO_ICMPV6);
946 cksum_vec[2].len = 8;
947 cksum_vec[3].ptr = &pd[offset];
948 cksum_vec[3].len = reported_length;
949 computed_cksum = in_cksum(cksum_vec, 4);
950 if (computed_cksum == 0) {
951 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
953 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
955 "Checksum: 0x%04x (correct)", cksum);
957 proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_checksum,
959 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
961 "Checksum: 0x%04x (incorrect, should be 0x%04x)",
962 cksum, in_cksum_shouldbe(cksum, computed_cksum));
965 proto_tree_add_uint(icmp6_tree, hf_icmpv6_checksum, NullTVB,
966 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
971 switch (dp->icmp6_type) {
972 case ICMP6_DST_UNREACH:
973 case ICMP6_TIME_EXCEEDED:
974 /* tiny sanity check */
975 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
976 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
978 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
981 case ICMP6_PACKET_TOO_BIG:
982 proto_tree_add_text(icmp6_tree, NullTVB,
983 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
984 "MTU: %d", pntohl(&dp->icmp6_mtu));
985 /* tiny sanity check */
986 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
987 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
989 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
992 case ICMP6_PARAM_PROB:
993 proto_tree_add_text(icmp6_tree, NullTVB,
994 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
995 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
996 /* tiny sanity check */
997 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
998 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
1000 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1003 case ICMP6_ECHO_REQUEST:
1004 case ICMP6_ECHO_REPLY:
1005 proto_tree_add_text(icmp6_tree, NullTVB,
1006 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
1007 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
1008 proto_tree_add_text(icmp6_tree, NullTVB,
1009 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
1010 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
1011 old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
1013 case ICMP6_MEMBERSHIP_QUERY:
1014 case ICMP6_MEMBERSHIP_REPORT:
1015 case ICMP6_MEMBERSHIP_REDUCTION:
1016 proto_tree_add_text(icmp6_tree, NullTVB,
1017 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
1018 "Maximum response delay: %d",
1019 (guint16)ntohs(dp->icmp6_maxdelay));
1020 proto_tree_add_text(icmp6_tree, NullTVB, offset + sizeof(*dp), 16,
1021 "Multicast Address: %s",
1022 ip6_to_str((struct e_in6_addr *)(dp + 1)));
1024 case ND_ROUTER_SOLICIT:
1025 dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
1027 case ND_ROUTER_ADVERT:
1029 struct nd_router_advert *ra = (struct nd_router_advert *)dp;
1033 proto_tree_add_text(icmp6_tree, NullTVB,
1034 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
1035 1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
1037 flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
1038 ra_flags = pntohl(&pd[flagoff]);
1039 tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", ra_flags);
1040 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1041 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1042 decode_boolean_bitfield(ra_flags,
1043 0x80000000, 32, "Managed", "Not managed"));
1044 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1045 decode_boolean_bitfield(ra_flags,
1046 0x40000000, 32, "Other", "Not other"));
1048 proto_tree_add_text(icmp6_tree, NullTVB,
1049 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
1050 2, "Router lifetime: %d",
1051 (guint16)ntohs(ra->nd_ra_router_lifetime));
1052 proto_tree_add_text(icmp6_tree, NullTVB,
1053 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
1054 "Reachable time: %d", pntohl(&ra->nd_ra_reachable));
1055 proto_tree_add_text(icmp6_tree, NullTVB,
1056 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
1057 "Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
1058 dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
1061 case ND_NEIGHBOR_SOLICIT:
1063 struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
1065 proto_tree_add_text(icmp6_tree, NullTVB,
1066 offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
1069 get_hostname6(&ns->nd_ns_target),
1073 ip6_to_str(&ns->nd_ns_target));
1075 dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
1078 case ND_NEIGHBOR_ADVERT:
1080 int flagoff, targetoff;
1082 struct e_in6_addr *na_target_p;
1084 flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
1085 na_flags = pntohl(&pd[flagoff]);
1087 tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", na_flags);
1088 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
1089 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1090 decode_boolean_bitfield(na_flags,
1091 ND_NA_FLAG_ROUTER, 32, "Router", "Not router"));
1092 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1093 decode_boolean_bitfield(na_flags,
1094 ND_NA_FLAG_SOLICITED, 32, "Solicited", "Not adverted"));
1095 proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
1096 decode_boolean_bitfield(na_flags,
1097 ND_NA_FLAG_OVERRIDE, 32, "Override", "Not override"));
1099 targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
1100 na_target_p = (struct e_in6_addr*) &pd[targetoff];
1101 proto_tree_add_text(icmp6_tree, NullTVB, targetoff, 16,
1104 get_hostname6(na_target_p),
1108 ip6_to_str(na_target_p));
1110 dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
1115 struct nd_redirect *rd = (struct nd_redirect *)dp;
1117 proto_tree_add_text(icmp6_tree, NullTVB,
1118 offset + offsetof(struct nd_redirect, nd_rd_target), 16,
1121 get_hostname6(&rd->nd_rd_target),
1125 ip6_to_str(&rd->nd_rd_target));
1127 proto_tree_add_text(icmp6_tree, NullTVB,
1128 offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
1130 "Destination: %s (%s)",
1131 get_hostname6(&rd->nd_rd_dst),
1135 ip6_to_str(&rd->nd_rd_dst));
1137 dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
1140 case ICMP6_ROUTER_RENUMBERING:
1141 dissect_rrenum(pd, offset, fd, icmp6_tree);
1143 case ICMP6_NI_QUERY:
1144 case ICMP6_NI_REPLY:
1145 ni = (struct icmp6_nodeinfo *)dp;
1146 proto_tree_add_text(icmp6_tree, NullTVB,
1147 offset + offsetof(struct icmp6_nodeinfo, ni_qtype),
1148 sizeof(ni->ni_qtype),
1149 "Query type: 0x%04x (%s)", pntohs(&ni->ni_qtype),
1150 val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
1152 dissect_nodeinfo(pd, offset, fd, icmp6_tree);
1155 old_dissect_data(pd, offset + sizeof(*dp), fd, tree);
1162 proto_register_icmpv6(void)
1164 static hf_register_info hf[] = {
1166 { "Type", "icmpv6.type", FT_UINT8, BASE_HEX, NULL, 0x0,
1169 { "Code", "icmpv6.code", FT_UINT8, BASE_HEX, NULL, 0x0,
1171 { &hf_icmpv6_checksum,
1172 { "Checksum", "icmpv6.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
1175 static gint *ett[] = {
1180 &ett_nodeinfo_subject4,
1181 &ett_nodeinfo_subject6,
1182 &ett_nodeinfo_node4,
1183 &ett_nodeinfo_node6,
1184 &ett_nodeinfo_nodebitmap,
1185 &ett_nodeinfo_nodedns,
1188 proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
1189 "ICMPv6", "icmpv6");
1190 proto_register_field_array(proto_icmpv6, hf, array_length(hf));
1191 proto_register_subtree_array(ett, array_length(ett));
1195 proto_reg_handoff_icmpv6(void)
1197 old_dissector_add("ip.proto", IP_PROTO_ICMPV6, dissect_icmpv6);