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 /* go to the next protocol layer */ 208 209 if (!interpret_reserved(flags, IPPROTO_TCP, 210 ntohs(tcp->th_sport), 211 ntohs(tcp->th_dport), 212 data, fraglen)) { 213 if (sunrpc && fraglen > 0) 214 interpret_rpc(flags, data, fraglen, IPPROTO_TCP); 215 } 216 217 return (tcplen); 218 } 219 220 static void 221 print_tcpoptions(opt, optlen) 222 uchar_t *opt; 223 int optlen; 224 { 225 int len; 226 char *line; 227 uchar_t *sack_opt; 228 uchar_t *end_opt; 229 int sack_len; 230 231 if (optlen <= 0) { 232 (void) sprintf(get_line((char *)&opt - dlc_header, 1), 233 "No options"); 234 return; 235 } 236 237 (void) sprintf(get_line((char *)&opt - dlc_header, 1), 238 "Options: (%d bytes)", optlen); 239 240 while (optlen > 0) { 241 line = get_line((char *)&opt - dlc_header, 1); 242 len = opt[1]; 243 switch (opt[0]) { 244 case TCPOPT_EOL: 245 (void) strcpy(line, " - End of option list"); 246 return; 247 case TCPOPT_NOP: 248 (void) strcpy(line, " - No operation"); 249 len = 1; 250 break; 251 case TCPOPT_MAXSEG: 252 (void) sprintf(line, 253 " - Maximum segment size = %d bytes", 254 (opt[2] << 8) + opt[3]); 255 break; 256 case TCPOPT_WSCALE: 257 (void) sprintf(line, " - Window scale = %d", opt[2]); 258 break; 259 case TCPOPT_TSTAMP: 260 /* Sanity check. */ 261 if (optlen < TCPOPT_TSTAMP_LEN) { 262 (void) sprintf(line, 263 " - Incomplete TS option"); 264 } else { 265 (void) sprintf(line, 266 " - TS Val = %u, TS Echo = %u", 267 GET_UINT32(opt + 2), 268 GET_UINT32(opt + 6)); 269 } 270 break; 271 case TCPOPT_SACK_PERMITTED: 272 (void) sprintf(line, " - SACK permitted option"); 273 break; 274 case TCPOPT_SACK: 275 /* 276 * Sanity check. Total length should be greater 277 * than just the option header length. 278 */ 279 if (len <= TCPOPT_HEADER_LEN || 280 opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) { 281 (void) sprintf(line, 282 " - Incomplete SACK option"); 283 break; 284 } 285 sack_len = opt[1] - TCPOPT_HEADER_LEN; 286 sack_opt = opt + TCPOPT_HEADER_LEN; 287 end_opt = opt + optlen; 288 289 (void) sprintf(line, " - SACK blocks:"); 290 line = get_line((char *)&opt - dlc_header, 1); 291 (void) sprintf(line, " "); 292 while (sack_len > 0) { 293 char sack_blk[MAXLINE + 1]; 294 295 /* 296 * sack_len may not tell us the truth about 297 * the real length... Need to be careful 298 * not to step beyond the option buffer. 299 */ 300 if (sack_opt + TCPOPT_SACK_LEN > end_opt) { 301 (void) strcat(line, 302 "...incomplete SACK block"); 303 break; 304 } 305 (void) sprintf(sack_blk, "(%u-%u) ", 306 GET_UINT32(sack_opt), 307 GET_UINT32(sack_opt + 4)); 308 (void) strcat(line, sack_blk); 309 sack_opt += TCPOPT_SACK_LEN; 310 sack_len -= TCPOPT_SACK_LEN; 311 } 312 break; 313 default: 314 (void) sprintf(line, 315 " - Option %d (unknown - %d bytes) %s", 316 opt[0], 317 len - 2, 318 tohex((char *)&opt[2], len - 2)); 319 break; 320 } 321 if (len <= 0) { 322 (void) sprintf(line, " - Incomplete option len %d", 323 len); 324 break; 325 } 326 opt += len; 327 optlen -= len; 328 } 329 } 330 331 /* 332 * This function is basically the same as print_tcpoptions() except that 333 * all options are printed on the same line. 334 */ 335 static void 336 print_tcpoptions_summary(uchar_t *opt, int optlen, char *line) 337 { 338 int len; 339 uchar_t *sack_opt; 340 uchar_t *end_opt; 341 int sack_len; 342 char options[MAXLINE + 1]; 343 344 if (optlen <= 0) { 345 return; 346 } 347 348 (void) strcat(line, " Options=<"); 349 while (optlen > 0) { 350 len = opt[1]; 351 switch (opt[0]) { 352 case TCPOPT_EOL: 353 (void) strcat(line, "eol>"); 354 return; 355 case TCPOPT_NOP: 356 (void) strcat(line, "nop"); 357 len = 1; 358 break; 359 case TCPOPT_MAXSEG: 360 (void) sprintf(options, "mss %d", 361 (opt[2] << 8) + opt[3]); 362 (void) strcat(line, options); 363 break; 364 case TCPOPT_WSCALE: 365 (void) sprintf(options, "wscale %d", opt[2]); 366 (void) strcat(line, options); 367 break; 368 case TCPOPT_TSTAMP: 369 /* Sanity check. */ 370 if (optlen < TCPOPT_TSTAMP_LEN) { 371 (void) strcat(line, "tstamp|"); 372 } else { 373 (void) sprintf(options, 374 "tstamp %u %u", GET_UINT32(opt + 2), 375 GET_UINT32(opt + 6)); 376 (void) strcat(line, options); 377 } 378 break; 379 case TCPOPT_SACK_PERMITTED: 380 (void) strcat(line, "sackOK"); 381 break; 382 case TCPOPT_SACK: 383 /* 384 * Sanity check. Total length should be greater 385 * than just the option header length. 386 */ 387 if (len <= TCPOPT_HEADER_LEN || 388 opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) { 389 (void) strcat(line, "sack|"); 390 break; 391 } 392 sack_len = opt[1] - TCPOPT_HEADER_LEN; 393 sack_opt = opt + TCPOPT_HEADER_LEN; 394 end_opt = opt + optlen; 395 396 (void) strcat(line, "sack"); 397 while (sack_len > 0) { 398 /* 399 * sack_len may not tell us the truth about 400 * the real length... Need to be careful 401 * not to step beyond the option buffer. 402 */ 403 if (sack_opt + TCPOPT_SACK_LEN > end_opt) { 404 (void) strcat(line, "|"); 405 break; 406 } 407 (void) sprintf(options, " %u-%u", 408 GET_UINT32(sack_opt), 409 GET_UINT32(sack_opt + 4)); 410 (void) strcat(line, options); 411 sack_opt += TCPOPT_SACK_LEN; 412 sack_len -= TCPOPT_SACK_LEN; 413 } 414 break; 415 default: 416 (void) sprintf(options, "unknown %d", opt[0]); 417 (void) strcat(line, options); 418 break; 419 } 420 if (len <= 0) { 421 (void) sprintf(options, "optlen %d", len); 422 (void) strcat(line, options); 423 break; 424 } 425 opt += len; 426 optlen -= len; 427 if (optlen > 0) { 428 (void) strcat(line, ","); 429 } 430 } 431 (void) strcat(line, ">"); 432 } 433