xref: /titanic_51/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c (revision 2e3b64671f0fdac42d7fb21a8fa7e3ce9fce3359)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*2e3b6467Skcpoon  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <string.h>
327c478bd9Sstevel@tonic-gate #include <fcntl.h>
337c478bd9Sstevel@tonic-gate #include <string.h>
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <sys/time.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/socket.h>
387c478bd9Sstevel@tonic-gate #include <net/if.h>
397c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
407c478bd9Sstevel@tonic-gate #include <netinet/in.h>
417c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
427c478bd9Sstevel@tonic-gate #include <netinet/if_ether.h>
437c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
447c478bd9Sstevel@tonic-gate #include "snoop.h"
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate extern char *dlc_header;
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #define	TCPOPT_HEADER_LEN	2
497c478bd9Sstevel@tonic-gate #define	TCPOPT_TSTAMP_LEN	10
507c478bd9Sstevel@tonic-gate #define	TCPOPT_SACK_LEN		8
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /*
537c478bd9Sstevel@tonic-gate  * Convert a network byte order 32 bit integer to a host order integer.
547c478bd9Sstevel@tonic-gate  * ntohl() cannot be used because option values may not be aligned properly.
557c478bd9Sstevel@tonic-gate  */
567c478bd9Sstevel@tonic-gate #define	GET_UINT32(opt)	(((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
577c478bd9Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
587c478bd9Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
597c478bd9Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 3)))
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate static void print_tcpoptions_summary(uchar_t *, int, char *);
627c478bd9Sstevel@tonic-gate static void print_tcpoptions(uchar_t *, int);
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate static const struct {
657c478bd9Sstevel@tonic-gate 	unsigned int	tf_flag;
667c478bd9Sstevel@tonic-gate 	const char	*tf_name;
677c478bd9Sstevel@tonic-gate } tcp_flags[] = {
687c478bd9Sstevel@tonic-gate 	{ TH_SYN, 	"Syn"	},
697c478bd9Sstevel@tonic-gate 	{ TH_FIN, 	"Fin"	},
707c478bd9Sstevel@tonic-gate 	{ TH_RST, 	"Rst"	},
717c478bd9Sstevel@tonic-gate 	{ TH_PUSH,	"Push"	},
727c478bd9Sstevel@tonic-gate 	{ TH_ECE,	"ECE"	},
737c478bd9Sstevel@tonic-gate 	{ TH_CWR,	"CWR"	},
747c478bd9Sstevel@tonic-gate 	{ 0,		NULL	}
757c478bd9Sstevel@tonic-gate };
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate int
787c478bd9Sstevel@tonic-gate interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate 	char *data;
817c478bd9Sstevel@tonic-gate 	int hdrlen, tcplen;
827c478bd9Sstevel@tonic-gate 	int sunrpc = 0;
837c478bd9Sstevel@tonic-gate 	char *pname;
847c478bd9Sstevel@tonic-gate 	char buff[32];
857c478bd9Sstevel@tonic-gate 	char *line, *endline;
867c478bd9Sstevel@tonic-gate 	unsigned int i;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	hdrlen = tcp->th_off * 4;
897c478bd9Sstevel@tonic-gate 	data = (char *)tcp + hdrlen;
907c478bd9Sstevel@tonic-gate 	tcplen = iplen - hdrlen;
917c478bd9Sstevel@tonic-gate 	fraglen -= hdrlen;
927c478bd9Sstevel@tonic-gate 	if (fraglen < 0)
93*2e3b6467Skcpoon 		return (fraglen + hdrlen);	/* incomplete header */
947c478bd9Sstevel@tonic-gate 	if (fraglen > tcplen)
957c478bd9Sstevel@tonic-gate 		fraglen = tcplen;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
987c478bd9Sstevel@tonic-gate 		line = get_sum_line();
997c478bd9Sstevel@tonic-gate 		endline = line + MAXLINE;
1007c478bd9Sstevel@tonic-gate 		(void) snprintf(line, endline - line, "TCP D=%d S=%d",
1017c478bd9Sstevel@tonic-gate 		    ntohs(tcp->th_dport), ntohs(tcp->th_sport));
1027c478bd9Sstevel@tonic-gate 		line += strlen(line);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 		for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
1057c478bd9Sstevel@tonic-gate 			if (tcp->th_flags & tcp_flags[i].tf_flag) {
1067c478bd9Sstevel@tonic-gate 				(void) snprintf(line, endline - line, " %s",
1077c478bd9Sstevel@tonic-gate 				    tcp_flags[i].tf_name);
1087c478bd9Sstevel@tonic-gate 				line += strlen(line);
1097c478bd9Sstevel@tonic-gate 			}
1107c478bd9Sstevel@tonic-gate 		}
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 		if (tcp->th_flags & TH_URG) {
1137c478bd9Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Urg=%u",
1147c478bd9Sstevel@tonic-gate 			    ntohs(tcp->th_urp));
1157c478bd9Sstevel@tonic-gate 			line += strlen(line);
1167c478bd9Sstevel@tonic-gate 		}
1177c478bd9Sstevel@tonic-gate 		if (tcp->th_flags & TH_ACK) {
1187c478bd9Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Ack=%u",
1197c478bd9Sstevel@tonic-gate 				ntohl(tcp->th_ack));
1207c478bd9Sstevel@tonic-gate 			line += strlen(line);
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 		if (ntohl(tcp->th_seq)) {
1237c478bd9Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Seq=%u Len=%d",
1247c478bd9Sstevel@tonic-gate 				ntohl(tcp->th_seq), tcplen);
1257c478bd9Sstevel@tonic-gate 			line += strlen(line);
1267c478bd9Sstevel@tonic-gate 		}
1277c478bd9Sstevel@tonic-gate 		(void) snprintf(line, endline - line, " Win=%d",
1287c478bd9Sstevel@tonic-gate 		    ntohs(tcp->th_win));
1297c478bd9Sstevel@tonic-gate 		print_tcpoptions_summary((uchar_t *)(tcp + 1),
1307c478bd9Sstevel@tonic-gate 		    (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
1317c478bd9Sstevel@tonic-gate 	}
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
1347c478bd9Sstevel@tonic-gate 		!reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
1357c478bd9Sstevel@tonic-gate 		valid_rpc(data + 4, fraglen - 4);
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	if (flags & F_DTAIL) {
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	show_header("TCP:  ", "TCP Header", tcplen);
1407c478bd9Sstevel@tonic-gate 	show_space();
141*2e3b6467Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport -
142*2e3b6467Skcpoon 		dlc_header, 2),	"Source port = %d", ntohs(tcp->th_sport));
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	if (sunrpc) {
1457c478bd9Sstevel@tonic-gate 		pname = "(Sun RPC)";
1467c478bd9Sstevel@tonic-gate 	} else {
1477c478bd9Sstevel@tonic-gate 		pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
1487c478bd9Sstevel@tonic-gate 		if (pname == NULL) {
1497c478bd9Sstevel@tonic-gate 			pname = "";
1507c478bd9Sstevel@tonic-gate 		} else {
1517c478bd9Sstevel@tonic-gate 			(void) sprintf(buff, "(%s)", pname);
1527c478bd9Sstevel@tonic-gate 			pname = buff;
1537c478bd9Sstevel@tonic-gate 		}
1547c478bd9Sstevel@tonic-gate 	}
155*2e3b6467Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport -
156*2e3b6467Skcpoon 		dlc_header, 2), "Destination port = %d %s",
1577c478bd9Sstevel@tonic-gate 		ntohs(tcp->th_dport), pname);
158*2e3b6467Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq -
159*2e3b6467Skcpoon 		dlc_header, 4),	"Sequence number = %u",
1607c478bd9Sstevel@tonic-gate 		ntohl(tcp->th_seq));
161*2e3b6467Skcpoon 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4),
1627c478bd9Sstevel@tonic-gate 		"Acknowledgement number = %u",
1637c478bd9Sstevel@tonic-gate 		ntohl(tcp->th_ack));
164*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) +
165*2e3b6467Skcpoon 		4, 1), "Data offset = %d bytes", tcp->th_off * 4);
166*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
167*2e3b6467Skcpoon 		dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags);
168*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
169*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_CWR,
1707c478bd9Sstevel@tonic-gate 		"ECN congestion window reduced",
1717c478bd9Sstevel@tonic-gate 		"No ECN congestion window reduced"));
172*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
173*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ECE,
1747c478bd9Sstevel@tonic-gate 		"ECN echo", "No ECN echo"));
175*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
176*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s",
1777c478bd9Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_URG,
1787c478bd9Sstevel@tonic-gate 		"Urgent pointer", "No urgent pointer"));
179*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
180*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ACK,
1817c478bd9Sstevel@tonic-gate 		"Acknowledgement", "No acknowledgement"));
182*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
183*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_PUSH,
1847c478bd9Sstevel@tonic-gate 		"Push", "No push"));
185*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
186*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_RST,
1877c478bd9Sstevel@tonic-gate 		"Reset", "No reset"));
188*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
189*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_SYN,
1907c478bd9Sstevel@tonic-gate 		"Syn", "No Syn"));
191*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
192*2e3b6467Skcpoon 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_FIN,
1937c478bd9Sstevel@tonic-gate 		"Fin", "No Fin"));
194*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) +
195*2e3b6467Skcpoon 		4, 1), "Window = %d", ntohs(tcp->th_win));
1967c478bd9Sstevel@tonic-gate 	/* XXX need to compute checksum and print whether correct */
197*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) +
198*2e3b6467Skcpoon 		4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum));
199*2e3b6467Skcpoon 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) +
200*2e3b6467Skcpoon 		4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp));
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	/* Print TCP options - if any */
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	print_tcpoptions((uchar_t *)(tcp + 1),
2057c478bd9Sstevel@tonic-gate 	    tcp->th_off * 4 - sizeof (struct tcphdr));
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	show_space();
2087c478bd9Sstevel@tonic-gate 	}
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	/* go to the next protocol layer */
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	if (!interpret_reserved(flags, IPPROTO_TCP,
2137c478bd9Sstevel@tonic-gate 		ntohs(tcp->th_sport),
2147c478bd9Sstevel@tonic-gate 		ntohs(tcp->th_dport),
2157c478bd9Sstevel@tonic-gate 		data, fraglen)) {
2167c478bd9Sstevel@tonic-gate 		if (sunrpc && fraglen > 0)
2177c478bd9Sstevel@tonic-gate 			interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	return (tcplen);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static void
2247c478bd9Sstevel@tonic-gate print_tcpoptions(opt, optlen)
2257c478bd9Sstevel@tonic-gate 	uchar_t *opt;
2267c478bd9Sstevel@tonic-gate 	int optlen;
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate 	int	 len;
2297c478bd9Sstevel@tonic-gate 	char	 *line;
2307c478bd9Sstevel@tonic-gate 	uchar_t	*sack_opt;
2317c478bd9Sstevel@tonic-gate 	uchar_t	*end_opt;
2327c478bd9Sstevel@tonic-gate 	int	sack_len;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	if (optlen <= 0) {
2357c478bd9Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&opt - dlc_header, 1),
2367c478bd9Sstevel@tonic-gate 		"No options");
2377c478bd9Sstevel@tonic-gate 		return;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	(void) sprintf(get_line((char *)&opt - dlc_header, 1),
2417c478bd9Sstevel@tonic-gate 	"Options: (%d bytes)", optlen);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	while (optlen > 0) {
2447c478bd9Sstevel@tonic-gate 		line = get_line((char *)&opt - dlc_header, 1);
2457c478bd9Sstevel@tonic-gate 		len = opt[1];
2467c478bd9Sstevel@tonic-gate 		switch (opt[0]) {
2477c478bd9Sstevel@tonic-gate 		case TCPOPT_EOL:
2487c478bd9Sstevel@tonic-gate 			(void) strcpy(line, "  - End of option list");
2497c478bd9Sstevel@tonic-gate 			return;
2507c478bd9Sstevel@tonic-gate 		case TCPOPT_NOP:
2517c478bd9Sstevel@tonic-gate 			(void) strcpy(line, "  - No operation");
2527c478bd9Sstevel@tonic-gate 			len = 1;
2537c478bd9Sstevel@tonic-gate 			break;
2547c478bd9Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
2557c478bd9Sstevel@tonic-gate 			(void) sprintf(line,
2567c478bd9Sstevel@tonic-gate 			"  - Maximum segment size = %d bytes",
2577c478bd9Sstevel@tonic-gate 				(opt[2] << 8) + opt[3]);
2587c478bd9Sstevel@tonic-gate 			break;
2597c478bd9Sstevel@tonic-gate 		case TCPOPT_WSCALE:
2607c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Window scale = %d", opt[2]);
2617c478bd9Sstevel@tonic-gate 			break;
2627c478bd9Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
2637c478bd9Sstevel@tonic-gate 			/* Sanity check. */
2647c478bd9Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
2657c478bd9Sstevel@tonic-gate 				(void) sprintf(line,
2667c478bd9Sstevel@tonic-gate 				    "  - Incomplete TS option");
2677c478bd9Sstevel@tonic-gate 			} else {
2687c478bd9Sstevel@tonic-gate 				(void) sprintf(line,
2697c478bd9Sstevel@tonic-gate 				    "  - TS Val = %u, TS Echo = %u",
2707c478bd9Sstevel@tonic-gate 				    GET_UINT32(opt + 2),
2717c478bd9Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
2727c478bd9Sstevel@tonic-gate 			}
2737c478bd9Sstevel@tonic-gate 			break;
2747c478bd9Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
2757c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK permitted option");
2767c478bd9Sstevel@tonic-gate 			break;
2777c478bd9Sstevel@tonic-gate 		case TCPOPT_SACK:
2787c478bd9Sstevel@tonic-gate 			/*
2797c478bd9Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
2807c478bd9Sstevel@tonic-gate 			 * than just the option header length.
2817c478bd9Sstevel@tonic-gate 			 */
2827c478bd9Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
2837c478bd9Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
2847c478bd9Sstevel@tonic-gate 				(void) sprintf(line,
2857c478bd9Sstevel@tonic-gate 				    "  - Incomplete SACK option");
2867c478bd9Sstevel@tonic-gate 				break;
2877c478bd9Sstevel@tonic-gate 			}
2887c478bd9Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
2897c478bd9Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
2907c478bd9Sstevel@tonic-gate 			end_opt = opt + optlen;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK blocks:");
2937c478bd9Sstevel@tonic-gate 			line = get_line((char *)&opt - dlc_header, 1);
2947c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "        ");
2957c478bd9Sstevel@tonic-gate 			while (sack_len > 0) {
2967c478bd9Sstevel@tonic-gate 				char sack_blk[MAXLINE + 1];
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 				/*
2997c478bd9Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
3007c478bd9Sstevel@tonic-gate 				 * the real length...  Need to be careful
3017c478bd9Sstevel@tonic-gate 				 * not to step beyond the option buffer.
3027c478bd9Sstevel@tonic-gate 				 */
3037c478bd9Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
3047c478bd9Sstevel@tonic-gate 					(void) strcat(line,
3057c478bd9Sstevel@tonic-gate 					    "...incomplete SACK block");
3067c478bd9Sstevel@tonic-gate 					break;
3077c478bd9Sstevel@tonic-gate 				}
3087c478bd9Sstevel@tonic-gate 				(void) sprintf(sack_blk, "(%u-%u) ",
3097c478bd9Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
3107c478bd9Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
3117c478bd9Sstevel@tonic-gate 				(void) strcat(line, sack_blk);
3127c478bd9Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
3137c478bd9Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
3147c478bd9Sstevel@tonic-gate 			}
3157c478bd9Sstevel@tonic-gate 			break;
3167c478bd9Sstevel@tonic-gate 		default:
3177c478bd9Sstevel@tonic-gate 			(void) sprintf(line,
3187c478bd9Sstevel@tonic-gate 			"  - Option %d (unknown - %d bytes) %s",
3197c478bd9Sstevel@tonic-gate 				opt[0],
3207c478bd9Sstevel@tonic-gate 				len - 2,
3217c478bd9Sstevel@tonic-gate 				tohex((char *)&opt[2], len - 2));
3227c478bd9Sstevel@tonic-gate 			break;
3237c478bd9Sstevel@tonic-gate 		}
3247c478bd9Sstevel@tonic-gate 		if (len <= 0) {
3257c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "  - Incomplete option len %d",
3267c478bd9Sstevel@tonic-gate 				len);
3277c478bd9Sstevel@tonic-gate 			break;
3287c478bd9Sstevel@tonic-gate 		}
3297c478bd9Sstevel@tonic-gate 		opt += len;
3307c478bd9Sstevel@tonic-gate 		optlen -= len;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate /*
3357c478bd9Sstevel@tonic-gate  * This function is basically the same as print_tcpoptions() except that
3367c478bd9Sstevel@tonic-gate  * all options are printed on the same line.
3377c478bd9Sstevel@tonic-gate  */
3387c478bd9Sstevel@tonic-gate static void
3397c478bd9Sstevel@tonic-gate print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
3407c478bd9Sstevel@tonic-gate {
3417c478bd9Sstevel@tonic-gate 	int	 len;
3427c478bd9Sstevel@tonic-gate 	uchar_t	*sack_opt;
3437c478bd9Sstevel@tonic-gate 	uchar_t	*end_opt;
3447c478bd9Sstevel@tonic-gate 	int	sack_len;
3457c478bd9Sstevel@tonic-gate 	char	options[MAXLINE + 1];
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	if (optlen <= 0) {
3487c478bd9Sstevel@tonic-gate 		return;
3497c478bd9Sstevel@tonic-gate 	}
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	(void) strcat(line, " Options=<");
3527c478bd9Sstevel@tonic-gate 	while (optlen > 0) {
3537c478bd9Sstevel@tonic-gate 		len = opt[1];
3547c478bd9Sstevel@tonic-gate 		switch (opt[0]) {
3557c478bd9Sstevel@tonic-gate 		case TCPOPT_EOL:
3567c478bd9Sstevel@tonic-gate 			(void) strcat(line, "eol>");
3577c478bd9Sstevel@tonic-gate 			return;
3587c478bd9Sstevel@tonic-gate 		case TCPOPT_NOP:
3597c478bd9Sstevel@tonic-gate 			(void) strcat(line, "nop");
3607c478bd9Sstevel@tonic-gate 			len = 1;
3617c478bd9Sstevel@tonic-gate 			break;
3627c478bd9Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
3637c478bd9Sstevel@tonic-gate 			(void) sprintf(options, "mss %d",
3647c478bd9Sstevel@tonic-gate 			    (opt[2] << 8) + opt[3]);
3657c478bd9Sstevel@tonic-gate 			(void) strcat(line, options);
3667c478bd9Sstevel@tonic-gate 			break;
3677c478bd9Sstevel@tonic-gate 		case TCPOPT_WSCALE:
3687c478bd9Sstevel@tonic-gate 			(void) sprintf(options, "wscale %d", opt[2]);
3697c478bd9Sstevel@tonic-gate 			(void) strcat(line, options);
3707c478bd9Sstevel@tonic-gate 			break;
3717c478bd9Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
3727c478bd9Sstevel@tonic-gate 			/* Sanity check. */
3737c478bd9Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
3747c478bd9Sstevel@tonic-gate 				(void) strcat(line, "tstamp|");
3757c478bd9Sstevel@tonic-gate 			} else {
3767c478bd9Sstevel@tonic-gate 				(void) sprintf(options,
3777c478bd9Sstevel@tonic-gate 				    "tstamp %u %u", GET_UINT32(opt + 2),
3787c478bd9Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
3797c478bd9Sstevel@tonic-gate 				(void) strcat(line, options);
3807c478bd9Sstevel@tonic-gate 			}
3817c478bd9Sstevel@tonic-gate 			break;
3827c478bd9Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
3837c478bd9Sstevel@tonic-gate 			(void) strcat(line, "sackOK");
3847c478bd9Sstevel@tonic-gate 			break;
3857c478bd9Sstevel@tonic-gate 		case TCPOPT_SACK:
3867c478bd9Sstevel@tonic-gate 			/*
3877c478bd9Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
3887c478bd9Sstevel@tonic-gate 			 * than just the option header length.
3897c478bd9Sstevel@tonic-gate 			 */
3907c478bd9Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
3917c478bd9Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
3927c478bd9Sstevel@tonic-gate 				(void) strcat(line, "sack|");
3937c478bd9Sstevel@tonic-gate 				break;
3947c478bd9Sstevel@tonic-gate 			}
3957c478bd9Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
3967c478bd9Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
3977c478bd9Sstevel@tonic-gate 			end_opt = opt + optlen;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 			(void) strcat(line, "sack");
4007c478bd9Sstevel@tonic-gate 			while (sack_len > 0) {
4017c478bd9Sstevel@tonic-gate 				/*
4027c478bd9Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
4037c478bd9Sstevel@tonic-gate 				 * the real length...  Need to be careful
4047c478bd9Sstevel@tonic-gate 				 * not to step beyond the option buffer.
4057c478bd9Sstevel@tonic-gate 				 */
4067c478bd9Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
4077c478bd9Sstevel@tonic-gate 					(void) strcat(line, "|");
4087c478bd9Sstevel@tonic-gate 					break;
4097c478bd9Sstevel@tonic-gate 				}
4107c478bd9Sstevel@tonic-gate 				(void) sprintf(options, " %u-%u",
4117c478bd9Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
4127c478bd9Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
4137c478bd9Sstevel@tonic-gate 				(void) strcat(line, options);
4147c478bd9Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
4157c478bd9Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
4167c478bd9Sstevel@tonic-gate 			}
4177c478bd9Sstevel@tonic-gate 			break;
4187c478bd9Sstevel@tonic-gate 		default:
4197c478bd9Sstevel@tonic-gate 			(void) sprintf(options, "unknown %d", opt[0]);
4207c478bd9Sstevel@tonic-gate 			(void) strcat(line, options);
4217c478bd9Sstevel@tonic-gate 			break;
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 		if (len <= 0) {
4247c478bd9Sstevel@tonic-gate 			(void) sprintf(options, "optlen %d", len);
4257c478bd9Sstevel@tonic-gate 			(void) strcat(line, options);
4267c478bd9Sstevel@tonic-gate 			break;
4277c478bd9Sstevel@tonic-gate 		}
4287c478bd9Sstevel@tonic-gate 		opt += len;
4297c478bd9Sstevel@tonic-gate 		optlen -= len;
4307c478bd9Sstevel@tonic-gate 		if (optlen > 0) {
4317c478bd9Sstevel@tonic-gate 			(void) strcat(line, ",");
4327c478bd9Sstevel@tonic-gate 		}
4337c478bd9Sstevel@tonic-gate 	}
4347c478bd9Sstevel@tonic-gate 	(void) strcat(line, ">");
4357c478bd9Sstevel@tonic-gate }
436