1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 */
26
27#include <stdio.h>
28#include <string.h>
29#include <fcntl.h>
30#include <string.h>
31#include <sys/types.h>
32#include <sys/time.h>
33
34#include <sys/stropts.h>
35#include <sys/socket.h>
36#include <net/if.h>
37#include <netinet/in_systm.h>
38#include <netinet/in.h>
39#include <netinet/ip.h>
40#include <netinet/ip6.h>
41#include <netinet/ip_icmp.h>
42#include <netinet/icmp6.h>
43#include <netinet/if_ether.h>
44#include <inet/ip.h>
45#include <inet/ip6.h>
46#include <arpa/inet.h>
47#include <netdb.h>
48#include <tsol/label.h>
49#include <sys/tsol/tndb.h>
50#include <sys/tsol/label_macro.h>
51
52#include "snoop.h"
53
54
55/*
56 * IPv6 extension header masks.  These are used by the print_ipv6_extensions()
57 * function to return information to the caller about which extension headers
58 * were processed.  This can be useful if the caller wants to know if the
59 * packet is an IPv6 fragment, for example.
60 */
61#define	SNOOP_HOPOPTS	0x01U
62#define	SNOOP_ROUTING	0x02U
63#define	SNOOP_DSTOPTS	0x04U
64#define	SNOOP_FRAGMENT	0x08U
65#define	SNOOP_AH	0x10U
66#define	SNOOP_ESP	0x20U
67#define	SNOOP_IPV6	0x40U
68
69static void prt_routing_hdr(int, const struct ip6_rthdr *);
70static void prt_fragment_hdr(int, const struct ip6_frag *);
71static void prt_hbh_options(int, const struct ip6_hbh *);
72static void prt_dest_options(int, const struct ip6_dest *);
73static void print_route(const uchar_t *);
74static void print_ipoptions(const uchar_t *, int);
75static void print_ripso(const uchar_t *);
76static void print_cipso(const uchar_t *);
77
78/* Keep track of how many nested IP headers we have. */
79unsigned int encap_levels;
80unsigned int total_encap_levels = 1;
81
82int
83interpret_ip(int flags, const struct ip *ip, int fraglen)
84{
85	uchar_t *data;
86	char buff[24];
87	boolean_t isfrag = B_FALSE;
88	boolean_t morefrag;
89	uint16_t fragoffset;
90	int hdrlen;
91	uint16_t iplen, uitmp;
92
93	if (ip->ip_v == IPV6_VERSION) {
94		iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen);
95		return (iplen);
96	}
97
98	if (encap_levels == 0)
99		total_encap_levels = 0;
100	encap_levels++;
101	total_encap_levels++;
102
103	hdrlen = ip->ip_hl * 4;
104	data = ((uchar_t *)ip) + hdrlen;
105	iplen = ntohs(ip->ip_len) - hdrlen;
106	fraglen -= hdrlen;
107	if (fraglen > iplen)
108		fraglen = iplen;
109	if (fraglen < 0) {
110		(void) snprintf(get_sum_line(), MAXLINE,
111		    "IP truncated: header missing %d bytes", -fraglen);
112		encap_levels--;
113		return (fraglen + iplen);
114	}
115	/*
116	 * We flag this as a fragment if the more fragments bit is set, or
117	 * if the fragment offset is non-zero.
118	 */
119	morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE;
120	fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8;
121	if (morefrag || fragoffset != 0)
122		isfrag = B_TRUE;
123
124	src_name = addrtoname(AF_INET, &ip->ip_src);
125	dst_name = addrtoname(AF_INET, &ip->ip_dst);
126
127	if (flags & F_SUM) {
128		if (isfrag) {
129			(void) snprintf(get_sum_line(), MAXLINE,
130			    "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x "
131			    "TTL=%d",
132			    getproto(ip->ip_p),
133			    ntohs(ip->ip_id),
134			    fragoffset,
135			    morefrag,
136			    ip->ip_tos,
137			    ip->ip_ttl);
138		} else {
139			(void) strlcpy(buff, inet_ntoa(ip->ip_dst),
140			    sizeof (buff));
141			uitmp = ntohs(ip->ip_len);
142			(void) snprintf(get_sum_line(), MAXLINE,
143			    "IP  D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d",
144			    buff,
145			    inet_ntoa(ip->ip_src),
146			    uitmp,
147			    iplen > fraglen ? "?" : "",
148			    ntohs(ip->ip_id),
149			    ip->ip_tos,
150			    ip->ip_ttl);
151		}
152	}
153
154	if (flags & F_DTAIL) {
155		show_header("IP:   ", "IP Header", iplen);
156		show_space();
157		(void) snprintf(get_line(0, 0), get_line_remain(),
158		    "Version = %d", ip->ip_v);
159		(void) snprintf(get_line(0, 0), get_line_remain(),
160		    "Header length = %d bytes", hdrlen);
161		(void) snprintf(get_line(0, 0), get_line_remain(),
162		    "Type of service = 0x%02x", ip->ip_tos);
163		(void) snprintf(get_line(0, 0), get_line_remain(),
164		    "      xxx. .... = %d (precedence)",
165		    ip->ip_tos >> 5);
166		(void) snprintf(get_line(0, 0), get_line_remain(),
167		    "      %s", getflag(ip->ip_tos, IPTOS_LOWDELAY,
168		    "low delay", "normal delay"));
169		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
170		    getflag(ip->ip_tos, IPTOS_THROUGHPUT,
171		    "high throughput", "normal throughput"));
172		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
173		    getflag(ip->ip_tos, IPTOS_RELIABILITY,
174		    "high reliability", "normal reliability"));
175		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
176		    getflag(ip->ip_tos, IPTOS_ECT,
177		    "ECN capable transport", "not ECN capable transport"));
178		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
179		    getflag(ip->ip_tos, IPTOS_CE,
180		    "ECN congestion experienced",
181		    "no ECN congestion experienced"));
182		/* warning: ip_len is signed in netinet/ip.h */
183		uitmp = ntohs(ip->ip_len);
184		(void) snprintf(get_line(0, 0), get_line_remain(),
185		    "Total length = %u bytes%s", uitmp,
186		    iplen > fraglen ? " -- truncated" : "");
187		(void) snprintf(get_line(0, 0), get_line_remain(),
188		    "Identification = %d", ntohs(ip->ip_id));
189		/* warning: ip_off is signed in netinet/ip.h */
190		uitmp = ntohs(ip->ip_off);
191		(void) snprintf(get_line(0, 0), get_line_remain(),
192		    "Flags = 0x%x", uitmp >> 12);
193		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
194		    getflag(uitmp >> 8, IP_DF >> 8,
195		    "do not fragment", "may fragment"));
196		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
197		    getflag(uitmp >> 8, IP_MF >> 8,
198		    "more fragments", "last fragment"));
199		(void) snprintf(get_line(0, 0), get_line_remain(),
200		    "Fragment offset = %u bytes",
201		    fragoffset);
202		(void) snprintf(get_line(0, 0), get_line_remain(),
203		    "Time to live = %d seconds/hops",
204		    ip->ip_ttl);
205		(void) snprintf(get_line(0, 0), get_line_remain(),
206		    "Protocol = %d (%s)", ip->ip_p,
207		    getproto(ip->ip_p));
208		/*
209		 * XXX need to compute checksum and print whether it's correct
210		 */
211		(void) snprintf(get_line(0, 0), get_line_remain(),
212		    "Header checksum = %04x",
213		    ntohs(ip->ip_sum));
214		(void) snprintf(get_line(0, 0), get_line_remain(),
215		    "Source address = %s, %s",
216		    inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src));
217		(void) snprintf(get_line(0, 0), get_line_remain(),
218		    "Destination address = %s, %s",
219		    inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst));
220
221		/* Print IP options - if any */
222
223		print_ipoptions((const uchar_t *)(ip + 1),
224		    hdrlen - sizeof (struct ip));
225		show_space();
226	}
227
228	/*
229	 * If we are in detail mode, and this is not the first fragment of
230	 * a fragmented packet, print out a little line stating this.
231	 * Otherwise, go to the next protocol layer only if this is not a
232	 * fragment, or we are in detail mode and this is the first fragment
233	 * of a fragmented packet.
234	 */
235	if (flags & F_DTAIL && fragoffset != 0) {
236		(void) snprintf(get_detail_line(0, 0), MAXLINE,
237		    "%s:  [%d byte(s) of data, continuation of IP ident=%d]",
238		    getproto(ip->ip_p),
239		    iplen,
240		    ntohs(ip->ip_id));
241	} else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) {
242		/* go to the next protocol layer */
243
244		if (fraglen > 0) {
245			switch (ip->ip_p) {
246			case IPPROTO_IP:
247				break;
248			case IPPROTO_ENCAP:
249				(void) interpret_ip(flags,
250				    /* LINTED: alignment */
251				    (const struct ip *)data, fraglen);
252				break;
253			case IPPROTO_ICMP:
254				(void) interpret_icmp(flags,
255				    /* LINTED: alignment */
256				    (struct icmp *)data, iplen, fraglen);
257				break;
258			case IPPROTO_IGMP:
259				interpret_igmp(flags, data, iplen, fraglen);
260				break;
261			case IPPROTO_GGP:
262				break;
263			case IPPROTO_TCP:
264				(void) interpret_tcp(flags,
265				    (struct tcphdr *)data, iplen, fraglen);
266				break;
267
268			case IPPROTO_ESP:
269				(void) interpret_esp(flags, data, iplen,
270				    fraglen);
271				break;
272			case IPPROTO_AH:
273				(void) interpret_ah(flags, data, iplen,
274				    fraglen);
275				break;
276
277			case IPPROTO_OSPF:
278				interpret_ospf(flags, data, iplen, fraglen);
279				break;
280
281			case IPPROTO_EGP:
282			case IPPROTO_PUP:
283				break;
284			case IPPROTO_UDP:
285				(void) interpret_udp(flags,
286				    (struct udphdr *)data, iplen, fraglen);
287				break;
288
289			case IPPROTO_IDP:
290			case IPPROTO_HELLO:
291			case IPPROTO_ND:
292			case IPPROTO_RAW:
293				break;
294			case IPPROTO_IPV6:	/* IPV6 encap */
295				/* LINTED: alignment */
296				(void) interpret_ipv6(flags, (ip6_t *)data,
297				    iplen);
298				break;
299			case IPPROTO_SCTP:
300				(void) interpret_sctp(flags,
301				    (struct sctp_hdr *)data, iplen, fraglen);
302				break;
303			}
304		}
305	}
306
307	encap_levels--;
308	return (iplen);
309}
310
311int
312interpret_ipv6(int flags, const ip6_t *ip6h, int fraglen)
313{
314	uint8_t *data;
315	int hdrlen, iplen;
316	int version, flow, class;
317	uchar_t proto;
318	boolean_t isfrag = B_FALSE;
319	uint8_t extmask;
320	/*
321	 * The print_srcname and print_dstname strings are the hostname
322	 * parts of the verbose IPv6 header output, including the comma
323	 * and the space after the litteral address strings.
324	 */
325	char print_srcname[MAXHOSTNAMELEN + 2];
326	char print_dstname[MAXHOSTNAMELEN + 2];
327	char src_addrstr[INET6_ADDRSTRLEN];
328	char dst_addrstr[INET6_ADDRSTRLEN];
329
330	iplen = ntohs(ip6h->ip6_plen);
331	hdrlen = IPV6_HDR_LEN;
332	fraglen -= hdrlen;
333	if (fraglen < 0)
334		return (fraglen + hdrlen);
335	data = ((uint8_t *)ip6h) + hdrlen;
336
337	proto = ip6h->ip6_nxt;
338
339	src_name = addrtoname(AF_INET6, &ip6h->ip6_src);
340	dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst);
341
342	/*
343	 * Use endian-aware masks to extract traffic class and
344	 * flowinfo.  Also, flowinfo is now 20 bits and class 8
345	 * rather than 24 and 4.
346	 */
347	class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20);
348	flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL);
349
350	/*
351	 * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive,
352	 * so the code within the first part of the following if statement
353	 * will not affect the detailed printing of the packet.
354	 */
355	if (flags & F_SUM) {
356		(void) snprintf(get_sum_line(), MAXLINE,
357		    "IPv6  S=%s D=%s LEN=%d HOPS=%d CLASS=0x%x FLOW=0x%x",
358		    src_name, dst_name, iplen, ip6h->ip6_hops, class, flow);
359	} else if (flags & F_DTAIL) {
360
361		(void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr,
362		    INET6_ADDRSTRLEN);
363		(void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr,
364		    INET6_ADDRSTRLEN);
365
366		version = ntohl(ip6h->ip6_vcf) >> 28;
367
368		if (strcmp(src_name, src_addrstr) == 0) {
369			print_srcname[0] = '\0';
370		} else {
371			snprintf(print_srcname, sizeof (print_srcname),
372			    ", %s", src_name);
373		}
374
375		if (strcmp(dst_name, dst_addrstr) == 0) {
376			print_dstname[0] = '\0';
377		} else {
378			snprintf(print_dstname, sizeof (print_dstname),
379			    ", %s", dst_name);
380		}
381
382		show_header("IPv6:   ", "IPv6 Header", iplen);
383		show_space();
384
385		(void) snprintf(get_line(0, 0), get_line_remain(),
386		    "Version = %d", version);
387		(void) snprintf(get_line(0, 0), get_line_remain(),
388		    "Traffic Class = %d", class);
389		(void) snprintf(get_line(0, 0), get_line_remain(),
390		    "Flow label = 0x%x", flow);
391		(void) snprintf(get_line(0, 0), get_line_remain(),
392		    "Payload length = %d", iplen);
393		(void) snprintf(get_line(0, 0), get_line_remain(),
394		    "Next Header = %d (%s)", proto,
395		    getproto(proto));
396		(void) snprintf(get_line(0, 0), get_line_remain(),
397		    "Hop Limit = %d", ip6h->ip6_hops);
398		(void) snprintf(get_line(0, 0), get_line_remain(),
399		    "Source address = %s%s", src_addrstr, print_srcname);
400		(void) snprintf(get_line(0, 0), get_line_remain(),
401		    "Destination address = %s%s", dst_addrstr, print_dstname);
402
403		show_space();
404	}
405
406	/*
407	 * Print IPv6 Extension Headers, or skip them in the summary case.
408	 * Set isfrag to true if one of the extension headers encounterred
409	 * was a fragment header.
410	 */
411	if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
412	    proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
413		extmask = print_ipv6_extensions(flags, &data, &proto, &iplen,
414		    &fraglen);
415		if ((extmask & SNOOP_FRAGMENT) != 0) {
416			isfrag = B_TRUE;
417		}
418	}
419
420	/*
421	 * We only want to print upper layer information if this is not
422	 * a fragment, or if we're printing in detail.  Note that the
423	 * proto variable will be set to IPPROTO_NONE if this is a fragment
424	 * with a non-zero fragment offset.
425	 */
426	if (!isfrag || flags & F_DTAIL) {
427		/* go to the next protocol layer */
428
429		switch (proto) {
430		case IPPROTO_IP:
431			break;
432		case IPPROTO_ENCAP:
433			/* LINTED: alignment */
434			(void) interpret_ip(flags, (const struct ip *)data,
435			    fraglen);
436			break;
437		case IPPROTO_ICMPV6:
438			/* LINTED: alignment */
439			(void) interpret_icmpv6(flags, (icmp6_t *)data, iplen,
440			    fraglen);
441			break;
442		case IPPROTO_IGMP:
443			interpret_igmp(flags, data, iplen, fraglen);
444			break;
445		case IPPROTO_GGP:
446			break;
447		case IPPROTO_TCP:
448			(void) interpret_tcp(flags, (struct tcphdr *)data,
449			    iplen, fraglen);
450			break;
451		case IPPROTO_ESP:
452			(void) interpret_esp(flags, data, iplen, fraglen);
453			break;
454		case IPPROTO_AH:
455			(void) interpret_ah(flags, data, iplen, fraglen);
456			break;
457		case IPPROTO_EGP:
458		case IPPROTO_PUP:
459			break;
460		case IPPROTO_UDP:
461			(void) interpret_udp(flags, (struct udphdr *)data,
462			    iplen, fraglen);
463			break;
464		case IPPROTO_IDP:
465		case IPPROTO_HELLO:
466		case IPPROTO_ND:
467		case IPPROTO_RAW:
468			break;
469		case IPPROTO_IPV6:
470			/* LINTED: alignment */
471			(void) interpret_ipv6(flags, (const ip6_t *)data,
472			    iplen);
473			break;
474		case IPPROTO_SCTP:
475			(void) interpret_sctp(flags, (struct sctp_hdr *)data,
476			    iplen, fraglen);
477			break;
478		case IPPROTO_OSPF:
479			interpret_ospf6(flags, data, iplen, fraglen);
480			break;
481		}
482	}
483
484	return (iplen);
485}
486
487/*
488 * ip_ext: data including the extension header.
489 * iplen: length of the data remaining in the packet.
490 * Returns a mask of IPv6 extension headers it processed.
491 */
492uint8_t
493print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen,
494    int *fraglen)
495{
496	uint8_t *data_ptr;
497	uchar_t proto = *next;
498	boolean_t is_extension_header;
499	struct ip6_hbh *ipv6ext_hbh;
500	struct ip6_dest *ipv6ext_dest;
501	struct ip6_rthdr *ipv6ext_rthdr;
502	struct ip6_frag *ipv6ext_frag;
503	uint32_t exthdrlen;
504	uint8_t extmask = 0;
505
506	if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0))
507		return (0);
508
509	data_ptr = *hdr;
510	is_extension_header = B_TRUE;
511	while (is_extension_header) {
512
513		/*
514		 * There must be at least enough data left to read the
515		 * next header and header length fields from the next
516		 * header.
517		 */
518		if (*fraglen < 2) {
519			return (extmask);
520		}
521
522		switch (proto) {
523		case IPPROTO_HOPOPTS:
524			ipv6ext_hbh = (struct ip6_hbh *)data_ptr;
525			exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8;
526			if (*fraglen <= exthdrlen) {
527				return (extmask);
528			}
529			prt_hbh_options(flags, ipv6ext_hbh);
530			extmask |= SNOOP_HOPOPTS;
531			proto = ipv6ext_hbh->ip6h_nxt;
532			break;
533		case IPPROTO_DSTOPTS:
534			ipv6ext_dest = (struct ip6_dest *)data_ptr;
535			exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8;
536			if (*fraglen <= exthdrlen) {
537				return (extmask);
538			}
539			prt_dest_options(flags, ipv6ext_dest);
540			extmask |= SNOOP_DSTOPTS;
541			proto = ipv6ext_dest->ip6d_nxt;
542			break;
543		case IPPROTO_ROUTING:
544			ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr;
545			exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8;
546			if (*fraglen <= exthdrlen) {
547				return (extmask);
548			}
549			prt_routing_hdr(flags, ipv6ext_rthdr);
550			extmask |= SNOOP_ROUTING;
551			proto = ipv6ext_rthdr->ip6r_nxt;
552			break;
553		case IPPROTO_FRAGMENT:
554			/* LINTED: alignment */
555			ipv6ext_frag = (struct ip6_frag *)data_ptr;
556			exthdrlen = sizeof (struct ip6_frag);
557			if (*fraglen <= exthdrlen) {
558				return (extmask);
559			}
560			prt_fragment_hdr(flags, ipv6ext_frag);
561			extmask |= SNOOP_FRAGMENT;
562			/*
563			 * If this is not the first fragment, forget about
564			 * the rest of the packet, snoop decoding is
565			 * stateless.
566			 */
567			if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0)
568				proto = IPPROTO_NONE;
569			else
570				proto = ipv6ext_frag->ip6f_nxt;
571			break;
572		default:
573			is_extension_header = B_FALSE;
574			break;
575		}
576
577		if (is_extension_header) {
578			*iplen -= exthdrlen;
579			*fraglen -= exthdrlen;
580			data_ptr += exthdrlen;
581		}
582	}
583
584	*next = proto;
585	*hdr = data_ptr;
586	return (extmask);
587}
588
589static void
590print_ipoptions(const uchar_t *opt, int optlen)
591{
592	int len;
593	int remain;
594	char *line;
595	const char *truncstr;
596
597	if (optlen <= 0) {
598		(void) snprintf(get_line(0, 0), get_line_remain(),
599		    "No options");
600		return;
601	}
602
603	(void) snprintf(get_line(0, 0), get_line_remain(),
604	    "Options: (%d bytes)", optlen);
605
606	while (optlen > 0) {
607		line = get_line(0, 0);
608		remain = get_line_remain();
609		len = opt[1];
610		truncstr = len > optlen ? "?" : "";
611		switch (opt[0]) {
612		case IPOPT_EOL:
613			(void) strlcpy(line, "  - End of option list", remain);
614			return;
615		case IPOPT_NOP:
616			(void) strlcpy(line, "  - No op", remain);
617			len = 1;
618			break;
619		case IPOPT_RR:
620			(void) snprintf(line, remain,
621			    "  - Record route (%d bytes%s)", len, truncstr);
622			print_route(opt);
623			break;
624		case IPOPT_TS:
625			(void) snprintf(line, remain,
626			    "  - Time stamp (%d bytes%s)", len, truncstr);
627			break;
628		case IPOPT_SECURITY:
629			(void) snprintf(line, remain, "  - RIPSO (%d bytes%s)",
630			    len, truncstr);
631			print_ripso(opt);
632			break;
633		case IPOPT_COMSEC:
634			(void) snprintf(line, remain, "  - CIPSO (%d bytes%s)",
635			    len, truncstr);
636			print_cipso(opt);
637			break;
638		case IPOPT_LSRR:
639			(void) snprintf(line, remain,
640			    "  - Loose source route (%d bytes%s)", len,
641			    truncstr);
642			print_route(opt);
643			break;
644		case IPOPT_SATID:
645			(void) snprintf(line, remain,
646			    "  - SATNET Stream id (%d bytes%s)",
647			    len, truncstr);
648			break;
649		case IPOPT_SSRR:
650			(void) snprintf(line, remain,
651			    "  - Strict source route, (%d bytes%s)", len,
652			    truncstr);
653			print_route(opt);
654			break;
655		default:
656			(void) snprintf(line, remain,
657			    "  - Option %d (unknown - %d bytes%s) %s",
658			    opt[0], len, truncstr,
659			    tohex((char *)&opt[2], len - 2));
660			break;
661		}
662		if (len <= 0) {
663			(void) snprintf(line, remain,
664			    "  - Incomplete option len %d", len);
665			break;
666		}
667		opt += len;
668		optlen -= len;
669	}
670}
671
672static void
673print_route(const uchar_t *opt)
674{
675	int len, pointer, remain;
676	struct in_addr addr;
677	char *line;
678
679	len = opt[1];
680	pointer = opt[2];
681
682	(void) snprintf(get_line(0, 0), get_line_remain(),
683	    "    Pointer = %d", pointer);
684
685	pointer -= IPOPT_MINOFF;
686	opt += (IPOPT_OFFSET + 1);
687	len -= (IPOPT_OFFSET + 1);
688
689	while (len > 0) {
690		line = get_line(0, 0);
691		remain = get_line_remain();
692		memcpy((char *)&addr, opt, sizeof (addr));
693		if (addr.s_addr == INADDR_ANY)
694			(void) strlcpy(line, "      -", remain);
695		else
696			(void) snprintf(line, remain, "      %s",
697			    addrtoname(AF_INET, &addr));
698		if (pointer == 0)
699			(void) strlcat(line, "  <-- (current)", remain);
700
701		opt += sizeof (addr);
702		len -= sizeof (addr);
703		pointer -= sizeof (addr);
704	}
705}
706
707char *
708getproto(int p)
709{
710	switch (p) {
711	case IPPROTO_HOPOPTS:	return ("IPv6-HopOpts");
712	case IPPROTO_IPV6:	return ("IPv6");
713	case IPPROTO_ROUTING:	return ("IPv6-Route");
714	case IPPROTO_FRAGMENT:	return ("IPv6-Frag");
715	case IPPROTO_RSVP:	return ("RSVP");
716	case IPPROTO_ENCAP:	return ("IP-in-IP");
717	case IPPROTO_AH:	return ("AH");
718	case IPPROTO_ESP:	return ("ESP");
719	case IPPROTO_ICMP:	return ("ICMP");
720	case IPPROTO_ICMPV6:	return ("ICMPv6");
721	case IPPROTO_DSTOPTS:	return ("IPv6-DstOpts");
722	case IPPROTO_IGMP:	return ("IGMP");
723	case IPPROTO_GGP:	return ("GGP");
724	case IPPROTO_TCP:	return ("TCP");
725	case IPPROTO_EGP:	return ("EGP");
726	case IPPROTO_PUP:	return ("PUP");
727	case IPPROTO_UDP:	return ("UDP");
728	case IPPROTO_IDP:	return ("IDP");
729	case IPPROTO_HELLO:	return ("HELLO");
730	case IPPROTO_ND:	return ("ND");
731	case IPPROTO_EON:	return ("EON");
732	case IPPROTO_RAW:	return ("RAW");
733	case IPPROTO_OSPF:	return ("OSPF");
734	default:		return ("");
735	}
736}
737
738static void
739prt_routing_hdr(int flags, const struct ip6_rthdr *ipv6ext_rthdr)
740{
741	uint8_t nxt_hdr;
742	uint8_t type;
743	uint32_t len;
744	uint8_t segleft;
745	uint32_t numaddrs;
746	int i;
747	struct ip6_rthdr0 *ipv6ext_rthdr0;
748	struct in6_addr *addrs;
749	char addr[INET6_ADDRSTRLEN];
750
751	/* in summary mode, we don't do anything. */
752	if (flags & F_SUM) {
753		return;
754	}
755
756	nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
757	type = ipv6ext_rthdr->ip6r_type;
758	len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
759	segleft = ipv6ext_rthdr->ip6r_segleft;
760
761	show_header("IPv6-Route:  ", "IPv6 Routing Header", 0);
762	show_space();
763
764	(void) snprintf(get_line(0, 0), get_line_remain(),
765	    "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
766	(void) snprintf(get_line(0, 0), get_line_remain(),
767	    "Header length = %d", len);
768	(void) snprintf(get_line(0, 0), get_line_remain(),
769	    "Routing type = %d", type);
770	(void) snprintf(get_line(0, 0), get_line_remain(),
771	    "Segments left = %d", segleft);
772
773	if (type == IPV6_RTHDR_TYPE_0) {
774		/*
775		 * XXX This loop will print all addresses in the routing header,
776		 * XXX not just the segments left.
777		 * XXX (The header length field is twice the number of
778		 * XXX addresses)
779		 * XXX At some future time, we may want to change this
780		 * XXX to differentiate between the hops yet to do
781		 * XXX and the hops already taken.
782		 */
783		/* LINTED: alignment */
784		ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
785		numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
786		addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
787		for (i = 0; i < numaddrs; i++) {
788			(void) inet_ntop(AF_INET6, &addrs[i], addr,
789			    INET6_ADDRSTRLEN);
790			(void) snprintf(get_line(0, 0), get_line_remain(),
791			    "address[%d]=%s", i, addr);
792		}
793	}
794
795	show_space();
796}
797
798static void
799prt_fragment_hdr(int flags, const struct ip6_frag *ipv6ext_frag)
800{
801	boolean_t morefrag;
802	uint16_t fragoffset;
803	uint8_t nxt_hdr;
804	uint32_t fragident;
805
806	/* extract the various fields from the fragment header */
807	nxt_hdr = ipv6ext_frag->ip6f_nxt;
808	morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
809	    ? B_FALSE : B_TRUE;
810	fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
811	fragident = ntohl(ipv6ext_frag->ip6f_ident);
812
813	if (flags & F_SUM) {
814		(void) snprintf(get_sum_line(), MAXLINE,
815		    "IPv6 fragment ID=%u Offset=%-4d MF=%d",
816		    fragident,
817		    fragoffset,
818		    morefrag);
819	} else { /* F_DTAIL */
820		show_header("IPv6-Frag:  ", "IPv6 Fragment Header", 0);
821		show_space();
822
823		(void) snprintf(get_line(0, 0), get_line_remain(),
824		    "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
825		(void) snprintf(get_line(0, 0), get_line_remain(),
826		    "Fragment Offset = %d", fragoffset);
827		(void) snprintf(get_line(0, 0), get_line_remain(),
828		    "More Fragments Flag = %s", morefrag ? "true" : "false");
829		(void) snprintf(get_line(0, 0), get_line_remain(),
830		    "Identification = %u", fragident);
831
832		show_space();
833	}
834}
835
836static void
837print_ip6opt_ls(const uchar_t *data, unsigned int op_len)
838{
839	uint32_t doi;
840	uint8_t sotype, solen;
841	uint16_t value, value2;
842	char *cp;
843	int remlen;
844	boolean_t printed;
845
846	(void) snprintf(get_line(0, 0), get_line_remain(),
847	    "Labeled Security Option len = %u bytes%s", op_len,
848	    op_len < sizeof (uint32_t) || (op_len & 1) != 0 ? "?" : "");
849	if (op_len < sizeof (uint32_t))
850		return;
851	GETINT32(doi, data);
852	(void) snprintf(get_line(0, 0), get_line_remain(),
853	    "    DOI = %d (%s)", doi, doi == IP6LS_DOI_V4 ? "IPv4" : "???");
854	op_len -= sizeof (uint32_t);
855	while (op_len > 0) {
856		GETINT8(sotype, data);
857		if (op_len < 2) {
858			(void) snprintf(get_line(0, 0), get_line_remain(),
859			    "    truncated %u suboption (no len)", sotype);
860			break;
861		}
862		GETINT8(solen, data);
863		if (solen < 2 || solen > op_len) {
864			(void) snprintf(get_line(0, 0), get_line_remain(),
865			    "    bad %u suboption (len 2 <= %u <= %u)",
866			    sotype, solen, op_len);
867			if (solen < 2)
868				solen = 2;
869			if (solen > op_len)
870				solen = op_len;
871		}
872		op_len -= solen;
873		solen -= 2;
874		cp = get_line(0, 0);
875		remlen = get_line_remain();
876		(void) strlcpy(cp, "    ", remlen);
877		cp += 4;
878		remlen -= 4;
879		printed = B_TRUE;
880		switch (sotype) {
881		case IP6LS_TT_LEVEL:
882			if (solen != 2) {
883				printed = B_FALSE;
884				break;
885			}
886			GETINT16(value, data);
887			(void) snprintf(cp, remlen, "Level %u", value);
888			solen = 0;
889			break;
890		case IP6LS_TT_VECTOR:
891			(void) strlcpy(cp, "Bit-Vector: ", remlen);
892			remlen -= strlen(cp);
893			cp += strlen(cp);
894			while (solen > 1) {
895				GETINT16(value, data);
896				solen -= 2;
897				(void) snprintf(cp, remlen, "%04x", value);
898				remlen -= strlen(cp);
899				cp += strlen(cp);
900			}
901			break;
902		case IP6LS_TT_ENUM:
903			(void) strlcpy(cp, "Enumeration:", remlen);
904			remlen -= strlen(cp);
905			cp += strlen(cp);
906			while (solen > 1) {
907				GETINT16(value, data);
908				solen -= 2;
909				(void) snprintf(cp, remlen, " %u", value);
910				remlen -= strlen(cp);
911				cp += strlen(cp);
912			}
913			break;
914		case IP6LS_TT_RANGES:
915			(void) strlcpy(cp, "Ranges:", remlen);
916			remlen -= strlen(cp);
917			cp += strlen(cp);
918			while (solen > 3) {
919				GETINT16(value, data);
920				GETINT16(value2, data);
921				solen -= 4;
922				(void) snprintf(cp, remlen, " %u-%u", value,
923				    value2);
924				remlen -= strlen(cp);
925				cp += strlen(cp);
926			}
927			break;
928		case IP6LS_TT_V4:
929			(void) strlcpy(cp, "IPv4 Option", remlen);
930			print_ipoptions(data, solen);
931			solen = 0;
932			break;
933		case IP6LS_TT_DEST:
934			(void) snprintf(cp, remlen,
935			    "Destination-Only Data length %u", solen);
936			solen = 0;
937			break;
938		default:
939			(void) snprintf(cp, remlen,
940			    "    unknown %u suboption (len %u)", sotype, solen);
941			solen = 0;
942			break;
943		}
944		if (solen != 0) {
945			if (printed) {
946				cp = get_line(0, 0);
947				remlen = get_line_remain();
948			}
949			(void) snprintf(cp, remlen,
950			    "    malformed %u suboption (remaining %u)",
951			    sotype, solen);
952			data += solen;
953		}
954	}
955}
956
957static void
958prt_hbh_options(int flags, const struct ip6_hbh *ipv6ext_hbh)
959{
960	const uint8_t *data, *ndata;
961	uint32_t len;
962	uint8_t op_type;
963	uint8_t op_len;
964	uint8_t nxt_hdr;
965
966	/* in summary mode, we don't do anything. */
967	if (flags & F_SUM) {
968		return;
969	}
970
971	show_header("IPv6-HopOpts:  ", "IPv6 Hop-by-Hop Options Header", 0);
972	show_space();
973
974	/*
975	 * Store the lengh of this ext hdr in bytes.  The caller has
976	 * ensured that there is at least len bytes of data left.
977	 */
978	len = ipv6ext_hbh->ip6h_len * 8 + 8;
979
980	ndata = (const uint8_t *)ipv6ext_hbh + 2;
981	len -= 2;
982
983	nxt_hdr = ipv6ext_hbh->ip6h_nxt;
984	(void) snprintf(get_line(0, 0), get_line_remain(),
985	    "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
986
987	while (len > 0) {
988		data = ndata;
989		GETINT8(op_type, data);
990		/* This is the only one-octet IPv6 option */
991		if (op_type == IP6OPT_PAD1) {
992			(void) snprintf(get_line(0, 0), get_line_remain(),
993			    "pad1 option ");
994			len--;
995			ndata = data;
996			continue;
997		}
998		GETINT8(op_len, data);
999		if (len < 2 || op_len + 2 > len) {
1000			(void) snprintf(get_line(0, 0), get_line_remain(),
1001			    "Error: option %u truncated (%u + 2 > %u)",
1002			    op_type, op_len, len);
1003			op_len = len - 2;
1004			/*
1005			 * Continue processing the malformed option so that we
1006			 * can display as much as possible.
1007			 */
1008		}
1009
1010		/* advance pointers to the next option */
1011		len -= op_len + 2;
1012		ndata = data + op_len;
1013
1014		/* process this option */
1015		switch (op_type) {
1016		case IP6OPT_PADN:
1017			(void) snprintf(get_line(0, 0), get_line_remain(),
1018			    "padN option len = %u", op_len);
1019			break;
1020		case IP6OPT_JUMBO: {
1021			uint32_t payload_len;
1022
1023			(void) snprintf(get_line(0, 0), get_line_remain(),
1024			    "Jumbo Payload Option len = %u bytes%s", op_len,
1025			    op_len == sizeof (uint32_t) ? "" : "?");
1026			if (op_len == sizeof (uint32_t)) {
1027				GETINT32(payload_len, data);
1028				(void) snprintf(get_line(0, 0),
1029				    get_line_remain(),
1030				    "Jumbo Payload Length = %u bytes",
1031				    payload_len);
1032			}
1033			break;
1034		}
1035		case IP6OPT_ROUTER_ALERT: {
1036			uint16_t value;
1037			const char *label[] = {"MLD", "RSVP", "AN"};
1038
1039			(void) snprintf(get_line(0, 0), get_line_remain(),
1040			    "Router Alert Option len = %u bytes%s", op_len,
1041			    op_len == sizeof (uint16_t) ? "" : "?");
1042			if (op_len == sizeof (uint16_t)) {
1043				GETINT16(value, data);
1044				(void) snprintf(get_line(0, 0),
1045				    get_line_remain(),
1046				    "Alert Type = %d (%s)", value,
1047				    value < sizeof (label) / sizeof (label[0]) ?
1048				    label[value] : "???");
1049			}
1050			break;
1051		}
1052		case IP6OPT_LS:
1053			print_ip6opt_ls(data, op_len);
1054			break;
1055		default:
1056			(void) snprintf(get_line(0, 0), get_line_remain(),
1057			    "Option type = %u, len = %u", op_type, op_len);
1058			break;
1059		}
1060	}
1061
1062	show_space();
1063}
1064
1065static void
1066prt_dest_options(int flags, const struct ip6_dest *ipv6ext_dest)
1067{
1068	const uint8_t *data, *ndata;
1069	uint32_t len;
1070	uint8_t op_type;
1071	uint32_t op_len;
1072	uint8_t nxt_hdr;
1073	uint8_t value;
1074
1075	/* in summary mode, we don't do anything. */
1076	if (flags & F_SUM) {
1077		return;
1078	}
1079
1080	show_header("IPv6-DstOpts:  ", "IPv6 Destination Options Header", 0);
1081	show_space();
1082
1083	/*
1084	 * Store the length of this ext hdr in bytes.  The caller has
1085	 * ensured that there is at least len bytes of data left.
1086	 */
1087	len = ipv6ext_dest->ip6d_len * 8 + 8;
1088
1089	ndata = (const uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
1090	len -= 2;
1091
1092	nxt_hdr = ipv6ext_dest->ip6d_nxt;
1093	(void) snprintf(get_line(0, 0), get_line_remain(),
1094	    "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
1095
1096	while (len > 0) {
1097		data = ndata;
1098		GETINT8(op_type, data);
1099		if (op_type == IP6OPT_PAD1) {
1100			(void) snprintf(get_line(0, 0), get_line_remain(),
1101			    "pad1 option ");
1102			len--;
1103			ndata = data;
1104			continue;
1105		}
1106		GETINT8(op_len, data);
1107		if (len < 2 || op_len + 2 > len) {
1108			(void) snprintf(get_line(0, 0), get_line_remain(),
1109			    "Error: option %u truncated (%u + 2 > %u)",
1110			    op_type, op_len, len);
1111			op_len = len - 2;
1112			/*
1113			 * Continue processing the malformed option so that we
1114			 * can display as much as possible.
1115			 */
1116		}
1117
1118		/* advance pointers to the next option */
1119		len -= op_len + 2;
1120		ndata = data + op_len;
1121
1122		/* process this option */
1123		switch (op_type) {
1124		case IP6OPT_PADN:
1125			(void) snprintf(get_line(0, 0), get_line_remain(),
1126			    "padN option len = %u", op_len);
1127			break;
1128		case IP6OPT_TUNNEL_LIMIT:
1129			GETINT8(value, data);
1130			(void) snprintf(get_line(0, 0), get_line_remain(),
1131			    "tunnel encapsulation limit len = %d, value = %d",
1132			    op_len, value);
1133			break;
1134		case IP6OPT_LS:
1135			print_ip6opt_ls(data, op_len);
1136			break;
1137		default:
1138			(void) snprintf(get_line(0, 0), get_line_remain(),
1139			    "Option type = %u, len = %u", op_type, op_len);
1140			break;
1141		}
1142	}
1143
1144	show_space();
1145}
1146
1147#define	ALABEL_MAXLEN	256
1148
1149static char ascii_label[ALABEL_MAXLEN];
1150static char *plabel = ascii_label;
1151
1152struct snoop_pair {
1153	int val;
1154	const char *name;
1155};
1156
1157static struct snoop_pair ripso_class_tbl[] = {
1158	TSOL_CL_TOP_SECRET,	"TOP SECRET",
1159	TSOL_CL_SECRET,		"SECRET",
1160	TSOL_CL_CONFIDENTIAL,	"CONFIDENTIAL",
1161	TSOL_CL_UNCLASSIFIED,	"UNCLASSIFIED",
1162	-1,			NULL
1163};
1164
1165static struct snoop_pair ripso_prot_tbl[] = {
1166	TSOL_PA_GENSER,		"GENSER",
1167	TSOL_PA_SIOP_ESI,	"SIOP-ESI",
1168	TSOL_PA_SCI,		"SCI",
1169	TSOL_PA_NSA,		"NSA",
1170	TSOL_PA_DOE,		"DOE",
1171	0x04,			"UNASSIGNED",
1172	0x02,			"UNASSIGNED",
1173	-1,			NULL
1174};
1175
1176static struct snoop_pair *
1177get_pair_byval(struct snoop_pair pairlist[], int val)
1178{
1179	int i;
1180
1181	for (i = 0; pairlist[i].name != NULL; i++)
1182		if (pairlist[i].val == val)
1183			return (&pairlist[i]);
1184	return (NULL);
1185}
1186
1187static void
1188print_ripso(const uchar_t *opt)
1189{
1190	struct snoop_pair *ripso_class;
1191	int i, index, prot_len;
1192	boolean_t first_prot;
1193	char line[100], *ptr;
1194
1195	prot_len = opt[1] - 3;
1196	if (prot_len < 0)
1197		return;
1198
1199	show_header("RIPSO:  ", "Revised IP Security Option", 0);
1200	show_space();
1201
1202	(void) snprintf(get_line(0, 0), get_line_remain(),
1203	    "Type = Basic Security Option (%d), Length = %d", opt[0], opt[1]);
1204
1205	/*
1206	 * Display Classification Level
1207	 */
1208	ripso_class = get_pair_byval(ripso_class_tbl, (int)opt[2]);
1209	if (ripso_class == NULL)
1210		(void) snprintf(get_line(0, 0), get_line_remain(),
1211		    "Classification = Unknown (0x%02x)", opt[2]);
1212	else
1213		(void) snprintf(get_line(0, 0), get_line_remain(),
1214		    "Classification = %s (0x%02x)",
1215		    ripso_class->name, ripso_class->val);
1216
1217	/*
1218	 * Display Protection Authority Flags
1219	 */
1220	(void) snprintf(line, sizeof (line), "Protection Authority = ");
1221	ptr = line;
1222	first_prot = B_TRUE;
1223	for (i = 0; i < prot_len; i++) {
1224		index = 0;
1225		while (ripso_prot_tbl[index].name != NULL) {
1226			if (opt[3 + i] & ripso_prot_tbl[index].val) {
1227				ptr = strchr(ptr, 0);
1228				if (!first_prot) {
1229					(void) strlcpy(ptr, ", ",
1230					    sizeof (line) - (ptr - line));
1231					ptr = strchr(ptr, 0);
1232				}
1233				(void) snprintf(ptr,
1234				    sizeof (line) - (ptr - line),
1235				    "%s (0x%02x)",
1236				    ripso_prot_tbl[index].name,
1237				    ripso_prot_tbl[index].val);
1238			}
1239			index++;
1240		}
1241		if ((opt[3 + i] & 1) == 0)
1242			break;
1243	}
1244	if (!first_prot)
1245		(void) snprintf(get_line(0, 0), get_line_remain(), "%s", line);
1246	else
1247		(void) snprintf(get_line(0, 0), get_line_remain(), "%sNone",
1248		    line);
1249}
1250
1251#define	CIPSO_GENERIC_ARRAY_LEN	200
1252
1253/*
1254 * Return 1 if CIPSO SL and Categories are all 1's; 0 otherwise.
1255 *
1256 * Note: opt starts with "Tag Type":
1257 *
1258 * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1259 *
1260 */
1261static boolean_t
1262cipso_high(const uchar_t *opt)
1263{
1264	int i;
1265
1266	if (((int)opt[1] + 6) < IP_MAX_OPT_LENGTH)
1267		return (B_FALSE);
1268	for (i = 0; i < ((int)opt[1] - 3); i++)
1269		if (opt[3 + i] != 0xff)
1270			return (B_FALSE);
1271	return (B_TRUE);
1272}
1273
1274/*
1275 * Converts CIPSO label to SL.
1276 *
1277 * Note: opt starts with "Tag Type":
1278 *
1279 * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1280 *
1281 */
1282static void
1283cipso2sl(const uchar_t *opt, bslabel_t *sl, int *high)
1284{
1285	int i, taglen;
1286	uchar_t *q = (uchar_t *)&((_bslabel_impl_t *)sl)->compartments;
1287
1288	*high = 0;
1289	taglen = opt[1];
1290	memset((caddr_t)sl, 0, sizeof (bslabel_t));
1291
1292	if (cipso_high(opt)) {
1293		BSLHIGH(sl);
1294		*high = 1;
1295	} else {
1296		LCLASS_SET((_bslabel_impl_t *)sl, opt[3]);
1297		for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++)
1298			q[i] = opt[TSOL_TT1_MIN_LENGTH + i];
1299	}
1300	SETBLTYPE(sl, SUN_SL_ID);
1301}
1302
1303static int
1304interpret_cipso_tagtype1(const uchar_t *opt)
1305{
1306	int i, taglen, ishigh;
1307	bslabel_t sl;
1308	char line[CIPSO_GENERIC_ARRAY_LEN], *ptr;
1309
1310	taglen = opt[1];
1311	if (taglen < TSOL_TT1_MIN_LENGTH ||
1312	    taglen > TSOL_TT1_MAX_LENGTH)
1313		return (taglen);
1314
1315	(void) snprintf(get_line(0, 0), get_line_remain(),
1316	    "Tag Type = %d, Tag Length = %d", opt[0], opt[1]);
1317	(void) snprintf(get_line(0, 0), get_line_remain(),
1318	    "Sensitivity Level = 0x%02x", opt[3]);
1319	ptr = line;
1320	for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) {
1321		(void) snprintf(ptr, sizeof (line) - (ptr - line), "%02x",
1322		    opt[TSOL_TT1_MIN_LENGTH + i]);
1323		ptr = strchr(ptr, 0);
1324	}
1325	if (i != 0) {
1326		(void) snprintf(get_line(0, 0), get_line_remain(),
1327		    "Categories = ");
1328		(void) snprintf(get_line(0, 0), get_line_remain(), "\t%s",
1329		    line);
1330	} else {
1331		(void) snprintf(get_line(0, 0), get_line_remain(),
1332		    "Categories = None");
1333	}
1334	cipso2sl(opt, &sl, &ishigh);
1335	if (is_system_labeled()) {
1336		if (bsltos(&sl, &plabel, ALABEL_MAXLEN,
1337		    LONG_CLASSIFICATION|LONG_WORDS|VIEW_INTERNAL) < 0) {
1338			(void) snprintf(get_line(0, 0), get_line_remain(),
1339			    "The Sensitivity Level and Categories can't be "
1340			    "mapped to a valid SL");
1341		} else {
1342			(void) snprintf(get_line(0, 0), get_line_remain(),
1343			    "The Sensitivity Level and Categories are mapped "
1344			    "to the SL:");
1345			(void) snprintf(get_line(0, 0), get_line_remain(),
1346			    "\t%s", ascii_label);
1347		}
1348	}
1349	return (taglen);
1350}
1351
1352/*
1353 * The following struct definition #define's are copied from TS1.x. They are
1354 * not used here (except TTYPE_3_MAX_TOKENS), but included as a reference for
1355 * the tag type 3 packet format.
1356 */
1357#define	TTYPE_3_MAX_TOKENS	7
1358
1359/*
1360 * Display CIPSO tag type 3 which is defined by MAXSIX.
1361 */
1362static int
1363interpret_cipso_tagtype3(const uchar_t *opt)
1364{
1365	uchar_t tagtype;
1366	int index, numtokens, taglen;
1367	uint16_t mask;
1368	uint32_t token;
1369	static const char *name[] = {
1370		"SL",
1371		"NCAV",
1372		"INTEG",
1373		"SID",
1374		"undefined",
1375		"undefined",
1376		"IL",
1377		"PRIVS",
1378		"LUID",
1379		"PID",
1380		"IDS",
1381		"ACL"
1382	};
1383
1384	tagtype = *opt++;
1385	(void) memcpy(&mask, opt + 3, sizeof (mask));
1386	(void) snprintf(get_line(0, 0), get_line_remain(),
1387	    "Tag Type = %d (MAXSIX)", tagtype);
1388	(void) snprintf(get_line(0, 0), get_line_remain(),
1389	    "Generation = 0x%02x%02x%02x, Mask = 0x%04x", opt[0], opt[1],
1390	    opt[2], mask);
1391	opt += 3 + sizeof (mask);
1392
1393	/*
1394	 * Display tokens
1395	 */
1396	numtokens = 0;
1397	index = 0;
1398	while (mask != 0 && numtokens < TTYPE_3_MAX_TOKENS) {
1399		if (mask & 0x0001) {
1400			(void) memcpy(&token, opt, sizeof (token));
1401			opt += sizeof (token);
1402			(void) snprintf(get_line(0, 0), get_line_remain(),
1403			    "Attribute = %s, Token = 0x%08x",
1404			    index < sizeof (name) / sizeof (*name) ?
1405			    name[index] : "unknown", token);
1406			numtokens++;
1407		}
1408		mask = mask >> 1;
1409		index++;
1410	}
1411
1412	taglen = 6 + numtokens * 4;
1413	return (taglen);
1414}
1415
1416static void
1417print_cipso(const uchar_t *opt)
1418{
1419	int optlen, taglen, tagnum;
1420	uint32_t doi;
1421	char line[CIPSO_GENERIC_ARRAY_LEN];
1422	char *oldnest;
1423
1424	optlen = opt[1];
1425	if (optlen < TSOL_CIPSO_MIN_LENGTH || optlen > TSOL_CIPSO_MAX_LENGTH)
1426		return;
1427
1428	oldnest = prot_nest_prefix;
1429	prot_nest_prefix = prot_prefix;
1430	show_header("CIPSO:  ", "Common IP Security Option", 0);
1431	show_space();
1432
1433	/*
1434	 * Display CIPSO Header
1435	 */
1436	(void) snprintf(get_line(0, 0), get_line_remain(),
1437	    "Type = CIPSO (%d), Length = %d", opt[0], opt[1]);
1438	(void) memcpy(&doi, opt + 2, sizeof (doi));
1439	(void) snprintf(get_line(0, 0), get_line_remain(),
1440	    "Domain of Interpretation = %u", (unsigned)ntohl(doi));
1441
1442	if (opt[1] == TSOL_CIPSO_MIN_LENGTH) {	/* no tags */
1443		show_space();
1444		prot_prefix = prot_nest_prefix;
1445		prot_nest_prefix = oldnest;
1446		return;
1447	}
1448	optlen -= TSOL_CIPSO_MIN_LENGTH;
1449	opt += TSOL_CIPSO_MIN_LENGTH;
1450
1451	/*
1452	 * Display Each Tag
1453	 */
1454	tagnum = 1;
1455	while (optlen >= TSOL_TT1_MIN_LENGTH) {
1456		(void) snprintf(line, sizeof (line), "Tag# %d", tagnum);
1457		show_header("CIPSO:  ", line, 0);
1458		/*
1459		 * We handle tag type 1 and 3 only. Note, tag type 3
1460		 * is MAXSIX defined.
1461		 */
1462		switch (opt[0]) {
1463		case 1:
1464			taglen = interpret_cipso_tagtype1(opt);
1465			break;
1466		case 3:
1467			taglen = interpret_cipso_tagtype3(opt);
1468			break;
1469		default:
1470			(void) snprintf(get_line(0, 0), get_line_remain(),
1471			    "Unknown Tag Type %d", opt[0]);
1472			show_space();
1473			prot_prefix = prot_nest_prefix;
1474			prot_nest_prefix = oldnest;
1475			return;
1476		}
1477
1478		/*
1479		 * Move to the next tag
1480		 */
1481		if (taglen <= 0)
1482			break;
1483		optlen -= taglen;
1484		opt += taglen;
1485		tagnum++;
1486	}
1487	show_space();
1488	prot_prefix = prot_nest_prefix;
1489	prot_nest_prefix = oldnest;
1490}
1491