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