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