1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate #include <stdio.h>
31*7c478bd9Sstevel@tonic-gate #include <ctype.h>
32*7c478bd9Sstevel@tonic-gate #include <string.h>
33*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
34*7c478bd9Sstevel@tonic-gate #include <string.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/socket.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
41*7c478bd9Sstevel@tonic-gate #include <net/if.h>
42*7c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
43*7c478bd9Sstevel@tonic-gate #include <netinet/in.h>
44*7c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
45*7c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
46*7c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h>
47*7c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
48*7c478bd9Sstevel@tonic-gate #include <netinet/if_ether.h>
49*7c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
50*7c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h>
51*7c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
52*7c478bd9Sstevel@tonic-gate #include <netdb.h>
53*7c478bd9Sstevel@tonic-gate #include "snoop.h"
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate 
56*7c478bd9Sstevel@tonic-gate /*
57*7c478bd9Sstevel@tonic-gate  * IPv6 extension header masks.  These are used by the print_ipv6_extensions()
58*7c478bd9Sstevel@tonic-gate  * function to return information to the caller about which extension headers
59*7c478bd9Sstevel@tonic-gate  * were processed.  This can be useful if the caller wants to know if the
60*7c478bd9Sstevel@tonic-gate  * packet is an IPv6 fragment, for example.
61*7c478bd9Sstevel@tonic-gate  */
62*7c478bd9Sstevel@tonic-gate #define	SNOOP_HOPOPTS	0x01U
63*7c478bd9Sstevel@tonic-gate #define	SNOOP_ROUTING	0x02U
64*7c478bd9Sstevel@tonic-gate #define	SNOOP_DSTOPTS	0x04U
65*7c478bd9Sstevel@tonic-gate #define	SNOOP_FRAGMENT	0x08U
66*7c478bd9Sstevel@tonic-gate #define	SNOOP_AH	0x10U
67*7c478bd9Sstevel@tonic-gate #define	SNOOP_ESP	0x20U
68*7c478bd9Sstevel@tonic-gate #define	SNOOP_IPV6	0x40U
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate extern char *dlc_header;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate static void prt_routing_hdr();
73*7c478bd9Sstevel@tonic-gate static void prt_fragment_hdr();
74*7c478bd9Sstevel@tonic-gate static void prt_hbh_options();
75*7c478bd9Sstevel@tonic-gate static void prt_dest_options();
76*7c478bd9Sstevel@tonic-gate static void print_route();
77*7c478bd9Sstevel@tonic-gate static void print_ipoptions();
78*7c478bd9Sstevel@tonic-gate char *getproto();
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate /* Keep track of how many nested IP headers we have. */
81*7c478bd9Sstevel@tonic-gate unsigned int encap_levels;
82*7c478bd9Sstevel@tonic-gate unsigned int total_encap_levels = 1;
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate int
85*7c478bd9Sstevel@tonic-gate interpret_ip(flags, ip, fraglen)
86*7c478bd9Sstevel@tonic-gate 	int flags;
87*7c478bd9Sstevel@tonic-gate 	struct ip *ip;
88*7c478bd9Sstevel@tonic-gate 	int fraglen;
89*7c478bd9Sstevel@tonic-gate {
90*7c478bd9Sstevel@tonic-gate 	char *data;
91*7c478bd9Sstevel@tonic-gate 	char buff[24];
92*7c478bd9Sstevel@tonic-gate 	boolean_t isfrag = B_FALSE;
93*7c478bd9Sstevel@tonic-gate 	boolean_t morefrag;
94*7c478bd9Sstevel@tonic-gate 	uint16_t fragoffset;
95*7c478bd9Sstevel@tonic-gate 	int hdrlen;
96*7c478bd9Sstevel@tonic-gate 	uint16_t iplen, uitmp;
97*7c478bd9Sstevel@tonic-gate 	extern char *src_name, *dst_name;
98*7c478bd9Sstevel@tonic-gate 
99*7c478bd9Sstevel@tonic-gate 	if (ip->ip_v == IPV6_VERSION) {
100*7c478bd9Sstevel@tonic-gate 		iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen);
101*7c478bd9Sstevel@tonic-gate 		return (iplen);
102*7c478bd9Sstevel@tonic-gate 	}
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate 	/* XXX Should this count for mix-and-match v4/v6 encapsulations? */
105*7c478bd9Sstevel@tonic-gate 	if (encap_levels == 0)
106*7c478bd9Sstevel@tonic-gate 		total_encap_levels = 0;
107*7c478bd9Sstevel@tonic-gate 	encap_levels++;
108*7c478bd9Sstevel@tonic-gate 	total_encap_levels++;
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate 	hdrlen = ip->ip_hl * 4;
111*7c478bd9Sstevel@tonic-gate 	data = ((char *)ip) + hdrlen;
112*7c478bd9Sstevel@tonic-gate 	iplen = ntohs(ip->ip_len) - hdrlen;
113*7c478bd9Sstevel@tonic-gate 	fraglen -= hdrlen;
114*7c478bd9Sstevel@tonic-gate 	if (fraglen > iplen)
115*7c478bd9Sstevel@tonic-gate 		fraglen = iplen;
116*7c478bd9Sstevel@tonic-gate 	if (fraglen < 0) {
117*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_sum_line(), MAXLINE,
118*7c478bd9Sstevel@tonic-gate 		    "IP truncated: header missing %d bytes", -fraglen);
119*7c478bd9Sstevel@tonic-gate 		encap_levels--;
120*7c478bd9Sstevel@tonic-gate 		return (fraglen + iplen);
121*7c478bd9Sstevel@tonic-gate 	}
122*7c478bd9Sstevel@tonic-gate 	/*
123*7c478bd9Sstevel@tonic-gate 	 * We flag this as a fragment if the more fragments bit is set, or
124*7c478bd9Sstevel@tonic-gate 	 * if the fragment offset is non-zero.
125*7c478bd9Sstevel@tonic-gate 	 */
126*7c478bd9Sstevel@tonic-gate 	morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE;
127*7c478bd9Sstevel@tonic-gate 	fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8;
128*7c478bd9Sstevel@tonic-gate 	if (morefrag || fragoffset != 0)
129*7c478bd9Sstevel@tonic-gate 		isfrag = B_TRUE;
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate 	if (encap_levels == 1) {
132*7c478bd9Sstevel@tonic-gate 		src_name = addrtoname(AF_INET, &ip->ip_src);
133*7c478bd9Sstevel@tonic-gate 		dst_name = addrtoname(AF_INET, &ip->ip_dst);
134*7c478bd9Sstevel@tonic-gate 	} /* Else we already have the src_name and dst_name we want! */
135*7c478bd9Sstevel@tonic-gate 
136*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
137*7c478bd9Sstevel@tonic-gate 		if (isfrag) {
138*7c478bd9Sstevel@tonic-gate 			(void) snprintf(get_sum_line(), MAXLINE,
139*7c478bd9Sstevel@tonic-gate 			    "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x "
140*7c478bd9Sstevel@tonic-gate 			    "TTL=%d",
141*7c478bd9Sstevel@tonic-gate 			    getproto(ip->ip_p),
142*7c478bd9Sstevel@tonic-gate 			    ntohs(ip->ip_id),
143*7c478bd9Sstevel@tonic-gate 			    fragoffset,
144*7c478bd9Sstevel@tonic-gate 			    morefrag,
145*7c478bd9Sstevel@tonic-gate 			    ip->ip_tos,
146*7c478bd9Sstevel@tonic-gate 			    ip->ip_ttl);
147*7c478bd9Sstevel@tonic-gate 		} else {
148*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(buff, inet_ntoa(ip->ip_dst),
149*7c478bd9Sstevel@tonic-gate 			    sizeof (buff));
150*7c478bd9Sstevel@tonic-gate 			uitmp = ntohs(ip->ip_len);
151*7c478bd9Sstevel@tonic-gate 			(void) snprintf(get_sum_line(), MAXLINE,
152*7c478bd9Sstevel@tonic-gate 			    "IP  D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d",
153*7c478bd9Sstevel@tonic-gate 			    buff,
154*7c478bd9Sstevel@tonic-gate 			    inet_ntoa(ip->ip_src),
155*7c478bd9Sstevel@tonic-gate 			    uitmp,
156*7c478bd9Sstevel@tonic-gate 			    iplen > fraglen ? "?" : "",
157*7c478bd9Sstevel@tonic-gate 			    ntohs(ip->ip_id),
158*7c478bd9Sstevel@tonic-gate 			    ip->ip_tos,
159*7c478bd9Sstevel@tonic-gate 			    ip->ip_ttl);
160*7c478bd9Sstevel@tonic-gate 		}
161*7c478bd9Sstevel@tonic-gate 	}
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 	if (flags & F_DTAIL) {
164*7c478bd9Sstevel@tonic-gate 		show_header("IP:   ", "IP Header", iplen);
165*7c478bd9Sstevel@tonic-gate 		show_space();
166*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)ip - dlc_header, 1),
167*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Version = %d", ip->ip_v);
168*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)ip - dlc_header, 1),
169*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Header length = %d bytes", hdrlen);
170*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
171*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Type of service = 0x%02x", ip->ip_tos);
172*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
173*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      xxx. .... = %d (precedence)",
174*7c478bd9Sstevel@tonic-gate 		    ip->ip_tos >> 5);
175*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
176*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
177*7c478bd9Sstevel@tonic-gate 		    getflag(ip->ip_tos, IPTOS_LOWDELAY,
178*7c478bd9Sstevel@tonic-gate 		    "low delay", "normal delay"));
179*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
180*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
181*7c478bd9Sstevel@tonic-gate 		    getflag(ip->ip_tos, IPTOS_THROUGHPUT,
182*7c478bd9Sstevel@tonic-gate 		    "high throughput", "normal throughput"));
183*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
184*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
185*7c478bd9Sstevel@tonic-gate 		    getflag(ip->ip_tos, IPTOS_RELIABILITY,
186*7c478bd9Sstevel@tonic-gate 		    "high reliability", "normal reliability"));
187*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
188*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
189*7c478bd9Sstevel@tonic-gate 		    getflag(ip->ip_tos, IPTOS_ECT,
190*7c478bd9Sstevel@tonic-gate 		    "ECN capable transport", "not ECN capable transport"));
191*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
192*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
193*7c478bd9Sstevel@tonic-gate 		    getflag(ip->ip_tos, IPTOS_CE,
194*7c478bd9Sstevel@tonic-gate 		    "ECN congestion experienced",
195*7c478bd9Sstevel@tonic-gate 		    "no ECN congestion experienced"));
196*7c478bd9Sstevel@tonic-gate 		/* warning: ip_len is signed in netinet/ip.h */
197*7c478bd9Sstevel@tonic-gate 		uitmp = ntohs(ip->ip_len);
198*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_len - dlc_header, 2),
199*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Total length = %u bytes%s", uitmp,
200*7c478bd9Sstevel@tonic-gate 		    iplen > fraglen ? " -- truncated" : "");
201*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_id - dlc_header, 2),
202*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Identification = %d", ntohs(ip->ip_id));
203*7c478bd9Sstevel@tonic-gate 		/* warning: ip_off is signed in netinet/ip.h */
204*7c478bd9Sstevel@tonic-gate 		uitmp = ntohs(ip->ip_off);
205*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
206*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Flags = 0x%x", uitmp >> 12);
207*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
208*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
209*7c478bd9Sstevel@tonic-gate 		    getflag(uitmp >> 8, IP_DF >> 8,
210*7c478bd9Sstevel@tonic-gate 		    "do not fragment", "may fragment"));
211*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
212*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "      %s",
213*7c478bd9Sstevel@tonic-gate 		    getflag(uitmp >> 8, IP_MF >> 8,
214*7c478bd9Sstevel@tonic-gate 		    "more fragments", "last fragment"));
215*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 2),
216*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Fragment offset = %u bytes",
217*7c478bd9Sstevel@tonic-gate 		    fragoffset);
218*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_ttl - dlc_header, 1),
219*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Time to live = %d seconds/hops",
220*7c478bd9Sstevel@tonic-gate 		    ip->ip_ttl);
221*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_p - dlc_header, 1),
222*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Protocol = %d (%s)", ip->ip_p,
223*7c478bd9Sstevel@tonic-gate 		    getproto(ip->ip_p));
224*7c478bd9Sstevel@tonic-gate 		/*
225*7c478bd9Sstevel@tonic-gate 		 * XXX need to compute checksum and print whether it's correct
226*7c478bd9Sstevel@tonic-gate 		 */
227*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_sum - dlc_header, 1),
228*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Header checksum = %04x",
229*7c478bd9Sstevel@tonic-gate 		    ntohs(ip->ip_sum));
230*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_src - dlc_header, 1),
231*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Source address = %s, %s",
232*7c478bd9Sstevel@tonic-gate 		    inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src));
233*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line((char *)&ip->ip_dst - dlc_header, 1),
234*7c478bd9Sstevel@tonic-gate 		    get_line_remain(), "Destination address = %s, %s",
235*7c478bd9Sstevel@tonic-gate 		    inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst));
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 		/* Print IP options - if any */
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 		print_ipoptions(ip + 1, hdrlen - sizeof (struct ip));
240*7c478bd9Sstevel@tonic-gate 		show_space();
241*7c478bd9Sstevel@tonic-gate 	}
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate 	/*
244*7c478bd9Sstevel@tonic-gate 	 * If we are in detail mode, and this is not the first fragment of
245*7c478bd9Sstevel@tonic-gate 	 * a fragmented packet, print out a little line stating this.
246*7c478bd9Sstevel@tonic-gate 	 * Otherwise, go to the next protocol layer only if this is not a
247*7c478bd9Sstevel@tonic-gate 	 * fragment, or we are in detail mode and this is the first fragment
248*7c478bd9Sstevel@tonic-gate 	 * of a fragmented packet.
249*7c478bd9Sstevel@tonic-gate 	 */
250*7c478bd9Sstevel@tonic-gate 	if (flags & F_DTAIL && fragoffset != 0) {
251*7c478bd9Sstevel@tonic-gate 		(void) snprintf(get_detail_line(data - dlc_header, iplen),
252*7c478bd9Sstevel@tonic-gate 		    MAXLINE,
253*7c478bd9Sstevel@tonic-gate 		    "%s:  [%d byte(s) of data, continuation of IP ident=%d]",
254*7c478bd9Sstevel@tonic-gate 		    getproto(ip->ip_p),
255*7c478bd9Sstevel@tonic-gate 		    iplen,
256*7c478bd9Sstevel@tonic-gate 		    ntohs(ip->ip_id));
257*7c478bd9Sstevel@tonic-gate 	} else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) {
258*7c478bd9Sstevel@tonic-gate 		/* go to the next protocol layer */
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 		if (fraglen > 0) {
261*7c478bd9Sstevel@tonic-gate 			switch (ip->ip_p) {
262*7c478bd9Sstevel@tonic-gate 			case IPPROTO_IP:
263*7c478bd9Sstevel@tonic-gate 				break;
264*7c478bd9Sstevel@tonic-gate 			case IPPROTO_ENCAP:
265*7c478bd9Sstevel@tonic-gate 				(void) interpret_ip(flags, (struct ip *)data,
266*7c478bd9Sstevel@tonic-gate 				    fraglen);
267*7c478bd9Sstevel@tonic-gate 				break;
268*7c478bd9Sstevel@tonic-gate 			case IPPROTO_ICMP:
269*7c478bd9Sstevel@tonic-gate 				interpret_icmp(flags, (struct icmp *)data,
270*7c478bd9Sstevel@tonic-gate 				    iplen, fraglen);
271*7c478bd9Sstevel@tonic-gate 				break;
272*7c478bd9Sstevel@tonic-gate 			case IPPROTO_IGMP:
273*7c478bd9Sstevel@tonic-gate 				interpret_igmp(flags, data, iplen, fraglen);
274*7c478bd9Sstevel@tonic-gate 				break;
275*7c478bd9Sstevel@tonic-gate 			case IPPROTO_GGP:
276*7c478bd9Sstevel@tonic-gate 				break;
277*7c478bd9Sstevel@tonic-gate 			case IPPROTO_TCP:
278*7c478bd9Sstevel@tonic-gate 				interpret_tcp(flags, data, iplen, fraglen);
279*7c478bd9Sstevel@tonic-gate 				break;
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 			case IPPROTO_ESP:
282*7c478bd9Sstevel@tonic-gate 				interpret_esp(flags, data, iplen, fraglen);
283*7c478bd9Sstevel@tonic-gate 				break;
284*7c478bd9Sstevel@tonic-gate 			case IPPROTO_AH:
285*7c478bd9Sstevel@tonic-gate 				interpret_ah(flags, data, iplen, fraglen);
286*7c478bd9Sstevel@tonic-gate 				break;
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 			case IPPROTO_OSPF:
289*7c478bd9Sstevel@tonic-gate 				interpret_ospf(flags, data, iplen, fraglen);
290*7c478bd9Sstevel@tonic-gate 				break;
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate 			case IPPROTO_EGP:
293*7c478bd9Sstevel@tonic-gate 			case IPPROTO_PUP:
294*7c478bd9Sstevel@tonic-gate 				break;
295*7c478bd9Sstevel@tonic-gate 			case IPPROTO_UDP:
296*7c478bd9Sstevel@tonic-gate 				interpret_udp(flags, data, iplen, fraglen);
297*7c478bd9Sstevel@tonic-gate 				break;
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 			case IPPROTO_IDP:
300*7c478bd9Sstevel@tonic-gate 			case IPPROTO_HELLO:
301*7c478bd9Sstevel@tonic-gate 			case IPPROTO_ND:
302*7c478bd9Sstevel@tonic-gate 			case IPPROTO_RAW:
303*7c478bd9Sstevel@tonic-gate 				break;
304*7c478bd9Sstevel@tonic-gate 			case IPPROTO_IPV6:	/* IPV6 encap */
305*7c478bd9Sstevel@tonic-gate 				(void) interpret_ipv6(flags, (ip6_t *)data,
306*7c478bd9Sstevel@tonic-gate 				    iplen);
307*7c478bd9Sstevel@tonic-gate 				break;
308*7c478bd9Sstevel@tonic-gate 			case IPPROTO_SCTP:
309*7c478bd9Sstevel@tonic-gate 				interpret_sctp(flags, data, iplen, fraglen);
310*7c478bd9Sstevel@tonic-gate 				break;
311*7c478bd9Sstevel@tonic-gate 			}
312*7c478bd9Sstevel@tonic-gate 		}
313*7c478bd9Sstevel@tonic-gate 	}
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 	encap_levels--;
316*7c478bd9Sstevel@tonic-gate 	return (iplen);
317*7c478bd9Sstevel@tonic-gate }
318*7c478bd9Sstevel@tonic-gate 
319*7c478bd9Sstevel@tonic-gate int
320*7c478bd9Sstevel@tonic-gate interpret_ipv6(flags, ip6h, fraglen)
321*7c478bd9Sstevel@tonic-gate 	int flags;
322*7c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
323*7c478bd9Sstevel@tonic-gate 	int fraglen;
324*7c478bd9Sstevel@tonic-gate {
325*7c478bd9Sstevel@tonic-gate 	uint8_t *data;
326*7c478bd9Sstevel@tonic-gate 	int hdrlen, iplen;
327*7c478bd9Sstevel@tonic-gate 	extern char *src_name, *dst_name;
328*7c478bd9Sstevel@tonic-gate 	int version, flow, class;
329*7c478bd9Sstevel@tonic-gate 	uchar_t proto;
330*7c478bd9Sstevel@tonic-gate 	boolean_t isfrag = B_FALSE;
331*7c478bd9Sstevel@tonic-gate 	uint8_t extmask;
332*7c478bd9Sstevel@tonic-gate 	/*
333*7c478bd9Sstevel@tonic-gate 	 * The print_srcname and print_dstname strings are the hostname
334*7c478bd9Sstevel@tonic-gate 	 * parts of the verbose IPv6 header output, including the comma
335*7c478bd9Sstevel@tonic-gate 	 * and the space after the litteral address strings.
336*7c478bd9Sstevel@tonic-gate 	 */
337*7c478bd9Sstevel@tonic-gate 	char print_srcname[MAXHOSTNAMELEN + 2];
338*7c478bd9Sstevel@tonic-gate 	char print_dstname[MAXHOSTNAMELEN + 2];
339*7c478bd9Sstevel@tonic-gate 	char src_addrstr[INET6_ADDRSTRLEN];
340*7c478bd9Sstevel@tonic-gate 	char dst_addrstr[INET6_ADDRSTRLEN];
341*7c478bd9Sstevel@tonic-gate 
342*7c478bd9Sstevel@tonic-gate 	iplen = ntohs(ip6h->ip6_plen);
343*7c478bd9Sstevel@tonic-gate 	hdrlen = IPV6_HDR_LEN;
344*7c478bd9Sstevel@tonic-gate 	fraglen -= hdrlen;
345*7c478bd9Sstevel@tonic-gate 	if (fraglen < 0)
346*7c478bd9Sstevel@tonic-gate 		return (fraglen + hdrlen);
347*7c478bd9Sstevel@tonic-gate 	data = ((uint8_t *)ip6h) + hdrlen;
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	proto = ip6h->ip6_nxt;
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	src_name = addrtoname(AF_INET6, &ip6h->ip6_src);
352*7c478bd9Sstevel@tonic-gate 	dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst);
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	/*
355*7c478bd9Sstevel@tonic-gate 	 * Use endian-aware masks to extract traffic class and
356*7c478bd9Sstevel@tonic-gate 	 * flowinfo.  Also, flowinfo is now 20 bits and class 8
357*7c478bd9Sstevel@tonic-gate 	 * rather than 24 and 4.
358*7c478bd9Sstevel@tonic-gate 	 */
359*7c478bd9Sstevel@tonic-gate 	class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20);
360*7c478bd9Sstevel@tonic-gate 	flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL);
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 	/*
363*7c478bd9Sstevel@tonic-gate 	 * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive,
364*7c478bd9Sstevel@tonic-gate 	 * so the code within the first part of the following if statement
365*7c478bd9Sstevel@tonic-gate 	 * will not affect the detailed printing of the packet.
366*7c478bd9Sstevel@tonic-gate 	 */
367*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
368*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_sum_line(), "IPv6  S=%s D=%s LEN=%d "
369*7c478bd9Sstevel@tonic-gate 		    "HOPS=%d CLASS=0x%x FLOW=0x%x",
370*7c478bd9Sstevel@tonic-gate 		    src_name, dst_name, iplen, ip6h->ip6_hops, class, flow);
371*7c478bd9Sstevel@tonic-gate 	} else if (flags & F_DTAIL) {
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr,
374*7c478bd9Sstevel@tonic-gate 		    INET6_ADDRSTRLEN);
375*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr,
376*7c478bd9Sstevel@tonic-gate 		    INET6_ADDRSTRLEN);
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 		version = ntohl(ip6h->ip6_vcf) >> 28;
379*7c478bd9Sstevel@tonic-gate 
380*7c478bd9Sstevel@tonic-gate 		if (strcmp(src_name, src_addrstr) == 0)
381*7c478bd9Sstevel@tonic-gate 			print_srcname[0] = '\0';
382*7c478bd9Sstevel@tonic-gate 		else
383*7c478bd9Sstevel@tonic-gate 			snprintf(print_srcname, sizeof (print_srcname),
384*7c478bd9Sstevel@tonic-gate 				", %s", src_name);
385*7c478bd9Sstevel@tonic-gate 
386*7c478bd9Sstevel@tonic-gate 		if (strcmp(dst_name, dst_addrstr) == 0)
387*7c478bd9Sstevel@tonic-gate 			print_dstname[0] = '\0';
388*7c478bd9Sstevel@tonic-gate 		else
389*7c478bd9Sstevel@tonic-gate 			snprintf(print_dstname, sizeof (print_dstname),
390*7c478bd9Sstevel@tonic-gate 				", %s", dst_name);
391*7c478bd9Sstevel@tonic-gate 
392*7c478bd9Sstevel@tonic-gate 		show_header("IPv6:   ", "IPv6 Header", iplen);
393*7c478bd9Sstevel@tonic-gate 		show_space();
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ip6h - dlc_header, 1),
396*7c478bd9Sstevel@tonic-gate 		    "Version = %d", version);
397*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ip6h - dlc_header, 1),
398*7c478bd9Sstevel@tonic-gate 		    "Traffic Class = %d", class);
399*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_vcf - dlc_header, 4),
400*7c478bd9Sstevel@tonic-gate 		    "Flow label = 0x%x", flow);
401*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_plen -
402*7c478bd9Sstevel@tonic-gate 		    dlc_header, 2), "Payload length = %d", iplen);
403*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_nxt -
404*7c478bd9Sstevel@tonic-gate 		    dlc_header, 1), "Next Header = %d (%s)", proto,
405*7c478bd9Sstevel@tonic-gate 		    getproto(proto));
406*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_hops -
407*7c478bd9Sstevel@tonic-gate 		    dlc_header, 1), "Hop Limit = %d", ip6h->ip6_hops);
408*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_src - dlc_header, 1),
409*7c478bd9Sstevel@tonic-gate 		    "Source address = %s%s", src_addrstr, print_srcname);
410*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&ip6h->ip6_dst - dlc_header, 1),
411*7c478bd9Sstevel@tonic-gate 		    "Destination address = %s%s", dst_addrstr, print_dstname);
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 		show_space();
414*7c478bd9Sstevel@tonic-gate 	}
415*7c478bd9Sstevel@tonic-gate 
416*7c478bd9Sstevel@tonic-gate 	/*
417*7c478bd9Sstevel@tonic-gate 	 * Print IPv6 Extension Headers, or skip them in the summary case.
418*7c478bd9Sstevel@tonic-gate 	 * Set isfrag to true if one of the extension headers encounterred
419*7c478bd9Sstevel@tonic-gate 	 * was a fragment header.
420*7c478bd9Sstevel@tonic-gate 	 */
421*7c478bd9Sstevel@tonic-gate 	if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
422*7c478bd9Sstevel@tonic-gate 	    proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
423*7c478bd9Sstevel@tonic-gate 		extmask = print_ipv6_extensions(flags, &data, &proto, &iplen,
424*7c478bd9Sstevel@tonic-gate 		    &fraglen);
425*7c478bd9Sstevel@tonic-gate 		if ((extmask & SNOOP_FRAGMENT) != 0) {
426*7c478bd9Sstevel@tonic-gate 			isfrag = B_TRUE;
427*7c478bd9Sstevel@tonic-gate 		}
428*7c478bd9Sstevel@tonic-gate 	}
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 	/*
431*7c478bd9Sstevel@tonic-gate 	 * We only want to print upper layer information if this is not
432*7c478bd9Sstevel@tonic-gate 	 * a fragment, or if we're printing in detail.  Note that the
433*7c478bd9Sstevel@tonic-gate 	 * proto variable will be set to IPPROTO_NONE if this is a fragment
434*7c478bd9Sstevel@tonic-gate 	 * with a non-zero fragment offset.
435*7c478bd9Sstevel@tonic-gate 	 */
436*7c478bd9Sstevel@tonic-gate 	if (!isfrag || flags & F_DTAIL) {
437*7c478bd9Sstevel@tonic-gate 		/* go to the next protocol layer */
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate 		switch (proto) {
440*7c478bd9Sstevel@tonic-gate 		case IPPROTO_IP:
441*7c478bd9Sstevel@tonic-gate 			break;
442*7c478bd9Sstevel@tonic-gate 		case IPPROTO_ENCAP:
443*7c478bd9Sstevel@tonic-gate 			(void) interpret_ip(flags, (struct ip *)data, fraglen);
444*7c478bd9Sstevel@tonic-gate 			break;
445*7c478bd9Sstevel@tonic-gate 		case IPPROTO_ICMPV6:
446*7c478bd9Sstevel@tonic-gate 			interpret_icmpv6(flags, (icmp6_t *)data, iplen,
447*7c478bd9Sstevel@tonic-gate 			    fraglen);
448*7c478bd9Sstevel@tonic-gate 			break;
449*7c478bd9Sstevel@tonic-gate 		case IPPROTO_IGMP:
450*7c478bd9Sstevel@tonic-gate 			interpret_igmp(flags, data, iplen, fraglen);
451*7c478bd9Sstevel@tonic-gate 			break;
452*7c478bd9Sstevel@tonic-gate 		case IPPROTO_GGP:
453*7c478bd9Sstevel@tonic-gate 			break;
454*7c478bd9Sstevel@tonic-gate 		case IPPROTO_TCP:
455*7c478bd9Sstevel@tonic-gate 			interpret_tcp(flags, data, iplen, fraglen);
456*7c478bd9Sstevel@tonic-gate 			break;
457*7c478bd9Sstevel@tonic-gate 		case IPPROTO_ESP:
458*7c478bd9Sstevel@tonic-gate 			interpret_esp(flags, data, iplen, fraglen);
459*7c478bd9Sstevel@tonic-gate 			break;
460*7c478bd9Sstevel@tonic-gate 		case IPPROTO_AH:
461*7c478bd9Sstevel@tonic-gate 			interpret_ah(flags, data, iplen, fraglen);
462*7c478bd9Sstevel@tonic-gate 			break;
463*7c478bd9Sstevel@tonic-gate 		case IPPROTO_EGP:
464*7c478bd9Sstevel@tonic-gate 		case IPPROTO_PUP:
465*7c478bd9Sstevel@tonic-gate 			break;
466*7c478bd9Sstevel@tonic-gate 		case IPPROTO_UDP:
467*7c478bd9Sstevel@tonic-gate 			interpret_udp(flags, data, iplen, fraglen);
468*7c478bd9Sstevel@tonic-gate 			break;
469*7c478bd9Sstevel@tonic-gate 		case IPPROTO_IDP:
470*7c478bd9Sstevel@tonic-gate 		case IPPROTO_HELLO:
471*7c478bd9Sstevel@tonic-gate 		case IPPROTO_ND:
472*7c478bd9Sstevel@tonic-gate 		case IPPROTO_RAW:
473*7c478bd9Sstevel@tonic-gate 			break;
474*7c478bd9Sstevel@tonic-gate 		case IPPROTO_IPV6:
475*7c478bd9Sstevel@tonic-gate 			(void) interpret_ipv6(flags, (ip6_t *)data, iplen);
476*7c478bd9Sstevel@tonic-gate 			break;
477*7c478bd9Sstevel@tonic-gate 		case IPPROTO_SCTP:
478*7c478bd9Sstevel@tonic-gate 			interpret_sctp(flags, data, iplen, fraglen);
479*7c478bd9Sstevel@tonic-gate 			break;
480*7c478bd9Sstevel@tonic-gate 		case IPPROTO_OSPF:
481*7c478bd9Sstevel@tonic-gate 			interpret_ospf6(flags, data, iplen, fraglen);
482*7c478bd9Sstevel@tonic-gate 			break;
483*7c478bd9Sstevel@tonic-gate 		}
484*7c478bd9Sstevel@tonic-gate 	}
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 	return (iplen);
487*7c478bd9Sstevel@tonic-gate }
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate /*
490*7c478bd9Sstevel@tonic-gate  * ip_ext: data including the extension header.
491*7c478bd9Sstevel@tonic-gate  * iplen: length of the data remaining in the packet.
492*7c478bd9Sstevel@tonic-gate  * Returns a mask of IPv6 extension headers it processed.
493*7c478bd9Sstevel@tonic-gate  */
494*7c478bd9Sstevel@tonic-gate uint8_t
495*7c478bd9Sstevel@tonic-gate print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen,
496*7c478bd9Sstevel@tonic-gate     int *fraglen)
497*7c478bd9Sstevel@tonic-gate {
498*7c478bd9Sstevel@tonic-gate 	uint8_t *data_ptr;
499*7c478bd9Sstevel@tonic-gate 	uchar_t proto = *next;
500*7c478bd9Sstevel@tonic-gate 	boolean_t is_extension_header;
501*7c478bd9Sstevel@tonic-gate 	struct ip6_hbh *ipv6ext_hbh;
502*7c478bd9Sstevel@tonic-gate 	struct ip6_dest *ipv6ext_dest;
503*7c478bd9Sstevel@tonic-gate 	struct ip6_rthdr *ipv6ext_rthdr;
504*7c478bd9Sstevel@tonic-gate 	struct ip6_frag *ipv6ext_frag;
505*7c478bd9Sstevel@tonic-gate 	uint32_t exthdrlen;
506*7c478bd9Sstevel@tonic-gate 	uint8_t extmask = 0;
507*7c478bd9Sstevel@tonic-gate 
508*7c478bd9Sstevel@tonic-gate 	if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0))
509*7c478bd9Sstevel@tonic-gate 		return (0);
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate 	data_ptr = *hdr;
512*7c478bd9Sstevel@tonic-gate 	is_extension_header = B_TRUE;
513*7c478bd9Sstevel@tonic-gate 	while (is_extension_header) {
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 		/*
516*7c478bd9Sstevel@tonic-gate 		 * There must be at least enough data left to read the
517*7c478bd9Sstevel@tonic-gate 		 * next header and header length fields from the next
518*7c478bd9Sstevel@tonic-gate 		 * header.
519*7c478bd9Sstevel@tonic-gate 		 */
520*7c478bd9Sstevel@tonic-gate 		if (*fraglen < 2) {
521*7c478bd9Sstevel@tonic-gate 			proto = IPPROTO_NONE;
522*7c478bd9Sstevel@tonic-gate 			return (extmask);
523*7c478bd9Sstevel@tonic-gate 		}
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 		switch (proto) {
526*7c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
527*7c478bd9Sstevel@tonic-gate 			ipv6ext_hbh = (struct ip6_hbh *)data_ptr;
528*7c478bd9Sstevel@tonic-gate 			exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8;
529*7c478bd9Sstevel@tonic-gate 			if (*fraglen <= exthdrlen) {
530*7c478bd9Sstevel@tonic-gate 				proto = IPPROTO_NONE;
531*7c478bd9Sstevel@tonic-gate 				return (extmask);
532*7c478bd9Sstevel@tonic-gate 			}
533*7c478bd9Sstevel@tonic-gate 			prt_hbh_options(flags, ipv6ext_hbh);
534*7c478bd9Sstevel@tonic-gate 			extmask |= SNOOP_HOPOPTS;
535*7c478bd9Sstevel@tonic-gate 			proto = ipv6ext_hbh->ip6h_nxt;
536*7c478bd9Sstevel@tonic-gate 			break;
537*7c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
538*7c478bd9Sstevel@tonic-gate 			ipv6ext_dest = (struct ip6_dest *)data_ptr;
539*7c478bd9Sstevel@tonic-gate 			exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8;
540*7c478bd9Sstevel@tonic-gate 			if (*fraglen <= exthdrlen) {
541*7c478bd9Sstevel@tonic-gate 				proto = IPPROTO_NONE;
542*7c478bd9Sstevel@tonic-gate 				return (extmask);
543*7c478bd9Sstevel@tonic-gate 			}
544*7c478bd9Sstevel@tonic-gate 			prt_dest_options(flags, ipv6ext_dest);
545*7c478bd9Sstevel@tonic-gate 			extmask |= SNOOP_DSTOPTS;
546*7c478bd9Sstevel@tonic-gate 			proto = ipv6ext_dest->ip6d_nxt;
547*7c478bd9Sstevel@tonic-gate 			break;
548*7c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
549*7c478bd9Sstevel@tonic-gate 			ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr;
550*7c478bd9Sstevel@tonic-gate 			exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8;
551*7c478bd9Sstevel@tonic-gate 			if (*fraglen <= exthdrlen) {
552*7c478bd9Sstevel@tonic-gate 				proto = IPPROTO_NONE;
553*7c478bd9Sstevel@tonic-gate 				return (extmask);
554*7c478bd9Sstevel@tonic-gate 			}
555*7c478bd9Sstevel@tonic-gate 			prt_routing_hdr(flags, ipv6ext_rthdr);
556*7c478bd9Sstevel@tonic-gate 			extmask |= SNOOP_ROUTING;
557*7c478bd9Sstevel@tonic-gate 			proto = ipv6ext_rthdr->ip6r_nxt;
558*7c478bd9Sstevel@tonic-gate 			break;
559*7c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
560*7c478bd9Sstevel@tonic-gate 			ipv6ext_frag = (struct ip6_frag *)data_ptr;
561*7c478bd9Sstevel@tonic-gate 			exthdrlen = sizeof (struct ip6_frag);
562*7c478bd9Sstevel@tonic-gate 			if (*fraglen <= exthdrlen) {
563*7c478bd9Sstevel@tonic-gate 				proto = IPPROTO_NONE;
564*7c478bd9Sstevel@tonic-gate 				return (extmask);
565*7c478bd9Sstevel@tonic-gate 			}
566*7c478bd9Sstevel@tonic-gate 			prt_fragment_hdr(flags, ipv6ext_frag);
567*7c478bd9Sstevel@tonic-gate 			extmask |= SNOOP_FRAGMENT;
568*7c478bd9Sstevel@tonic-gate 			/*
569*7c478bd9Sstevel@tonic-gate 			 * If this is not the first fragment, forget about
570*7c478bd9Sstevel@tonic-gate 			 * the rest of the packet, snoop decoding is
571*7c478bd9Sstevel@tonic-gate 			 * stateless.
572*7c478bd9Sstevel@tonic-gate 			 */
573*7c478bd9Sstevel@tonic-gate 			if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0)
574*7c478bd9Sstevel@tonic-gate 				proto = IPPROTO_NONE;
575*7c478bd9Sstevel@tonic-gate 			else
576*7c478bd9Sstevel@tonic-gate 				proto = ipv6ext_frag->ip6f_nxt;
577*7c478bd9Sstevel@tonic-gate 			break;
578*7c478bd9Sstevel@tonic-gate 		default:
579*7c478bd9Sstevel@tonic-gate 			is_extension_header = B_FALSE;
580*7c478bd9Sstevel@tonic-gate 			break;
581*7c478bd9Sstevel@tonic-gate 		}
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 		if (is_extension_header) {
584*7c478bd9Sstevel@tonic-gate 			*iplen -= exthdrlen;
585*7c478bd9Sstevel@tonic-gate 			*fraglen -= exthdrlen;
586*7c478bd9Sstevel@tonic-gate 			data_ptr += exthdrlen;
587*7c478bd9Sstevel@tonic-gate 		}
588*7c478bd9Sstevel@tonic-gate 	}
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate 	*next = proto;
591*7c478bd9Sstevel@tonic-gate 	*hdr = data_ptr;
592*7c478bd9Sstevel@tonic-gate 	return (extmask);
593*7c478bd9Sstevel@tonic-gate }
594*7c478bd9Sstevel@tonic-gate 
595*7c478bd9Sstevel@tonic-gate static void
596*7c478bd9Sstevel@tonic-gate print_ipoptions(opt, optlen)
597*7c478bd9Sstevel@tonic-gate 	uchar_t *opt;
598*7c478bd9Sstevel@tonic-gate 	int optlen;
599*7c478bd9Sstevel@tonic-gate {
600*7c478bd9Sstevel@tonic-gate 	int len;
601*7c478bd9Sstevel@tonic-gate 	char *line;
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	if (optlen <= 0) {
604*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&opt - dlc_header, 1),
605*7c478bd9Sstevel@tonic-gate 		    "No options");
606*7c478bd9Sstevel@tonic-gate 		return;
607*7c478bd9Sstevel@tonic-gate 	}
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)&opt - dlc_header, 1),
610*7c478bd9Sstevel@tonic-gate 	    "Options: (%d bytes)", optlen);
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate 	while (optlen > 0) {
613*7c478bd9Sstevel@tonic-gate 		line = get_line((char *)&opt - dlc_header, 1);
614*7c478bd9Sstevel@tonic-gate 		len = opt[1];
615*7c478bd9Sstevel@tonic-gate 		switch (opt[0]) {
616*7c478bd9Sstevel@tonic-gate 		case IPOPT_EOL:
617*7c478bd9Sstevel@tonic-gate 			(void) strcpy(line, "  - End of option list");
618*7c478bd9Sstevel@tonic-gate 			return;
619*7c478bd9Sstevel@tonic-gate 		case IPOPT_NOP:
620*7c478bd9Sstevel@tonic-gate 			(void) strcpy(line, "  - No op");
621*7c478bd9Sstevel@tonic-gate 			len = 1;
622*7c478bd9Sstevel@tonic-gate 			break;
623*7c478bd9Sstevel@tonic-gate 		case IPOPT_RR:
624*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Record route (%d bytes)",
625*7c478bd9Sstevel@tonic-gate 			    len);
626*7c478bd9Sstevel@tonic-gate 			print_route(opt);
627*7c478bd9Sstevel@tonic-gate 			break;
628*7c478bd9Sstevel@tonic-gate 		case IPOPT_TS:
629*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Time stamp (%d bytes)", len);
630*7c478bd9Sstevel@tonic-gate 			break;
631*7c478bd9Sstevel@tonic-gate 		case IPOPT_SECURITY:
632*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Security (%d bytes)", len);
633*7c478bd9Sstevel@tonic-gate 			break;
634*7c478bd9Sstevel@tonic-gate 		case IPOPT_LSRR:
635*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line,
636*7c478bd9Sstevel@tonic-gate 			    "  - Loose source route (%d bytes)", len);
637*7c478bd9Sstevel@tonic-gate 			print_route(opt);
638*7c478bd9Sstevel@tonic-gate 			break;
639*7c478bd9Sstevel@tonic-gate 		case IPOPT_SATID:
640*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - SATNET Stream id (%d bytes)",
641*7c478bd9Sstevel@tonic-gate 			    len);
642*7c478bd9Sstevel@tonic-gate 			break;
643*7c478bd9Sstevel@tonic-gate 		case IPOPT_SSRR:
644*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line,
645*7c478bd9Sstevel@tonic-gate 			    "  - Strict source route, (%d bytes)", len);
646*7c478bd9Sstevel@tonic-gate 			print_route(opt);
647*7c478bd9Sstevel@tonic-gate 			break;
648*7c478bd9Sstevel@tonic-gate 		default:
649*7c478bd9Sstevel@tonic-gate 			sprintf(line, "  - Option %d (unknown - %d bytes) %s",
650*7c478bd9Sstevel@tonic-gate 			    opt[0], len, tohex((char *)&opt[2], len - 2));
651*7c478bd9Sstevel@tonic-gate 			break;
652*7c478bd9Sstevel@tonic-gate 		}
653*7c478bd9Sstevel@tonic-gate 		if (len <= 0) {
654*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Incomplete option len %d",
655*7c478bd9Sstevel@tonic-gate 				len);
656*7c478bd9Sstevel@tonic-gate 			break;
657*7c478bd9Sstevel@tonic-gate 		}
658*7c478bd9Sstevel@tonic-gate 		opt += len;
659*7c478bd9Sstevel@tonic-gate 		optlen -= len;
660*7c478bd9Sstevel@tonic-gate 	}
661*7c478bd9Sstevel@tonic-gate }
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate static void
664*7c478bd9Sstevel@tonic-gate print_route(opt)
665*7c478bd9Sstevel@tonic-gate 	uchar_t *opt;
666*7c478bd9Sstevel@tonic-gate {
667*7c478bd9Sstevel@tonic-gate 	int len, pointer;
668*7c478bd9Sstevel@tonic-gate 	struct in_addr addr;
669*7c478bd9Sstevel@tonic-gate 	char *line;
670*7c478bd9Sstevel@tonic-gate 
671*7c478bd9Sstevel@tonic-gate 	len = opt[1];
672*7c478bd9Sstevel@tonic-gate 	pointer = opt[2];
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)(&opt + 2) - dlc_header, 1),
675*7c478bd9Sstevel@tonic-gate 	    "    Pointer = %d", pointer);
676*7c478bd9Sstevel@tonic-gate 
677*7c478bd9Sstevel@tonic-gate 	pointer -= IPOPT_MINOFF;
678*7c478bd9Sstevel@tonic-gate 	opt += (IPOPT_OFFSET + 1);
679*7c478bd9Sstevel@tonic-gate 	len -= (IPOPT_OFFSET + 1);
680*7c478bd9Sstevel@tonic-gate 
681*7c478bd9Sstevel@tonic-gate 	while (len > 0) {
682*7c478bd9Sstevel@tonic-gate 		line = get_line((char *)&(opt) - dlc_header, 4);
683*7c478bd9Sstevel@tonic-gate 		memcpy((char *)&addr, opt, sizeof (addr));
684*7c478bd9Sstevel@tonic-gate 		if (addr.s_addr == INADDR_ANY)
685*7c478bd9Sstevel@tonic-gate 			(void) strcpy(line, "      -");
686*7c478bd9Sstevel@tonic-gate 		else
687*7c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "      %s",
688*7c478bd9Sstevel@tonic-gate 			    addrtoname(AF_INET, &addr));
689*7c478bd9Sstevel@tonic-gate 		if (pointer == 0)
690*7c478bd9Sstevel@tonic-gate 			(void) strcat(line, "  <-- (current)");
691*7c478bd9Sstevel@tonic-gate 
692*7c478bd9Sstevel@tonic-gate 		opt += sizeof (addr);
693*7c478bd9Sstevel@tonic-gate 		len -= sizeof (addr);
694*7c478bd9Sstevel@tonic-gate 		pointer -= sizeof (addr);
695*7c478bd9Sstevel@tonic-gate 	}
696*7c478bd9Sstevel@tonic-gate }
697*7c478bd9Sstevel@tonic-gate 
698*7c478bd9Sstevel@tonic-gate char *
699*7c478bd9Sstevel@tonic-gate getproto(p)
700*7c478bd9Sstevel@tonic-gate 	int p;
701*7c478bd9Sstevel@tonic-gate {
702*7c478bd9Sstevel@tonic-gate 	switch (p) {
703*7c478bd9Sstevel@tonic-gate 	case IPPROTO_HOPOPTS:	return ("IPv6-HopOpts");
704*7c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6:	return ("IPv6");
705*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ROUTING:	return ("IPv6-Route");
706*7c478bd9Sstevel@tonic-gate 	case IPPROTO_FRAGMENT:	return ("IPv6-Frag");
707*7c478bd9Sstevel@tonic-gate 	case IPPROTO_RSVP:	return ("RSVP");
708*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ENCAP:	return ("IP-in-IP");
709*7c478bd9Sstevel@tonic-gate 	case IPPROTO_AH:	return ("AH");
710*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ESP:	return ("ESP");
711*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMP:	return ("ICMP");
712*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMPV6:	return ("ICMPv6");
713*7c478bd9Sstevel@tonic-gate 	case IPPROTO_DSTOPTS:	return ("IPv6-DstOpts");
714*7c478bd9Sstevel@tonic-gate 	case IPPROTO_IGMP:	return ("IGMP");
715*7c478bd9Sstevel@tonic-gate 	case IPPROTO_GGP:	return ("GGP");
716*7c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:	return ("TCP");
717*7c478bd9Sstevel@tonic-gate 	case IPPROTO_EGP:	return ("EGP");
718*7c478bd9Sstevel@tonic-gate 	case IPPROTO_PUP:	return ("PUP");
719*7c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:	return ("UDP");
720*7c478bd9Sstevel@tonic-gate 	case IPPROTO_IDP:	return ("IDP");
721*7c478bd9Sstevel@tonic-gate 	case IPPROTO_HELLO:	return ("HELLO");
722*7c478bd9Sstevel@tonic-gate 	case IPPROTO_ND:	return ("ND");
723*7c478bd9Sstevel@tonic-gate 	case IPPROTO_EON:	return ("EON");
724*7c478bd9Sstevel@tonic-gate 	case IPPROTO_RAW:	return ("RAW");
725*7c478bd9Sstevel@tonic-gate 	case IPPROTO_OSPF:	return ("OSPF");
726*7c478bd9Sstevel@tonic-gate 	default:		return ("");
727*7c478bd9Sstevel@tonic-gate 	}
728*7c478bd9Sstevel@tonic-gate }
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate static void
731*7c478bd9Sstevel@tonic-gate prt_routing_hdr(flags, ipv6ext_rthdr)
732*7c478bd9Sstevel@tonic-gate 	int flags;
733*7c478bd9Sstevel@tonic-gate 	struct ip6_rthdr *ipv6ext_rthdr;
734*7c478bd9Sstevel@tonic-gate {
735*7c478bd9Sstevel@tonic-gate 	uint8_t nxt_hdr;
736*7c478bd9Sstevel@tonic-gate 	uint8_t type;
737*7c478bd9Sstevel@tonic-gate 	uint32_t len;
738*7c478bd9Sstevel@tonic-gate 	uint8_t segleft;
739*7c478bd9Sstevel@tonic-gate 	uint32_t numaddrs;
740*7c478bd9Sstevel@tonic-gate 	int i;
741*7c478bd9Sstevel@tonic-gate 	struct ip6_rthdr0 *ipv6ext_rthdr0;
742*7c478bd9Sstevel@tonic-gate 	struct in6_addr *addrs;
743*7c478bd9Sstevel@tonic-gate 	char addr[INET6_ADDRSTRLEN];
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate 	/* in summary mode, we don't do anything. */
746*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
747*7c478bd9Sstevel@tonic-gate 		return;
748*7c478bd9Sstevel@tonic-gate 	}
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate 	nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
751*7c478bd9Sstevel@tonic-gate 	type = ipv6ext_rthdr->ip6r_type;
752*7c478bd9Sstevel@tonic-gate 	len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
753*7c478bd9Sstevel@tonic-gate 	segleft = ipv6ext_rthdr->ip6r_segleft;
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 	show_header("IPv6-Route:  ", "IPv6 Routing Header", 0);
756*7c478bd9Sstevel@tonic-gate 	show_space();
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
759*7c478bd9Sstevel@tonic-gate 	    "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
760*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
761*7c478bd9Sstevel@tonic-gate 	    "Header length = %d", len);
762*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
763*7c478bd9Sstevel@tonic-gate 	    "Routing type = %d", type);
764*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
765*7c478bd9Sstevel@tonic-gate 	    "Segments left = %d", segleft);
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate 	if (type == IPV6_RTHDR_TYPE_0) {
768*7c478bd9Sstevel@tonic-gate 		/*
769*7c478bd9Sstevel@tonic-gate 		 * XXX This loop will print all addresses in the routing header,
770*7c478bd9Sstevel@tonic-gate 		 * XXX not just the segments left.
771*7c478bd9Sstevel@tonic-gate 		 * XXX (The header length field is twice the number of
772*7c478bd9Sstevel@tonic-gate 		 * XXX addresses)
773*7c478bd9Sstevel@tonic-gate 		 * XXX At some future time, we may want to change this
774*7c478bd9Sstevel@tonic-gate 		 * XXX to differentiate between the hops yet to do
775*7c478bd9Sstevel@tonic-gate 		 * XXX and the hops already taken.
776*7c478bd9Sstevel@tonic-gate 		 */
777*7c478bd9Sstevel@tonic-gate 		ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
778*7c478bd9Sstevel@tonic-gate 		numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
779*7c478bd9Sstevel@tonic-gate 		addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
780*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < numaddrs; i++) {
781*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6, &addrs[i], addr,
782*7c478bd9Sstevel@tonic-gate 			    INET6_ADDRSTRLEN);
783*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_rthdr -
784*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
785*7c478bd9Sstevel@tonic-gate 			    "address[%d]=%s", i, addr);
786*7c478bd9Sstevel@tonic-gate 		}
787*7c478bd9Sstevel@tonic-gate 	}
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate 	show_space();
790*7c478bd9Sstevel@tonic-gate }
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate static void
793*7c478bd9Sstevel@tonic-gate prt_fragment_hdr(flags, ipv6ext_frag)
794*7c478bd9Sstevel@tonic-gate 	int flags;
795*7c478bd9Sstevel@tonic-gate 	struct ip6_frag *ipv6ext_frag;
796*7c478bd9Sstevel@tonic-gate {
797*7c478bd9Sstevel@tonic-gate 	boolean_t morefrag;
798*7c478bd9Sstevel@tonic-gate 	uint16_t fragoffset;
799*7c478bd9Sstevel@tonic-gate 	uint8_t nxt_hdr;
800*7c478bd9Sstevel@tonic-gate 	uint32_t fragident;
801*7c478bd9Sstevel@tonic-gate 
802*7c478bd9Sstevel@tonic-gate 	/* extract the various fields from the fragment header */
803*7c478bd9Sstevel@tonic-gate 	nxt_hdr = ipv6ext_frag->ip6f_nxt;
804*7c478bd9Sstevel@tonic-gate 	morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
805*7c478bd9Sstevel@tonic-gate 	    ? B_FALSE : B_TRUE;
806*7c478bd9Sstevel@tonic-gate 	fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
807*7c478bd9Sstevel@tonic-gate 	fragident = ntohl(ipv6ext_frag->ip6f_ident);
808*7c478bd9Sstevel@tonic-gate 
809*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
810*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_sum_line(),
811*7c478bd9Sstevel@tonic-gate 		    "IPv6 fragment ID=%d Offset=%-4d MF=%d",
812*7c478bd9Sstevel@tonic-gate 		    fragident,
813*7c478bd9Sstevel@tonic-gate 		    fragoffset,
814*7c478bd9Sstevel@tonic-gate 		    morefrag);
815*7c478bd9Sstevel@tonic-gate 	} else { /* F_DTAIL */
816*7c478bd9Sstevel@tonic-gate 		show_header("IPv6-Frag:  ", "IPv6 Fragment Header", 0);
817*7c478bd9Sstevel@tonic-gate 		show_space();
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
820*7c478bd9Sstevel@tonic-gate 		    "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
821*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
822*7c478bd9Sstevel@tonic-gate 		    "Fragment Offset = %d", fragoffset);
823*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
824*7c478bd9Sstevel@tonic-gate 		    "More Fragments Flag = %s", morefrag ? "true" : "false");
825*7c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
826*7c478bd9Sstevel@tonic-gate 		    "Identification = %d", fragident);
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 		show_space();
829*7c478bd9Sstevel@tonic-gate 	}
830*7c478bd9Sstevel@tonic-gate }
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate static void
833*7c478bd9Sstevel@tonic-gate prt_hbh_options(flags, ipv6ext_hbh)
834*7c478bd9Sstevel@tonic-gate 	int flags;
835*7c478bd9Sstevel@tonic-gate 	struct ip6_hbh *ipv6ext_hbh;
836*7c478bd9Sstevel@tonic-gate {
837*7c478bd9Sstevel@tonic-gate 	uint8_t *data;
838*7c478bd9Sstevel@tonic-gate 	uint32_t len, olen;
839*7c478bd9Sstevel@tonic-gate 	uint8_t op_type;
840*7c478bd9Sstevel@tonic-gate 	uint8_t op_len;
841*7c478bd9Sstevel@tonic-gate 	uint8_t nxt_hdr;
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 	/* in summary mode, we don't do anything. */
844*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
845*7c478bd9Sstevel@tonic-gate 		return;
846*7c478bd9Sstevel@tonic-gate 	}
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 	show_header("IPv6-HopOpts:  ", "IPv6 Hop-by-Hop Options Header", 0);
849*7c478bd9Sstevel@tonic-gate 	show_space();
850*7c478bd9Sstevel@tonic-gate 
851*7c478bd9Sstevel@tonic-gate 	/*
852*7c478bd9Sstevel@tonic-gate 	 * Store the lengh of this ext hdr in bytes.  The caller has
853*7c478bd9Sstevel@tonic-gate 	 * ensured that there is at least len bytes of data left.
854*7c478bd9Sstevel@tonic-gate 	 */
855*7c478bd9Sstevel@tonic-gate 	len = ipv6ext_hbh->ip6h_len * 8 + 8;
856*7c478bd9Sstevel@tonic-gate 
857*7c478bd9Sstevel@tonic-gate 	data = (uint8_t *)ipv6ext_hbh + 2;
858*7c478bd9Sstevel@tonic-gate 	len -= 2;
859*7c478bd9Sstevel@tonic-gate 
860*7c478bd9Sstevel@tonic-gate 	nxt_hdr = ipv6ext_hbh->ip6h_nxt;
861*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_hbh - dlc_header, 1),
862*7c478bd9Sstevel@tonic-gate 	    "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 	while (len > 0) {
865*7c478bd9Sstevel@tonic-gate 		GETINT8(op_type, data);
866*7c478bd9Sstevel@tonic-gate 		olen = len;
867*7c478bd9Sstevel@tonic-gate 		switch (op_type) {
868*7c478bd9Sstevel@tonic-gate 		case IP6OPT_PAD1:
869*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_hbh -
870*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
871*7c478bd9Sstevel@tonic-gate 			    "pad1 option ");
872*7c478bd9Sstevel@tonic-gate 			len--;
873*7c478bd9Sstevel@tonic-gate 			break;
874*7c478bd9Sstevel@tonic-gate 		case IP6OPT_PADN:
875*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
876*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_hbh -
877*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
878*7c478bd9Sstevel@tonic-gate 			    "padN option len = %u", op_len);
879*7c478bd9Sstevel@tonic-gate 			data += op_len;	/* skip pads */
880*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
881*7c478bd9Sstevel@tonic-gate 			break;
882*7c478bd9Sstevel@tonic-gate 		case IP6OPT_JUMBO: {
883*7c478bd9Sstevel@tonic-gate 			uint32_t payload_len;
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
886*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_hbh -
887*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
888*7c478bd9Sstevel@tonic-gate 			    "Jumbo Payload Option len = %u bytes", op_len);
889*7c478bd9Sstevel@tonic-gate 			if (op_len == sizeof (uint32_t)) {
890*7c478bd9Sstevel@tonic-gate 				GETINT32(payload_len, data);
891*7c478bd9Sstevel@tonic-gate 				(void) sprintf(get_line((char *)ipv6ext_hbh -
892*7c478bd9Sstevel@tonic-gate 				    dlc_header, 1),
893*7c478bd9Sstevel@tonic-gate 				    "Jumbo Payload Length = %u bytes",
894*7c478bd9Sstevel@tonic-gate 				    payload_len);
895*7c478bd9Sstevel@tonic-gate 			} else {
896*7c478bd9Sstevel@tonic-gate 				data += op_len;
897*7c478bd9Sstevel@tonic-gate 			}
898*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
899*7c478bd9Sstevel@tonic-gate 			break;
900*7c478bd9Sstevel@tonic-gate 		}
901*7c478bd9Sstevel@tonic-gate 		case IP6OPT_ROUTER_ALERT: {
902*7c478bd9Sstevel@tonic-gate 			uint16_t value;
903*7c478bd9Sstevel@tonic-gate 			const char *label[] = {"MLD", "RSVP", "AN"};
904*7c478bd9Sstevel@tonic-gate 
905*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
906*7c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line((char *)ipv6ext_hbh -
907*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1), get_line_remain(),
908*7c478bd9Sstevel@tonic-gate 			    "Router Alert Option len = %u bytes", op_len);
909*7c478bd9Sstevel@tonic-gate 			if (op_len == sizeof (uint16_t)) {
910*7c478bd9Sstevel@tonic-gate 				GETINT16(value, data);
911*7c478bd9Sstevel@tonic-gate 				(void) snprintf(get_line((char *)ipv6ext_hbh -
912*7c478bd9Sstevel@tonic-gate 				    dlc_header, 1), get_line_remain(),
913*7c478bd9Sstevel@tonic-gate 				    "Alert Type = %d (%s)", value,
914*7c478bd9Sstevel@tonic-gate 				    value < sizeof (label) / sizeof (label[0]) ?
915*7c478bd9Sstevel@tonic-gate 				    label[value] : "???");
916*7c478bd9Sstevel@tonic-gate 			} else {
917*7c478bd9Sstevel@tonic-gate 				data += op_len;
918*7c478bd9Sstevel@tonic-gate 			}
919*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
920*7c478bd9Sstevel@tonic-gate 			break;
921*7c478bd9Sstevel@tonic-gate 		}
922*7c478bd9Sstevel@tonic-gate 		default:
923*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
924*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_hbh -
925*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
926*7c478bd9Sstevel@tonic-gate 			    "Option type = %u, len = %u", op_type, op_len);
927*7c478bd9Sstevel@tonic-gate 			data += op_len;
928*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
929*7c478bd9Sstevel@tonic-gate 		}
930*7c478bd9Sstevel@tonic-gate 		/* check for corrupt length */
931*7c478bd9Sstevel@tonic-gate 		if (olen <= len) {
932*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_hbh -
933*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
934*7c478bd9Sstevel@tonic-gate 			    "Incomplete option len = %u, len = %u", op_type,
935*7c478bd9Sstevel@tonic-gate 			    len);
936*7c478bd9Sstevel@tonic-gate 			break;
937*7c478bd9Sstevel@tonic-gate 		}
938*7c478bd9Sstevel@tonic-gate 	}
939*7c478bd9Sstevel@tonic-gate 
940*7c478bd9Sstevel@tonic-gate 	show_space();
941*7c478bd9Sstevel@tonic-gate }
942*7c478bd9Sstevel@tonic-gate 
943*7c478bd9Sstevel@tonic-gate static void
944*7c478bd9Sstevel@tonic-gate prt_dest_options(flags, ipv6ext_dest)
945*7c478bd9Sstevel@tonic-gate 	int flags;
946*7c478bd9Sstevel@tonic-gate 	struct ip6_dest *ipv6ext_dest;
947*7c478bd9Sstevel@tonic-gate {
948*7c478bd9Sstevel@tonic-gate 	uint8_t *data;
949*7c478bd9Sstevel@tonic-gate 	uint32_t len, olen;
950*7c478bd9Sstevel@tonic-gate 	uint8_t op_type;
951*7c478bd9Sstevel@tonic-gate 	uint32_t op_len;
952*7c478bd9Sstevel@tonic-gate 	uint8_t nxt_hdr;
953*7c478bd9Sstevel@tonic-gate 	uint8_t value;
954*7c478bd9Sstevel@tonic-gate 
955*7c478bd9Sstevel@tonic-gate 	/* in summary mode, we don't do anything. */
956*7c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
957*7c478bd9Sstevel@tonic-gate 		return;
958*7c478bd9Sstevel@tonic-gate 	}
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	show_header("IPv6-DstOpts:  ", "IPv6 Destination Options Header", 0);
961*7c478bd9Sstevel@tonic-gate 	show_space();
962*7c478bd9Sstevel@tonic-gate 
963*7c478bd9Sstevel@tonic-gate 	/*
964*7c478bd9Sstevel@tonic-gate 	 * Store the length of this ext hdr in bytes.  The caller has
965*7c478bd9Sstevel@tonic-gate 	 * ensured that there is at least len bytes of data left.
966*7c478bd9Sstevel@tonic-gate 	 */
967*7c478bd9Sstevel@tonic-gate 	len = ipv6ext_dest->ip6d_len * 8 + 8;
968*7c478bd9Sstevel@tonic-gate 
969*7c478bd9Sstevel@tonic-gate 	data = (uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
970*7c478bd9Sstevel@tonic-gate 	len -= 2;
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 	nxt_hdr = ipv6ext_dest->ip6d_nxt;
973*7c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)ipv6ext_dest - dlc_header, 1),
974*7c478bd9Sstevel@tonic-gate 	    "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
975*7c478bd9Sstevel@tonic-gate 
976*7c478bd9Sstevel@tonic-gate 	while (len > 0) {
977*7c478bd9Sstevel@tonic-gate 		GETINT8(op_type, data);
978*7c478bd9Sstevel@tonic-gate 		olen = len;
979*7c478bd9Sstevel@tonic-gate 		switch (op_type) {
980*7c478bd9Sstevel@tonic-gate 		case IP6OPT_PAD1:
981*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_dest -
982*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
983*7c478bd9Sstevel@tonic-gate 			    "pad1 option ");
984*7c478bd9Sstevel@tonic-gate 			len--;
985*7c478bd9Sstevel@tonic-gate 			break;
986*7c478bd9Sstevel@tonic-gate 		case IP6OPT_PADN:
987*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
988*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_dest -
989*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
990*7c478bd9Sstevel@tonic-gate 			    "padN option len = %u", op_len);
991*7c478bd9Sstevel@tonic-gate 			data += op_len;
992*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
993*7c478bd9Sstevel@tonic-gate 			break;
994*7c478bd9Sstevel@tonic-gate 		case IP6OPT_TUNNEL_LIMIT:
995*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
996*7c478bd9Sstevel@tonic-gate 			GETINT8(value, data);
997*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_dest -
998*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
999*7c478bd9Sstevel@tonic-gate 			    "tunnel encapsulation limit len = %d, value = %d",
1000*7c478bd9Sstevel@tonic-gate 			    op_len, value);
1001*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
1002*7c478bd9Sstevel@tonic-gate 			break;
1003*7c478bd9Sstevel@tonic-gate 		default:
1004*7c478bd9Sstevel@tonic-gate 			GETINT8(op_len, data);
1005*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_dest -
1006*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
1007*7c478bd9Sstevel@tonic-gate 			    "Option type = %u, len = %u", op_type, op_len);
1008*7c478bd9Sstevel@tonic-gate 			data += op_len;
1009*7c478bd9Sstevel@tonic-gate 			len -= (op_len + 2);
1010*7c478bd9Sstevel@tonic-gate 		}
1011*7c478bd9Sstevel@tonic-gate 		/* check for corrupt length */
1012*7c478bd9Sstevel@tonic-gate 		if (olen <= len) {
1013*7c478bd9Sstevel@tonic-gate 			(void) sprintf(get_line((char *)ipv6ext_dest -
1014*7c478bd9Sstevel@tonic-gate 			    dlc_header, 1),
1015*7c478bd9Sstevel@tonic-gate 			    "Incomplete option len = %u, len = %u", op_type,
1016*7c478bd9Sstevel@tonic-gate 			    len);
1017*7c478bd9Sstevel@tonic-gate 			break;
1018*7c478bd9Sstevel@tonic-gate 		}
1019*7c478bd9Sstevel@tonic-gate 	}
1020*7c478bd9Sstevel@tonic-gate 
1021*7c478bd9Sstevel@tonic-gate 	show_space();
1022*7c478bd9Sstevel@tonic-gate }
1023