snoop_icmp.c revision 7ba7860f5af89005c23337fb7cdc48145cc6b8ac
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/stropts.h>
#include <sys/sysmacros.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "snoop.h"
#include "snoop_mip.h"
static void interpret_options(char *, int);
static void interpret_mldv2qry(icmp6_t *, int);
static void interpret_mldv2rpt(icmp6_t *, int);
/* Mobile-IP routines from snoop_mip.c */
extern void interpret_icmp_mip_ext(uchar_t *, int);
extern const char *get_mip_adv_desc(uint8_t);
/* Router advertisement message structure. */
struct icmp_ra_addr {
uint32_t addr;
uint32_t preference;
};
/*ARGSUSED*/
void
interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
{
char *pt, *pc, *px;
char *line;
char buff[67627]; /* Router adv. can have 256 routers .... */
/* Each router has a name 256 char long .. */
char extbuff[MAXHOSTNAMELEN + 1];
struct udphdr *orig_uhdr;
int num_rtr_addrs = 0;
extern char *prot_nest_prefix;
if (ilen < ICMP_MINLEN)
return; /* incomplete header */
pt = "Unknown";
pc = "";
px = "";
switch (icmp->icmp_type) {
case ICMP_ECHOREPLY:
pt = "Echo reply";
(void) sprintf(buff, "ID: %d Sequence number: %d",
ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
pc = buff;
break;
case ICMP_UNREACH:
pt = "Destination unreachable";
switch (icmp->icmp_code) {
case ICMP_UNREACH_NET:
if (ilen >= ICMP_ADVLENMIN) {
(void) sprintf(buff, "Net %s unreachable",
addrtoname(AF_INET,
&icmp->icmp_ip.ip_dst));
pc = buff;
} else {
pc = "Bad net";
}
break;
case ICMP_UNREACH_HOST:
if (ilen >= ICMP_ADVLENMIN) {
(void) sprintf(buff, "Host %s unreachable",
addrtoname(AF_INET,
&icmp->icmp_ip.ip_dst));
pc = buff;
} else {
pc = "Bad host";
}
break;
case ICMP_UNREACH_PROTOCOL:
if (ilen >= ICMP_ADVLENMIN) {
(void) sprintf(buff, "Bad protocol %d",
icmp->icmp_ip.ip_p);
pc = buff;
} else {
pc = "Bad protocol";
}
break;
case ICMP_UNREACH_PORT:
if (ilen >= ICMP_ADVLENMIN) {
orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
switch (icmp->icmp_ip.ip_p) {
case IPPROTO_TCP:
(void) sprintf(buff, "TCP port %d"
" unreachable",
ntohs(orig_uhdr->uh_dport));
pc = buff;
break;
case IPPROTO_UDP:
(void) sprintf(buff, "UDP port %d"
" unreachable",
ntohs(orig_uhdr->uh_dport));
pc = buff;
break;
default:
pc = "Port unreachable";
break;
}
} else {
pc = "Bad port";
}
break;
case ICMP_UNREACH_NEEDFRAG:
if (ntohs(icmp->icmp_nextmtu) != 0) {
(void) sprintf(buff, "Needed to fragment:"
" next hop MTU = %d",
ntohs(icmp->icmp_nextmtu));
pc = buff;
} else {
pc = "Needed to fragment";
}
break;
case ICMP_UNREACH_SRCFAIL:
pc = "Source route failed";
break;
case ICMP_UNREACH_NET_UNKNOWN:
pc = "Unknown network";
break;
case ICMP_UNREACH_HOST_UNKNOWN:
pc = "Unknown host";
break;
case ICMP_UNREACH_ISOLATED:
pc = "Source host isolated";
break;
case ICMP_UNREACH_NET_PROHIB:
pc = "Net administratively prohibited";
break;
case ICMP_UNREACH_HOST_PROHIB:
pc = "Host administratively prohibited";
break;
case ICMP_UNREACH_TOSNET:
pc = "Net unreachable for this TOS";
break;
case ICMP_UNREACH_TOSHOST:
pc = "Host unreachable for this TOS";
break;
case ICMP_UNREACH_FILTER_PROHIB:
pc = "Communication administratively prohibited";
break;
case ICMP_UNREACH_HOST_PRECEDENCE:
pc = "Host precedence violation";
break;
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
pc = "Precedence cutoff in effect";
break;
default:
break;
}
break;
case ICMP_SOURCEQUENCH:
pt = "Packet lost, slow down";
break;
case ICMP_REDIRECT:
pt = "Redirect";
switch (icmp->icmp_code) {
case ICMP_REDIRECT_NET:
pc = "for network";
break;
case ICMP_REDIRECT_HOST:
pc = "for host";
break;
case ICMP_REDIRECT_TOSNET:
pc = "for tos and net";
break;
case ICMP_REDIRECT_TOSHOST:
pc = "for tos and host";
break;
default:
break;
}
(void) sprintf(buff, "%s %s to %s",
pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
addrtoname(AF_INET, &icmp->icmp_gwaddr));
pc = buff;
break;
case ICMP_ECHO:
pt = "Echo request";
(void) sprintf(buff, "ID: %d Sequence number: %d",
ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
pc = buff;
break;
case ICMP_ROUTERADVERT:
#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
pt = "Router advertisement";
(void) sprintf(buff, "Lifetime %ds [%d]:",
ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
if (icmp->icmp_wpa == 2) {
struct icmp_ra_addr *ra;
char ra_buf[MAXHOSTNAMELEN + 32];
char ra_ext_buf[50];
struct in_addr sin;
int icmp_ra_len;
int i;
/* Cannot trust anything from the network... */
num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
icmp->icmp_num_addrs);
ra = (struct icmp_ra_addr *)icmp->icmp_data;
for (i = 0; i < num_rtr_addrs; i++) {
sin.s_addr = ra->addr;
(void) snprintf(ra_buf, sizeof (ra_buf),
" {%s %u}",
addrtoname(AF_INET, &sin),
ntohl(ra->preference));
if (strlcat(buff, ra_buf, sizeof (buff)) >=
sizeof (buff)) {
buff[sizeof (buff) -
strlen("<Too Long>)")] = '\0';
(void) strlcat(buff, "<Too Long>",
sizeof (buff));
break;
}
ra++;
}
icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
sizeof (struct icmp_ra_addr);
if (ilen > icmp_ra_len) {
int curr_len = ilen - icmp_ra_len;
int ocurr_len;
exthdr_t *exthdr = (exthdr_t *)ra;
extbuff[0] = '\0';
while (curr_len > 0) {
/* Append Mobile-IP description */
(void) snprintf(ra_ext_buf,
sizeof (ra_ext_buf), ", %s",
get_mip_adv_desc(exthdr->type));
(void) strlcat(extbuff, ra_ext_buf,
sizeof (extbuff));
/* Special case for padding */
if (exthdr->type ==
ICMP_ADV_MSG_PADDING_EXT) {
curr_len--;
exthdr = (exthdr_t *)
((char *)exthdr + 1);
continue;
}
/* else normal extension */
ocurr_len = curr_len;
curr_len -= sizeof (*exthdr) +
exthdr->length;
/* detect bad length */
if (ocurr_len < curr_len)
break;
exthdr = (exthdr_t *)
((char *)exthdr +
sizeof (*exthdr) +
exthdr->length);
}
px = extbuff;
}
pc = buff;
}
break;
case ICMP_ROUTERSOLICIT:
pt = "Router solicitation";
break;
case ICMP_TIMXCEED:
pt = "Time exceeded";
switch (icmp->icmp_code) {
case ICMP_TIMXCEED_INTRANS:
pc = "in transit";
break;
case ICMP_TIMXCEED_REASS:
pc = "in reassembly";
break;
default:
break;
}
break;
case ICMP_PARAMPROB:
pt = "IP parameter problem";
switch (icmp->icmp_code) {
case ICMP_PARAMPROB_OPTABSENT:
pc = "Required option missing";
break;
case ICMP_PARAMPROB_BADLENGTH:
pc = "Bad length";
break;
case 0: /* Should this be the default? */
(void) sprintf(buff, "Problem at octet %d\n",
icmp->icmp_pptr);
pc = buff;
default:
break;
}
break;
case ICMP_TSTAMP:
pt = "Timestamp request";
break;
case ICMP_TSTAMPREPLY:
pt = "Timestamp reply";
break;
case ICMP_IREQ:
pt = "Information request";
break;
case ICMP_IREQREPLY:
pt = "Information reply";
break;
case ICMP_MASKREQ:
pt = "Address mask request";
break;
case ICMP_MASKREPLY:
pt = "Address mask reply";
(void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
pc = buff;
break;
default:
break;
}
if (flags & F_SUM) {
line = get_sum_line();
if (*pc) {
if (*px) {
(void) sprintf(line, "ICMP %s (%s)%s",
pt, pc, px);
} else {
(void) sprintf(line, "ICMP %s (%s)", pt, pc);
}
} else {
(void) sprintf(line, "ICMP %s", pt);
}
}
if (flags & F_DTAIL) {
show_header("ICMP: ", "ICMP Header", ilen);
show_space();
(void) sprintf(get_line(0, 0), "Type = %d (%s)",
icmp->icmp_type, pt);
if (*pc) {
(void) sprintf(get_line(0, 0), "Code = %d (%s)",
icmp->icmp_code, pc);
} else {
(void) sprintf(get_line(0, 0), "Code = %d",
icmp->icmp_code);
}
(void) sprintf(get_line(0, 0), "Checksum = %x",
ntohs(icmp->icmp_cksum));
if (icmp->icmp_type == ICMP_UNREACH ||
icmp->icmp_type == ICMP_REDIRECT) {
if (ilen > 28) {
show_space();
(void) sprintf(get_line(0, 0),
"[ subject header follows ]");
show_space();
prot_nest_prefix = "ICMP:";
(void) interpret_ip(flags,
(struct ip *)icmp->icmp_data, 28);
prot_nest_prefix = "";
}
} else if (icmp->icmp_type == ICMP_PARAMPROB) {
if (ilen > 28) {
show_space();
(void) sprintf(get_line(0, 0),
"[ subject header follows ]");
show_space();
prot_nest_prefix = "ICMP:";
(void) interpret_ip(flags,
(struct ip *)icmp->icmp_data, 28);
prot_nest_prefix = "";
}
} else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
if (icmp->icmp_wpa == 2) {
int icmp_ra_len;
show_space();
icmp_ra_len = ICMP_MINLEN +
num_rtr_addrs *
sizeof (struct icmp_ra_addr);
prot_nest_prefix = "";
if (ilen > icmp_ra_len) {
interpret_icmp_mip_ext(
(uchar_t *)icmp + icmp_ra_len,
ilen - icmp_ra_len);
}
}
}
show_space();
}
}
/*ARGSUSED*/
void
interpret_icmpv6(flags, icmp6, iplen, ilen)
int flags;
icmp6_t *icmp6;
int iplen, ilen;
{
char *pt, *pc;
char *line;
extern char *prot_nest_prefix;
char addrstr[INET6_ADDRSTRLEN];
char buff[2048];
if (ilen < ICMP6_MINLEN)
return; /* incomplete header */
pt = "Unknown";
pc = "";
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
pt = "Destination unreachable";
switch (icmp6->icmp6_code) {
case ICMP6_DST_UNREACH_NOROUTE:
pc = "No route to destination";
break;
case ICMP6_DST_UNREACH_ADMIN:
pc = "Communication administratively prohibited";
break;
case ICMP6_DST_UNREACH_ADDR:
pc = "Address unreachable";
break;
case ICMP6_DST_UNREACH_NOPORT:
if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
sizeof (struct udphdr)) {
ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
switch (orig_ip6hdr->ip6_nxt) {
case IPPROTO_TCP: {
struct tcphdr *orig_thdr =
(struct tcphdr *)&orig_ip6hdr[1];
(void) sprintf(buff, "TCP port %hu"
" unreachable",
ntohs(orig_thdr->th_dport));
pc = buff;
break;
}
case IPPROTO_UDP: {
struct udphdr *orig_uhdr =
(struct udphdr *)&orig_ip6hdr[1];
(void) sprintf(buff, "UDP port %hu"
" unreachable",
ntohs(orig_uhdr->uh_dport));
pc = buff;
break;
}
default:
pc = "Port unreachable";
break;
}
} else {
pc = "Bad port";
}
break;
default:
break;
}
break;
case ICMP6_PACKET_TOO_BIG:
pt = "Packet too big";
break;
case ND_REDIRECT:
pt = "Redirect";
break;
case ICMP6_TIME_EXCEEDED:
pt = "Time exceeded";
switch (icmp6->icmp6_code) {
case ICMP6_TIME_EXCEED_TRANSIT:
pc = "Hop limit exceeded in transit";
break;
case ICMP6_TIME_EXCEED_REASSEMBLY:
pc = "Fragment reassembly time exceeded";
break;
default:
break;
}
break;
case ICMP6_PARAM_PROB:
pt = "Parameter problem";
switch (icmp6->icmp6_code) {
case ICMP6_PARAMPROB_HEADER:
pc = "Erroneous header field";
break;
case ICMP6_PARAMPROB_NEXTHEADER:
pc = "Unrecognized next header type";
break;
case ICMP6_PARAMPROB_OPTION:
pc = "Unrecognized IPv6 option";
break;
}
break;
case ICMP6_ECHO_REQUEST:
pt = "Echo request";
(void) sprintf(buff, "ID: %d Sequence number: %d",
ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
pc = buff;
break;
case ICMP6_ECHO_REPLY:
pt = "Echo reply";
(void) sprintf(buff, "ID: %d Sequence number: %d",
ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
pc = buff;
break;
case MLD_LISTENER_QUERY:
if (ilen == MLD_MINLEN)
pt = "Group membership query - MLDv1";
else if (ilen >= MLD_V2_QUERY_MINLEN)
pt = "Group membership query - MLDv2";
else
pt = "Unknown membership query";
break;
case MLD_LISTENER_REPORT:
pt = "Group membership report - MLDv1";
break;
case MLD_LISTENER_REDUCTION:
pt = "Group membership termination - MLDv1";
break;
case MLD_V2_LISTENER_REPORT:
pt = "Group membership report - MLDv2";
break;
case ND_ROUTER_SOLICIT:
pt = "Router solicitation";
break;
case ND_ROUTER_ADVERT:
pt = "Router advertisement";
break;
case ND_NEIGHBOR_SOLICIT:
pt = "Neighbor solicitation";
break;
case ND_NEIGHBOR_ADVERT:
pt = "Neighbor advertisement";
break;
default:
break;
}
if (flags & F_SUM) {
line = get_sum_line();
if (*pc)
(void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
else
(void) sprintf(line, "ICMPv6 %s", pt);
}
if (flags & F_DTAIL) {
show_header("ICMPv6: ", "ICMPv6 Header", ilen);
show_space();
(void) sprintf(get_line(0, 0), "Type = %d (%s)",
icmp6->icmp6_type, pt);
if (*pc)
(void) sprintf(get_line(0, 0), "Code = %d (%s)",
icmp6->icmp6_code, pc);
else
(void) sprintf(get_line(0, 0), "Code = %d",
icmp6->icmp6_code);
(void) sprintf(get_line(0, 0), "Checksum = %x",
ntohs(icmp6->icmp6_cksum));
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
show_space();
(void) sprintf(get_line(0, 0),
"[ subject header follows ]");
show_space();
prot_nest_prefix = "ICMPv6:";
(void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
ICMP6_MINLEN + IPV6_HDR_LEN);
prot_nest_prefix = "";
}
break;
case ICMP6_PACKET_TOO_BIG:
show_space();
(void) sprintf(get_line(0, 0),
" Packet too big MTU = %d",
ntohl(icmp6->icmp6_mtu));
show_space();
break;
case ND_REDIRECT: {
nd_redirect_t *rd = (nd_redirect_t *)icmp6;
(void) sprintf(get_line(0, 0), "Target address= %s",
inet_ntop(AF_INET6, (char *)&rd->nd_rd_target,
addrstr, INET6_ADDRSTRLEN));
(void) sprintf(get_line(0, 0),
"Destination address= %s",
inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst,
addrstr, INET6_ADDRSTRLEN));
show_space();
interpret_options((char *)icmp6 + sizeof (*rd),
ilen - sizeof (*rd));
break;
}
case ND_NEIGHBOR_SOLICIT: {
struct nd_neighbor_solicit *ns;
if (ilen < sizeof (*ns))
break;
ns = (struct nd_neighbor_solicit *)icmp6;
(void) sprintf(get_line(0, 0), "Target node = %s, %s",
inet_ntop(AF_INET6, (char *)&ns->nd_ns_target,
addrstr, INET6_ADDRSTRLEN),
addrtoname(AF_INET6, &ns->nd_ns_target));
show_space();
interpret_options((char *)icmp6 + sizeof (*ns),
ilen - sizeof (*ns));
break;
}
case ND_NEIGHBOR_ADVERT: {
struct nd_neighbor_advert *na;
if (ilen < sizeof (*na))
break;
na = (struct nd_neighbor_advert *)icmp6;
(void) sprintf(get_line(0, 0), "Target node = %s, %s",
inet_ntop(AF_INET6, (char *)&na->nd_na_target,
addrstr, INET6_ADDRSTRLEN),
addrtoname(AF_INET6, &na->nd_na_target));
(void) sprintf(get_line(0, 0),
"Router flag: %s, Solicited flag: %s, "
"Override flag: %s",
na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ?
"SET" : "NOT SET",
na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ?
"SET" : "NOT SET",
na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ?
"SET" : "NOT SET");
show_space();
interpret_options((char *)icmp6 + sizeof (*na),
ilen - sizeof (*na));
}
break;
case ND_ROUTER_SOLICIT: {
if (ilen < sizeof (struct nd_router_solicit))
break;
interpret_options(
(char *)icmp6 + sizeof (struct nd_router_solicit),
ilen - sizeof (struct nd_router_solicit));
break;
}
case ND_ROUTER_ADVERT: {
struct nd_router_advert *ra;
if (ilen < sizeof (*ra))
break;
ra = (struct nd_router_advert *)icmp6;
(void) sprintf(get_line(0, 0),
"Max hops= %d, Router lifetime= %d",
ra->nd_ra_curhoplimit,
ntohs(ra->nd_ra_router_lifetime));
(void) sprintf(get_line(0, 0),
"Managed addr conf flag: %s, Other conf flag: %s",
ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ?
"SET" : "NOT SET",
ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ?
"SET" : "NOT SET");
(void) sprintf(get_line(0, 0),
"Reachable time: %u, Reachable retrans time %u",
ntohl(ra->nd_ra_reachable),
ntohl(ra->nd_ra_retransmit));
show_space();
interpret_options((char *)icmp6 + sizeof (*ra),
ilen - sizeof (*ra));
break;
}
case ICMP6_PARAM_PROB:
if (ilen < sizeof (*icmp6))
break;
(void) sprintf(get_line(0, 0), "Ptr = %u",
ntohl(icmp6->icmp6_pptr));
show_space();
break;
case MLD_LISTENER_QUERY: {
struct mld_hdr *mldg = (struct mld_hdr *)icmp6;
if (ilen < MLD_MINLEN)
break;
if (ilen >= MLD_V2_QUERY_MINLEN) {
interpret_mldv2qry(icmp6, ilen);
} else {
(void) snprintf(get_line(0, 0),
get_line_remain(),
"Multicast address= %s",
inet_ntop(AF_INET6, mldg->mld_addr.s6_addr,
addrstr, INET6_ADDRSTRLEN));
}
show_space();
break;
}
case MLD_LISTENER_REPORT:
case MLD_LISTENER_REDUCTION: {
struct mld_hdr *mldg;
if (ilen < sizeof (*mldg))
break;
mldg = (struct mld_hdr *)icmp6;
(void) snprintf(get_line(0, 0), get_line_remain(),
"Multicast address= %s", inet_ntop(AF_INET6,
mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
show_space();
break;
}
case MLD_V2_LISTENER_REPORT: {
interpret_mldv2rpt(icmp6, ilen);
show_space();
break;
}
default:
break;
}
}
}
static void
interpret_options(optc, ilen)
char *optc;
int ilen;
{
#define PREFIX_OPTION_LENGTH 4
#define MTU_OPTION_LENGTH 1
#define PREFIX_INFINITY 0xffffffffUL
struct nd_opt_hdr *opt;
for (; ilen >= sizeof (*opt); ) {
opt = (struct nd_opt_hdr *)optc;
if (opt->nd_opt_len == 0)
return;
switch (opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_TARGET_LINKADDR:
{
struct nd_opt_lla *lopt;
char *buf, chbuf[128];
uint_t addr_len;
int i;
if (ilen < (int)opt->nd_opt_len * 8)
break;
buf = chbuf;
lopt = (struct nd_opt_lla *)opt;
if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
(void) sprintf(get_line(0, 0),
"+++ ICMPv6 Source LL Addr option +++");
} else {
(void) sprintf(get_line(0, 0),
"+++ ICMPv6 Target LL Addr option +++");
}
/*
* The option length is in 8 octet units, and
* includes the first two bytes (the type and
* lenght fields) of the option.
*/
addr_len = lopt->nd_opt_lla_len * 8 - 2;
for (i = 0; i < addr_len; i++) {
snprintf(buf, sizeof (chbuf) - (buf - chbuf),
"%x:", lopt->nd_opt_lla_hdw_addr[i]);
buf += strlen(buf);
if (buf >= &chbuf[sizeof (chbuf)]) {
buf = NULL;
chbuf[sizeof (chbuf) -
strlen("<Too Long>)")] = '\0';
(void) strlcat(chbuf, "<Too Long>",
sizeof (chbuf));
break;
}
}
if (buf)
*(buf - 1) = '\0'; /* Erase last colon */
(void) sprintf(get_line(0, 0),
"Link Layer address: %s", chbuf);
show_space();
break;
}
case ND_OPT_MTU: {
struct nd_opt_mtu *mopt;
if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
ilen < sizeof (struct nd_opt_mtu))
break;
(void) sprintf(get_line(0, 0),
"+++ ICMPv6 MTU option +++");
mopt = (struct nd_opt_mtu *)opt;
(void) sprintf(get_line(0, 0),
"MTU = %u ", ntohl(mopt->nd_opt_mtu_mtu));
show_space();
break;
}
case ND_OPT_PREFIX_INFORMATION: {
struct nd_opt_prefix_info *popt;
char validstr[30];
char preferredstr[30];
char prefixstr[INET6_ADDRSTRLEN];
if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
ilen < sizeof (struct nd_opt_prefix_info))
break;
popt = (struct nd_opt_prefix_info *)opt;
(void) sprintf(get_line(0, 0),
"+++ ICMPv6 Prefix option +++");
(void) sprintf(get_line(0, 0),
"Prefix length = %d ", popt->nd_opt_pi_prefix_len);
(void) sprintf(get_line(0, 0),
"Onlink flag: %s, Autonomous addr conf flag: %s",
popt->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
popt->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
if (ntohl(popt->nd_opt_pi_valid_time) ==
PREFIX_INFINITY)
sprintf(validstr, "INFINITY");
else
sprintf(validstr, "%lu",
ntohl(popt->nd_opt_pi_valid_time));
if (ntohl(popt->nd_opt_pi_preferred_time) ==
PREFIX_INFINITY)
sprintf(preferredstr, "INFINITY");
else
sprintf(preferredstr, "%lu",
ntohl(popt->nd_opt_pi_preferred_time));
(void) sprintf(get_line(0, 0),
"Valid Lifetime %s, Preferred Lifetime %s",
validstr, preferredstr);
(void) sprintf(get_line(0, 0), "Prefix %s",
inet_ntop(AF_INET6,
(char *)&popt->nd_opt_pi_prefix, prefixstr,
INET6_ADDRSTRLEN));
show_space();
}
default:
break;
}
optc += opt->nd_opt_len * 8;
ilen -= opt->nd_opt_len * 8;
}
}
static void
interpret_mldv2qry(icmp6_t *icmp6, int ilen)
{
mld2q_t *qry;
in6_addr_t *src;
int rem = ilen;
int srccnt;
char addrstr[INET6_ADDRSTRLEN];
if (ilen < sizeof (*qry)) {
(void) snprintf(get_line(0, 0), get_line_remain(),
"Malformed MLD Query");
return;
}
qry = (mld2q_t *)icmp6;
rem -= sizeof (*qry);
srccnt = ntohs(qry->mld2q_numsrc);
(void) snprintf(get_line(0, 0), get_line_remain(),
"Multicast address= %s", inet_ntop(AF_INET6,
&qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
(void) snprintf(get_line(0, 0), get_line_remain(),
"%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
src = (in6_addr_t *)&qry[1];
while (srccnt > 0 && rem >= sizeof (*src)) {
rem -= sizeof (*src);
(void) snprintf(get_line(0, 0), get_line_remain(), " %s",
inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
srccnt--;
src++;
}
}
#define MAX_MLDV2_REPORT_TYPE 6
const char *mldv2rpt_types[] = {
"<unknown>",
"MODE_IS_INCLUDE",
"MODE_IS_EXCLUDE",
"CHANGE_TO_INCLUDE",
"CHANGE_TO_EXCLUDE",
"ALLOW_NEW_SOURCES",
"BLOCK_OLD_SOURCES",
};
static void
interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
{
mld2r_t *rpt;
mld2mar_t *mar;
in6_addr_t *src;
int rem = ilen, auxlen;
uint16_t marcnt, srccnt;
char addrstr[INET6_ADDRSTRLEN];
if (ilen < sizeof (*rpt)) {
(void) snprintf(get_line(0, 0), get_line_remain(),
"Malformed MLDv2 Report");
return;
}
rpt = (mld2r_t *)icmp6;
mar = (mld2mar_t *)&rpt[1];
marcnt = ntohs(rpt->mld2r_nummar);
(void) snprintf(get_line(0, 0), get_line_remain(),
"%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
rem -= sizeof (*rpt);
while (marcnt > 0 && rem >= sizeof (*mar)) {
rem -= sizeof (*mar);
(void) snprintf(get_line(0, 0), get_line_remain(),
"Multicast address= %s type = %s", inet_ntop(AF_INET6,
&mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
(mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
"<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
srccnt = ntohs(mar->mld2mar_numsrc);
(void) snprintf(get_line(0, 0), get_line_remain(),
"%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
src = (in6_addr_t *)&mar[1];
while (srccnt > 0 && rem >= sizeof (*src)) {
rem -= sizeof (*src);
(void) snprintf(get_line(0, 0), get_line_remain(),
" %s", inet_ntop(AF_INET6, src, addrstr,
INET6_ADDRSTRLEN));
srccnt--;
src++;
}
marcnt--;
auxlen = mar->mld2mar_auxlen * 4;
rem -= auxlen;
mar = (mld2mar_t *)((uint8_t *)src + auxlen);
}
}