summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/cmd/mdb/common/modules/ip/ip.c142
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_time_wait.c15
-rw-r--r--usr/src/uts/common/inet/tcp_impl.h7
3 files changed, 160 insertions, 4 deletions
diff --git a/usr/src/cmd/mdb/common/modules/ip/ip.c b/usr/src/cmd/mdb/common/modules/ip/ip.c
index ab6b6291e5..99f43ce940 100644
--- a/usr/src/cmd/mdb/common/modules/ip/ip.c
+++ b/usr/src/cmd/mdb/common/modules/ip/ip.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
@@ -1022,6 +1021,128 @@ static const mdb_bitmask_t tcp_flags[] = {
{ NULL, 0, 0 }
};
+/* TCP option length */
+#define TCPOPT_HEADER_LEN 2
+#define TCPOPT_MAXSEG_LEN 4
+#define TCPOPT_WS_LEN 3
+#define TCPOPT_TSTAMP_LEN 10
+#define TCPOPT_SACK_OK_LEN 2
+
+static void
+tcphdr_print_options(uint8_t *opts, uint32_t opts_len)
+{
+ uint8_t *endp;
+ uint32_t len, val;
+
+ mdb_printf("%<b>Options:%</b>");
+ endp = opts + opts_len;
+ while (opts < endp) {
+ len = endp - opts;
+ switch (*opts) {
+ case TCPOPT_EOL:
+ mdb_printf(" EOL");
+ opts++;
+ break;
+
+ case TCPOPT_NOP:
+ mdb_printf(" NOP");
+ opts++;
+ break;
+
+ case TCPOPT_MAXSEG: {
+ uint16_t mss;
+
+ if (len < TCPOPT_MAXSEG_LEN ||
+ opts[1] != TCPOPT_MAXSEG_LEN) {
+ mdb_printf(" <Truncated MSS>\n");
+ return;
+ }
+ mdb_nhconvert(&mss, opts + TCPOPT_HEADER_LEN,
+ sizeof (mss));
+ mdb_printf(" MSS=%u", mss);
+ opts += TCPOPT_MAXSEG_LEN;
+ break;
+ }
+
+ case TCPOPT_WSCALE:
+ if (len < TCPOPT_WS_LEN || opts[1] != TCPOPT_WS_LEN) {
+ mdb_printf(" <Truncated WS>\n");
+ return;
+ }
+ mdb_printf(" WS=%u", opts[2]);
+ opts += TCPOPT_WS_LEN;
+ break;
+
+ case TCPOPT_TSTAMP: {
+ if (len < TCPOPT_TSTAMP_LEN ||
+ opts[1] != TCPOPT_TSTAMP_LEN) {
+ mdb_printf(" <Truncated TS>\n");
+ return;
+ }
+
+ opts += TCPOPT_HEADER_LEN;
+ mdb_nhconvert(&val, opts, sizeof (val));
+ mdb_printf(" TS_VAL=%u,", val);
+
+ opts += sizeof (val);
+ mdb_nhconvert(&val, opts, sizeof (val));
+ mdb_printf("TS_ECHO=%u", val);
+
+ opts += sizeof (val);
+ break;
+ }
+
+ case TCPOPT_SACK_PERMITTED:
+ if (len < TCPOPT_SACK_OK_LEN ||
+ opts[1] != TCPOPT_SACK_OK_LEN) {
+ mdb_printf(" <Truncated SACK_OK>\n");
+ return;
+ }
+ mdb_printf(" SACK_OK");
+ opts += TCPOPT_SACK_OK_LEN;
+ break;
+
+ case TCPOPT_SACK: {
+ uint32_t sack_len;
+
+ if (len <= TCPOPT_HEADER_LEN || len < opts[1] ||
+ opts[1] <= TCPOPT_HEADER_LEN) {
+ mdb_printf(" <Truncated SACK>\n");
+ return;
+ }
+ sack_len = opts[1] - TCPOPT_HEADER_LEN;
+ opts += TCPOPT_HEADER_LEN;
+
+ mdb_printf(" SACK=");
+ while (sack_len > 0) {
+ if (opts + 2 * sizeof (val) > endp) {
+ mdb_printf("<Truncated SACK>\n");
+ opts = endp;
+ break;
+ }
+
+ mdb_nhconvert(&val, opts, sizeof (val));
+ mdb_printf("<%u,", val);
+ opts += sizeof (val);
+ mdb_nhconvert(&val, opts, sizeof (val));
+ mdb_printf("%u>", val);
+ opts += sizeof (val);
+
+ sack_len -= 2 * sizeof (val);
+ }
+ break;
+ }
+
+ default:
+ mdb_printf(" Opts=<val=%u,len=%u>", *opts,
+ opts[1]);
+ opts += opts[1];
+ break;
+ }
+ }
+ mdb_printf("\n");
+}
+
static void
tcphdr_print(struct tcphdr *tcph)
{
@@ -1053,6 +1174,7 @@ static int
tcphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
{
struct tcphdr tcph;
+ uint32_t opt_len;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
@@ -1062,6 +1184,22 @@ tcphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
return (DCMD_ERR);
}
tcphdr_print(&tcph);
+
+ /* If there are options, print them out also. */
+ opt_len = (tcph.th_off << 2) - TCP_MIN_HEADER_LENGTH;
+ if (opt_len > 0) {
+ uint8_t *opts, *opt_buf;
+
+ opt_buf = mdb_alloc(opt_len, UM_SLEEP);
+ opts = (uint8_t *)addr + sizeof (tcph);
+ if (mdb_vread(opt_buf, opt_len, (uintptr_t)opts) == -1) {
+ mdb_warn("failed to read TCP options at %p", opts);
+ return (DCMD_ERR);
+ }
+ tcphdr_print_options(opt_buf, opt_len);
+ mdb_free(opt_buf, opt_len);
+ }
+
return (DCMD_OK);
}
diff --git a/usr/src/uts/common/inet/tcp/tcp_time_wait.c b/usr/src/uts/common/inet/tcp/tcp_time_wait.c
index 4cfe13bb30..eb058a1c5f 100644
--- a/usr/src/uts/common/inet/tcp/tcp_time_wait.c
+++ b/usr/src/uts/common/inet/tcp/tcp_time_wait.c
@@ -171,14 +171,19 @@ tcp_time_wait_append(tcp_t *tcp)
CALLOUT_FLAG_ROUNDUP);
}
} else {
+ /*
+ * The list is not empty, so a timer must be running. If not,
+ * tcp_time_wait_collector() must be running on this
+ * tcp_time_wait list at the same time.
+ */
+ ASSERT(tcp_time_wait->tcp_time_wait_tid != 0 ||
+ tcp_time_wait->tcp_time_wait_running);
ASSERT(tcp_time_wait->tcp_time_wait_tail != NULL);
ASSERT(tcp_time_wait->tcp_time_wait_tail->tcp_state ==
TCPS_TIME_WAIT);
tcp_time_wait->tcp_time_wait_tail->tcp_time_wait_next = tcp;
tcp->tcp_time_wait_prev = tcp_time_wait->tcp_time_wait_tail;
- /* The list is not empty, so a timer must be running. */
- ASSERT(tcp_time_wait->tcp_time_wait_tid != 0);
}
tcp_time_wait->tcp_time_wait_tail = tcp;
mutex_exit(&tcp_time_wait->tcp_time_wait_lock);
@@ -241,6 +246,9 @@ tcp_time_wait_collector(void *arg)
mutex_enter(&tcp_time_wait->tcp_time_wait_lock);
tcp_time_wait->tcp_time_wait_tid = 0;
+#ifdef DEBUG
+ tcp_time_wait->tcp_time_wait_running = B_TRUE;
+#endif
if (tcp_time_wait->tcp_free_list != NULL &&
tcp_time_wait->tcp_free_list->tcp_in_free_list == B_TRUE) {
@@ -415,6 +423,9 @@ tcp_time_wait_collector(void *arg)
sqp, firetime, CALLOUT_TCP_RESOLUTION,
CALLOUT_FLAG_ROUNDUP);
}
+#ifdef DEBUG
+ tcp_time_wait->tcp_time_wait_running = B_FALSE;
+#endif
mutex_exit(&tcp_time_wait->tcp_time_wait_lock);
}
diff --git a/usr/src/uts/common/inet/tcp_impl.h b/usr/src/uts/common/inet/tcp_impl.h
index f2f70777e7..9d99d933bd 100644
--- a/usr/src/uts/common/inet/tcp_impl.h
+++ b/usr/src/uts/common/inet/tcp_impl.h
@@ -153,6 +153,13 @@ typedef struct tcp_squeue_priv_s {
tcp_t *tcp_time_wait_tail;
tcp_t *tcp_free_list;
uint_t tcp_free_list_cnt;
+#ifdef DEBUG
+ /*
+ * For debugging purpose, true when tcp_time_wait_collector() is
+ * running.
+ */
+ boolean_t tcp_time_wait_running;
+#endif
} tcp_squeue_priv_t;
/*