xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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