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