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
interpret_tcp(int flags,struct tcphdr * tcp,int iplen,int fraglen)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
print_tcpoptions(opt,optlen)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
print_tcpoptions_summary(uchar_t * opt,int optlen,char * line)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