summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
new file mode 100644
index 0000000000..4ed56b8c52
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
@@ -0,0 +1,448 @@
+/*
+ * 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 1991-2001,2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#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; /* 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 *)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 *)tcp->th_dport - dlc_header, 2),
+ "Destination port = %d %s",
+ ntohs(tcp->th_dport), pname);
+ (void) sprintf(get_line((char *)tcp->th_seq - dlc_header, 4),
+ "Sequence number = %u",
+ ntohl(tcp->th_seq));
+ (void) sprintf(get_line((char *)tcp->th_ack - dlc_header, 4),
+ "Acknowledgement number = %u",
+ ntohl(tcp->th_ack));
+ (void) sprintf(get_line(((char *)tcp->th_ack - dlc_header) + 4, 1),
+ "Data offset = %d bytes",
+ tcp->th_off * 4);
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ "Flags = 0x%02x",
+ tcp->th_flags);
+ (void) sprintf(get_line(((char *)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 *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_ECE,
+ "ECN echo", "No ECN echo"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_URG,
+ "Urgent pointer", "No urgent pointer"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_ACK,
+ "Acknowledgement", "No acknowledgement"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_PUSH,
+ "Push", "No push"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_RST,
+ "Reset", "No reset"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_SYN,
+ "Syn", "No Syn"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_FIN,
+ "Fin", "No Fin"));
+ (void) sprintf(get_line(((char *)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 *)tcp->th_sum - dlc_header) + 4, 1),
+ "Checksum = 0x%04x",
+ ntohs(tcp->th_sum));
+ (void) sprintf(get_line(((char *)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, ">");
+}