/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snoop.h" extern char *dlc_header; #define TCPOPT_HEADER_LEN 2 #define TCPOPT_TSTAMP_LEN 10 #define TCPOPT_SACK_LEN 8 /* * Convert a network byte order 32 bit integer to a host order integer. * ntohl() cannot be used because option values may not be aligned properly. */ #define GET_UINT32(opt) (((uint_t)*((uchar_t *)(opt) + 0) << 24) | \ ((uint_t)*((uchar_t *)(opt) + 1) << 16) | \ ((uint_t)*((uchar_t *)(opt) + 2) << 8) | \ ((uint_t)*((uchar_t *)(opt) + 3))) static void print_tcpoptions_summary(uchar_t *, int, char *); static void print_tcpoptions(uchar_t *, int); static const struct { unsigned int tf_flag; const char *tf_name; } tcp_flags[] = { { TH_SYN, "Syn" }, { TH_FIN, "Fin" }, { TH_RST, "Rst" }, { TH_PUSH, "Push" }, { TH_ECE, "ECE" }, { TH_CWR, "CWR" }, { 0, NULL } }; int interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen) { char *data; int hdrlen, tcplen; int sunrpc = 0; char *pname; char buff[32]; char *line, *endline; unsigned int i; hdrlen = tcp->th_off * 4; data = (char *)tcp + hdrlen; tcplen = iplen - hdrlen; fraglen -= hdrlen; if (fraglen < 0) return (fraglen + hdrlen); /* incomplete header */ if (fraglen > tcplen) fraglen = tcplen; if (flags & F_SUM) { line = get_sum_line(); endline = line + MAXLINE; (void) snprintf(line, endline - line, "TCP D=%d S=%d", ntohs(tcp->th_dport), ntohs(tcp->th_sport)); line += strlen(line); for (i = 0; tcp_flags[i].tf_name != NULL; i++) { if (tcp->th_flags & tcp_flags[i].tf_flag) { (void) snprintf(line, endline - line, " %s", tcp_flags[i].tf_name); line += strlen(line); } } if (tcp->th_flags & TH_URG) { (void) snprintf(line, endline - line, " Urg=%u", ntohs(tcp->th_urp)); line += strlen(line); } if (tcp->th_flags & TH_ACK) { (void) snprintf(line, endline - line, " Ack=%u", ntohl(tcp->th_ack)); line += strlen(line); } if (ntohl(tcp->th_seq)) { (void) snprintf(line, endline - line, " Seq=%u Len=%d", ntohl(tcp->th_seq), tcplen); line += strlen(line); } (void) snprintf(line, endline - line, " Win=%d", ntohs(tcp->th_win)); print_tcpoptions_summary((uchar_t *)(tcp + 1), (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line); } sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) && !reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) && valid_rpc(data + 4, fraglen - 4); if (flags & F_DTAIL) { show_header("TCP: ", "TCP Header", tcplen); show_space(); (void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport - dlc_header, 2), "Source port = %d", ntohs(tcp->th_sport)); if (sunrpc) { pname = "(Sun RPC)"; } else { pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport)); if (pname == NULL) { pname = ""; } else { (void) sprintf(buff, "(%s)", pname); pname = buff; } } (void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport - dlc_header, 2), "Destination port = %d %s", ntohs(tcp->th_dport), pname); (void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq - dlc_header, 4), "Sequence number = %u", ntohl(tcp->th_seq)); (void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4), "Acknowledgement number = %u", ntohl(tcp->th_ack)); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) + 4, 1), "Data offset = %d bytes", tcp->th_off * 4); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_CWR, "ECN congestion window reduced", "No ECN congestion window reduced")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ECE, "ECN echo", "No ECN echo")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_URG, "Urgent pointer", "No urgent pointer")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ACK, "Acknowledgement", "No acknowledgement")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_PUSH, "Push", "No push")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_RST, "Reset", "No reset")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_SYN, "Syn", "No Syn")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_FIN, "Fin", "No Fin")); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) + 4, 1), "Window = %d", ntohs(tcp->th_win)); /* XXX need to compute checksum and print whether correct */ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) + 4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum)); (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) + 4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp)); /* Print TCP options - if any */ print_tcpoptions((uchar_t *)(tcp + 1), tcp->th_off * 4 - sizeof (struct tcphdr)); show_space(); } /* go to the next protocol layer */ if (!interpret_reserved(flags, IPPROTO_TCP, ntohs(tcp->th_sport), ntohs(tcp->th_dport), data, fraglen)) { if (sunrpc && fraglen > 0) interpret_rpc(flags, data, fraglen, IPPROTO_TCP); } return (tcplen); } static void print_tcpoptions(opt, optlen) uchar_t *opt; int optlen; { int len; char *line; uchar_t *sack_opt; uchar_t *end_opt; int sack_len; if (optlen <= 0) { (void) sprintf(get_line((char *)&opt - dlc_header, 1), "No options"); return; } (void) sprintf(get_line((char *)&opt - dlc_header, 1), "Options: (%d bytes)", optlen); while (optlen > 0) { line = get_line((char *)&opt - dlc_header, 1); len = opt[1]; switch (opt[0]) { case TCPOPT_EOL: (void) strcpy(line, " - End of option list"); return; case TCPOPT_NOP: (void) strcpy(line, " - No operation"); len = 1; break; case TCPOPT_MAXSEG: (void) sprintf(line, " - Maximum segment size = %d bytes", (opt[2] << 8) + opt[3]); break; case TCPOPT_WSCALE: (void) sprintf(line, " - Window scale = %d", opt[2]); break; case TCPOPT_TSTAMP: /* Sanity check. */ if (optlen < TCPOPT_TSTAMP_LEN) { (void) sprintf(line, " - Incomplete TS option"); } else { (void) sprintf(line, " - TS Val = %u, TS Echo = %u", GET_UINT32(opt + 2), GET_UINT32(opt + 6)); } break; case TCPOPT_SACK_PERMITTED: (void) sprintf(line, " - SACK permitted option"); break; case TCPOPT_SACK: /* * Sanity check. Total length should be greater * than just the option header length. */ if (len <= TCPOPT_HEADER_LEN || opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) { (void) sprintf(line, " - Incomplete SACK option"); break; } sack_len = opt[1] - TCPOPT_HEADER_LEN; sack_opt = opt + TCPOPT_HEADER_LEN; end_opt = opt + optlen; (void) sprintf(line, " - SACK blocks:"); line = get_line((char *)&opt - dlc_header, 1); (void) sprintf(line, " "); while (sack_len > 0) { char sack_blk[MAXLINE + 1]; /* * sack_len may not tell us the truth about * the real length... Need to be careful * not to step beyond the option buffer. */ if (sack_opt + TCPOPT_SACK_LEN > end_opt) { (void) strcat(line, "...incomplete SACK block"); break; } (void) sprintf(sack_blk, "(%u-%u) ", GET_UINT32(sack_opt), GET_UINT32(sack_opt + 4)); (void) strcat(line, sack_blk); sack_opt += TCPOPT_SACK_LEN; sack_len -= TCPOPT_SACK_LEN; } break; default: (void) sprintf(line, " - Option %d (unknown - %d bytes) %s", opt[0], len - 2, tohex((char *)&opt[2], len - 2)); break; } if (len <= 0) { (void) sprintf(line, " - Incomplete option len %d", len); break; } opt += len; optlen -= len; } } /* * This function is basically the same as print_tcpoptions() except that * all options are printed on the same line. */ static void print_tcpoptions_summary(uchar_t *opt, int optlen, char *line) { int len; uchar_t *sack_opt; uchar_t *end_opt; int sack_len; char options[MAXLINE + 1]; if (optlen <= 0) { return; } (void) strcat(line, " Options=<"); while (optlen > 0) { len = opt[1]; switch (opt[0]) { case TCPOPT_EOL: (void) strcat(line, "eol>"); return; case TCPOPT_NOP: (void) strcat(line, "nop"); len = 1; break; case TCPOPT_MAXSEG: (void) sprintf(options, "mss %d", (opt[2] << 8) + opt[3]); (void) strcat(line, options); break; case TCPOPT_WSCALE: (void) sprintf(options, "wscale %d", opt[2]); (void) strcat(line, options); break; case TCPOPT_TSTAMP: /* Sanity check. */ if (optlen < TCPOPT_TSTAMP_LEN) { (void) strcat(line, "tstamp|"); } else { (void) sprintf(options, "tstamp %u %u", GET_UINT32(opt + 2), GET_UINT32(opt + 6)); (void) strcat(line, options); } break; case TCPOPT_SACK_PERMITTED: (void) strcat(line, "sackOK"); break; case TCPOPT_SACK: /* * Sanity check. Total length should be greater * than just the option header length. */ if (len <= TCPOPT_HEADER_LEN || opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) { (void) strcat(line, "sack|"); break; } sack_len = opt[1] - TCPOPT_HEADER_LEN; sack_opt = opt + TCPOPT_HEADER_LEN; end_opt = opt + optlen; (void) strcat(line, "sack"); while (sack_len > 0) { /* * sack_len may not tell us the truth about * the real length... Need to be careful * not to step beyond the option buffer. */ if (sack_opt + TCPOPT_SACK_LEN > end_opt) { (void) strcat(line, "|"); break; } (void) sprintf(options, " %u-%u", GET_UINT32(sack_opt), GET_UINT32(sack_opt + 4)); (void) strcat(line, options); sack_opt += TCPOPT_SACK_LEN; sack_len -= TCPOPT_SACK_LEN; } break; default: (void) sprintf(options, "unknown %d", opt[0]); (void) strcat(line, options); break; } if (len <= 0) { (void) sprintf(options, "optlen %d", len); (void) strcat(line, options); break; } opt += len; optlen -= len; if (optlen > 0) { (void) strcat(line, ","); } } (void) strcat(line, ">"); }