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