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