xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 1991-2001,2003 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate 
28*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #include <stdio.h>
31*0Sstevel@tonic-gate #include <string.h>
32*0Sstevel@tonic-gate #include <fcntl.h>
33*0Sstevel@tonic-gate #include <string.h>
34*0Sstevel@tonic-gate #include <sys/types.h>
35*0Sstevel@tonic-gate #include <sys/time.h>
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #include <sys/socket.h>
38*0Sstevel@tonic-gate #include <net/if.h>
39*0Sstevel@tonic-gate #include <netinet/in_systm.h>
40*0Sstevel@tonic-gate #include <netinet/in.h>
41*0Sstevel@tonic-gate #include <netinet/ip.h>
42*0Sstevel@tonic-gate #include <netinet/if_ether.h>
43*0Sstevel@tonic-gate #include <netinet/tcp.h>
44*0Sstevel@tonic-gate #include "snoop.h"
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate extern char *dlc_header;
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate #define	TCPOPT_HEADER_LEN	2
49*0Sstevel@tonic-gate #define	TCPOPT_TSTAMP_LEN	10
50*0Sstevel@tonic-gate #define	TCPOPT_SACK_LEN		8
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate /*
53*0Sstevel@tonic-gate  * Convert a network byte order 32 bit integer to a host order integer.
54*0Sstevel@tonic-gate  * ntohl() cannot be used because option values may not be aligned properly.
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate #define	GET_UINT32(opt)	(((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
57*0Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
58*0Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
59*0Sstevel@tonic-gate 	((uint_t)*((uchar_t *)(opt) + 3)))
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate static void print_tcpoptions_summary(uchar_t *, int, char *);
62*0Sstevel@tonic-gate static void print_tcpoptions(uchar_t *, int);
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate static const struct {
65*0Sstevel@tonic-gate 	unsigned int	tf_flag;
66*0Sstevel@tonic-gate 	const char	*tf_name;
67*0Sstevel@tonic-gate } tcp_flags[] = {
68*0Sstevel@tonic-gate 	{ TH_SYN, 	"Syn"	},
69*0Sstevel@tonic-gate 	{ TH_FIN, 	"Fin"	},
70*0Sstevel@tonic-gate 	{ TH_RST, 	"Rst"	},
71*0Sstevel@tonic-gate 	{ TH_PUSH,	"Push"	},
72*0Sstevel@tonic-gate 	{ TH_ECE,	"ECE"	},
73*0Sstevel@tonic-gate 	{ TH_CWR,	"CWR"	},
74*0Sstevel@tonic-gate 	{ 0,		NULL	}
75*0Sstevel@tonic-gate };
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate int
78*0Sstevel@tonic-gate interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
79*0Sstevel@tonic-gate {
80*0Sstevel@tonic-gate 	char *data;
81*0Sstevel@tonic-gate 	int hdrlen, tcplen;
82*0Sstevel@tonic-gate 	int sunrpc = 0;
83*0Sstevel@tonic-gate 	char *pname;
84*0Sstevel@tonic-gate 	char buff[32];
85*0Sstevel@tonic-gate 	char *line, *endline;
86*0Sstevel@tonic-gate 	unsigned int i;
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	hdrlen = tcp->th_off * 4;
89*0Sstevel@tonic-gate 	data = (char *)tcp + hdrlen;
90*0Sstevel@tonic-gate 	tcplen = iplen - hdrlen;
91*0Sstevel@tonic-gate 	fraglen -= hdrlen;
92*0Sstevel@tonic-gate 	if (fraglen < 0)
93*0Sstevel@tonic-gate 		return;		/* incomplete header */
94*0Sstevel@tonic-gate 	if (fraglen > tcplen)
95*0Sstevel@tonic-gate 		fraglen = tcplen;
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate 	if (flags & F_SUM) {
98*0Sstevel@tonic-gate 		line = get_sum_line();
99*0Sstevel@tonic-gate 		endline = line + MAXLINE;
100*0Sstevel@tonic-gate 		(void) snprintf(line, endline - line, "TCP D=%d S=%d",
101*0Sstevel@tonic-gate 		    ntohs(tcp->th_dport), ntohs(tcp->th_sport));
102*0Sstevel@tonic-gate 		line += strlen(line);
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate 		for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
105*0Sstevel@tonic-gate 			if (tcp->th_flags & tcp_flags[i].tf_flag) {
106*0Sstevel@tonic-gate 				(void) snprintf(line, endline - line, " %s",
107*0Sstevel@tonic-gate 				    tcp_flags[i].tf_name);
108*0Sstevel@tonic-gate 				line += strlen(line);
109*0Sstevel@tonic-gate 			}
110*0Sstevel@tonic-gate 		}
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 		if (tcp->th_flags & TH_URG) {
113*0Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Urg=%u",
114*0Sstevel@tonic-gate 			    ntohs(tcp->th_urp));
115*0Sstevel@tonic-gate 			line += strlen(line);
116*0Sstevel@tonic-gate 		}
117*0Sstevel@tonic-gate 		if (tcp->th_flags & TH_ACK) {
118*0Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Ack=%u",
119*0Sstevel@tonic-gate 				ntohl(tcp->th_ack));
120*0Sstevel@tonic-gate 			line += strlen(line);
121*0Sstevel@tonic-gate 		}
122*0Sstevel@tonic-gate 		if (ntohl(tcp->th_seq)) {
123*0Sstevel@tonic-gate 			(void) snprintf(line, endline - line, " Seq=%u Len=%d",
124*0Sstevel@tonic-gate 				ntohl(tcp->th_seq), tcplen);
125*0Sstevel@tonic-gate 			line += strlen(line);
126*0Sstevel@tonic-gate 		}
127*0Sstevel@tonic-gate 		(void) snprintf(line, endline - line, " Win=%d",
128*0Sstevel@tonic-gate 		    ntohs(tcp->th_win));
129*0Sstevel@tonic-gate 		print_tcpoptions_summary((uchar_t *)(tcp + 1),
130*0Sstevel@tonic-gate 		    (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
131*0Sstevel@tonic-gate 	}
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate 	sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
134*0Sstevel@tonic-gate 		!reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
135*0Sstevel@tonic-gate 		valid_rpc(data + 4, fraglen - 4);
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	if (flags & F_DTAIL) {
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	show_header("TCP:  ", "TCP Header", tcplen);
140*0Sstevel@tonic-gate 	show_space();
141*0Sstevel@tonic-gate 	(void) sprintf(get_line((char *)tcp->th_sport - dlc_header, 2),
142*0Sstevel@tonic-gate 		"Source port = %d",
143*0Sstevel@tonic-gate 		ntohs(tcp->th_sport));
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	if (sunrpc) {
146*0Sstevel@tonic-gate 		pname = "(Sun RPC)";
147*0Sstevel@tonic-gate 	} else {
148*0Sstevel@tonic-gate 		pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
149*0Sstevel@tonic-gate 		if (pname == NULL) {
150*0Sstevel@tonic-gate 			pname = "";
151*0Sstevel@tonic-gate 		} else {
152*0Sstevel@tonic-gate 			(void) sprintf(buff, "(%s)", pname);
153*0Sstevel@tonic-gate 			pname = buff;
154*0Sstevel@tonic-gate 		}
155*0Sstevel@tonic-gate 	}
156*0Sstevel@tonic-gate 	(void) sprintf(get_line((char *)tcp->th_dport - dlc_header, 2),
157*0Sstevel@tonic-gate 		"Destination port = %d %s",
158*0Sstevel@tonic-gate 		ntohs(tcp->th_dport), pname);
159*0Sstevel@tonic-gate 	(void) sprintf(get_line((char *)tcp->th_seq - dlc_header, 4),
160*0Sstevel@tonic-gate 		"Sequence number = %u",
161*0Sstevel@tonic-gate 		ntohl(tcp->th_seq));
162*0Sstevel@tonic-gate 	(void) sprintf(get_line((char *)tcp->th_ack - dlc_header, 4),
163*0Sstevel@tonic-gate 		"Acknowledgement number = %u",
164*0Sstevel@tonic-gate 		ntohl(tcp->th_ack));
165*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_ack - dlc_header) + 4, 1),
166*0Sstevel@tonic-gate 		"Data offset = %d bytes",
167*0Sstevel@tonic-gate 		tcp->th_off * 4);
168*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
169*0Sstevel@tonic-gate 		"Flags = 0x%02x",
170*0Sstevel@tonic-gate 		tcp->th_flags);
171*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
172*0Sstevel@tonic-gate 		"      %s",
173*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_CWR,
174*0Sstevel@tonic-gate 			"ECN congestion window reduced",
175*0Sstevel@tonic-gate 			"No ECN congestion window reduced"));
176*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
177*0Sstevel@tonic-gate 		"      %s",
178*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_ECE,
179*0Sstevel@tonic-gate 			"ECN echo", "No ECN echo"));
180*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
181*0Sstevel@tonic-gate 		"      %s",
182*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_URG,
183*0Sstevel@tonic-gate 			"Urgent pointer", "No urgent pointer"));
184*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
185*0Sstevel@tonic-gate 		"      %s",
186*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_ACK,
187*0Sstevel@tonic-gate 			"Acknowledgement", "No acknowledgement"));
188*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
189*0Sstevel@tonic-gate 		"      %s",
190*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_PUSH,
191*0Sstevel@tonic-gate 			"Push", "No push"));
192*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
193*0Sstevel@tonic-gate 		"      %s",
194*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_RST,
195*0Sstevel@tonic-gate 			"Reset", "No reset"));
196*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
197*0Sstevel@tonic-gate 		"      %s",
198*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_SYN,
199*0Sstevel@tonic-gate 			"Syn", "No Syn"));
200*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
201*0Sstevel@tonic-gate 		"      %s",
202*0Sstevel@tonic-gate 		getflag(tcp->th_flags, TH_FIN,
203*0Sstevel@tonic-gate 			"Fin", "No Fin"));
204*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_win - dlc_header) + 4, 1),
205*0Sstevel@tonic-gate 		"Window = %d",
206*0Sstevel@tonic-gate 		ntohs(tcp->th_win));
207*0Sstevel@tonic-gate 	/* XXX need to compute checksum and print whether correct */
208*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_sum - dlc_header) + 4, 1),
209*0Sstevel@tonic-gate 		"Checksum = 0x%04x",
210*0Sstevel@tonic-gate 		ntohs(tcp->th_sum));
211*0Sstevel@tonic-gate 	(void) sprintf(get_line(((char *)tcp->th_urp - dlc_header) + 4, 1),
212*0Sstevel@tonic-gate 		"Urgent pointer = %d",
213*0Sstevel@tonic-gate 		ntohs(tcp->th_urp));
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 	/* Print TCP options - if any */
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	print_tcpoptions((uchar_t *)(tcp + 1),
218*0Sstevel@tonic-gate 	    tcp->th_off * 4 - sizeof (struct tcphdr));
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 	show_space();
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	/* go to the next protocol layer */
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	if (!interpret_reserved(flags, IPPROTO_TCP,
226*0Sstevel@tonic-gate 		ntohs(tcp->th_sport),
227*0Sstevel@tonic-gate 		ntohs(tcp->th_dport),
228*0Sstevel@tonic-gate 		data, fraglen)) {
229*0Sstevel@tonic-gate 		if (sunrpc && fraglen > 0)
230*0Sstevel@tonic-gate 			interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
231*0Sstevel@tonic-gate 	}
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	return (tcplen);
234*0Sstevel@tonic-gate }
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate static void
237*0Sstevel@tonic-gate print_tcpoptions(opt, optlen)
238*0Sstevel@tonic-gate 	uchar_t *opt;
239*0Sstevel@tonic-gate 	int optlen;
240*0Sstevel@tonic-gate {
241*0Sstevel@tonic-gate 	int	 len;
242*0Sstevel@tonic-gate 	char	 *line;
243*0Sstevel@tonic-gate 	uchar_t	*sack_opt;
244*0Sstevel@tonic-gate 	uchar_t	*end_opt;
245*0Sstevel@tonic-gate 	int	sack_len;
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	if (optlen <= 0) {
248*0Sstevel@tonic-gate 		(void) sprintf(get_line((char *)&opt - dlc_header, 1),
249*0Sstevel@tonic-gate 		"No options");
250*0Sstevel@tonic-gate 		return;
251*0Sstevel@tonic-gate 	}
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	(void) sprintf(get_line((char *)&opt - dlc_header, 1),
254*0Sstevel@tonic-gate 	"Options: (%d bytes)", optlen);
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	while (optlen > 0) {
257*0Sstevel@tonic-gate 		line = get_line((char *)&opt - dlc_header, 1);
258*0Sstevel@tonic-gate 		len = opt[1];
259*0Sstevel@tonic-gate 		switch (opt[0]) {
260*0Sstevel@tonic-gate 		case TCPOPT_EOL:
261*0Sstevel@tonic-gate 			(void) strcpy(line, "  - End of option list");
262*0Sstevel@tonic-gate 			return;
263*0Sstevel@tonic-gate 		case TCPOPT_NOP:
264*0Sstevel@tonic-gate 			(void) strcpy(line, "  - No operation");
265*0Sstevel@tonic-gate 			len = 1;
266*0Sstevel@tonic-gate 			break;
267*0Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
268*0Sstevel@tonic-gate 			(void) sprintf(line,
269*0Sstevel@tonic-gate 			"  - Maximum segment size = %d bytes",
270*0Sstevel@tonic-gate 				(opt[2] << 8) + opt[3]);
271*0Sstevel@tonic-gate 			break;
272*0Sstevel@tonic-gate 		case TCPOPT_WSCALE:
273*0Sstevel@tonic-gate 			(void) sprintf(line, "  - Window scale = %d", opt[2]);
274*0Sstevel@tonic-gate 			break;
275*0Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
276*0Sstevel@tonic-gate 			/* Sanity check. */
277*0Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
278*0Sstevel@tonic-gate 				(void) sprintf(line,
279*0Sstevel@tonic-gate 				    "  - Incomplete TS option");
280*0Sstevel@tonic-gate 			} else {
281*0Sstevel@tonic-gate 				(void) sprintf(line,
282*0Sstevel@tonic-gate 				    "  - TS Val = %u, TS Echo = %u",
283*0Sstevel@tonic-gate 				    GET_UINT32(opt + 2),
284*0Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
285*0Sstevel@tonic-gate 			}
286*0Sstevel@tonic-gate 			break;
287*0Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
288*0Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK permitted option");
289*0Sstevel@tonic-gate 			break;
290*0Sstevel@tonic-gate 		case TCPOPT_SACK:
291*0Sstevel@tonic-gate 			/*
292*0Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
293*0Sstevel@tonic-gate 			 * than just the option header length.
294*0Sstevel@tonic-gate 			 */
295*0Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
296*0Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
297*0Sstevel@tonic-gate 				(void) sprintf(line,
298*0Sstevel@tonic-gate 				    "  - Incomplete SACK option");
299*0Sstevel@tonic-gate 				break;
300*0Sstevel@tonic-gate 			}
301*0Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
302*0Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
303*0Sstevel@tonic-gate 			end_opt = opt + optlen;
304*0Sstevel@tonic-gate 
305*0Sstevel@tonic-gate 			(void) sprintf(line, "  - SACK blocks:");
306*0Sstevel@tonic-gate 			line = get_line((char *)&opt - dlc_header, 1);
307*0Sstevel@tonic-gate 			(void) sprintf(line, "        ");
308*0Sstevel@tonic-gate 			while (sack_len > 0) {
309*0Sstevel@tonic-gate 				char sack_blk[MAXLINE + 1];
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 				/*
312*0Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
313*0Sstevel@tonic-gate 				 * the real length...  Need to be careful
314*0Sstevel@tonic-gate 				 * not to step beyond the option buffer.
315*0Sstevel@tonic-gate 				 */
316*0Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
317*0Sstevel@tonic-gate 					(void) strcat(line,
318*0Sstevel@tonic-gate 					    "...incomplete SACK block");
319*0Sstevel@tonic-gate 					break;
320*0Sstevel@tonic-gate 				}
321*0Sstevel@tonic-gate 				(void) sprintf(sack_blk, "(%u-%u) ",
322*0Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
323*0Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
324*0Sstevel@tonic-gate 				(void) strcat(line, sack_blk);
325*0Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
326*0Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
327*0Sstevel@tonic-gate 			}
328*0Sstevel@tonic-gate 			break;
329*0Sstevel@tonic-gate 		default:
330*0Sstevel@tonic-gate 			(void) sprintf(line,
331*0Sstevel@tonic-gate 			"  - Option %d (unknown - %d bytes) %s",
332*0Sstevel@tonic-gate 				opt[0],
333*0Sstevel@tonic-gate 				len - 2,
334*0Sstevel@tonic-gate 				tohex((char *)&opt[2], len - 2));
335*0Sstevel@tonic-gate 			break;
336*0Sstevel@tonic-gate 		}
337*0Sstevel@tonic-gate 		if (len <= 0) {
338*0Sstevel@tonic-gate 			(void) sprintf(line, "  - Incomplete option len %d",
339*0Sstevel@tonic-gate 				len);
340*0Sstevel@tonic-gate 			break;
341*0Sstevel@tonic-gate 		}
342*0Sstevel@tonic-gate 		opt += len;
343*0Sstevel@tonic-gate 		optlen -= len;
344*0Sstevel@tonic-gate 	}
345*0Sstevel@tonic-gate }
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate /*
348*0Sstevel@tonic-gate  * This function is basically the same as print_tcpoptions() except that
349*0Sstevel@tonic-gate  * all options are printed on the same line.
350*0Sstevel@tonic-gate  */
351*0Sstevel@tonic-gate static void
352*0Sstevel@tonic-gate print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
353*0Sstevel@tonic-gate {
354*0Sstevel@tonic-gate 	int	 len;
355*0Sstevel@tonic-gate 	uchar_t	*sack_opt;
356*0Sstevel@tonic-gate 	uchar_t	*end_opt;
357*0Sstevel@tonic-gate 	int	sack_len;
358*0Sstevel@tonic-gate 	char	options[MAXLINE + 1];
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 	if (optlen <= 0) {
361*0Sstevel@tonic-gate 		return;
362*0Sstevel@tonic-gate 	}
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	(void) strcat(line, " Options=<");
365*0Sstevel@tonic-gate 	while (optlen > 0) {
366*0Sstevel@tonic-gate 		len = opt[1];
367*0Sstevel@tonic-gate 		switch (opt[0]) {
368*0Sstevel@tonic-gate 		case TCPOPT_EOL:
369*0Sstevel@tonic-gate 			(void) strcat(line, "eol>");
370*0Sstevel@tonic-gate 			return;
371*0Sstevel@tonic-gate 		case TCPOPT_NOP:
372*0Sstevel@tonic-gate 			(void) strcat(line, "nop");
373*0Sstevel@tonic-gate 			len = 1;
374*0Sstevel@tonic-gate 			break;
375*0Sstevel@tonic-gate 		case TCPOPT_MAXSEG:
376*0Sstevel@tonic-gate 			(void) sprintf(options, "mss %d",
377*0Sstevel@tonic-gate 			    (opt[2] << 8) + opt[3]);
378*0Sstevel@tonic-gate 			(void) strcat(line, options);
379*0Sstevel@tonic-gate 			break;
380*0Sstevel@tonic-gate 		case TCPOPT_WSCALE:
381*0Sstevel@tonic-gate 			(void) sprintf(options, "wscale %d", opt[2]);
382*0Sstevel@tonic-gate 			(void) strcat(line, options);
383*0Sstevel@tonic-gate 			break;
384*0Sstevel@tonic-gate 		case TCPOPT_TSTAMP:
385*0Sstevel@tonic-gate 			/* Sanity check. */
386*0Sstevel@tonic-gate 			if (optlen < TCPOPT_TSTAMP_LEN) {
387*0Sstevel@tonic-gate 				(void) strcat(line, "tstamp|");
388*0Sstevel@tonic-gate 			} else {
389*0Sstevel@tonic-gate 				(void) sprintf(options,
390*0Sstevel@tonic-gate 				    "tstamp %u %u", GET_UINT32(opt + 2),
391*0Sstevel@tonic-gate 				    GET_UINT32(opt + 6));
392*0Sstevel@tonic-gate 				(void) strcat(line, options);
393*0Sstevel@tonic-gate 			}
394*0Sstevel@tonic-gate 			break;
395*0Sstevel@tonic-gate 		case TCPOPT_SACK_PERMITTED:
396*0Sstevel@tonic-gate 			(void) strcat(line, "sackOK");
397*0Sstevel@tonic-gate 			break;
398*0Sstevel@tonic-gate 		case TCPOPT_SACK:
399*0Sstevel@tonic-gate 			/*
400*0Sstevel@tonic-gate 			 * Sanity check.  Total length should be greater
401*0Sstevel@tonic-gate 			 * than just the option header length.
402*0Sstevel@tonic-gate 			 */
403*0Sstevel@tonic-gate 			if (len <= TCPOPT_HEADER_LEN ||
404*0Sstevel@tonic-gate 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
405*0Sstevel@tonic-gate 				(void) strcat(line, "sack|");
406*0Sstevel@tonic-gate 				break;
407*0Sstevel@tonic-gate 			}
408*0Sstevel@tonic-gate 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
409*0Sstevel@tonic-gate 			sack_opt = opt + TCPOPT_HEADER_LEN;
410*0Sstevel@tonic-gate 			end_opt = opt + optlen;
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 			(void) strcat(line, "sack");
413*0Sstevel@tonic-gate 			while (sack_len > 0) {
414*0Sstevel@tonic-gate 				/*
415*0Sstevel@tonic-gate 				 * sack_len may not tell us the truth about
416*0Sstevel@tonic-gate 				 * the real length...  Need to be careful
417*0Sstevel@tonic-gate 				 * not to step beyond the option buffer.
418*0Sstevel@tonic-gate 				 */
419*0Sstevel@tonic-gate 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
420*0Sstevel@tonic-gate 					(void) strcat(line, "|");
421*0Sstevel@tonic-gate 					break;
422*0Sstevel@tonic-gate 				}
423*0Sstevel@tonic-gate 				(void) sprintf(options, " %u-%u",
424*0Sstevel@tonic-gate 				    GET_UINT32(sack_opt),
425*0Sstevel@tonic-gate 				    GET_UINT32(sack_opt + 4));
426*0Sstevel@tonic-gate 				(void) strcat(line, options);
427*0Sstevel@tonic-gate 				sack_opt += TCPOPT_SACK_LEN;
428*0Sstevel@tonic-gate 				sack_len -= TCPOPT_SACK_LEN;
429*0Sstevel@tonic-gate 			}
430*0Sstevel@tonic-gate 			break;
431*0Sstevel@tonic-gate 		default:
432*0Sstevel@tonic-gate 			(void) sprintf(options, "unknown %d", opt[0]);
433*0Sstevel@tonic-gate 			(void) strcat(line, options);
434*0Sstevel@tonic-gate 			break;
435*0Sstevel@tonic-gate 		}
436*0Sstevel@tonic-gate 		if (len <= 0) {
437*0Sstevel@tonic-gate 			(void) sprintf(options, "optlen %d", len);
438*0Sstevel@tonic-gate 			(void) strcat(line, options);
439*0Sstevel@tonic-gate 			break;
440*0Sstevel@tonic-gate 		}
441*0Sstevel@tonic-gate 		opt += len;
442*0Sstevel@tonic-gate 		optlen -= len;
443*0Sstevel@tonic-gate 		if (optlen > 0) {
444*0Sstevel@tonic-gate 			(void) strcat(line, ",");
445*0Sstevel@tonic-gate 		}
446*0Sstevel@tonic-gate 	}
447*0Sstevel@tonic-gate 	(void) strcat(line, ">");
448*0Sstevel@tonic-gate }
449