Home | History | Annotate | Download | only in snoop
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <string.h>
     28 #include <sys/types.h>
     29 #include <sys/socket.h>
     30 #include <net/if.h>
     31 #include <sys/stropts.h>
     32 #include <sys/sysmacros.h>
     33 #include <netinet/in_systm.h>
     34 #include <netinet/in.h>
     35 #include <netinet/ip.h>
     36 #include <netinet/ip_icmp.h>
     37 #include <netinet/udp.h>
     38 #include <netinet/tcp.h>
     39 #include <netinet/icmp6.h>
     40 #include <netinet/ip6.h>
     41 #include <inet/ip.h>
     42 #include <inet/ip6.h>
     43 #include <arpa/inet.h>
     44 #include <netdb.h>
     45 #include "snoop.h"
     46 #include "snoop_mip.h"
     47 
     48 static void interpret_options(char *, int);
     49 static void interpret_mldv2qry(icmp6_t *, int);
     50 static void interpret_mldv2rpt(icmp6_t *, int);
     51 
     52 
     53 /* Mobile-IP routines from snoop_mip.c */
     54 extern void interpret_icmp_mip_ext(uchar_t *, int);
     55 extern const char *get_mip_adv_desc(uint8_t);
     56 
     57 /* Router advertisement message structure. */
     58 struct icmp_ra_addr {
     59 	uint32_t addr;
     60 	uint32_t preference;
     61 };
     62 
     63 /*ARGSUSED*/
     64 void
     65 interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
     66 {
     67 	char *pt, *pc, *px;
     68 	char *line;
     69 	char buff[67627];	/* Router adv. can have 256 routers ....   */
     70 				/* Each router has a name 256 char long .. */
     71 	char extbuff[MAXHOSTNAMELEN + 1];
     72 	struct udphdr *orig_uhdr;
     73 	int num_rtr_addrs = 0;
     74 	extern char *prot_nest_prefix;
     75 
     76 	if (ilen < ICMP_MINLEN)
     77 		return;		/* incomplete header */
     78 
     79 	pt = "Unknown";
     80 	pc = "";
     81 	px = "";
     82 
     83 	switch (icmp->icmp_type) {
     84 	case ICMP_ECHOREPLY:
     85 		pt = "Echo reply";
     86 		(void) sprintf(buff, "ID: %d Sequence number: %d",
     87 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
     88 		pc = buff;
     89 		break;
     90 	case ICMP_UNREACH:
     91 		pt = "Destination unreachable";
     92 		switch (icmp->icmp_code) {
     93 		case ICMP_UNREACH_NET:
     94 			if (ilen >= ICMP_ADVLENMIN) {
     95 				(void) sprintf(buff, "Net %s unreachable",
     96 				    addrtoname(AF_INET,
     97 				    &icmp->icmp_ip.ip_dst));
     98 				pc = buff;
     99 			} else {
    100 				pc = "Bad net";
    101 			}
    102 			break;
    103 		case ICMP_UNREACH_HOST:
    104 			if (ilen >= ICMP_ADVLENMIN) {
    105 				(void) sprintf(buff, "Host %s unreachable",
    106 				    addrtoname(AF_INET,
    107 				    &icmp->icmp_ip.ip_dst));
    108 				pc = buff;
    109 			} else {
    110 				pc = "Bad host";
    111 			}
    112 			break;
    113 		case ICMP_UNREACH_PROTOCOL:
    114 			if (ilen >= ICMP_ADVLENMIN) {
    115 				(void) sprintf(buff, "Bad protocol %d",
    116 				    icmp->icmp_ip.ip_p);
    117 				pc = buff;
    118 			} else {
    119 				pc = "Bad protocol";
    120 			}
    121 			break;
    122 		case ICMP_UNREACH_PORT:
    123 			if (ilen >= ICMP_ADVLENMIN) {
    124 				orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
    125 				    ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
    126 				switch (icmp->icmp_ip.ip_p) {
    127 				case IPPROTO_TCP:
    128 					(void) sprintf(buff, "TCP port %d"
    129 					    " unreachable",
    130 					    ntohs(orig_uhdr->uh_dport));
    131 					pc = buff;
    132 					break;
    133 				case IPPROTO_UDP:
    134 					(void) sprintf(buff, "UDP port %d"
    135 					    " unreachable",
    136 					    ntohs(orig_uhdr->uh_dport));
    137 					pc = buff;
    138 					break;
    139 				default:
    140 					pc = "Port unreachable";
    141 					break;
    142 				}
    143 			} else {
    144 				pc = "Bad port";
    145 			}
    146 			break;
    147 		case ICMP_UNREACH_NEEDFRAG:
    148 			if (ntohs(icmp->icmp_nextmtu) != 0) {
    149 				(void) sprintf(buff, "Needed to fragment:"
    150 				    " next hop MTU = %d",
    151 				    ntohs(icmp->icmp_nextmtu));
    152 				pc = buff;
    153 			} else {
    154 				pc = "Needed to fragment";
    155 			}
    156 			break;
    157 		case ICMP_UNREACH_SRCFAIL:
    158 			pc = "Source route failed";
    159 			break;
    160 		case ICMP_UNREACH_NET_UNKNOWN:
    161 			pc = "Unknown network";
    162 			break;
    163 		case ICMP_UNREACH_HOST_UNKNOWN:
    164 			pc = "Unknown host";
    165 			break;
    166 		case ICMP_UNREACH_ISOLATED:
    167 			pc = "Source host isolated";
    168 			break;
    169 		case ICMP_UNREACH_NET_PROHIB:
    170 			pc = "Net administratively prohibited";
    171 			break;
    172 		case ICMP_UNREACH_HOST_PROHIB:
    173 			pc = "Host administratively prohibited";
    174 			break;
    175 		case ICMP_UNREACH_TOSNET:
    176 			pc = "Net unreachable for this TOS";
    177 			break;
    178 		case ICMP_UNREACH_TOSHOST:
    179 			pc = "Host unreachable for this TOS";
    180 			break;
    181 		case ICMP_UNREACH_FILTER_PROHIB:
    182 			pc = "Communication administratively prohibited";
    183 			break;
    184 		case ICMP_UNREACH_HOST_PRECEDENCE:
    185 			pc = "Host precedence violation";
    186 			break;
    187 		case ICMP_UNREACH_PRECEDENCE_CUTOFF:
    188 			pc = "Precedence cutoff in effect";
    189 			break;
    190 		default:
    191 			break;
    192 		}
    193 		break;
    194 	case ICMP_SOURCEQUENCH:
    195 		pt = "Packet lost, slow down";
    196 		break;
    197 	case ICMP_REDIRECT:
    198 		pt = "Redirect";
    199 		switch (icmp->icmp_code) {
    200 		case ICMP_REDIRECT_NET:
    201 			pc = "for network";
    202 			break;
    203 		case ICMP_REDIRECT_HOST:
    204 			pc = "for host";
    205 			break;
    206 		case ICMP_REDIRECT_TOSNET:
    207 			pc = "for tos and net";
    208 			break;
    209 		case ICMP_REDIRECT_TOSHOST:
    210 			pc = "for tos and host";
    211 			break;
    212 		default:
    213 			break;
    214 		}
    215 		(void) sprintf(buff, "%s %s to %s",
    216 			pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
    217 			addrtoname(AF_INET, &icmp->icmp_gwaddr));
    218 		pc = buff;
    219 		break;
    220 	case ICMP_ECHO:
    221 		pt = "Echo request";
    222 		(void) sprintf(buff, "ID: %d Sequence number: %d",
    223 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
    224 		pc = buff;
    225 		break;
    226 	case ICMP_ROUTERADVERT:
    227 
    228 #define	icmp_num_addrs	icmp_hun.ih_rtradv.irt_num_addrs
    229 #define	icmp_wpa	icmp_hun.ih_rtradv.irt_wpa
    230 #define	icmp_lifetime	icmp_hun.ih_rtradv.irt_lifetime
    231 
    232 		pt = "Router advertisement";
    233 		(void) sprintf(buff, "Lifetime %ds [%d]:",
    234 		    ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
    235 		if (icmp->icmp_wpa == 2) {
    236 			struct icmp_ra_addr *ra;
    237 			char ra_buf[MAXHOSTNAMELEN + 32];
    238 			char ra_ext_buf[50];
    239 			struct in_addr sin;
    240 			int icmp_ra_len;
    241 			int i;
    242 
    243 			/* Cannot trust anything from the network... */
    244 			num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
    245 			    icmp->icmp_num_addrs);
    246 
    247 			ra = (struct icmp_ra_addr *)icmp->icmp_data;
    248 			for (i = 0; i < num_rtr_addrs; i++) {
    249 				sin.s_addr = ra->addr;
    250 				(void) snprintf(ra_buf, sizeof (ra_buf),
    251 				    " {%s %u}",
    252 				    addrtoname(AF_INET, &sin),
    253 				    ntohl(ra->preference));
    254 				if (strlcat(buff, ra_buf, sizeof (buff)) >=
    255 					sizeof (buff)) {
    256 					buff[sizeof (buff) -
    257 					    strlen("<Too Long>)")] = '\0';
    258 					(void) strlcat(buff, "<Too Long>",
    259 						sizeof (buff));
    260 					break;
    261 				}
    262 				ra++;
    263 			}
    264 
    265 			icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
    266 			    sizeof (struct icmp_ra_addr);
    267 			if (ilen > icmp_ra_len) {
    268 				int curr_len = ilen - icmp_ra_len;
    269 				int ocurr_len;
    270 				exthdr_t *exthdr = (exthdr_t *)ra;
    271 
    272 				extbuff[0] = '\0';
    273 
    274 				while (curr_len > 0) {
    275 				    /* Append Mobile-IP description */
    276 				    (void) snprintf(ra_ext_buf,
    277 					sizeof (ra_ext_buf), ", %s",
    278 					get_mip_adv_desc(exthdr->type));
    279 				    (void) strlcat(extbuff, ra_ext_buf,
    280 					sizeof (extbuff));
    281 
    282 				    /* Special case for padding */
    283 				    if (exthdr->type ==
    284 					ICMP_ADV_MSG_PADDING_EXT) {
    285 
    286 					curr_len--;
    287 					exthdr = (exthdr_t *)
    288 						((char *)exthdr + 1);
    289 					continue;
    290 				    }
    291 
    292 				    /* else normal extension */
    293 				    ocurr_len = curr_len;
    294 				    curr_len -= sizeof (*exthdr) +
    295 							exthdr->length;
    296 				    /* detect bad length */
    297 				    if (ocurr_len < curr_len)
    298 						break;
    299 				    exthdr = (exthdr_t *)
    300 						((char *)exthdr +
    301 						sizeof (*exthdr) +
    302 						exthdr->length);
    303 				}
    304 				px = extbuff;
    305 			}
    306 			pc = buff;
    307 		}
    308 		break;
    309 	case ICMP_ROUTERSOLICIT:
    310 		pt = "Router solicitation";
    311 		break;
    312 	case ICMP_TIMXCEED:
    313 		pt = "Time exceeded";
    314 		switch (icmp->icmp_code) {
    315 		case ICMP_TIMXCEED_INTRANS:
    316 			pc = "in transit";
    317 			break;
    318 		case ICMP_TIMXCEED_REASS:
    319 			pc = "in reassembly";
    320 			break;
    321 		default:
    322 			break;
    323 		}
    324 		break;
    325 	case ICMP_PARAMPROB:
    326 		pt = "IP parameter problem";
    327 		switch (icmp->icmp_code) {
    328 		case ICMP_PARAMPROB_OPTABSENT:
    329 			pc = "Required option missing";
    330 			break;
    331 		case ICMP_PARAMPROB_BADLENGTH:
    332 			pc = "Bad length";
    333 			break;
    334 		case 0: /* Should this be the default? */
    335 			(void) sprintf(buff, "Problem at octet %d\n",
    336 			    icmp->icmp_pptr);
    337 			pc = buff;
    338 		default:
    339 			break;
    340 		}
    341 		break;
    342 	case ICMP_TSTAMP:
    343 		pt = "Timestamp request";
    344 		break;
    345 	case ICMP_TSTAMPREPLY:
    346 		pt = "Timestamp reply";
    347 		break;
    348 	case ICMP_IREQ:
    349 		pt = "Information request";
    350 		break;
    351 	case ICMP_IREQREPLY:
    352 		pt = "Information reply";
    353 		break;
    354 	case ICMP_MASKREQ:
    355 		pt = "Address mask request";
    356 		break;
    357 	case ICMP_MASKREPLY:
    358 		pt = "Address mask reply";
    359 		(void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
    360 		pc = buff;
    361 		break;
    362 	default:
    363 		break;
    364 	}
    365 
    366 	if (flags & F_SUM) {
    367 		line = get_sum_line();
    368 		if (*pc) {
    369 			if (*px) {
    370 				(void) sprintf(line, "ICMP %s (%s)%s",
    371 				    pt, pc, px);
    372 			} else {
    373 				(void) sprintf(line, "ICMP %s (%s)", pt, pc);
    374 			}
    375 		} else {
    376 			(void) sprintf(line, "ICMP %s", pt);
    377 		}
    378 	}
    379 
    380 	if (flags & F_DTAIL) {
    381 		show_header("ICMP:  ", "ICMP Header", ilen);
    382 		show_space();
    383 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
    384 		    icmp->icmp_type, pt);
    385 		if (*pc) {
    386 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
    387 			    icmp->icmp_code, pc);
    388 		} else {
    389 			(void) sprintf(get_line(0, 0), "Code = %d",
    390 			    icmp->icmp_code);
    391 		}
    392 		(void) sprintf(get_line(0, 0), "Checksum = %x",
    393 		    ntohs(icmp->icmp_cksum));
    394 
    395 		if (icmp->icmp_type == ICMP_UNREACH ||
    396 		    icmp->icmp_type == ICMP_REDIRECT) {
    397 			if (ilen > 28) {
    398 				show_space();
    399 				(void) sprintf(get_line(0, 0),
    400 				    "[ subject header follows ]");
    401 				show_space();
    402 				prot_nest_prefix = "ICMP:";
    403 				(void) interpret_ip(flags,
    404 				    (struct ip *)icmp->icmp_data, 28);
    405 				prot_nest_prefix = "";
    406 			}
    407 		} else if (icmp->icmp_type == ICMP_PARAMPROB) {
    408 			if (ilen > 28) {
    409 				show_space();
    410 				(void) sprintf(get_line(0, 0),
    411 				    "[ subject header follows ]");
    412 				show_space();
    413 				prot_nest_prefix = "ICMP:";
    414 				(void) interpret_ip(flags,
    415 				    (struct ip *)icmp->icmp_data, 28);
    416 				prot_nest_prefix = "";
    417 			}
    418 		} else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
    419 			if (icmp->icmp_wpa == 2) {
    420 				int icmp_ra_len;
    421 
    422 				show_space();
    423 				icmp_ra_len = ICMP_MINLEN +
    424 				    num_rtr_addrs *
    425 					sizeof (struct icmp_ra_addr);
    426 				prot_nest_prefix = "";
    427 				if (ilen > icmp_ra_len) {
    428 					interpret_icmp_mip_ext(
    429 					    (uchar_t *)icmp + icmp_ra_len,
    430 					    ilen - icmp_ra_len);
    431 				}
    432 			}
    433 		}
    434 		show_space();
    435 	}
    436 }
    437 
    438 /*ARGSUSED*/
    439 void
    440 interpret_icmpv6(flags, icmp6, iplen, ilen)
    441 	int flags;
    442 	icmp6_t *icmp6;
    443 	int iplen, ilen;
    444 {
    445 	char *pt, *pc;
    446 	char *line;
    447 	extern char *prot_nest_prefix;
    448 	char addrstr[INET6_ADDRSTRLEN];
    449 	char buff[2048];
    450 
    451 	if (ilen < ICMP6_MINLEN)
    452 		return;		/* incomplete header */
    453 
    454 	pt = "Unknown";
    455 	pc = "";
    456 
    457 	switch (icmp6->icmp6_type) {
    458 	case ICMP6_DST_UNREACH:
    459 		pt = "Destination unreachable";
    460 		switch (icmp6->icmp6_code) {
    461 		case ICMP6_DST_UNREACH_NOROUTE:
    462 			pc = "No route to destination";
    463 			break;
    464 		case ICMP6_DST_UNREACH_ADMIN:
    465 			pc = "Communication administratively prohibited";
    466 			break;
    467 		case ICMP6_DST_UNREACH_ADDR:
    468 			pc = "Address unreachable";
    469 			break;
    470 		case ICMP6_DST_UNREACH_NOPORT:
    471 			if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
    472 				sizeof (struct udphdr)) {
    473 
    474 				ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
    475 
    476 				switch (orig_ip6hdr->ip6_nxt) {
    477 				case IPPROTO_TCP: {
    478 					struct tcphdr *orig_thdr =
    479 					    (struct tcphdr *)&orig_ip6hdr[1];
    480 
    481 					(void) sprintf(buff, "TCP port %hu"
    482 					    " unreachable",
    483 					    ntohs(orig_thdr->th_dport));
    484 					pc = buff;
    485 					break;
    486 				    }
    487 				case IPPROTO_UDP: {
    488 					struct udphdr *orig_uhdr =
    489 					    (struct udphdr *)&orig_ip6hdr[1];
    490 
    491 					(void) sprintf(buff, "UDP port %hu"
    492 					    " unreachable",
    493 					    ntohs(orig_uhdr->uh_dport));
    494 					pc = buff;
    495 					break;
    496 				    }
    497 				default:
    498 					pc = "Port unreachable";
    499 					break;
    500 				}
    501 			} else {
    502 				pc = "Bad port";
    503 			}
    504 			break;
    505 		default:
    506 			break;
    507 		}
    508 		break;
    509 	case ICMP6_PACKET_TOO_BIG:
    510 		pt = "Packet too big";
    511 		break;
    512 	case ND_REDIRECT:
    513 		pt = "Redirect";
    514 		break;
    515 	case ICMP6_TIME_EXCEEDED:
    516 		pt = "Time exceeded";
    517 		switch (icmp6->icmp6_code) {
    518 		case ICMP6_TIME_EXCEED_TRANSIT:
    519 			pc = "Hop limit exceeded in transit";
    520 			break;
    521 		case ICMP6_TIME_EXCEED_REASSEMBLY:
    522 			pc = "Fragment reassembly time exceeded";
    523 			break;
    524 		default:
    525 			break;
    526 		}
    527 		break;
    528 	case ICMP6_PARAM_PROB:
    529 		pt = "Parameter problem";
    530 		switch (icmp6->icmp6_code) {
    531 		case ICMP6_PARAMPROB_HEADER:
    532 			pc = "Erroneous header field";
    533 			break;
    534 		case ICMP6_PARAMPROB_NEXTHEADER:
    535 			pc = "Unrecognized next header type";
    536 			break;
    537 		case ICMP6_PARAMPROB_OPTION:
    538 			pc = "Unrecognized IPv6 option";
    539 			break;
    540 		}
    541 		break;
    542 	case ICMP6_ECHO_REQUEST:
    543 		pt = "Echo request";
    544 		(void) sprintf(buff, "ID: %d Sequence number: %d",
    545 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
    546 		pc = buff;
    547 		break;
    548 	case ICMP6_ECHO_REPLY:
    549 		pt = "Echo reply";
    550 		(void) sprintf(buff, "ID: %d Sequence number: %d",
    551 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
    552 		pc = buff;
    553 		break;
    554 	case MLD_LISTENER_QUERY:
    555 		if (ilen == MLD_MINLEN)
    556 			pt = "Group membership query - MLDv1";
    557 		else if (ilen >= MLD_V2_QUERY_MINLEN)
    558 			pt = "Group membership query - MLDv2";
    559 		else
    560 			pt = "Unknown membership query";
    561 		break;
    562 	case MLD_LISTENER_REPORT:
    563 		pt = "Group membership report - MLDv1";
    564 		break;
    565 	case MLD_LISTENER_REDUCTION:
    566 		pt = "Group membership termination - MLDv1";
    567 		break;
    568 	case MLD_V2_LISTENER_REPORT:
    569 		pt = "Group membership report - MLDv2";
    570 		break;
    571 	case ND_ROUTER_SOLICIT:
    572 		pt = "Router solicitation";
    573 		break;
    574 	case ND_ROUTER_ADVERT:
    575 		pt = "Router advertisement";
    576 		break;
    577 	case ND_NEIGHBOR_SOLICIT:
    578 		pt = "Neighbor solicitation";
    579 		break;
    580 	case ND_NEIGHBOR_ADVERT:
    581 		pt = "Neighbor advertisement";
    582 		break;
    583 	default:
    584 		break;
    585 	}
    586 
    587 	if (flags & F_SUM) {
    588 		line = get_sum_line();
    589 		if (*pc)
    590 			(void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
    591 		else
    592 			(void) sprintf(line, "ICMPv6 %s", pt);
    593 	}
    594 
    595 	if (flags & F_DTAIL) {
    596 		show_header("ICMPv6:  ", "ICMPv6 Header", ilen);
    597 		show_space();
    598 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
    599 		    icmp6->icmp6_type, pt);
    600 		if (*pc)
    601 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
    602 			    icmp6->icmp6_code, pc);
    603 		else
    604 			(void) sprintf(get_line(0, 0), "Code = %d",
    605 			    icmp6->icmp6_code);
    606 		(void) sprintf(get_line(0, 0), "Checksum = %x",
    607 		    ntohs(icmp6->icmp6_cksum));
    608 
    609 		switch (icmp6->icmp6_type) {
    610 		case ICMP6_DST_UNREACH:
    611 			if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
    612 				show_space();
    613 				(void) sprintf(get_line(0, 0),
    614 				    "[ subject header follows ]");
    615 				show_space();
    616 				prot_nest_prefix = "ICMPv6:";
    617 				(void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
    618 				    ICMP6_MINLEN + IPV6_HDR_LEN);
    619 				prot_nest_prefix = "";
    620 			}
    621 			break;
    622 		case ICMP6_PACKET_TOO_BIG:
    623 			show_space();
    624 			(void) sprintf(get_line(0, 0),
    625 			    " Packet too big MTU = %d",
    626 			    ntohl(icmp6->icmp6_mtu));
    627 			show_space();
    628 			break;
    629 		case ND_REDIRECT: {
    630 			nd_redirect_t *rd = (nd_redirect_t *)icmp6;
    631 
    632 			(void) sprintf(get_line(0, 0), "Target address= %s",
    633 			    inet_ntop(AF_INET6, (char *)&rd->nd_rd_target,
    634 			    addrstr, INET6_ADDRSTRLEN));
    635 
    636 			(void) sprintf(get_line(0, 0),
    637 			    "Destination address= %s",
    638 			    inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst,
    639 			    addrstr, INET6_ADDRSTRLEN));
    640 			show_space();
    641 			interpret_options((char *)icmp6 + sizeof (*rd),
    642 			    ilen - sizeof (*rd));
    643 			break;
    644 		}
    645 		case ND_NEIGHBOR_SOLICIT: {
    646 			struct nd_neighbor_solicit *ns;
    647 			if (ilen < sizeof (*ns))
    648 				break;
    649 			ns = (struct nd_neighbor_solicit *)icmp6;
    650 			(void) sprintf(get_line(0, 0), "Target node = %s, %s",
    651 			    inet_ntop(AF_INET6, (char *)&ns->nd_ns_target,
    652 			    addrstr, INET6_ADDRSTRLEN),
    653 			    addrtoname(AF_INET6, &ns->nd_ns_target));
    654 			show_space();
    655 			interpret_options((char *)icmp6 + sizeof (*ns),
    656 			    ilen - sizeof (*ns));
    657 			break;
    658 		}
    659 
    660 		case ND_NEIGHBOR_ADVERT: {
    661 			struct nd_neighbor_advert *na;
    662 
    663 			if (ilen < sizeof (*na))
    664 				break;
    665 			na = (struct nd_neighbor_advert *)icmp6;
    666 			(void) sprintf(get_line(0, 0), "Target node = %s, %s",
    667 			    inet_ntop(AF_INET6, (char *)&na->nd_na_target,
    668 			    addrstr, INET6_ADDRSTRLEN),
    669 			    addrtoname(AF_INET6, &na->nd_na_target));
    670 			(void) sprintf(get_line(0, 0),
    671 			    "Router flag: %s, Solicited flag: %s, "
    672 			    "Override flag: %s",
    673 			    na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ?
    674 			    "SET" : "NOT SET",
    675 			    na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ?
    676 			    "SET" : "NOT SET",
    677 			    na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ?
    678 			    "SET" : "NOT SET");
    679 
    680 			show_space();
    681 			interpret_options((char *)icmp6 + sizeof (*na),
    682 			    ilen - sizeof (*na));
    683 		}
    684 		break;
    685 
    686 		case ND_ROUTER_SOLICIT: {
    687 			if (ilen < sizeof (struct nd_router_solicit))
    688 				break;
    689 			interpret_options(
    690 			    (char *)icmp6 + sizeof (struct nd_router_solicit),
    691 			    ilen - sizeof (struct nd_router_solicit));
    692 			break;
    693 		}
    694 
    695 		case ND_ROUTER_ADVERT: {
    696 			struct nd_router_advert *ra;
    697 
    698 			if (ilen < sizeof (*ra))
    699 				break;
    700 			ra = (struct nd_router_advert *)icmp6;
    701 			(void) sprintf(get_line(0, 0),
    702 			    "Max hops= %d, Router lifetime= %d",
    703 			    ra->nd_ra_curhoplimit,
    704 			    ntohs(ra->nd_ra_router_lifetime));
    705 
    706 			(void) sprintf(get_line(0, 0),
    707 			    "Managed addr conf flag: %s, Other conf flag: %s",
    708 			    ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ?
    709 			    "SET" : "NOT SET",
    710 			    ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ?
    711 			    "SET" : "NOT SET");
    712 
    713 			(void) sprintf(get_line(0, 0),
    714 			    "Reachable time: %u, Reachable retrans time %u",
    715 			    ntohl(ra->nd_ra_reachable),
    716 			    ntohl(ra->nd_ra_retransmit));
    717 			show_space();
    718 
    719 			interpret_options((char *)icmp6 + sizeof (*ra),
    720 			    ilen - sizeof (*ra));
    721 			break;
    722 		}
    723 		case ICMP6_PARAM_PROB:
    724 			if (ilen < sizeof (*icmp6))
    725 				break;
    726 			(void) sprintf(get_line(0, 0), "Ptr = %u",
    727 			    ntohl(icmp6->icmp6_pptr));
    728 			show_space();
    729 			break;
    730 
    731 		case MLD_LISTENER_QUERY: {
    732 			struct mld_hdr *mldg = (struct mld_hdr *)icmp6;
    733 
    734 			if (ilen < MLD_MINLEN)
    735 				break;
    736 
    737 			if (ilen >= MLD_V2_QUERY_MINLEN) {
    738 				interpret_mldv2qry(icmp6, ilen);
    739 			} else {
    740 				(void) snprintf(get_line(0, 0),
    741 				    get_line_remain(),
    742 				    "Multicast address= %s",
    743 				    inet_ntop(AF_INET6, mldg->mld_addr.s6_addr,
    744 				    addrstr, INET6_ADDRSTRLEN));
    745 			}
    746 			show_space();
    747 			break;
    748 		}
    749 
    750 		case MLD_LISTENER_REPORT:
    751 		case MLD_LISTENER_REDUCTION: {
    752 			struct mld_hdr *mldg;
    753 
    754 			if (ilen < sizeof (*mldg))
    755 				break;
    756 			mldg = (struct mld_hdr *)icmp6;
    757 			(void) snprintf(get_line(0, 0), get_line_remain(),
    758 			    "Multicast address= %s", inet_ntop(AF_INET6,
    759 			    mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
    760 			show_space();
    761 			break;
    762 		}
    763 
    764 		case MLD_V2_LISTENER_REPORT: {
    765 			interpret_mldv2rpt(icmp6, ilen);
    766 			show_space();
    767 			break;
    768 		}
    769 
    770 		default:
    771 			break;
    772 		}
    773 	}
    774 }
    775 
    776 static void
    777 interpret_options(optc, ilen)
    778 	char *optc;
    779 	int ilen;
    780 {
    781 #define	PREFIX_OPTION_LENGTH    4
    782 #define	MTU_OPTION_LENGTH	1
    783 
    784 #define	PREFIX_INFINITY		0xffffffffUL
    785 
    786 	struct nd_opt_hdr *opt;
    787 
    788 	for (; ilen >= sizeof (*opt); ) {
    789 		opt = (struct nd_opt_hdr *)optc;
    790 		if (opt->nd_opt_len == 0)
    791 			return;
    792 		switch (opt->nd_opt_type) {
    793 		case ND_OPT_SOURCE_LINKADDR:
    794 		case ND_OPT_TARGET_LINKADDR:
    795 		{
    796 			struct nd_opt_lla *lopt;
    797 			char	*buf, chbuf[128];
    798 			uint_t	addr_len;
    799 			int	i;
    800 
    801 			if (ilen < (int)opt->nd_opt_len * 8)
    802 				break;
    803 
    804 			buf = chbuf;
    805 
    806 			lopt = (struct nd_opt_lla *)opt;
    807 			if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
    808 				(void) sprintf(get_line(0, 0),
    809 				    "+++ ICMPv6 Source LL Addr option +++");
    810 			} else {
    811 				(void) sprintf(get_line(0, 0),
    812 				    "+++ ICMPv6 Target LL Addr option +++");
    813 			}
    814 
    815 			/*
    816 			 * The option length is in 8 octet units, and
    817 			 * includes the first two bytes (the type and
    818 			 * lenght fields) of the option.
    819 			 */
    820 			addr_len = lopt->nd_opt_lla_len * 8 - 2;
    821 			for (i = 0; i < addr_len; i++) {
    822 				snprintf(buf, sizeof (chbuf) - (buf - chbuf),
    823 				    "%x:", lopt->nd_opt_lla_hdw_addr[i]);
    824 				buf += strlen(buf);
    825 				if (buf >= &chbuf[sizeof (chbuf)]) {
    826 					buf = NULL;
    827 					chbuf[sizeof (chbuf) -
    828 					    strlen("<Too Long>)")] = '\0';
    829 					(void) strlcat(chbuf, "<Too Long>",
    830 						sizeof (chbuf));
    831 					break;
    832 				}
    833 			}
    834 			if (buf)
    835 				*(buf - 1) = '\0'; /* Erase last colon */
    836 			(void) sprintf(get_line(0, 0),
    837 			    "Link Layer address: %s", chbuf);
    838 			show_space();
    839 			break;
    840 		}
    841 		case ND_OPT_MTU: {
    842 			struct nd_opt_mtu *mopt;
    843 			if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
    844 			    ilen < sizeof (struct nd_opt_mtu))
    845 				break;
    846 			(void) sprintf(get_line(0, 0),
    847 			    "+++ ICMPv6 MTU option +++");
    848 			mopt = (struct nd_opt_mtu *)opt;
    849 			(void) sprintf(get_line(0, 0),
    850 			    "MTU = %u ", ntohl(mopt->nd_opt_mtu_mtu));
    851 			show_space();
    852 			break;
    853 		}
    854 		case ND_OPT_PREFIX_INFORMATION: {
    855 			struct nd_opt_prefix_info *popt;
    856 			char validstr[30];
    857 			char preferredstr[30];
    858 			char prefixstr[INET6_ADDRSTRLEN];
    859 
    860 			if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
    861 			    ilen < sizeof (struct nd_opt_prefix_info))
    862 				break;
    863 			popt = (struct nd_opt_prefix_info *)opt;
    864 			(void) sprintf(get_line(0, 0),
    865 			    "+++ ICMPv6 Prefix option +++");
    866 			(void) sprintf(get_line(0, 0),
    867 			    "Prefix length = %d ", popt->nd_opt_pi_prefix_len);
    868 			(void) sprintf(get_line(0, 0),
    869 			    "Onlink flag: %s, Autonomous addr conf flag: %s",
    870 			    popt->nd_opt_pi_flags_reserved &
    871 			    ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
    872 			    popt->nd_opt_pi_flags_reserved &
    873 			    ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
    874 
    875 			if (ntohl(popt->nd_opt_pi_valid_time) ==
    876 			    PREFIX_INFINITY)
    877 				sprintf(validstr, "INFINITY");
    878 			else
    879 				sprintf(validstr, "%lu",
    880 				    ntohl(popt->nd_opt_pi_valid_time));
    881 
    882 			if (ntohl(popt->nd_opt_pi_preferred_time) ==
    883 			    PREFIX_INFINITY)
    884 				sprintf(preferredstr, "INFINITY");
    885 			else
    886 				sprintf(preferredstr, "%lu",
    887 				    ntohl(popt->nd_opt_pi_preferred_time));
    888 
    889 			(void) sprintf(get_line(0, 0),
    890 			    "Valid Lifetime %s, Preferred Lifetime %s",
    891 			    validstr, preferredstr);
    892 			(void) sprintf(get_line(0, 0), "Prefix %s",
    893 			    inet_ntop(AF_INET6,
    894 			    (char *)&popt->nd_opt_pi_prefix, prefixstr,
    895 			    INET6_ADDRSTRLEN));
    896 			show_space();
    897 		}
    898 		default:
    899 			break;
    900 		}
    901 		optc += opt->nd_opt_len * 8;
    902 		ilen -= opt->nd_opt_len * 8;
    903 	}
    904 }
    905 
    906 static void
    907 interpret_mldv2qry(icmp6_t *icmp6, int ilen)
    908 {
    909 	mld2q_t *qry;
    910 	in6_addr_t *src;
    911 	int rem = ilen;
    912 	int srccnt;
    913 	char addrstr[INET6_ADDRSTRLEN];
    914 
    915 	if (ilen < sizeof (*qry)) {
    916 		(void) snprintf(get_line(0, 0), get_line_remain(),
    917 		    "Malformed MLD Query");
    918 		return;
    919 	}
    920 	qry = (mld2q_t *)icmp6;
    921 	rem -= sizeof (*qry);
    922 	srccnt = ntohs(qry->mld2q_numsrc);
    923 	(void) snprintf(get_line(0, 0), get_line_remain(),
    924 	    "Multicast address= %s", inet_ntop(AF_INET6,
    925 	    &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
    926 	(void) snprintf(get_line(0, 0), get_line_remain(),
    927 	    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
    928 
    929 	src = (in6_addr_t *)&qry[1];
    930 	while (srccnt > 0 && rem >= sizeof (*src)) {
    931 		rem -= sizeof (*src);
    932 
    933 		(void) snprintf(get_line(0, 0), get_line_remain(), "    %s",
    934 		    inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
    935 
    936 		srccnt--;
    937 		src++;
    938 	}
    939 }
    940 
    941 #define	MAX_MLDV2_REPORT_TYPE	6
    942 
    943 const char *mldv2rpt_types[] = {
    944 	"<unknown>",
    945 	"MODE_IS_INCLUDE",
    946 	"MODE_IS_EXCLUDE",
    947 	"CHANGE_TO_INCLUDE",
    948 	"CHANGE_TO_EXCLUDE",
    949 	"ALLOW_NEW_SOURCES",
    950 	"BLOCK_OLD_SOURCES",
    951 };
    952 
    953 static void
    954 interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
    955 {
    956 	mld2r_t *rpt;
    957 	mld2mar_t *mar;
    958 	in6_addr_t *src;
    959 	int rem = ilen, auxlen;
    960 	uint16_t marcnt, srccnt;
    961 	char addrstr[INET6_ADDRSTRLEN];
    962 
    963 	if (ilen < sizeof (*rpt)) {
    964 		(void) snprintf(get_line(0, 0), get_line_remain(),
    965 		    "Malformed MLDv2 Report");
    966 		return;
    967 	}
    968 	rpt = (mld2r_t *)icmp6;
    969 	mar = (mld2mar_t *)&rpt[1];
    970 	marcnt = ntohs(rpt->mld2r_nummar);
    971 	(void) snprintf(get_line(0, 0), get_line_remain(),
    972 	    "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
    973 	rem -= sizeof (*rpt);
    974 	while (marcnt > 0 && rem >= sizeof (*mar)) {
    975 		rem -= sizeof (*mar);
    976 
    977 		(void) snprintf(get_line(0, 0), get_line_remain(),
    978 		    "Multicast address= %s  type = %s", inet_ntop(AF_INET6,
    979 		    &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
    980 		    (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
    981 		    "<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
    982 		srccnt = ntohs(mar->mld2mar_numsrc);
    983 		(void) snprintf(get_line(0, 0), get_line_remain(),
    984 		    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
    985 
    986 		src = (in6_addr_t *)&mar[1];
    987 		while (srccnt > 0 && rem >= sizeof (*src)) {
    988 			rem -= sizeof (*src);
    989 
    990 			(void) snprintf(get_line(0, 0), get_line_remain(),
    991 			    "    %s", inet_ntop(AF_INET6, src, addrstr,
    992 			    INET6_ADDRSTRLEN));
    993 
    994 			srccnt--;
    995 			src++;
    996 		}
    997 
    998 		marcnt--;
    999 		auxlen = mar->mld2mar_auxlen * 4;
   1000 		rem -= auxlen;
   1001 		mar = (mld2mar_t *)((uint8_t *)src + auxlen);
   1002 	}
   1003 }
   1004