2 * Routines for ICMPv6 packet disassembly
4 * $Id: packet-icmpv6.c,v 1.2 1999/03/29 02:24:29 gram 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.
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
39 #ifdef HAVE_NETINET_IN_H
40 # include <netinet/in.h>
43 #ifdef NEED_SNPRINTF_H
49 # include "snprintf.h"
54 #include "packet-ipv6.h"
59 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
63 dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
65 proto_tree *icmp6opt_tree, *field_tree;
67 struct nd_opt_hdr *opt;
75 if (!(fd->cap_len > offset))
78 opt = (struct nd_opt_hdr *)&pd[offset];
79 len = opt->nd_opt_len << 3;
81 /* !!! specify length */
82 ti = proto_tree_add_item(tree, offset, len, "ICMPv6 options");
83 icmp6opt_tree = proto_tree_new();
84 proto_item_add_subtree(ti, icmp6opt_tree, ETT_ICMPv6OPT);
86 switch (opt->nd_opt_type) {
87 case ND_OPT_SOURCE_LINKADDR:
88 typename = "Source link-layer address";
90 case ND_OPT_TARGET_LINKADDR:
91 typename = "Target link-layer address";
93 case ND_OPT_PREFIX_INFORMATION:
94 typename = "Prefix information";
96 case ND_OPT_REDIRECTED_HEADER:
97 typename = "Redirected header";
103 typename = "Unknown";
107 proto_tree_add_item(icmp6opt_tree,
108 offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
109 "Type: 0x%02x (%s)", opt->nd_opt_type, typename);
110 proto_tree_add_item(icmp6opt_tree,
111 offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
112 "Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
115 switch (opt->nd_opt_type) {
116 case ND_OPT_SOURCE_LINKADDR:
117 case ND_OPT_TARGET_LINKADDR:
122 len = (opt->nd_opt_len << 3) - sizeof(*opt);
123 t = (char *)malloc(len * 3);
124 memset(t, 0, len * 3);
125 p = &pd[offset + sizeof(*opt)];
126 for (i = 0; i < len; i++) {
129 sprintf(&t[i * 3], "%02x", p[i] & 0xff);
131 proto_tree_add_item(icmp6opt_tree,
132 offset + sizeof(*opt), len, "Link-layer address: %s", t);
135 case ND_OPT_PREFIX_INFORMATION:
137 struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
139 proto_tree_add_item(icmp6opt_tree,
140 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
141 1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
143 flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
144 tf = proto_tree_add_item(icmp6opt_tree, flagoff, 1, "Flags: 0x%02x",
145 pntohl(&pi->nd_opt_pi_flags_reserved));
146 field_tree = proto_tree_new();
147 proto_item_add_subtree(tf, field_tree, ETT_ICMPv6FLAG);
148 proto_tree_add_item(field_tree, flagoff, 1, "%s",
149 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
150 0x80, 8, "Onlink", "Not onlink"));
151 proto_tree_add_item(field_tree, flagoff, 1, "%s",
152 decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
153 0x40, 8, "Auto", "Not auto"));
155 proto_tree_add_item(icmp6opt_tree,
156 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
157 4, "Valid lifetime: 0x%08x",
158 pntohl(&pi->nd_opt_pi_valid_time));
159 proto_tree_add_item(icmp6opt_tree,
160 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
161 4, "Preferred lifetime: 0x%08x",
162 pntohl(&pi->nd_opt_pi_preferred_time));
163 proto_tree_add_item(icmp6opt_tree,
164 offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
165 16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
168 case ND_OPT_REDIRECTED_HEADER:
169 proto_tree_add_item(icmp6opt_tree,
170 offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
171 /* tiny sanity check */
172 if ((pd[offset + 8] & 0xf0) == 0x60)
173 dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
175 dissect_data(pd, offset + 8, fd, icmp6opt_tree);
179 struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
180 proto_tree_add_item(icmp6opt_tree,
181 offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
182 "MTU: %d", pi->nd_opt_mtu_mtu);
187 offset += (opt->nd_opt_len << 3);
192 dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
194 proto_tree *icmp6_tree, *field_tree;
195 proto_item *ti, *tf = NULL;
196 struct icmp6_hdr *dp;
197 char *codename, *typename;
200 dp = (struct icmp6_hdr *)&pd[offset];
201 codename = typename = "Unknown";
203 switch (dp->icmp6_type) {
204 case ICMP6_DST_UNREACH:
205 typename = "Unreachable";
206 switch (dp->icmp6_code) {
207 case ICMP6_DST_UNREACH_NOROUTE:
208 codename = "Route unreachable";
210 case ICMP6_DST_UNREACH_ADMIN:
211 codename = "Administratively prohibited";
213 case ICMP6_DST_UNREACH_NOTNEIGHBOR:
214 codename = "Not a neighbor";
216 case ICMP6_DST_UNREACH_ADDR:
217 codename = "Address unreachable";
219 case ICMP6_DST_UNREACH_NOPORT:
220 codename = "Port unreachable";
224 case ICMP6_PACKET_TOO_BIG:
225 typename = "Too big";
228 case ICMP6_TIME_EXCEEDED:
229 typename = "Time exceeded";
230 switch (dp->icmp6_code) {
231 case ICMP6_TIME_EXCEED_TRANSIT:
232 codename = "In-transit";
234 case ICMP6_TIME_EXCEED_REASSEMBLY:
235 codename = "Reassembly";
238 case ICMP6_PARAM_PROB:
239 typename = "Parameter problem";
240 switch (dp->icmp6_code) {
241 case ICMP6_PARAMPROB_HEADER:
244 case ICMP6_PARAMPROB_NEXTHEADER:
245 codename = "Next header";
247 case ICMP6_PARAMPROB_OPTION:
251 case ICMP6_ECHO_REQUEST:
252 typename = "Echo request";
255 case ICMP6_ECHO_REPLY:
256 typename = "Echo reply";
259 case ICMP6_MEMBERSHIP_QUERY:
260 typename = "Multicast listener query";
263 case ICMP6_MEMBERSHIP_REPORT:
264 typename = "Multicast listener report";
267 case ICMP6_MEMBERSHIP_REDUCTION:
268 typename = "Multicast listener done";
271 case ND_ROUTER_SOLICIT:
272 typename = "Router solicitation";
274 len = sizeof(struct nd_router_solicit);
276 case ND_ROUTER_ADVERT:
277 typename = "Router advertisement";
279 len = sizeof(struct nd_router_advert);
281 case ND_NEIGHBOR_SOLICIT:
282 typename = "Neighbor solicitation";
284 len = sizeof(struct nd_neighbor_solicit);
286 case ND_NEIGHBOR_ADVERT:
287 typename = "Neighbor advertisement";
289 len = sizeof(struct nd_neighbor_advert);
292 typename = "Redirect";
294 len = sizeof(struct nd_redirect);
296 case ICMP6_ROUTER_RENUMBERING:
297 typename = "Router renumbering";
298 switch (dp->icmp6_code) {
299 case ICMP6_ROUTER_RENUMBERING_COMMAND:
300 codename = "Command";
302 case ICMP6_ROUTER_RENUMBERING_RESULT:
306 len = sizeof(struct icmp6_router_renum);
310 if (check_col(fd, COL_PROTOCOL))
311 col_add_str(fd, COL_PROTOCOL, "ICMPv6");
312 if (check_col(fd, COL_INFO)) {
313 char typebuf[256], codebuf[256];
315 if (typename && strcmp(typename, "Unknown") == 0) {
316 snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
320 if (codename && strcmp(codename, "Unknown") == 0) {
321 snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
326 col_add_fstr(fd, COL_INFO, "%s (%s)",
329 col_add_fstr(fd, COL_INFO, "%s", typename);
334 /* !!! specify length */
335 ti = proto_tree_add_item(tree, offset, len,
337 icmp6_tree = proto_tree_new();
338 proto_item_add_subtree(ti, icmp6_tree, ETT_ICMPv6);
340 proto_tree_add_item(icmp6_tree,
341 offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
342 "Type: 0x%02x (%s)", dp->icmp6_type, typename);
344 proto_tree_add_item(icmp6_tree,
345 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
346 "Code: 0x%02x (%s)", dp->icmp6_code, codename);
348 proto_tree_add_item(icmp6_tree,
349 offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
350 "Checksum: 0x%04x", (guint16)htons(dp->icmp6_cksum));
353 switch (dp->icmp6_type) {
354 case ICMP6_DST_UNREACH:
355 case ICMP6_TIME_EXCEEDED:
356 /* tiny sanity check */
357 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
358 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
360 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
363 case ICMP6_PACKET_TOO_BIG:
364 proto_tree_add_item(icmp6_tree,
365 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
366 "MTU: %d", pntohl(&dp->icmp6_mtu));
367 /* tiny sanity check */
368 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
369 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
371 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
374 case ICMP6_PARAM_PROB:
375 proto_tree_add_item(icmp6_tree,
376 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
377 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
378 /* tiny sanity check */
379 if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
380 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
382 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
385 case ICMP6_ECHO_REQUEST:
386 case ICMP6_ECHO_REPLY:
387 proto_tree_add_item(icmp6_tree,
388 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
389 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
390 proto_tree_add_item(icmp6_tree,
391 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
392 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
393 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
395 case ICMP6_MEMBERSHIP_QUERY:
396 case ICMP6_MEMBERSHIP_REPORT:
397 case ICMP6_MEMBERSHIP_REDUCTION:
398 proto_tree_add_item(icmp6_tree,
399 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
400 "Maximum response delay: %d",
401 (guint16)ntohs(dp->icmp6_maxdelay));
402 proto_tree_add_item(icmp6_tree, offset + sizeof(*dp), 16,
403 "Multicast Address: %s",
404 ip6_to_str((struct e_in6_addr *)(dp + 1)));
406 case ND_ROUTER_SOLICIT:
407 dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
409 case ND_ROUTER_ADVERT:
411 struct nd_router_advert *ra = (struct nd_router_advert *)dp;
415 proto_tree_add_item(icmp6_tree,
416 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
417 1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
419 flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
420 ra_flags = pntohl(&pd[flagoff]);
421 tf = proto_tree_add_item(icmp6_tree, flagoff, 4, "Flags: 0x%08x", ra_flags);
422 field_tree = proto_tree_new();
423 proto_item_add_subtree(tf, field_tree, ETT_ICMPv6FLAG);
424 proto_tree_add_item(field_tree, flagoff, 4, "%s",
425 decode_boolean_bitfield(ra_flags,
426 0x80000000, 32, "Managed", "Not managed"));
427 proto_tree_add_item(field_tree, flagoff, 4, "%s",
428 decode_boolean_bitfield(ra_flags,
429 0x40000000, 32, "Other", "Not other"));
431 proto_tree_add_item(icmp6_tree,
432 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
433 2, "Router lifetime: %d",
434 (guint16)ntohs(ra->nd_ra_router_lifetime));
435 proto_tree_add_item(icmp6_tree,
436 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
437 "Reachable time: %d", pntohl(&ra->nd_ra_reachable));
438 proto_tree_add_item(icmp6_tree,
439 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
440 "Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
441 dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
444 case ND_NEIGHBOR_SOLICIT:
446 struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
448 proto_tree_add_item(icmp6_tree,
449 offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
452 get_hostname6(&ns->nd_ns_target),
456 ip6_to_str(&ns->nd_ns_target));
458 dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
461 case ND_NEIGHBOR_ADVERT:
463 int flagoff, targetoff;
465 struct e_in6_addr *na_target_p;
467 flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
468 na_flags = pntohl(&pd[flagoff]);
470 tf = proto_tree_add_item(icmp6_tree, flagoff, 4, "Flags: 0x%08x", na_flags);
471 field_tree = proto_tree_new();
472 proto_item_add_subtree(tf, field_tree, ETT_ICMPv6FLAG);
473 proto_tree_add_item(field_tree, flagoff, 4, "%s",
474 decode_boolean_bitfield(na_flags,
475 0x80000000, 32, "Router", "Not router"));
476 proto_tree_add_item(field_tree, flagoff, 4, "%s",
477 decode_boolean_bitfield(na_flags,
478 0x40000000, 32, "Solicited", "Not adverted"));
479 proto_tree_add_item(field_tree, flagoff, 4, "%s",
480 decode_boolean_bitfield(na_flags,
481 0x20000000, 32, "Override", "Not override"));
483 targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
484 na_target_p = (struct e_in6_addr*) &pd[targetoff];
485 proto_tree_add_item(icmp6_tree, targetoff, 16,
488 get_hostname6(na_target_p),
492 ip6_to_str(na_target_p));
494 dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
499 struct nd_redirect *rd = (struct nd_redirect *)dp;
501 proto_tree_add_item(icmp6_tree,
502 offset + offsetof(struct nd_redirect, nd_rd_target), 16,
505 get_hostname6(&rd->nd_rd_target),
509 ip6_to_str(&rd->nd_rd_target));
511 proto_tree_add_item(icmp6_tree,
512 offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
514 "Destination: %s (%s)",
515 get_hostname6(&rd->nd_rd_dst),
519 ip6_to_str(&rd->nd_rd_dst));
521 dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
524 case ICMP6_ROUTER_RENUMBERING:
526 struct icmp6_router_renum *rr = (struct icmp6_router_renum *)dp;
528 proto_tree_add_item(icmp6_tree,
529 offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
530 /*"Sequence number: 0x%08x", (u_int32_t)htonl(rr->rr_seqnum));*/
531 "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
532 proto_tree_add_item(icmp6_tree,
533 offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
534 "Segment number: 0x%02x", rr->rr_segnum);
536 flagoff = offset + offsetof(struct icmp6_router_renum, rr_segnum) + 1;
537 tf = proto_tree_add_item(icmp6_tree, flagoff, 4, "Flags: 0x%08x",
539 field_tree = proto_tree_new();
540 proto_item_add_subtree(tf, field_tree, ETT_ICMPv6FLAG);
541 proto_tree_add_item(field_tree, flagoff, 1, "%s",
542 decode_boolean_bitfield(pd[flagoff], 0x80, 8,
543 "Test command", "Not test command"));
544 proto_tree_add_item(field_tree, flagoff, 1, "%s",
545 decode_boolean_bitfield(pd[flagoff], 0x40, 8,
546 "Result requested", "Result not requested"));
547 proto_tree_add_item(field_tree, flagoff, 1, "%s",
548 decode_boolean_bitfield(pd[flagoff], 0x20, 8,
549 "All interfaces", "Not all interfaces"));
550 proto_tree_add_item(field_tree, flagoff, 1, "%s",
551 decode_boolean_bitfield(pd[flagoff], 0x10, 8,
552 "Site specific", "Not site specific"));
553 proto_tree_add_item(field_tree, flagoff, 1, "%s",
554 decode_boolean_bitfield(pd[flagoff], 0x08, 8,
555 "Processed previously", "Complete result"));
557 proto_tree_add_item(icmp6_tree,
558 offset + offsetof(struct icmp6_router_renum, rr_segnum), 2,
559 "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
560 dissect_data(pd, offset + sizeof(*rr), fd, tree); /*XXX*/
563 dissect_data(pd, offset + sizeof(*dp), fd, tree);