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