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