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