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