xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c (revision 410:51e39f6c6311)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*410Skcpoon  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <stdio.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/time.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <sys/socket.h>
380Sstevel@tonic-gate #include <net/if.h>
390Sstevel@tonic-gate #include <netinet/in_systm.h>
400Sstevel@tonic-gate #include <netinet/in.h>
410Sstevel@tonic-gate #include <netinet/ip.h>
420Sstevel@tonic-gate #include <netinet/if_ether.h>
430Sstevel@tonic-gate #include <netinet/tcp.h>
440Sstevel@tonic-gate #include "snoop.h"
450Sstevel@tonic-gate 
460Sstevel@tonic-gate extern char *dlc_header;
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #define	TCPOPT_HEADER_LEN	2
490Sstevel@tonic-gate #define	TCPOPT_TSTAMP_LEN	10
500Sstevel@tonic-gate #define	TCPOPT_SACK_LEN		8
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /*
530Sstevel@tonic-gate  * Convert a network byte order 32 bit integer to a host order integer.
540Sstevel@tonic-gate  * ntohl() cannot be used because option values may not be aligned properly.
550Sstevel@tonic-gate  */
560Sstevel@tonic-gate #define	GET_UINT32(opt)	(((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
570Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
580Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
590Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 3)))
600Sstevel@tonic-gate 
610Sstevel@tonic-gate static void print_tcpoptions_summary(uchar_t *, int, char *);
620Sstevel@tonic-gate static void print_tcpoptions(uchar_t *, int);
630Sstevel@tonic-gate 
640Sstevel@tonic-gate static const struct {
650Sstevel@tonic-gate 	unsigned int	tf_flag;
660Sstevel@tonic-gate 	const char	*tf_name;
670Sstevel@tonic-gate } tcp_flags[] = {
680Sstevel@tonic-gate 	{ TH_SYN, 	"Syn"	},
690Sstevel@tonic-gate 	{ TH_FIN, 	"Fin"	},
700Sstevel@tonic-gate 	{ TH_RST, 	"Rst"	},
710Sstevel@tonic-gate 	{ TH_PUSH,	"Push"	},
720Sstevel@tonic-gate 	{ TH_ECE,	"ECE"	},
730Sstevel@tonic-gate 	{ TH_CWR,	"CWR"	},
740Sstevel@tonic-gate 	{ 0,		NULL	}
750Sstevel@tonic-gate };
760Sstevel@tonic-gate 
770Sstevel@tonic-gate int
interpret_tcp(int flags,struct tcphdr * tcp,int iplen,int fraglen)780Sstevel@tonic-gate interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
790Sstevel@tonic-gate {
800Sstevel@tonic-gate 	char *data;
810Sstevel@tonic-gate 	int hdrlen, tcplen;
820Sstevel@tonic-gate 	int sunrpc = 0;
830Sstevel@tonic-gate 	char *pname;
840Sstevel@tonic-gate 	char buff[32];
850Sstevel@tonic-gate 	char *line, *endline;
860Sstevel@tonic-gate 	unsigned int i;
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	hdrlen = tcp->th_off * 4;
890Sstevel@tonic-gate 	data = (char *)tcp + hdrlen;
900Sstevel@tonic-gate 	tcplen = iplen - hdrlen;
910Sstevel@tonic-gate 	fraglen -= hdrlen;
920Sstevel@tonic-gate 	if (fraglen < 0)
93*410Skcpoon 		return (fraglen + hdrlen);	/* incomplete header */
940Sstevel@tonic-gate 	if (fraglen > tcplen)
950Sstevel@tonic-gate 		fraglen = tcplen;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	if (flags & F_SUM) {
980Sstevel@tonic-gate 		line = get_sum_line();
990Sstevel@tonic-gate 		endline = line + MAXLINE;
1000Sstevel@tonic-gate 		(void) snprintf(line, endline - line, "TCP D=%d S=%d",
1010Sstevel@tonic-gate 		    ntohs(tcp->th_dport), ntohs(tcp->th_sport));
1020Sstevel@tonic-gate 		line += strlen(line);
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 		for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
1050Sstevel@tonic-gate 			if (tcp->th_flags & tcp_flags[i].tf_flag) {
1060Sstevel@tonic-gate 				(void) snprintf(line, endline - line, " %s",
1070Sstevel@tonic-gate 				    tcp_flags[i].tf_name);
1080Sstevel@tonic-gate 				line += strlen(line);
1090Sstevel@tonic-gate 			}
1100Sstevel@tonic-gate 		}
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 		if (tcp->th_flags & TH_URG) {
1130Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Urg=%u",
1140Sstevel@tonic-gate 			    ntohs(tcp->th_urp));
1150Sstevel@tonic-gate 			line += strlen(line);
1160Sstevel@tonic-gate 		}
1170Sstevel@tonic-gate 		if (tcp->th_flags & TH_ACK) {
1180Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Ack=%u",
1190Sstevel@tonic-gate 				ntohl(tcp->th_ack));
1200Sstevel@tonic-gate 			line += strlen(line);
1210Sstevel@tonic-gate 		}
1220Sstevel@tonic-gate 		if (ntohl(tcp->th_seq)) {
1230Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Seq=%u Len=%d",
1240Sstevel@tonic-gate 				ntohl(tcp->th_seq), tcplen);
1250Sstevel@tonic-gate 			line += strlen(line);
1260Sstevel@tonic-gate 		}
1270Sstevel@tonic-gate 		(void) snprintf(line, endline - line, " Win=%d",
1280Sstevel@tonic-gate 		    ntohs(tcp->th_win));
1290Sstevel@tonic-gate 		print_tcpoptions_summary((uchar_t *)(tcp + 1),
1300Sstevel@tonic-gate 		    (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
1310Sstevel@tonic-gate 	}
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
1340Sstevel@tonic-gate 		!reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
1350Sstevel@tonic-gate 		valid_rpc(data + 4, fraglen - 4);
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 	if (flags & F_DTAIL) {
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	show_header("TCP:  ", "TCP Header", tcplen);
1400Sstevel@tonic-gate 	show_space();
141*410Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport -
142*410Skcpoon 		dlc_header, 2),	"Source port = %d", ntohs(tcp->th_sport));
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	if (sunrpc) {
1450Sstevel@tonic-gate 		pname = "(Sun RPC)";
1460Sstevel@tonic-gate 	} else {
1470Sstevel@tonic-gate 		pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
1480Sstevel@tonic-gate 		if (pname == NULL) {
1490Sstevel@tonic-gate 			pname = "";
1500Sstevel@tonic-gate 		} else {
1510Sstevel@tonic-gate 			(void) sprintf(buff, "(%s)", pname);
1520Sstevel@tonic-gate 			pname = buff;
1530Sstevel@tonic-gate 		}
1540Sstevel@tonic-gate 	}
155*410Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport -
156*410Skcpoon 		dlc_header, 2), "Destination port = %d %s",
1570Sstevel@tonic-gate 		ntohs(tcp->th_dport), pname);
158*410Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq -
159*410Skcpoon 		dlc_header, 4),	"Sequence number = %u",
1600Sstevel@tonic-gate 		ntohl(tcp->th_seq));
161*410Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4),
1620Sstevel@tonic-gate 		"Acknowledgement number = %u",
1630Sstevel@tonic-gate 		ntohl(tcp->th_ack));
164*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) +
165*410Skcpoon 		4, 1), "Data offset = %d bytes", tcp->th_off * 4);
166*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
167*410Skcpoon 		dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags);
168*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
169*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_CWR,
170*410Skcpoon 		"ECN congestion window reduced",
171*410Skcpoon 		"No ECN congestion window reduced"));
172*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
173*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ECE,
174*410Skcpoon 		"ECN echo", "No ECN echo"));
175*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
176*410Skcpoon 		dlc_header) + 4, 1), "      %s",
1770Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_URG,
178*410Skcpoon 		"Urgent pointer", "No urgent pointer"));
179*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
180*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ACK,
181*410Skcpoon 		"Acknowledgement", "No acknowledgement"));
182*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
183*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_PUSH,
184*410Skcpoon 		"Push", "No push"));
185*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
186*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_RST,
187*410Skcpoon 		"Reset", "No reset"));
188*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
189*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_SYN,
190*410Skcpoon 		"Syn", "No Syn"));
191*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
192*410Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_FIN,
193*410Skcpoon 		"Fin", "No Fin"));
194*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) +
195*410Skcpoon 		4, 1), "Window = %d", ntohs(tcp->th_win));
1960Sstevel@tonic-gate 	/* XXX need to compute checksum and print whether correct */
197*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) +
198*410Skcpoon 		4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum));
199*410Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) +
200*410Skcpoon 		4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp));
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	/* Print TCP options - if any */
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	print_tcpoptions((uchar_t *)(tcp + 1),
2050Sstevel@tonic-gate 	    tcp->th_off * 4 - sizeof (struct tcphdr));
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	show_space();
2080Sstevel@tonic-gate 	}
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	/* go to the next protocol layer */
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	if (!interpret_reserved(flags, IPPROTO_TCP,
2130Sstevel@tonic-gate 		ntohs(tcp->th_sport),
2140Sstevel@tonic-gate 		ntohs(tcp->th_dport),
2150Sstevel@tonic-gate 		data, fraglen)) {
2160Sstevel@tonic-gate 		if (sunrpc && fraglen > 0)
2170Sstevel@tonic-gate 			interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
2180Sstevel@tonic-gate 	}
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	return (tcplen);
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate static void
print_tcpoptions(opt,optlen)2240Sstevel@tonic-gate print_tcpoptions(opt, optlen)
2250Sstevel@tonic-gate 	uchar_t *opt;
2260Sstevel@tonic-gate 	int optlen;
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate 	int	 len;
2290Sstevel@tonic-gate 	char	 *line;
2300Sstevel@tonic-gate 	uchar_t	*sack_opt;
2310Sstevel@tonic-gate 	uchar_t	*end_opt;
2320Sstevel@tonic-gate 	int	sack_len;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	if (optlen <= 0) {
2350Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&opt - dlc_header, 1),
2360Sstevel@tonic-gate 		"No options");
2370Sstevel@tonic-gate 		return;
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	(void) sprintf(get_line((char *)&opt - dlc_header, 1),
2410Sstevel@tonic-gate 	"Options: (%d bytes)", optlen);
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	while (optlen > 0) {
2440Sstevel@tonic-gate 		line = get_line((char *)&opt - dlc_header, 1);
2450Sstevel@tonic-gate 		len = opt[1];
2460Sstevel@tonic-gate 		switch (opt[0]) {
2470Sstevel@tonic-gate 		case TCPOPT_EOL:
2480Sstevel@tonic-gate 			(void) strcpy(line, "  - End of option list");
2490Sstevel@tonic-gate 			return;
2500Sstevel@tonic-gate 		case TCPOPT_NOP:
2510Sstevel@tonic-gate 			(void) strcpy(line, "  - No operation");
2520Sstevel@tonic-gate 			len = 1;
2530Sstevel@tonic-gate 			break;
2540Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
2550Sstevel@tonic-gate 			(void) sprintf(line,
2560Sstevel@tonic-gate 			"  - Maximum segment size = %d bytes",
2570Sstevel@tonic-gate 				(opt[2] << 8) + opt[3]);
2580Sstevel@tonic-gate 			break;
2590Sstevel@tonic-gate 		case TCPOPT_WSCALE:
2600Sstevel@tonic-gate 			(void) sprintf(line, "  - Window scale = %d", opt[2]);
2610Sstevel@tonic-gate 			break;
2620Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
2630Sstevel@tonic-gate 			/* Sanity check. */
2640Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
2650Sstevel@tonic-gate 				(void) sprintf(line,
2660Sstevel@tonic-gate 				    "  - Incomplete TS option");
2670Sstevel@tonic-gate 			} else {
2680Sstevel@tonic-gate 				(void) sprintf(line,
2690Sstevel@tonic-gate 				    "  - TS Val = %u, TS Echo = %u",
2700Sstevel@tonic-gate 				    GET_UINT32(opt + 2),
2710Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
2720Sstevel@tonic-gate 			}
2730Sstevel@tonic-gate 			break;
2740Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
2750Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK permitted option");
2760Sstevel@tonic-gate 			break;
2770Sstevel@tonic-gate 		case TCPOPT_SACK:
2780Sstevel@tonic-gate 			/*
2790Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
2800Sstevel@tonic-gate 			 * than just the option header length.
2810Sstevel@tonic-gate 			 */
2820Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
2830Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
2840Sstevel@tonic-gate 				(void) sprintf(line,
2850Sstevel@tonic-gate 				    "  - Incomplete SACK option");
2860Sstevel@tonic-gate 				break;
2870Sstevel@tonic-gate 			}
2880Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
2890Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
2900Sstevel@tonic-gate 			end_opt = opt + optlen;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK blocks:");
2930Sstevel@tonic-gate 			line = get_line((char *)&opt - dlc_header, 1);
2940Sstevel@tonic-gate 			(void) sprintf(line, "        ");
2950Sstevel@tonic-gate 			while (sack_len > 0) {
2960Sstevel@tonic-gate 				char sack_blk[MAXLINE + 1];
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 				/*
2990Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
3000Sstevel@tonic-gate 				 * the real length...  Need to be careful
3010Sstevel@tonic-gate 				 * not to step beyond the option buffer.
3020Sstevel@tonic-gate 				 */
3030Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
3040Sstevel@tonic-gate 					(void) strcat(line,
3050Sstevel@tonic-gate 					    "...incomplete SACK block");
3060Sstevel@tonic-gate 					break;
3070Sstevel@tonic-gate 				}
3080Sstevel@tonic-gate 				(void) sprintf(sack_blk, "(%u-%u) ",
3090Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
3100Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
3110Sstevel@tonic-gate 				(void) strcat(line, sack_blk);
3120Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
3130Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
3140Sstevel@tonic-gate 			}
3150Sstevel@tonic-gate 			break;
3160Sstevel@tonic-gate 		default:
3170Sstevel@tonic-gate 			(void) sprintf(line,
3180Sstevel@tonic-gate 			"  - Option %d (unknown - %d bytes) %s",
3190Sstevel@tonic-gate 				opt[0],
3200Sstevel@tonic-gate 				len - 2,
3210Sstevel@tonic-gate 				tohex((char *)&opt[2], len - 2));
3220Sstevel@tonic-gate 			break;
3230Sstevel@tonic-gate 		}
3240Sstevel@tonic-gate 		if (len <= 0) {
3250Sstevel@tonic-gate 			(void) sprintf(line, "  - Incomplete option len %d",
3260Sstevel@tonic-gate 				len);
3270Sstevel@tonic-gate 			break;
3280Sstevel@tonic-gate 		}
3290Sstevel@tonic-gate 		opt += len;
3300Sstevel@tonic-gate 		optlen -= len;
3310Sstevel@tonic-gate 	}
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate /*
3350Sstevel@tonic-gate  * This function is basically the same as print_tcpoptions() except that
3360Sstevel@tonic-gate  * all options are printed on the same line.
3370Sstevel@tonic-gate  */
3380Sstevel@tonic-gate static void
print_tcpoptions_summary(uchar_t * opt,int optlen,char * line)3390Sstevel@tonic-gate print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	int	 len;
3420Sstevel@tonic-gate 	uchar_t	*sack_opt;
3430Sstevel@tonic-gate 	uchar_t	*end_opt;
3440Sstevel@tonic-gate 	int	sack_len;
3450Sstevel@tonic-gate 	char	options[MAXLINE + 1];
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	if (optlen <= 0) {
3480Sstevel@tonic-gate 		return;
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	(void) strcat(line, " Options=<");
3520Sstevel@tonic-gate 	while (optlen > 0) {
3530Sstevel@tonic-gate 		len = opt[1];
3540Sstevel@tonic-gate 		switch (opt[0]) {
3550Sstevel@tonic-gate 		case TCPOPT_EOL:
3560Sstevel@tonic-gate 			(void) strcat(line, "eol>");
3570Sstevel@tonic-gate 			return;
3580Sstevel@tonic-gate 		case TCPOPT_NOP:
3590Sstevel@tonic-gate 			(void) strcat(line, "nop");
3600Sstevel@tonic-gate 			len = 1;
3610Sstevel@tonic-gate 			break;
3620Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
3630Sstevel@tonic-gate 			(void) sprintf(options, "mss %d",
3640Sstevel@tonic-gate 			    (opt[2] << 8) + opt[3]);
3650Sstevel@tonic-gate 			(void) strcat(line, options);
3660Sstevel@tonic-gate 			break;
3670Sstevel@tonic-gate 		case TCPOPT_WSCALE:
3680Sstevel@tonic-gate 			(void) sprintf(options, "wscale %d", opt[2]);
3690Sstevel@tonic-gate 			(void) strcat(line, options);
3700Sstevel@tonic-gate 			break;
3710Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
3720Sstevel@tonic-gate 			/* Sanity check. */
3730Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
3740Sstevel@tonic-gate 				(void) strcat(line, "tstamp|");
3750Sstevel@tonic-gate 			} else {
3760Sstevel@tonic-gate 				(void) sprintf(options,
3770Sstevel@tonic-gate 				    "tstamp %u %u", GET_UINT32(opt + 2),
3780Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
3790Sstevel@tonic-gate 				(void) strcat(line, options);
3800Sstevel@tonic-gate 			}
3810Sstevel@tonic-gate 			break;
3820Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
3830Sstevel@tonic-gate 			(void) strcat(line, "sackOK");
3840Sstevel@tonic-gate 			break;
3850Sstevel@tonic-gate 		case TCPOPT_SACK:
3860Sstevel@tonic-gate 			/*
3870Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
3880Sstevel@tonic-gate 			 * than just the option header length.
3890Sstevel@tonic-gate 			 */
3900Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
3910Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
3920Sstevel@tonic-gate 				(void) strcat(line, "sack|");
3930Sstevel@tonic-gate 				break;
3940Sstevel@tonic-gate 			}
3950Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
3960Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
3970Sstevel@tonic-gate 			end_opt = opt + optlen;
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 			(void) strcat(line, "sack");
4000Sstevel@tonic-gate 			while (sack_len > 0) {
4010Sstevel@tonic-gate 				/*
4020Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
4030Sstevel@tonic-gate 				 * the real length...  Need to be careful
4040Sstevel@tonic-gate 				 * not to step beyond the option buffer.
4050Sstevel@tonic-gate 				 */
4060Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
4070Sstevel@tonic-gate 					(void) strcat(line, "|");
4080Sstevel@tonic-gate 					break;
4090Sstevel@tonic-gate 				}
4100Sstevel@tonic-gate 				(void) sprintf(options, " %u-%u",
4110Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
4120Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
4130Sstevel@tonic-gate 				(void) strcat(line, options);
4140Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
4150Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
4160Sstevel@tonic-gate 			}
4170Sstevel@tonic-gate 			break;
4180Sstevel@tonic-gate 		default:
4190Sstevel@tonic-gate 			(void) sprintf(options, "unknown %d", opt[0]);
4200Sstevel@tonic-gate 			(void) strcat(line, options);
4210Sstevel@tonic-gate 			break;
4220Sstevel@tonic-gate 		}
4230Sstevel@tonic-gate 		if (len <= 0) {
4240Sstevel@tonic-gate 			(void) sprintf(options, "optlen %d", len);
4250Sstevel@tonic-gate 			(void) strcat(line, options);
4260Sstevel@tonic-gate 			break;
4270Sstevel@tonic-gate 		}
4280Sstevel@tonic-gate 		opt += len;
4290Sstevel@tonic-gate 		optlen -= len;
4300Sstevel@tonic-gate 		if (optlen > 0) {
4310Sstevel@tonic-gate 			(void) strcat(line, ",");
4320Sstevel@tonic-gate 		}
4330Sstevel@tonic-gate 	}
4340Sstevel@tonic-gate 	(void) strcat(line, ">");
4350Sstevel@tonic-gate }
436