2 * Routines for ICMPv6 packet disassembly
4 * $Id: packet-icmpv6.c,v 1.11 1999/11/16 11:42:31 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>
47 #ifdef NEED_SNPRINTF_H
53 # include "snprintf.h"
58 #include "packet-ipv6.h"
63 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
66 static int proto_icmpv6 = -1;
67 static int hf_icmpv6_type = -1;
68 static int hf_icmpv6_code = -1;
69 static int hf_icmpv6_checksum = -1;
71 static gint ett_icmpv6 = -1;
72 static gint ett_icmpv6opt = -1;
73 static gint ett_icmpv6flag = -1;
76 dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
78 proto_tree *icmp6opt_tree, *field_tree;
80 struct nd_opt_hdr *opt;
88 if (!(fd->cap_len > offset))
91 opt = (struct nd_opt_hdr *)&pd[offset];
92 len = opt->nd_opt_len << 3;
94 /* !!! specify length */
95 ti = proto_tree_add_text(tree, offset, len, "ICMPv6 options");
96 icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
98 switch (opt->nd_opt_type) {
99 case ND_OPT_SOURCE_LINKADDR:
100 typename = "Source link-layer address";
102 case ND_OPT_TARGET_LINKADDR:
103 typename = "Target link-layer address";
105 case ND_OPT_PREFIX_INFORMATION:
106 typename = "Prefix information";
108 case ND_OPT_REDIRECTED_HEADER:
109 typename = "Redirected header";
115 typename = "Unknown";
119 proto_tree_add_text(icmp6opt_tree,
120 offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
121 "Type: 0x%02x (%s)", opt->nd_opt_type, typename);
122 proto_tree_add_text(icmp6opt_tree,
123 offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
124 "Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
127 switch (opt->nd_opt_type) {
128 case ND_OPT_SOURCE_LINKADDR:
129 case ND_OPT_TARGET_LINKADDR:
134 len = (opt->nd_opt_len << 3) - sizeof(*opt);
135 t = (char *)malloc(len * 3);
136 memset(t, 0, len * 3);
137 p = &pd[offset + sizeof(*opt)];
138 for (i = 0; i < len; i++) {
141 sprintf(&t[i * 3], "%02x", p[i] & 0xff);
143 proto_tree_add_text(icmp6opt_tree,
144 offset + sizeof(*opt), len, "Link-layer address: %s", t);
147 case ND_OPT_PREFIX_INFORMATION:
149 struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
151 proto_tree_add_text(icmp6opt_tree,
152 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
153 1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
155 flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
156 tf = proto_tree_add_text(icmp6opt_tree, flagoff, 1, "Flags: 0x%02x",
157 pntohl(&pi->nd_opt_pi_flags_reserved));
158 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
159 proto_tree_add_text(field_tree, flagoff, 1, "%s",
160 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
161 0x80, 8, "Onlink", "Not onlink"));
162 proto_tree_add_text(field_tree, flagoff, 1, "%s",
163 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
164 0x40, 8, "Auto", "Not auto"));
166 proto_tree_add_text(icmp6opt_tree,
167 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
168 4, "Valid lifetime: 0x%08x",
169 pntohl(&pi->nd_opt_pi_valid_time));
170 proto_tree_add_text(icmp6opt_tree,
171 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
172 4, "Preferred lifetime: 0x%08x",
173 pntohl(&pi->nd_opt_pi_preferred_time));
174 proto_tree_add_text(icmp6opt_tree,
175 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
176 16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
179 case ND_OPT_REDIRECTED_HEADER:
180 proto_tree_add_text(icmp6opt_tree,
181 offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
182 /* tiny sanity check */
183 if ((pd[offset + 8] & 0xf0) == 0x60)
184 dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
186 dissect_data(pd, offset + 8, fd, icmp6opt_tree);
190 struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
191 proto_tree_add_text(icmp6opt_tree,
192 offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
193 "MTU: %d", pntohl(&pi->nd_opt_mtu_mtu));
198 offset += (opt->nd_opt_len << 3);
203 dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
205 proto_tree *icmp6_tree, *field_tree;
206 proto_item *ti, *tf = NULL;
207 struct icmp6_hdr *dp;
208 char *codename, *typename;
211 dp = (struct icmp6_hdr *)&pd[offset];
212 codename = typename = "Unknown";
214 switch (dp->icmp6_type) {
215 case ICMP6_DST_UNREACH:
216 typename = "Unreachable";
217 switch (dp->icmp6_code) {
218 case ICMP6_DST_UNREACH_NOROUTE:
219 codename = "Route unreachable";
221 case ICMP6_DST_UNREACH_ADMIN:
222 codename = "Administratively prohibited";
224 case ICMP6_DST_UNREACH_NOTNEIGHBOR:
225 codename = "Not a neighbor";
227 case ICMP6_DST_UNREACH_ADDR:
228 codename = "Address unreachable";
230 case ICMP6_DST_UNREACH_NOPORT:
231 codename = "Port unreachable";
235 case ICMP6_PACKET_TOO_BIG:
236 typename = "Too big";
239 case ICMP6_TIME_EXCEEDED:
240 typename = "Time exceeded";
241 switch (dp->icmp6_code) {
242 case ICMP6_TIME_EXCEED_TRANSIT:
243 codename = "In-transit";
245 case ICMP6_TIME_EXCEED_REASSEMBLY:
246 codename = "Reassembly";
250 case ICMP6_PARAM_PROB:
251 typename = "Parameter problem";
252 switch (dp->icmp6_code) {
253 case ICMP6_PARAMPROB_HEADER:
256 case ICMP6_PARAMPROB_NEXTHEADER:
257 codename = "Next header";
259 case ICMP6_PARAMPROB_OPTION:
264 case ICMP6_ECHO_REQUEST:
265 typename = "Echo request";
268 case ICMP6_ECHO_REPLY:
269 typename = "Echo reply";
272 case ICMP6_MEMBERSHIP_QUERY:
273 typename = "Multicast listener query";
276 case ICMP6_MEMBERSHIP_REPORT:
277 typename = "Multicast listener report";
280 case ICMP6_MEMBERSHIP_REDUCTION:
281 typename = "Multicast listener done";
284 case ND_ROUTER_SOLICIT:
285 typename = "Router solicitation";
287 len = sizeof(struct nd_router_solicit);
289 case ND_ROUTER_ADVERT:
290 typename = "Router advertisement";
292 len = sizeof(struct nd_router_advert);
294 case ND_NEIGHBOR_SOLICIT:
295 typename = "Neighbor solicitation";
297 len = sizeof(struct nd_neighbor_solicit);
299 case ND_NEIGHBOR_ADVERT:
300 typename = "Neighbor advertisement";
302 len = sizeof(struct nd_neighbor_advert);
305 typename = "Redirect";
307 len = sizeof(struct nd_redirect);
309 case ICMP6_ROUTER_RENUMBERING:
310 typename = "Router renumbering";
311 switch (dp->icmp6_code) {
312 case ICMP6_ROUTER_RENUMBERING_COMMAND:
313 codename = "Command";
315 case ICMP6_ROUTER_RENUMBERING_RESULT:
319 len = sizeof(struct icmp6_router_renum);
323 if (check_col(fd, COL_PROTOCOL))
324 col_add_str(fd, COL_PROTOCOL, "ICMPv6");
325 if (check_col(fd, COL_INFO)) {
326 char typebuf[256], codebuf[256];
328 if (typename && strcmp(typename, "Unknown") == 0) {
329 snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
333 if (codename && strcmp(codename, "Unknown") == 0) {
334 snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
339 col_add_fstr(fd, COL_INFO, "%s (%s)",
342 col_add_fstr(fd, COL_INFO, "%s", typename);
347 /* !!! specify length */
348 ti = proto_tree_add_item(tree, proto_icmpv6, offset, len, NULL);
349 icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
351 proto_tree_add_item_format(icmp6_tree, hf_icmpv6_type,
352 offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
354 "Type: 0x%02x (%s)", dp->icmp6_type, typename);
356 proto_tree_add_item_format(icmp6_tree, hf_icmpv6_code,
357 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
359 "Code: 0x%02x (%s)", dp->icmp6_code, codename);
361 proto_tree_add_item(icmp6_tree, hf_icmpv6_checksum,
362 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
363 (guint16)htons(dp->icmp6_cksum));
366 switch (dp->icmp6_type) {
367 case ICMP6_DST_UNREACH:
368 case ICMP6_TIME_EXCEEDED:
369 /* tiny sanity check */
370 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
371 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
373 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
376 case ICMP6_PACKET_TOO_BIG:
377 proto_tree_add_text(icmp6_tree,
378 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
379 "MTU: %d", pntohl(&dp->icmp6_mtu));
380 /* tiny sanity check */
381 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
382 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
384 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
387 case ICMP6_PARAM_PROB:
388 proto_tree_add_text(icmp6_tree,
389 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
390 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
391 /* tiny sanity check */
392 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
393 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
395 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
398 case ICMP6_ECHO_REQUEST:
399 case ICMP6_ECHO_REPLY:
400 proto_tree_add_text(icmp6_tree,
401 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
402 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
403 proto_tree_add_text(icmp6_tree,
404 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
405 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
406 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
408 case ICMP6_MEMBERSHIP_QUERY:
409 case ICMP6_MEMBERSHIP_REPORT:
410 case ICMP6_MEMBERSHIP_REDUCTION:
411 proto_tree_add_text(icmp6_tree,
412 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
413 "Maximum response delay: %d",
414 (guint16)ntohs(dp->icmp6_maxdelay));
415 proto_tree_add_text(icmp6_tree, offset + sizeof(*dp), 16,
416 "Multicast Address: %s",
417 ip6_to_str((struct e_in6_addr *)(dp + 1)));
419 case ND_ROUTER_SOLICIT:
420 dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
422 case ND_ROUTER_ADVERT:
424 struct nd_router_advert *ra = (struct nd_router_advert *)dp;
428 proto_tree_add_text(icmp6_tree,
429 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
430 1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
432 flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
433 ra_flags = pntohl(&pd[flagoff]);
434 tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x", ra_flags);
435 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
436 proto_tree_add_text(field_tree, flagoff, 4, "%s",
437 decode_boolean_bitfield(ra_flags,
438 0x80000000, 32, "Managed", "Not managed"));
439 proto_tree_add_text(field_tree, flagoff, 4, "%s",
440 decode_boolean_bitfield(ra_flags,
441 0x40000000, 32, "Other", "Not other"));
443 proto_tree_add_text(icmp6_tree,
444 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
445 2, "Router lifetime: %d",
446 (guint16)ntohs(ra->nd_ra_router_lifetime));
447 proto_tree_add_text(icmp6_tree,
448 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
449 "Reachable time: %d", pntohl(&ra->nd_ra_reachable));
450 proto_tree_add_text(icmp6_tree,
451 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
452 "Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
453 dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
456 case ND_NEIGHBOR_SOLICIT:
458 struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
460 proto_tree_add_text(icmp6_tree,
461 offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
464 get_hostname6(&ns->nd_ns_target),
468 ip6_to_str(&ns->nd_ns_target));
470 dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
473 case ND_NEIGHBOR_ADVERT:
475 int flagoff, targetoff;
477 struct e_in6_addr *na_target_p;
479 flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
480 na_flags = pntohl(&pd[flagoff]);
482 tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x", na_flags);
483 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
484 proto_tree_add_text(field_tree, flagoff, 4, "%s",
485 decode_boolean_bitfield(na_flags,
486 0x80000000, 32, "Router", "Not router"));
487 proto_tree_add_text(field_tree, flagoff, 4, "%s",
488 decode_boolean_bitfield(na_flags,
489 0x40000000, 32, "Solicited", "Not adverted"));
490 proto_tree_add_text(field_tree, flagoff, 4, "%s",
491 decode_boolean_bitfield(na_flags,
492 0x20000000, 32, "Override", "Not override"));
494 targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
495 na_target_p = (struct e_in6_addr*) &pd[targetoff];
496 proto_tree_add_text(icmp6_tree, targetoff, 16,
499 get_hostname6(na_target_p),
503 ip6_to_str(na_target_p));
505 dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
510 struct nd_redirect *rd = (struct nd_redirect *)dp;
512 proto_tree_add_text(icmp6_tree,
513 offset + offsetof(struct nd_redirect, nd_rd_target), 16,
516 get_hostname6(&rd->nd_rd_target),
520 ip6_to_str(&rd->nd_rd_target));
522 proto_tree_add_text(icmp6_tree,
523 offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
525 "Destination: %s (%s)",
526 get_hostname6(&rd->nd_rd_dst),
530 ip6_to_str(&rd->nd_rd_dst));
532 dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
535 case ICMP6_ROUTER_RENUMBERING:
537 struct icmp6_router_renum *rr = (struct icmp6_router_renum *)dp;
539 proto_tree_add_text(icmp6_tree,
540 offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
541 /*"Sequence number: 0x%08x", (u_int32_t)htonl(rr->rr_seqnum));*/
542 "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
543 proto_tree_add_text(icmp6_tree,
544 offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
545 "Segment number: 0x%02x", rr->rr_segnum);
547 flagoff = offset + offsetof(struct icmp6_router_renum, rr_segnum) + 1;
548 tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x",
550 field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
551 proto_tree_add_text(field_tree, flagoff, 1, "%s",
552 decode_boolean_bitfield(pd[flagoff], 0x80, 8,
553 "Test command", "Not test command"));
554 proto_tree_add_text(field_tree, flagoff, 1, "%s",
555 decode_boolean_bitfield(pd[flagoff], 0x40, 8,
556 "Result requested", "Result not requested"));
557 proto_tree_add_text(field_tree, flagoff, 1, "%s",
558 decode_boolean_bitfield(pd[flagoff], 0x20, 8,
559 "All interfaces", "Not all interfaces"));
560 proto_tree_add_text(field_tree, flagoff, 1, "%s",
561 decode_boolean_bitfield(pd[flagoff], 0x10, 8,
562 "Site specific", "Not site specific"));
563 proto_tree_add_text(field_tree, flagoff, 1, "%s",
564 decode_boolean_bitfield(pd[flagoff], 0x08, 8,
565 "Processed previously", "Complete result"));
567 proto_tree_add_text(icmp6_tree,
568 offset + offsetof(struct icmp6_router_renum, rr_segnum), 2,
569 "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
570 dissect_data(pd, offset + sizeof(*rr), fd, tree); /*XXX*/
573 dissect_data(pd, offset + sizeof(*dp), fd, tree);
580 proto_register_icmpv6(void)
582 static hf_register_info hf[] = {
584 { "Type", "icmpv6.type", FT_UINT8, BASE_HEX, NULL, 0x0,
587 { "Code", "icmpv6.code", FT_UINT8, BASE_HEX, NULL, 0x0,
589 { &hf_icmpv6_checksum,
590 { "Checksum", "icmpv6.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
593 static gint *ett[] = {
599 proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
601 proto_register_field_array(proto_icmpv6, hf, array_length(hf));
602 proto_register_subtree_array(ett, array_length(ett));