xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34 #include <sys/stropts.h>
35 #include <sys/sysmacros.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #include <netinet/ip.h>
39 #include <netinet/ip_icmp.h>
40 #include <netinet/udp.h>
41 #include <netinet/tcp.h>
42 #include <netinet/icmp6.h>
43 #include <netinet/ip6.h>
44 #include <inet/ip.h>
45 #include <inet/ip6.h>
46 #include <arpa/inet.h>
47 #include <netdb.h>
48 #include "snoop.h"
49 #include "snoop_mip.h"
50 
51 static void interpret_options(char *, int);
52 static void interpret_mldv2qry(icmp6_t *, int);
53 static void interpret_mldv2rpt(icmp6_t *, int);
54 
55 
56 /* Mobile-IP routines from snoop_mip.c */
57 extern void interpret_icmp_mip_ext(uchar_t *, int);
58 extern const char *get_mip_adv_desc(uint8_t);
59 
60 /* Router advertisement message structure. */
61 struct icmp_ra_addr {
62 	uint32_t addr;
63 	uint32_t preference;
64 };
65 
66 /*ARGSUSED*/
67 void
68 interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
69 {
70 	char *pt, *pc, *px;
71 	char *line;
72 	char buff[67627];	/* Router adv. can have 256 routers ....   */
73 				/* Each router has a name 256 char long .. */
74 	char extbuff[MAXHOSTNAMELEN + 1];
75 	struct udphdr *orig_uhdr;
76 	int num_rtr_addrs = 0;
77 	extern char *prot_nest_prefix;
78 
79 	if (ilen < ICMP_MINLEN)
80 		return;		/* incomplete header */
81 
82 	pt = "Unknown";
83 	pc = "";
84 	px = "";
85 
86 	switch (icmp->icmp_type) {
87 	case ICMP_ECHOREPLY:
88 		pt = "Echo reply";
89 		(void) sprintf(buff, "ID: %d Sequence number: %d",
90 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
91 		pc = buff;
92 		break;
93 	case ICMP_UNREACH:
94 		pt = "Destination unreachable";
95 		switch (icmp->icmp_code) {
96 		case ICMP_UNREACH_NET:
97 			if (ilen >= ICMP_ADVLENMIN) {
98 				(void) sprintf(buff, "Net %s unreachable",
99 				    addrtoname(AF_INET,
100 				    &icmp->icmp_ip.ip_dst));
101 				pc = buff;
102 			} else {
103 				pc = "Bad net";
104 			}
105 			break;
106 		case ICMP_UNREACH_HOST:
107 			if (ilen >= ICMP_ADVLENMIN) {
108 				(void) sprintf(buff, "Host %s unreachable",
109 				    addrtoname(AF_INET,
110 				    &icmp->icmp_ip.ip_dst));
111 				pc = buff;
112 			} else {
113 				pc = "Bad host";
114 			}
115 			break;
116 		case ICMP_UNREACH_PROTOCOL:
117 			if (ilen >= ICMP_ADVLENMIN) {
118 				(void) sprintf(buff, "Bad protocol %d",
119 				    icmp->icmp_ip.ip_p);
120 				pc = buff;
121 			} else {
122 				pc = "Bad protocol";
123 			}
124 			break;
125 		case ICMP_UNREACH_PORT:
126 			if (ilen >= ICMP_ADVLENMIN) {
127 				orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
128 				    ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
129 				switch (icmp->icmp_ip.ip_p) {
130 				case IPPROTO_TCP:
131 					(void) sprintf(buff, "TCP port %d"
132 					    " unreachable",
133 					    ntohs(orig_uhdr->uh_dport));
134 					pc = buff;
135 					break;
136 				case IPPROTO_UDP:
137 					(void) sprintf(buff, "UDP port %d"
138 					    " unreachable",
139 					    ntohs(orig_uhdr->uh_dport));
140 					pc = buff;
141 					break;
142 				default:
143 					pc = "Port unreachable";
144 					break;
145 				}
146 			} else {
147 				pc = "Bad port";
148 			}
149 			break;
150 		case ICMP_UNREACH_NEEDFRAG:
151 			if (ntohs(icmp->icmp_nextmtu) != 0) {
152 				(void) sprintf(buff, "Needed to fragment:"
153 				    " next hop MTU = %d",
154 				    ntohs(icmp->icmp_nextmtu));
155 				pc = buff;
156 			} else {
157 				pc = "Needed to fragment";
158 			}
159 			break;
160 		case ICMP_UNREACH_SRCFAIL:
161 			pc = "Source route failed";
162 			break;
163 		case ICMP_UNREACH_NET_UNKNOWN:
164 			pc = "Unknown network";
165 			break;
166 		case ICMP_UNREACH_HOST_UNKNOWN:
167 			pc = "Unknown host";
168 			break;
169 		case ICMP_UNREACH_ISOLATED:
170 			pc = "Source host isolated";
171 			break;
172 		case ICMP_UNREACH_NET_PROHIB:
173 			pc = "Net administratively prohibited";
174 			break;
175 		case ICMP_UNREACH_HOST_PROHIB:
176 			pc = "Host administratively prohibited";
177 			break;
178 		case ICMP_UNREACH_TOSNET:
179 			pc = "Net unreachable for this TOS";
180 			break;
181 		case ICMP_UNREACH_TOSHOST:
182 			pc = "Host unreachable for this TOS";
183 			break;
184 		case ICMP_UNREACH_FILTER_PROHIB:
185 			pc = "Communication administratively prohibited";
186 			break;
187 		case ICMP_UNREACH_HOST_PRECEDENCE:
188 			pc = "Host precedence violation";
189 			break;
190 		case ICMP_UNREACH_PRECEDENCE_CUTOFF:
191 			pc = "Precedence cutoff in effect";
192 			break;
193 		default:
194 			break;
195 		}
196 		break;
197 	case ICMP_SOURCEQUENCH:
198 		pt = "Packet lost, slow down";
199 		break;
200 	case ICMP_REDIRECT:
201 		pt = "Redirect";
202 		switch (icmp->icmp_code) {
203 		case ICMP_REDIRECT_NET:
204 			pc = "for network";
205 			break;
206 		case ICMP_REDIRECT_HOST:
207 			pc = "for host";
208 			break;
209 		case ICMP_REDIRECT_TOSNET:
210 			pc = "for tos and net";
211 			break;
212 		case ICMP_REDIRECT_TOSHOST:
213 			pc = "for tos and host";
214 			break;
215 		default:
216 			break;
217 		}
218 		(void) sprintf(buff, "%s %s to %s",
219 			pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
220 			addrtoname(AF_INET, &icmp->icmp_gwaddr));
221 		pc = buff;
222 		break;
223 	case ICMP_ECHO:
224 		pt = "Echo request";
225 		(void) sprintf(buff, "ID: %d Sequence number: %d",
226 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
227 		pc = buff;
228 		break;
229 	case ICMP_ROUTERADVERT:
230 
231 #define	icmp_num_addrs	icmp_hun.ih_rtradv.irt_num_addrs
232 #define	icmp_wpa	icmp_hun.ih_rtradv.irt_wpa
233 #define	icmp_lifetime	icmp_hun.ih_rtradv.irt_lifetime
234 
235 		pt = "Router advertisement";
236 		(void) sprintf(buff, "Lifetime %ds [%d]:",
237 		    ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
238 		if (icmp->icmp_wpa == 2) {
239 			struct icmp_ra_addr *ra;
240 			char ra_buf[MAXHOSTNAMELEN + 32];
241 			char ra_ext_buf[50];
242 			struct in_addr sin;
243 			int icmp_ra_len;
244 			int i;
245 
246 			/* Cannot trust anything from the network... */
247 			num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
248 			    icmp->icmp_num_addrs);
249 
250 			ra = (struct icmp_ra_addr *)icmp->icmp_data;
251 			for (i = 0; i < num_rtr_addrs; i++) {
252 				sin.s_addr = ra->addr;
253 				(void) snprintf(ra_buf, sizeof (ra_buf),
254 				    " {%s %u}",
255 				    addrtoname(AF_INET, &sin),
256 				    ntohl(ra->preference));
257 				if (strlcat(buff, ra_buf, sizeof (buff)) >=
258 					sizeof (buff)) {
259 					buff[sizeof (buff) -
260 					    strlen("<Too Long>)")] = '\0';
261 					(void) strlcat(buff, "<Too Long>",
262 						sizeof (buff));
263 					break;
264 				}
265 				ra++;
266 			}
267 
268 			icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
269 			    sizeof (struct icmp_ra_addr);
270 			if (ilen > icmp_ra_len) {
271 				int curr_len = ilen - icmp_ra_len;
272 				int ocurr_len;
273 				exthdr_t *exthdr = (exthdr_t *)ra;
274 
275 				extbuff[0] = '\0';
276 
277 				while (curr_len > 0) {
278 				    /* Append Mobile-IP description */
279 				    (void) snprintf(ra_ext_buf,
280 					sizeof (ra_ext_buf), ", %s",
281 					get_mip_adv_desc(exthdr->type));
282 				    (void) strlcat(extbuff, ra_ext_buf,
283 					sizeof (extbuff));
284 
285 				    /* Special case for padding */
286 				    if (exthdr->type ==
287 					ICMP_ADV_MSG_PADDING_EXT) {
288 
289 					curr_len--;
290 					exthdr = (exthdr_t *)
291 						((char *)exthdr + 1);
292 					continue;
293 				    }
294 
295 				    /* else normal extension */
296 				    ocurr_len = curr_len;
297 				    curr_len -= sizeof (*exthdr) +
298 							exthdr->length;
299 				    /* detect bad length */
300 				    if (ocurr_len < curr_len)
301 						break;
302 				    exthdr = (exthdr_t *)
303 						((char *)exthdr +
304 						sizeof (*exthdr) +
305 						exthdr->length);
306 				}
307 				px = extbuff;
308 			}
309 			pc = buff;
310 		}
311 		break;
312 	case ICMP_ROUTERSOLICIT:
313 		pt = "Router solicitation";
314 		break;
315 	case ICMP_TIMXCEED:
316 		pt = "Time exceeded";
317 		switch (icmp->icmp_code) {
318 		case ICMP_TIMXCEED_INTRANS:
319 			pc = "in transit";
320 			break;
321 		case ICMP_TIMXCEED_REASS:
322 			pc = "in reassembly";
323 			break;
324 		default:
325 			break;
326 		}
327 		break;
328 	case ICMP_PARAMPROB:
329 		pt = "IP parameter problem";
330 		switch (icmp->icmp_code) {
331 		case ICMP_PARAMPROB_OPTABSENT:
332 			pc = "Required option missing";
333 			break;
334 		case ICMP_PARAMPROB_BADLENGTH:
335 			pc = "Bad length";
336 			break;
337 		case 0: /* Should this be the default? */
338 			(void) sprintf(buff, "Problem at octet %d\n",
339 			    icmp->icmp_pptr);
340 			pc = buff;
341 		default:
342 			break;
343 		}
344 		break;
345 	case ICMP_TSTAMP:
346 		pt = "Timestamp request";
347 		break;
348 	case ICMP_TSTAMPREPLY:
349 		pt = "Timestamp reply";
350 		break;
351 	case ICMP_IREQ:
352 		pt = "Information request";
353 		break;
354 	case ICMP_IREQREPLY:
355 		pt = "Information reply";
356 		break;
357 	case ICMP_MASKREQ:
358 		pt = "Address mask request";
359 		break;
360 	case ICMP_MASKREPLY:
361 		pt = "Address mask reply";
362 		(void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
363 		pc = buff;
364 		break;
365 	default:
366 		break;
367 	}
368 
369 	if (flags & F_SUM) {
370 		line = get_sum_line();
371 		if (*pc) {
372 			if (*px) {
373 				(void) sprintf(line, "ICMP %s (%s)%s",
374 				    pt, pc, px);
375 			} else {
376 				(void) sprintf(line, "ICMP %s (%s)", pt, pc);
377 			}
378 		} else {
379 			(void) sprintf(line, "ICMP %s", pt);
380 		}
381 	}
382 
383 	if (flags & F_DTAIL) {
384 		show_header("ICMP:  ", "ICMP Header", ilen);
385 		show_space();
386 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
387 		    icmp->icmp_type, pt);
388 		if (*pc) {
389 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
390 			    icmp->icmp_code, pc);
391 		} else {
392 			(void) sprintf(get_line(0, 0), "Code = %d",
393 			    icmp->icmp_code);
394 		}
395 		(void) sprintf(get_line(0, 0), "Checksum = %x",
396 		    ntohs(icmp->icmp_cksum));
397 
398 		if (icmp->icmp_type == ICMP_UNREACH ||
399 		    icmp->icmp_type == ICMP_REDIRECT) {
400 			if (ilen > 28) {
401 				show_space();
402 				(void) sprintf(get_line(0, 0),
403 				    "[ subject header follows ]");
404 				show_space();
405 				prot_nest_prefix = "ICMP:";
406 				(void) interpret_ip(flags,
407 				    (struct ip *)icmp->icmp_data, 28);
408 				prot_nest_prefix = "";
409 			}
410 		} else if (icmp->icmp_type == ICMP_PARAMPROB) {
411 			if (ilen > 28) {
412 				show_space();
413 				(void) sprintf(get_line(0, 0),
414 				    "[ subject header follows ]");
415 				show_space();
416 				prot_nest_prefix = "ICMP:";
417 				(void) interpret_ip(flags,
418 				    (struct ip *)icmp->icmp_data, 28);
419 				prot_nest_prefix = "";
420 			}
421 		} else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
422 			if (icmp->icmp_wpa == 2) {
423 				int icmp_ra_len;
424 
425 				show_space();
426 				icmp_ra_len = ICMP_MINLEN +
427 				    num_rtr_addrs *
428 					sizeof (struct icmp_ra_addr);
429 				prot_nest_prefix = "";
430 				if (ilen > icmp_ra_len) {
431 					interpret_icmp_mip_ext(
432 					    (uchar_t *)icmp + icmp_ra_len,
433 					    ilen - icmp_ra_len);
434 				}
435 			}
436 		}
437 		show_space();
438 	}
439 }
440 
441 /*ARGSUSED*/
442 void
443 interpret_icmpv6(flags, icmp6, iplen, ilen)
444 	int flags;
445 	icmp6_t *icmp6;
446 	int iplen, ilen;
447 {
448 	char *pt, *pc;
449 	char *line;
450 	extern char *prot_nest_prefix;
451 	char addrstr[INET6_ADDRSTRLEN];
452 	char buff[2048];
453 
454 	if (ilen < ICMP6_MINLEN)
455 		return;		/* incomplete header */
456 
457 	pt = "Unknown";
458 	pc = "";
459 
460 	switch (icmp6->icmp6_type) {
461 	case ICMP6_DST_UNREACH:
462 		pt = "Destination unreachable";
463 		switch (icmp6->icmp6_code) {
464 		case ICMP6_DST_UNREACH_NOROUTE:
465 			pc = "No route to destination";
466 			break;
467 		case ICMP6_DST_UNREACH_ADMIN:
468 			pc = "Communication administratively prohibited";
469 			break;
470 		case ICMP6_DST_UNREACH_ADDR:
471 			pc = "Address unreachable";
472 			break;
473 		case ICMP6_DST_UNREACH_NOPORT:
474 			if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
475 				sizeof (struct udphdr)) {
476 
477 				ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
478 
479 				switch (orig_ip6hdr->ip6_nxt) {
480 				case IPPROTO_TCP: {
481 					struct tcphdr *orig_thdr =
482 					    (struct tcphdr *)&orig_ip6hdr[1];
483 
484 					(void) sprintf(buff, "TCP port %hu"
485 					    " unreachable",
486 					    ntohs(orig_thdr->th_dport));
487 					pc = buff;
488 					break;
489 				    }
490 				case IPPROTO_UDP: {
491 					struct udphdr *orig_uhdr =
492 					    (struct udphdr *)&orig_ip6hdr[1];
493 
494 					(void) sprintf(buff, "UDP port %hu"
495 					    " unreachable",
496 					    ntohs(orig_uhdr->uh_dport));
497 					pc = buff;
498 					break;
499 				    }
500 				default:
501 					pc = "Port unreachable";
502 					break;
503 				}
504 			} else {
505 				pc = "Bad port";
506 			}
507 			break;
508 		default:
509 			break;
510 		}
511 		break;
512 	case ICMP6_PACKET_TOO_BIG:
513 		pt = "Packet too big";
514 		break;
515 	case ND_REDIRECT:
516 		pt = "Redirect";
517 		break;
518 	case ICMP6_TIME_EXCEEDED:
519 		pt = "Time exceeded";
520 		switch (icmp6->icmp6_code) {
521 		case ICMP6_TIME_EXCEED_TRANSIT:
522 			pc = "Hop limit exceeded in transit";
523 			break;
524 		case ICMP6_TIME_EXCEED_REASSEMBLY:
525 			pc = "Fragment reassembly time exceeded";
526 			break;
527 		default:
528 			break;
529 		}
530 		break;
531 	case ICMP6_PARAM_PROB:
532 		pt = "Parameter problem";
533 		switch (icmp6->icmp6_code) {
534 		case ICMP6_PARAMPROB_HEADER:
535 			pc = "Erroneous header field";
536 			break;
537 		case ICMP6_PARAMPROB_NEXTHEADER:
538 			pc = "Unrecognized next header type";
539 			break;
540 		case ICMP6_PARAMPROB_OPTION:
541 			pc = "Unrecognized IPv6 option";
542 			break;
543 		}
544 		break;
545 	case ICMP6_ECHO_REQUEST:
546 		pt = "Echo request";
547 		(void) sprintf(buff, "ID: %d Sequence number: %d",
548 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
549 		pc = buff;
550 		break;
551 	case ICMP6_ECHO_REPLY:
552 		pt = "Echo reply";
553 		(void) sprintf(buff, "ID: %d Sequence number: %d",
554 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
555 		pc = buff;
556 		break;
557 	case MLD_LISTENER_QUERY:
558 		if (ilen == MLD_MINLEN)
559 			pt = "Group membership query - MLDv1";
560 		else if (ilen >= MLD_V2_QUERY_MINLEN)
561 			pt = "Group membership query - MLDv2";
562 		else
563 			pt = "Unknown membership query";
564 		break;
565 	case MLD_LISTENER_REPORT:
566 		pt = "Group membership report - MLDv1";
567 		break;
568 	case MLD_LISTENER_REDUCTION:
569 		pt = "Group membership termination - MLDv1";
570 		break;
571 	case MLD_V2_LISTENER_REPORT:
572 		pt = "Group membership report - MLDv2";
573 		break;
574 	case ND_ROUTER_SOLICIT:
575 		pt = "Router solicitation";
576 		break;
577 	case ND_ROUTER_ADVERT:
578 		pt = "Router advertisement";
579 		break;
580 	case ND_NEIGHBOR_SOLICIT:
581 		pt = "Neighbor solicitation";
582 		break;
583 	case ND_NEIGHBOR_ADVERT:
584 		pt = "Neighbor advertisement";
585 		break;
586 	default:
587 		break;
588 	}
589 
590 	if (flags & F_SUM) {
591 		line = get_sum_line();
592 		if (*pc)
593 			(void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
594 		else
595 			(void) sprintf(line, "ICMPv6 %s", pt);
596 	}
597 
598 	if (flags & F_DTAIL) {
599 		show_header("ICMPv6:  ", "ICMPv6 Header", ilen);
600 		show_space();
601 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
602 		    icmp6->icmp6_type, pt);
603 		if (*pc)
604 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
605 			    icmp6->icmp6_code, pc);
606 		else
607 			(void) sprintf(get_line(0, 0), "Code = %d",
608 			    icmp6->icmp6_code);
609 		(void) sprintf(get_line(0, 0), "Checksum = %x",
610 		    ntohs(icmp6->icmp6_cksum));
611 
612 		switch (icmp6->icmp6_type) {
613 		case ICMP6_DST_UNREACH:
614 			if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
615 				show_space();
616 				(void) sprintf(get_line(0, 0),
617 				    "[ subject header follows ]");
618 				show_space();
619 				prot_nest_prefix = "ICMPv6:";
620 				(void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
621 				    ICMP6_MINLEN + IPV6_HDR_LEN);
622 				prot_nest_prefix = "";
623 			}
624 			break;
625 		case ICMP6_PACKET_TOO_BIG:
626 			show_space();
627 			(void) sprintf(get_line(0, 0),
628 			    " Packet too big MTU = %d", 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