wireshark/packet-icmpv6.c

1159 lines
37 KiB
C

/* packet-icmpv6.c
* Routines for ICMPv6 packet disassembly
*
* $Id: packet-icmpv6.c,v 1.31 2000/11/19 08:53:57 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
* Copyright 1998 Gerald Combs
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <glib.h>
#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif
#include "packet.h"
#include "packet-ipv6.h"
#include "packet-ip.h"
#include "packet-dns.h"
#include "resolv.h"
#ifndef offsetof
#define offsetof(type, member) ((size_t)(&((type *)0)->member))
#endif
static int proto_icmpv6 = -1;
static int hf_icmpv6_type = -1;
static int hf_icmpv6_code = -1;
static int hf_icmpv6_checksum = -1;
static gint ett_icmpv6 = -1;
static gint ett_icmpv6opt = -1;
static gint ett_icmpv6flag = -1;
static gint ett_nodeinfo_flag = -1;
static gint ett_nodeinfo_subject4 = -1;
static gint ett_nodeinfo_subject6 = -1;
static gint ett_nodeinfo_node4 = -1;
static gint ett_nodeinfo_node6 = -1;
static gint ett_nodeinfo_nodebitmap = -1;
static gint ett_nodeinfo_nodedns = -1;
static const value_string names_nodeinfo_qtype[] = {
{ NI_QTYPE_NOOP, "NOOP" },
{ NI_QTYPE_SUPTYPES, "Supported query types" },
{ NI_QTYPE_DNSNAME, "DNS name" },
{ NI_QTYPE_NODEADDR, "Node addresses" },
{ NI_QTYPE_IPV4ADDR, "IPv4 node addresses" },
{ 0, NULL }
};
static const value_string names_rrenum_matchcode[] = {
{ RPM_PCO_ADD, "Add" },
{ RPM_PCO_CHANGE, "Change" },
{ RPM_PCO_SETGLOBAL, "Set Global" },
{ 0, NULL }
};
static void
dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
proto_tree *icmp6opt_tree, *field_tree;
proto_item *ti, *tf;
struct nd_opt_hdr *opt;
int len;
char *typename;
if (!tree)
return;
again:
if (!IS_DATA_IN_FRAME(offset))
return;
opt = (struct nd_opt_hdr *)&pd[offset];
len = opt->nd_opt_len << 3;
/* !!! specify length */
ti = proto_tree_add_text(tree, NullTVB, offset, len, "ICMPv6 options");
icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
switch (opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
typename = "Source link-layer address";
break;
case ND_OPT_TARGET_LINKADDR:
typename = "Target link-layer address";
break;
case ND_OPT_PREFIX_INFORMATION:
typename = "Prefix information";
break;
case ND_OPT_REDIRECTED_HEADER:
typename = "Redirected header";
break;
case ND_OPT_MTU:
typename = "MTU";
break;
default:
typename = "Unknown";
break;
}
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
"Type: 0x%02x (%s)", opt->nd_opt_type, typename);
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
"Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
/* decode... */
switch (opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_TARGET_LINKADDR:
{
char *t;
const char *p;
int len, i;
len = (opt->nd_opt_len << 3) - sizeof(*opt);
t = (char *)malloc(len * 3);
memset(t, 0, len * 3);
p = &pd[offset + sizeof(*opt)];
for (i = 0; i < len; i++) {
if (i)
t[i * 3 - 1] = ':';
sprintf(&t[i * 3], "%02x", p[i] & 0xff);
}
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + sizeof(*opt), len, "Link-layer address: %s", t);
break;
}
case ND_OPT_PREFIX_INFORMATION:
{
struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
int flagoff;
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
tf = proto_tree_add_text(icmp6opt_tree, NullTVB, flagoff, 1, "Flags: 0x%02x",
pntohl(&pi->nd_opt_pi_flags_reserved));
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
0x80, 8, "Onlink", "Not onlink"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
0x40, 8, "Auto", "Not auto"));
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
4, "Valid lifetime: 0x%08x",
pntohl(&pi->nd_opt_pi_valid_time));
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
4, "Preferred lifetime: 0x%08x",
pntohl(&pi->nd_opt_pi_preferred_time));
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
break;
}
case ND_OPT_REDIRECTED_HEADER:
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
/* tiny sanity check */
if ((pd[offset + 8] & 0xf0) == 0x60)
dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
else
old_dissect_data(pd, offset + 8, fd, icmp6opt_tree);
break;
case ND_OPT_MTU:
{
struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
"MTU: %d", pntohl(&pi->nd_opt_mtu_mtu));
break;
}
}
if (opt->nd_opt_len == 0) {
proto_tree_add_text(icmp6opt_tree, NullTVB,
offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
"Invalid option length: %d",
opt->nd_opt_len);
return;
}
offset += (opt->nd_opt_len << 3);
goto again;
}
/*
* draft-ietf-ipngwg-icmp-name-lookups-06.txt
* Note that the packet format was changed several times in the past.
*/
static const char *
bitrange0(v, s, buf, buflen)
guint32 v;
int s;
char *buf;
int buflen;
{
guint32 v0;
char *p, *ep;
int off;
int i, l;
if (buflen < 1)
return NULL;
if (buflen == 1) {
buf[0] = '\0';
return NULL;
}
v0 = v;
p = buf;
ep = buf + buflen - 1;
memset(buf, 0, buflen);
off = 0;
while (off < 32) {
/* shift till we have 0x01 */
if ((v & 0x01) == 0) {
switch (v & 0x0f) {
case 0x00:
v >>= 4; off += 4; continue;
case 0x08:
v >>= 3; off += 3; continue;
case 0x04: case 0x0c:
v >>= 2; off += 2; continue;
default:
v >>= 1; off += 1; continue;
}
}
/* we have 0x01 with us */
for (i = 0; i < 32 - off; i++) {
if ((v & (0x01 << i)) == 0)
break;
}
if (i == 1)
l = snprintf(p, ep - p, ",%d", s + off);
else {
l = snprintf(p, ep - p, ",%d-%d", s + off,
s + off + i - 1);
}
if (l > ep - p) {
buf[0] = '\0';
return NULL;
}
v >>= i; off += i;
}
return buf;
}
static const char *
bitrange(u_char *p, int l, int s)
{
static char buf[1024];
char *q, *eq;
int i;
memset(buf, 0, sizeof(buf));
q = buf;
eq = buf + sizeof(buf) - 1;
for (i = 0; i < l; i++) {
if (bitrange0(pntohl(p + i * 4), s + i * 4, q, eq - q) == NULL) {
if (q != buf && q + 5 < buf + sizeof(buf))
strncpy(q, ",...", 5);
return buf;
}
}
return buf + 1;
}
static void
dissect_nodeinfo(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
proto_tree *field_tree;
proto_item *tf;
struct icmp6_nodeinfo *ni;
int off;
int i, n, l;
guint16 flags;
u_char *p;
char dname[MAXDNAME];
ni = (struct icmp6_nodeinfo *)&pd[offset];
/* flags */
flags = pntohs(&ni->ni_flags);
tf = proto_tree_add_text(tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "Flags: 0x%04x", flags);
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_flag);
switch (pntohs(&ni->ni_qtype)) {
case NI_QTYPE_SUPTYPES:
if (ni->ni_type == ICMP6_NI_QUERY) {
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
"Compressed reply supported",
"No compressed reply support"));
} else {
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_SUPTYPE_FLAG_COMPRESS, sizeof(flags) * 8,
"Compressed", "Not compressed"));
}
break;
case NI_QTYPE_DNSNAME:
if (ni->ni_type == ICMP6_NI_REPLY) {
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_FQDN_FLAG_VALIDTTL, sizeof(flags) * 8,
"Valid TTL field", "Meaningless TTL field"));
}
break;
case NI_QTYPE_NODEADDR:
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_GLOBAL, sizeof(flags) * 8,
"Global address",
"Not global address"));
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_SITELOCAL, sizeof(flags) * 8,
"Site-local address",
"Not site-local address"));
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_LINKLOCAL, sizeof(flags) * 8,
"Link-local address",
"Not link-local address"));
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_COMPAT, sizeof(flags) * 8,
"IPv4 compatible/mapped address",
"Not IPv4 compatible/mapped address"));
/* fall through */
case NI_QTYPE_IPV4ADDR:
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_ALL, sizeof(flags) * 8,
"All unicast address",
"Unicast addresses on the queried interface"));
proto_tree_add_text(field_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_flags),
sizeof(ni->ni_flags), "%s",
decode_boolean_bitfield(flags, NI_NODEADDR_FLAG_TRUNCATE, sizeof(flags) * 8,
"Truncated", "Not truncated"));
break;
}
/* nonce */
proto_tree_add_text(tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, icmp6_ni_nonce[0]),
sizeof(ni->icmp6_ni_nonce), "Nonce: 0x%08x%08x",
pntohl(&ni->icmp6_ni_nonce[0]), pntohl(&ni->icmp6_ni_nonce[4]));
/* offset for "the rest of data" */
off = sizeof(*ni);
/* rest of data */
if (!IS_DATA_IN_FRAME(offset + sizeof(*ni)))
goto nodata;
if (ni->ni_type == ICMP6_NI_QUERY) {
switch (ni->ni_code) {
case ICMP6_NI_SUBJ_IPV6:
n = pi.captured_len - (offset + sizeof(*ni));
n /= sizeof(struct e_in6_addr);
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni), END_OF_FRAME, "IPv6 subject addresses");
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject6);
p = (u_char *)(ni + 1);
for (i = 0; i < n; i++) {
proto_tree_add_text(field_tree, NullTVB,
p - pd, sizeof(struct e_in6_addr),
"%s", ip6_to_str((struct e_in6_addr *)p));
p += sizeof(struct e_in6_addr);
}
off = pi.captured_len - offset;
break;
case ICMP6_NI_SUBJ_FQDN:
l = get_dns_name(pd, offset + sizeof(*ni), offset + sizeof(*ni),
dname, sizeof(dname));
if (IS_DATA_IN_FRAME(offset + sizeof(*ni) + l) &&
pd[offset + sizeof(*ni) + l] == 0) {
l++;
proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
"DNS label: %s (truncated)", dname);
} else {
proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni), l,
"DNS label: %s", dname);
}
off = offset + sizeof(*ni) + l;
break;
case ICMP6_NI_SUBJ_IPV4:
n = pi.captured_len - (offset + sizeof(*ni));
n /= sizeof(guint32);
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni), END_OF_FRAME, "IPv4 subject addresses");
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject4);
p = (u_char *)(ni + 1);
for (i = 0; i < n; i++) {
proto_tree_add_text(field_tree, NullTVB,
p - pd, sizeof(guint32), "%s", ip_to_str(p));
p += sizeof(guint32);
}
off = pi.captured_len - offset;
break;
}
} else {
switch (pntohs(&ni->ni_qtype)) {
case NI_QTYPE_NOOP:
break;
case NI_QTYPE_SUPTYPES:
p = (u_char *)(ni + 1);
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni), END_OF_FRAME,
"Supported type bitmap%s",
(flags & 0x0001) ? ", compressed" : "");
field_tree = proto_item_add_subtree(tf,
ett_nodeinfo_nodebitmap);
n = 0;
while (IS_DATA_IN_FRAME(p - pd)) {
if ((flags & 0x0001) == 0) {
l = pi.captured_len - (offset + sizeof(*ni));
l /= sizeof(guint32);
i = 0;
} else {
if (!IS_DATA_IN_FRAME(p + sizeof(guint32) - 1 - pd))
break;
l = pntohs(p);
i = pntohs(p + sizeof(guint16)); /*skip*/
}
if (n + l * 32 > (1 << 16))
break;
if (n + (l + i) * 32 > (1 << 16))
break;
if ((flags & 0x0001) == 0) {
proto_tree_add_text(field_tree, NullTVB, p - pd,
l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
bitrange(p, l, n));
p += l * 4;
} else {
proto_tree_add_text(field_tree, NullTVB, p - pd,
4 + l * 4, "Bitmap (%d to %d): %s", n, n + l * 32 - 1,
bitrange(p + 4, l, n));
p += (4 + l * 4);
}
n += l * 32 + i * 32;
}
off = pi.captured_len - offset;
break;
case NI_QTYPE_DNSNAME:
proto_tree_add_text(tree, NullTVB, offset + sizeof(*ni),
sizeof(gint32), "TTL: %d", (gint32)pntohl(ni + 1));
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni) + sizeof(guint32), END_OF_FRAME,
"DNS labels");
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_nodedns);
n = pi.captured_len;
i = offset + sizeof(*ni) + sizeof(guint32);
while (i < pi.captured_len) {
l = get_dns_name(pd, i, offset + sizeof(*ni), dname,
sizeof(dname));
if (IS_DATA_IN_FRAME(i + l) && pd[i + l] == 0) {
l++;
proto_tree_add_text(field_tree, NullTVB, i, l,
"DNS label: %s (truncated)", dname);
} else {
proto_tree_add_text(field_tree, NullTVB, i, l,
"DNS label: %s", dname);
}
i += l;
}
off = pi.captured_len - offset;
break;
case NI_QTYPE_NODEADDR:
n = pi.captured_len - (offset + sizeof(*ni));
n /= sizeof(struct e_in6_addr) + sizeof(gint32);;
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni), END_OF_FRAME, "IPv6 node addresses");
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node6);
p = (u_char *)(ni + 1);
for (i = 0; i < n; i++) {
proto_tree_add_text(field_tree, NullTVB,
p - pd, sizeof(struct e_in6_addr) + sizeof(gint32),
"%s (TTL %d)", ip6_to_str((struct e_in6_addr *)p),
(gint32)pntohl(p + sizeof(struct e_in6_addr)));
p += sizeof(struct e_in6_addr) + sizeof(gint32);
}
off = pi.captured_len - offset;
break;
case NI_QTYPE_IPV4ADDR:
n = pi.captured_len - (offset + sizeof(*ni));
n /= sizeof(guint32);
tf = proto_tree_add_text(tree, NullTVB,
offset + sizeof(*ni), END_OF_FRAME, "IPv4 node addresses");
field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node4);
p = (u_char *)(ni + 1);
for (i = 0; i < n; i++) {
proto_tree_add_text(field_tree, NullTVB,
p - pd, sizeof(guint32), "%s", ip_to_str(p));
p += sizeof(guint32);
}
off = pi.captured_len - offset;
break;
}
}
nodata:;
/* the rest of data */
old_dissect_data(pd, offset + off, fd, tree);
}
static void
dissect_rrenum(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
proto_tree *field_tree, *opt_tree;
proto_item *tf;
struct icmp6_router_renum *rr = (struct icmp6_router_renum *)&pd[offset];
struct rr_pco_match *match;
struct rr_pco_use *use;
int flagoff, off, l;
proto_tree_add_text(tree, NullTVB,
offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
"Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
proto_tree_add_text(tree, NullTVB,
offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
"Segment number: 0x%02x", rr->rr_segnum);
flagoff = offset + offsetof(struct icmp6_router_renum, rr_flags);
tf = proto_tree_add_text(tree, NullTVB, flagoff, 1,
"Flags: 0x%02x", pd[flagoff]);
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff], 0x80, 8,
"Test command", "Not test command"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff], 0x40, 8,
"Result requested", "Result not requested"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff], 0x20, 8,
"All interfaces", "Not all interfaces"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff], 0x10, 8,
"Site specific", "Not site specific"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff], 0x08, 8,
"Processed previously", "Complete result"));
proto_tree_add_text(tree, NullTVB,
offset + offsetof(struct icmp6_router_renum, rr_maxdelay), 2,
"Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
old_dissect_data(pd, offset + sizeof(*rr), fd, tree); /*XXX*/
if (rr->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
off = offset + sizeof(*rr);
match = (struct rr_pco_match *)&pd[off];
tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*match),
"Match-Prefix: %s/%u (%u-%u)", ip6_to_str(&match->rpm_prefix),
match->rpm_matchlen, match->rpm_minlen, match->rpm_maxlen);
opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_code),
sizeof(match->rpm_code), "OpCode: %s (%u)",
val_to_str(match->rpm_code, names_rrenum_matchcode, "Unknown"),
match->rpm_code);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_len),
sizeof(match->rpm_len), "OpLength: %u (%u octets)",
match->rpm_len, match->rpm_len * 8);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_ordinal),
sizeof(match->rpm_ordinal), "Ordinal: %u", match->rpm_ordinal);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_matchlen),
sizeof(match->rpm_matchlen), "MatchLen: %u", match->rpm_matchlen);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_minlen),
sizeof(match->rpm_minlen), "MinLen: %u", match->rpm_minlen);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_maxlen),
sizeof(match->rpm_maxlen), "MaxLen: %u", match->rpm_maxlen);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_match, rpm_prefix),
sizeof(match->rpm_prefix), "MatchPrefix: %s",
ip6_to_str(&match->rpm_prefix));
off += sizeof(*match);
use = (struct rr_pco_use *)&pd[off];
for (l = match->rpm_len * 8 - sizeof(*match);
l >= sizeof(*use); l -= sizeof(*use), off += sizeof(*use)) {
tf = proto_tree_add_text(tree, NullTVB, off, sizeof(*use),
"Use-Prefix: %s/%u (keep %u)", ip6_to_str(&use->rpu_prefix),
use->rpu_uselen, use->rpu_keeplen);
opt_tree = proto_item_add_subtree(tf, ett_icmpv6opt);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_uselen),
sizeof(use->rpu_uselen), "UseLen: %u", use->rpu_uselen);
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_keeplen),
sizeof(use->rpu_keeplen), "KeepLen: %u", use->rpu_keeplen);
tf = proto_tree_add_text(opt_tree, NullTVB,
flagoff = off + offsetof(struct rr_pco_use, rpu_ramask),
sizeof(use->rpu_ramask), "FlagMask: 0x%x", use->rpu_ramask);
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
"Onlink", "Not onlink"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8,
"Auto", "Not auto"));
tf = proto_tree_add_text(opt_tree, NullTVB,
flagoff = off + offsetof(struct rr_pco_use, rpu_raflags),
sizeof(use->rpu_raflags), "RAFlags: 0x%x", use->rpu_raflags);
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_RAFLAGS_ONLINK, 8,
"Onlink", "Not onlink"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 1, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_RAFLAGS_AUTO, 8, "Auto", "Not auto"));
if (pntohl(&use->rpu_vltime) == 0xffffffff)
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_vltime),
sizeof(use->rpu_vltime), "Valid Lifetime: infinity");
else
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_vltime),
sizeof(use->rpu_vltime), "Valid Lifetime: %u",
pntohl(&use->rpu_vltime));
if (pntohl(&use->rpu_pltime) == 0xffffffff)
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_pltime),
sizeof(use->rpu_pltime), "Preferred Lifetime: infinity");
else
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_pltime),
sizeof(use->rpu_pltime), "Preferred Lifetime: %u",
pntohl(&use->rpu_pltime));
tf = proto_tree_add_text(opt_tree, NullTVB,
flagoff = off + offsetof(struct rr_pco_use, rpu_flags),
sizeof(use->rpu_flags), "Flags: 0x%08x",
pntohl(&use->rpu_flags));
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, 32,
"Decrement valid lifetime", "No decrement valid lifetime"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(pd[flagoff],
ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, 32,
"Decrement preferred lifetime",
"No decrement preferred lifetime"));
proto_tree_add_text(opt_tree, NullTVB,
off + offsetof(struct rr_pco_use, rpu_prefix),
sizeof(use->rpu_prefix), "UsePrefix: %s",
ip6_to_str(&use->rpu_prefix));
}
}
}
static void
dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
proto_tree *icmp6_tree, *field_tree;
proto_item *ti, *tf = NULL;
struct icmp6_hdr *dp;
struct icmp6_nodeinfo *ni = NULL;
char *codename, *typename;
char *colcodename, *coltypename;
int len;
OLD_CHECK_DISPLAY_AS_DATA(proto_icmpv6, pd, offset, fd, tree);
dp = (struct icmp6_hdr *)&pd[offset];
codename = typename = colcodename = coltypename = "Unknown";
len = sizeof(*dp);
switch (dp->icmp6_type) {
case ICMP6_DST_UNREACH:
typename = coltypename = "Unreachable";
switch (dp->icmp6_code) {
case ICMP6_DST_UNREACH_NOROUTE:
codename = colcodename = "Route unreachable";
break;
case ICMP6_DST_UNREACH_ADMIN:
codename = colcodename = "Administratively prohibited";
break;
case ICMP6_DST_UNREACH_NOTNEIGHBOR:
codename = colcodename = "Not a neighbor";
break;
case ICMP6_DST_UNREACH_ADDR:
codename = colcodename = "Address unreachable";
break;
case ICMP6_DST_UNREACH_NOPORT:
codename = colcodename = "Port unreachable";
break;
}
break;
case ICMP6_PACKET_TOO_BIG:
typename = coltypename = "Too big";
codename = colcodename = NULL;
break;
case ICMP6_TIME_EXCEEDED:
typename = coltypename = "Time exceeded";
switch (dp->icmp6_code) {
case ICMP6_TIME_EXCEED_TRANSIT:
codename = colcodename = "In-transit";
break;
case ICMP6_TIME_EXCEED_REASSEMBLY:
codename = colcodename = "Reassembly";
break;
}
break;
case ICMP6_PARAM_PROB:
typename = coltypename = "Parameter problem";
switch (dp->icmp6_code) {
case ICMP6_PARAMPROB_HEADER:
codename = colcodename = "Header";
break;
case ICMP6_PARAMPROB_NEXTHEADER:
codename = colcodename = "Next header";
break;
case ICMP6_PARAMPROB_OPTION:
codename = colcodename = "Option";
break;
}
break;
case ICMP6_ECHO_REQUEST:
typename = coltypename = "Echo request";
codename = colcodename = NULL;
break;
case ICMP6_ECHO_REPLY:
typename = coltypename = "Echo reply";
codename = colcodename = NULL;
break;
case ICMP6_MEMBERSHIP_QUERY:
typename = coltypename = "Multicast listener query";
codename = colcodename = NULL;
break;
case ICMP6_MEMBERSHIP_REPORT:
typename = coltypename = "Multicast listener report";
codename = colcodename = NULL;
break;
case ICMP6_MEMBERSHIP_REDUCTION:
typename = coltypename = "Multicast listener done";
codename = colcodename = NULL;
break;
case ND_ROUTER_SOLICIT:
typename = coltypename = "Router solicitation";
codename = colcodename = NULL;
len = sizeof(struct nd_router_solicit);
break;
case ND_ROUTER_ADVERT:
typename = coltypename = "Router advertisement";
codename = colcodename = NULL;
len = sizeof(struct nd_router_advert);
break;
case ND_NEIGHBOR_SOLICIT:
typename = coltypename = "Neighbor solicitation";
codename = colcodename = NULL;
len = sizeof(struct nd_neighbor_solicit);
break;
case ND_NEIGHBOR_ADVERT:
typename = coltypename = "Neighbor advertisement";
codename = colcodename = NULL;
len = sizeof(struct nd_neighbor_advert);
break;
case ND_REDIRECT:
typename = coltypename = "Redirect";
codename = colcodename = NULL;
len = sizeof(struct nd_redirect);
break;
case ICMP6_ROUTER_RENUMBERING:
typename = coltypename = "Router renumbering";
switch (dp->icmp6_code) {
case ICMP6_ROUTER_RENUMBERING_COMMAND:
codename = colcodename = "Command";
break;
case ICMP6_ROUTER_RENUMBERING_RESULT:
codename = colcodename = "Result";
break;
case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
codename = colcodename = "Sequence number reset";
break;
}
len = sizeof(struct icmp6_router_renum);
break;
case ICMP6_NI_QUERY:
case ICMP6_NI_REPLY:
ni = (struct icmp6_nodeinfo *)dp;
if (ni->ni_type == ICMP6_NI_QUERY) {
typename = coltypename = "Node information query";
switch (ni->ni_code) {
case ICMP6_NI_SUBJ_IPV6:
codename = "Query subject = IPv6 addresses";
break;
case ICMP6_NI_SUBJ_FQDN:
if (IS_DATA_IN_FRAME(offset + sizeof(*ni)))
codename = "Query subject = DNS name";
else
codename = "Query subject = empty";
break;
case ICMP6_NI_SUBJ_IPV4:
codename = "Query subject = IPv4 addresses";
break;
}
} else {
typename = coltypename = "Node information reply";
switch (ni->ni_code) {
case ICMP6_NI_SUCCESS:
codename = "Successful";
break;
case ICMP6_NI_REFUSED:
codename = "Refused";
break;
case ICMP6_NI_UNKNOWN:
codename = "Unknown query type";
break;
}
}
colcodename = val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
"Unknown");
len = sizeof(struct icmp6_nodeinfo);
break;
}
if (check_col(fd, COL_PROTOCOL))
col_set_str(fd, COL_PROTOCOL, "ICMPv6");
if (check_col(fd, COL_INFO)) {
char typebuf[256], codebuf[256];
if (coltypename && strcmp(coltypename, "Unknown") == 0) {
snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
dp->icmp6_type);
coltypename = typebuf;
}
if (colcodename && strcmp(colcodename, "Unknown") == 0) {
snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
dp->icmp6_code);
colcodename = codebuf;
}
if (colcodename) {
col_add_fstr(fd, COL_INFO, "%s (%s)", coltypename, colcodename);
} else {
col_add_fstr(fd, COL_INFO, "%s", coltypename);
}
}
if (tree) {
/* !!! specify length */
ti = proto_tree_add_item(tree, proto_icmpv6, NullTVB, offset, len, FALSE);
icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_type, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
dp->icmp6_type,
"Type: 0x%02x (%s)", dp->icmp6_type, typename);
if (codename) {
proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
dp->icmp6_code,
"Code: 0x%02x (%s)", dp->icmp6_code, codename);
} else {
proto_tree_add_uint_format(icmp6_tree, hf_icmpv6_code, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
dp->icmp6_code,
"Code: 0x%02x", dp->icmp6_code);
}
proto_tree_add_uint(icmp6_tree, hf_icmpv6_checksum, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
(guint16)htons(dp->icmp6_cksum));
/* decode... */
switch (dp->icmp6_type) {
case ICMP6_DST_UNREACH:
case ICMP6_TIME_EXCEEDED:
/* tiny sanity check */
if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
} else {
old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
}
break;
case ICMP6_PACKET_TOO_BIG:
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
"MTU: %d", pntohl(&dp->icmp6_mtu));
/* tiny sanity check */
if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
} else {
old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
}
break;
case ICMP6_PARAM_PROB:
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
"Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
/* tiny sanity check */
if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
} else {
old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
}
break;
case ICMP6_ECHO_REQUEST:
case ICMP6_ECHO_REPLY:
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
"ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
"Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
old_dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
break;
case ICMP6_MEMBERSHIP_QUERY:
case ICMP6_MEMBERSHIP_REPORT:
case ICMP6_MEMBERSHIP_REDUCTION:
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
"Maximum response delay: %d",
(guint16)ntohs(dp->icmp6_maxdelay));
proto_tree_add_text(icmp6_tree, NullTVB, offset + sizeof(*dp), 16,
"Multicast Address: %s",
ip6_to_str((struct e_in6_addr *)(dp + 1)));
break;
case ND_ROUTER_SOLICIT:
dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
break;
case ND_ROUTER_ADVERT:
{
struct nd_router_advert *ra = (struct nd_router_advert *)dp;
int flagoff;
guint32 ra_flags;
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
ra_flags = pntohl(&pd[flagoff]);
tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", ra_flags);
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(ra_flags,
0x80000000, 32, "Managed", "Not managed"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(ra_flags,
0x40000000, 32, "Other", "Not other"));
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
2, "Router lifetime: %d",
(guint16)ntohs(ra->nd_ra_router_lifetime));
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
"Reachable time: %d", pntohl(&ra->nd_ra_reachable));
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
"Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
break;
}
case ND_NEIGHBOR_SOLICIT:
{
struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
#ifdef INET6
"Target: %s (%s)",
get_hostname6(&ns->nd_ns_target),
#else
"Target: %s",
#endif
ip6_to_str(&ns->nd_ns_target));
dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
break;
}
case ND_NEIGHBOR_ADVERT:
{
int flagoff, targetoff;
guint32 na_flags;
struct e_in6_addr *na_target_p;
flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
na_flags = pntohl(&pd[flagoff]);
tf = proto_tree_add_text(icmp6_tree, NullTVB, flagoff, 4, "Flags: 0x%08x", na_flags);
field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(na_flags,
ND_NA_FLAG_ROUTER, 32, "Router", "Not router"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(na_flags,
ND_NA_FLAG_SOLICITED, 32, "Solicited", "Not adverted"));
proto_tree_add_text(field_tree, NullTVB, flagoff, 4, "%s",
decode_boolean_bitfield(na_flags,
ND_NA_FLAG_OVERRIDE, 32, "Override", "Not override"));
targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
na_target_p = (struct e_in6_addr*) &pd[targetoff];
proto_tree_add_text(icmp6_tree, NullTVB, targetoff, 16,
#ifdef INET6
"Target: %s (%s)",
get_hostname6(na_target_p),
#else
"Target: %s",
#endif
ip6_to_str(na_target_p));
dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
break;
}
case ND_REDIRECT:
{
struct nd_redirect *rd = (struct nd_redirect *)dp;
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_redirect, nd_rd_target), 16,
#ifdef INET6
"Target: %s (%s)",
get_hostname6(&rd->nd_rd_target),
#else
"Target: %s",
#endif
ip6_to_str(&rd->nd_rd_target));
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
#ifdef INET6
"Destination: %s (%s)",
get_hostname6(&rd->nd_rd_dst),
#else
"Destination: %s",
#endif
ip6_to_str(&rd->nd_rd_dst));
dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
break;
}
case ICMP6_ROUTER_RENUMBERING:
dissect_rrenum(pd, offset, fd, icmp6_tree);
break;
case ICMP6_NI_QUERY:
case ICMP6_NI_REPLY:
ni = (struct icmp6_nodeinfo *)dp;
proto_tree_add_text(icmp6_tree, NullTVB,
offset + offsetof(struct icmp6_nodeinfo, ni_qtype),
sizeof(ni->ni_qtype),
"Query type: 0x%04x (%s)", pntohs(&ni->ni_qtype),
val_to_str(pntohs(&ni->ni_qtype), names_nodeinfo_qtype,
"Unknown"));
dissect_nodeinfo(pd, offset, fd, icmp6_tree);
break;
default:
old_dissect_data(pd, offset + sizeof(*dp), fd, tree);
break;
}
}
}
void
proto_register_icmpv6(void)
{
static hf_register_info hf[] = {
{ &hf_icmpv6_type,
{ "Type", "icmpv6.type", FT_UINT8, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_icmpv6_code,
{ "Code", "icmpv6.code", FT_UINT8, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_icmpv6_checksum,
{ "Checksum", "icmpv6.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
"" }}
};
static gint *ett[] = {
&ett_icmpv6,
&ett_icmpv6opt,
&ett_icmpv6flag,
&ett_nodeinfo_flag,
&ett_nodeinfo_subject4,
&ett_nodeinfo_subject6,
&ett_nodeinfo_node4,
&ett_nodeinfo_node6,
&ett_nodeinfo_nodebitmap,
&ett_nodeinfo_nodedns,
};
proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
"icmpv6");
proto_register_field_array(proto_icmpv6, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
void
proto_reg_handoff_icmpv6(void)
{
old_dissector_add("ip.proto", IP_PROTO_ICMPV6, dissect_icmpv6);
}