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
48static void interpret_options(char *, int);
49static void interpret_mldv2qry(icmp6_t *, int);
50static void interpret_mldv2rpt(icmp6_t *, int);
51
52
53/* Mobile-IP routines from snoop_mip.c */
54extern void interpret_icmp_mip_ext(uchar_t *, int);
55extern const char *get_mip_adv_desc(uint8_t);
56
57/* Router advertisement message structure. */
58struct icmp_ra_addr {
59	uint32_t addr;
60	uint32_t preference;
61};
62
63/*ARGSUSED*/
64void
65interpret_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*/
439void
440interpret_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
776static void
777interpret_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
906static void
907interpret_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
943const 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
953static void
954interpret_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