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