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 * Copyright 2024 Oxide Computer Company 27 */ 28 29 #include <stdio.h> 30 #include <string.h> 31 #include <fcntl.h> 32 #include <string.h> 33 #include <sys/types.h> 34 #include <sys/time.h> 35 36 #include <sys/socket.h> 37 #include <net/if.h> 38 #include <netinet/in_systm.h> 39 #include <netinet/in.h> 40 #include <netinet/ip.h> 41 #include <netinet/if_ether.h> 42 #include <netinet/tcp.h> 43 #include "snoop.h" 44 45 extern char *dlc_header; 46 47 #define TCPOPT_HEADER_LEN 2 48 #define TCPOPT_TSTAMP_LEN 10 49 #define TCPOPT_SACK_LEN 8 50 #define TCPOPT_MD5_LEN 18 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 (fraglen + hdrlen); /* 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 *)(uintptr_t)tcp->th_sport - 142 dlc_header, 2), "Source port = %d", ntohs(tcp->th_sport)); 143 144 if (sunrpc) { 145 pname = "(Sun RPC)"; 146 } else { 147 pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport)); 148 if (pname == NULL) { 149 pname = ""; 150 } else { 151 (void) sprintf(buff, "(%s)", pname); 152 pname = buff; 153 } 154 } 155 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport - 156 dlc_header, 2), "Destination port = %d %s", 157 ntohs(tcp->th_dport), pname); 158 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq - 159 dlc_header, 4), "Sequence number = %u", 160 ntohl(tcp->th_seq)); 161 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4), 162 "Acknowledgement number = %u", 163 ntohl(tcp->th_ack)); 164 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) + 165 4, 1), "Data offset = %d bytes", tcp->th_off * 4); 166 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 167 dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags); 168 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 169 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_CWR, 170 "ECN congestion window reduced", 171 "No ECN congestion window reduced")); 172 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 173 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ECE, 174 "ECN echo", "No ECN echo")); 175 (void) sprintf( 176 get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), 177 " %s", getflag(tcp->th_flags, TH_URG, 178 "Urgent pointer", "No urgent pointer")); 179 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 180 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ACK, 181 "Acknowledgement", "No acknowledgement")); 182 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 183 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_PUSH, 184 "Push", "No push")); 185 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 186 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_RST, 187 "Reset", "No reset")); 188 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 189 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_SYN, 190 "Syn", "No Syn")); 191 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - 192 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_FIN, 193 "Fin", "No Fin")); 194 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) + 195 4, 1), "Window = %d", ntohs(tcp->th_win)); 196 /* XXX need to compute checksum and print whether correct */ 197 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) + 198 4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum)); 199 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) + 200 4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp)); 201 202 /* Print TCP options - if any */ 203 204 print_tcpoptions((uchar_t *)(tcp + 1), 205 tcp->th_off * 4 - sizeof (struct tcphdr)); 206 207 show_space(); 208 } 209 210 /* is there any data? */ 211 if (tcplen == 0) 212 return (tcplen); 213 214 /* go to the next protocol layer */ 215 216 if (!interpret_reserved(flags, IPPROTO_TCP, 217 ntohs(tcp->th_sport), ntohs(tcp->th_dport), data, fraglen)) { 218 if (sunrpc && fraglen > 0) 219 interpret_rpc(flags, data, fraglen, IPPROTO_TCP); 220 } 221 222 return (tcplen); 223 } 224 225 static void 226 print_tcpoptions(uchar_t *opt, int optlen) 227 { 228 int len; 229 char *line; 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 uchar_t *sack_opt, *end_opt; 276 int sack_len; 277 278 /* 279 * Sanity check. Total length should be greater 280 * than just the option header length. 281 */ 282 if (optlen <= TCPOPT_HEADER_LEN || 283 len < TCPOPT_HEADER_LEN) { 284 (void) sprintf(line, 285 " - Incomplete SACK option"); 286 break; 287 } 288 sack_len = len - TCPOPT_HEADER_LEN; 289 sack_opt = opt + TCPOPT_HEADER_LEN; 290 end_opt = opt + optlen; 291 292 (void) sprintf(line, " - SACK blocks:"); 293 line = get_line((char *)&opt - dlc_header, 1); 294 (void) sprintf(line, " "); 295 while (sack_len > 0) { 296 char sack_blk[MAXLINE + 1]; 297 298 /* 299 * sack_len may not tell us the truth about 300 * the real length... Need to be careful 301 * not to step beyond the option buffer. 302 */ 303 if (sack_opt + TCPOPT_SACK_LEN > end_opt) { 304 (void) strcat(line, 305 "...incomplete SACK block"); 306 break; 307 } 308 (void) sprintf(sack_blk, "(%u-%u) ", 309 GET_UINT32(sack_opt), 310 GET_UINT32(sack_opt + 4)); 311 (void) strcat(line, sack_blk); 312 sack_opt += TCPOPT_SACK_LEN; 313 sack_len -= TCPOPT_SACK_LEN; 314 } 315 break; 316 } 317 case TCPOPT_MD5: { 318 uint_t i; 319 320 if (optlen < TCPOPT_MD5_LEN || len != TCPOPT_MD5_LEN) { 321 (void) sprintf(line, 322 " - Incomplete MD5 option"); 323 break; 324 } 325 326 (void) sprintf(line, " - TCP MD5 Signature = 0x"); 327 for (i = 2; i < len; i++) { 328 char options[3]; 329 330 (void) sprintf(options, "%02x", opt[i]); 331 (void) strcat(line, options); 332 } 333 break; 334 } 335 default: 336 (void) sprintf(line, 337 " - Option %d (unknown - %d bytes) %s", 338 opt[0], len - 2, tohex((char *)&opt[2], len - 2)); 339 break; 340 } 341 if (len <= 0) { 342 (void) sprintf(line, " - Incomplete option len %d", 343 len); 344 break; 345 } 346 opt += len; 347 optlen -= len; 348 } 349 } 350 351 /* 352 * This function is basically the same as print_tcpoptions() except that 353 * all options are printed on the same line. 354 */ 355 static void 356 print_tcpoptions_summary(uchar_t *opt, int optlen, char *line) 357 { 358 int len; 359 char options[MAXLINE + 1]; 360 361 if (optlen <= 0) { 362 return; 363 } 364 365 (void) strcat(line, " Options=<"); 366 while (optlen > 0) { 367 len = opt[1]; 368 switch (opt[0]) { 369 case TCPOPT_EOL: 370 (void) strcat(line, "eol>"); 371 return; 372 case TCPOPT_NOP: 373 (void) strcat(line, "nop"); 374 len = 1; 375 break; 376 case TCPOPT_MAXSEG: 377 (void) sprintf(options, "mss %d", 378 (opt[2] << 8) + opt[3]); 379 (void) strcat(line, options); 380 break; 381 case TCPOPT_WSCALE: 382 (void) sprintf(options, "wscale %d", opt[2]); 383 (void) strcat(line, options); 384 break; 385 case TCPOPT_TSTAMP: 386 /* Sanity check. */ 387 if (optlen < TCPOPT_TSTAMP_LEN) { 388 (void) strcat(line, "tstamp|"); 389 } else { 390 (void) sprintf(options, 391 "tstamp %u %u", GET_UINT32(opt + 2), 392 GET_UINT32(opt + 6)); 393 (void) strcat(line, options); 394 } 395 break; 396 case TCPOPT_SACK_PERMITTED: 397 (void) strcat(line, "sackOK"); 398 break; 399 case TCPOPT_SACK: { 400 uchar_t *sack_opt, *end_opt; 401 int sack_len; 402 403 /* 404 * Sanity check. Total length should be greater 405 * than just the option header length. 406 */ 407 if (optlen <= TCPOPT_HEADER_LEN || 408 len < TCPOPT_HEADER_LEN) { 409 (void) strcat(line, "sack|"); 410 break; 411 } 412 sack_len = len - TCPOPT_HEADER_LEN; 413 sack_opt = opt + TCPOPT_HEADER_LEN; 414 end_opt = opt + optlen; 415 416 (void) strcat(line, "sack"); 417 while (sack_len > 0) { 418 /* 419 * sack_len may not tell us the truth about 420 * the real length... Need to be careful 421 * not to step beyond the option buffer. 422 */ 423 if (sack_opt + TCPOPT_SACK_LEN > end_opt) { 424 (void) strcat(line, "|"); 425 break; 426 } 427 (void) sprintf(options, " %u-%u", 428 GET_UINT32(sack_opt), 429 GET_UINT32(sack_opt + 4)); 430 (void) strcat(line, options); 431 sack_opt += TCPOPT_SACK_LEN; 432 sack_len -= TCPOPT_SACK_LEN; 433 } 434 break; 435 } 436 case TCPOPT_MD5: { 437 uint_t i; 438 439 if (optlen < TCPOPT_MD5_LEN || len != TCPOPT_MD5_LEN) { 440 (void) strcat(line, "md5|"); 441 break; 442 } 443 444 (void) strcat(line, "md5 0x"); 445 for (i = 2; i < len; i++) { 446 (void) sprintf(options, "%02x", opt[i]); 447 (void) strcat(line, options); 448 } 449 break; 450 } 451 default: 452 (void) sprintf(options, "unknown %d", opt[0]); 453 (void) strcat(line, options); 454 break; 455 } 456 if (len <= 0) { 457 (void) sprintf(options, "optlen %d", len); 458 (void) strcat(line, options); 459 break; 460 } 461 opt += len; 462 optlen -= len; 463 if (optlen > 0) { 464 (void) strcat(line, ","); 465 } 466 } 467 (void) strcat(line, ">"); 468 } 469