--- old/exception_lists/cstyle	Thu Mar  1 14:54:12 2018
+++ new/exception_lists/cstyle	Thu Mar  1 14:54:12 2018
@@ -851,6 +851,10 @@
 usr/src/uts/common/gssapi/mechs/krb5/mech/verify.c
 usr/src/uts/common/gssapi/mechs/krb5/mech/wrap_size_limit.c
 usr/src/uts/common/inet/ipf/ip_fil_solaris.c
+usr/src/uts/common/inet/cc.h
+usr/src/uts/common/inet/cc/cc_cubic.c
+usr/src/uts/common/inet/cc/cc_module.h
+usr/src/uts/common/inet/cc/cc_newreno.c
 usr/src/uts/common/io/bnxe/577xx/common/bnxe_clc.c
 usr/src/uts/common/io/bnxe/577xx/drivers/common/ecore/ecore_common.h
 usr/src/uts/common/io/bnxe/577xx/drivers/common/ecore/ecore_init_ops.h
--- old/exception_lists/hdrchk	Thu Mar  1 14:54:12 2018
+++ new/exception_lists/hdrchk	Thu Mar  1 14:54:12 2018
@@ -243,6 +243,8 @@
 usr/src/uts/common/gssapi/mechs/krb5/include/old.h
 usr/src/uts/common/gssapi/mechs/krb5/include/raw.h
 usr/src/uts/common/gssapi/mechs/krb5/include/rsa-md4.h
+usr/src/uts/common/inet/cc.h
+usr/src/uts/common/inet/cc/cc_module.h
 usr/src/uts/common/io/axf/ax88172reg.h
 usr/src/uts/common/io/bnxe/577xx/drivers/common/ecore/ecore_common.h
 usr/src/uts/common/io/bnxe/577xx/drivers/common/ecore/ecore_init_ops.h
--- old/usr/src/cmd/Makefile	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/Makefile	Thu Mar  1 14:54:12 2018
@@ -22,7 +22,7 @@
 #
 # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2017, Joyent, Inc.
-# Copyright (c) 2012 by Delphix. All rights reserved.
+# Copyright (c) 2012, 2015 by Delphix. All rights reserved.
 # Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2016 Toomas Soome <tsoome@me.com>
@@ -97,6 +97,7 @@
 	col		\
 	column		\
 	compress	\
+	connstat	\
 	consadm		\
 	coreadm		\
 	cpio		\
@@ -545,6 +546,7 @@
 	cmd-inet	\
 	col		\
 	compress	\
+	connstat	\
 	consadm		\
 	coreadm		\
 	cpio		\
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/Makefile	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2015 by Delphix. All rights reserved.
+#
+
+PROG=	connstat
+OBJS=	connstat_main.o connstat_mib.o connstat_tcp.o
+SRCS=	$(OBJS:%.o=%.c)
+POFILES= connstat_main.po connstat_tcp.po connstat_mib.po
+POFILE=	connstat.po
+
+include ../Makefile.cmd
+
+CSTD= $(CSTD_GNU99)
+C99LMODE= -Xc99=%all
+LDLIBS += -lsocket -lnsl -linetutil -lumem
+XGETFLAGS += -a -x $(PROG).xcl
+
+#
+# Instrument connstat with CTF data to ease debugging.
+#
+CTFCONVERT_HOOK = && $(CTFCONVERT_O)
+CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS)
+$(OBJS) := CFLAGS += $(CTF_FLAGS)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(POST_PROCESS)
+
+$(POFILE): $(POFILES)
+	$(RM) $@
+	cat $(POFILES) > $@
+
+install: all $(ROOTPROG)
+
+clean:
+	$(RM) $(OBJS)
+
+lint:   lint_SRCS
+
+include ../Makefile.targ
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat.h	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,77 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ */
+
+#ifndef	_CONNSTAT_H
+#define	_CONNSTAT_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ofmt.h>
+#include <sys/stropts.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct connstat_conn_attr_s {
+	struct sockaddr_storage	ca_laddr;
+	struct sockaddr_storage	ca_raddr;
+	int			ca_state;
+} connstat_conn_attr_t;
+
+typedef struct conn_walk_state_s {
+	ofmt_handle_t		cws_ofmt;
+	uint_t			cws_flags;
+	connstat_conn_attr_t	cws_filter;
+} conn_walk_state_t;
+
+/* cws_flags */
+#define	CS_LOOPBACK	0x0001	/* Include loopback connections */
+#define	CS_IPV4		0x0002	/* Show only IPv4 connections */
+#define	CS_IPV6		0x0004	/* Show only IPv6 connections */
+#define	CS_LADDR	0x0008	/* Filter by laddr in cws_filter */
+#define	CS_RADDR	0x0010	/* Filter by raddr in cws_filter */
+#define	CS_LPORT	0x0020	/* Filter by lport in cws_filter */
+#define	CS_RPORT	0x0040	/* Filter by rport in cws_filter */
+#define	CS_STATE	0x0080	/* Filter by state in cws_filter */
+#define	CS_PARSABLE	0x0100	/* Parsable output */
+
+typedef ofmt_field_t *connstat_getfieldsfunc_t();
+typedef void connstat_walkfunc_t(struct strbuf *, conn_walk_state_t *);
+
+typedef struct connstat_proto_s {
+	char	*csp_proto;
+	char	*csp_default_fields;
+	int	csp_miblevel;
+	int	csp_mibv4name;
+	int	csp_mibv6name;
+	connstat_getfieldsfunc_t *csp_getfields;
+	connstat_walkfunc_t *csp_v4walk;
+	connstat_walkfunc_t *csp_v6walk;
+} connstat_proto_t;
+
+boolean_t print_string(ofmt_arg_t *, char *, uint_t);
+boolean_t print_uint16(ofmt_arg_t *, char *, uint_t);
+boolean_t print_uint32(ofmt_arg_t *, char *, uint_t);
+boolean_t print_uint64(ofmt_arg_t *, char *, uint_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _CONNSTAT_H */
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat.xcl	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,84 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2015 by Delphix. All rights reserved.
+#
+
+msgid  "count"
+msgid  "established"
+msgid  "filter"
+msgid  "help"
+msgid  "interval"
+msgid  "ipv4"
+msgid  "ipv6"
+msgid  "no-loopback"
+msgid  "output"
+msgid  "parsable"
+msgid  "protocol"
+msgid  "timestamp"
+msgid  "tcp"
+msgid  "laddr,lport,raddr,rport,inbytes,outbytes,"
+       "retransbytes,suna,swnd,cwnd,rwnd"
+msgid  "  %s\n"
+msgid  ""
+msgid  "c:eF:hi:Lo:Pp:T:46"
+msgid  "all"
+msgid  "laddr"
+msgid  "raddr"
+msgid  "lport"
+msgid  "rport"
+msgid  "= "
+msgid  "%s%ld\n"
+msgid  "%s%s\n"
+msgid  "%hu"
+msgid  "%u"
+msgid  "%llu"
+msgid  "%s: "
+msgid  "LADDR"
+msgid  "RADDR"
+msgid  "LPORT"
+msgid  "RPORT"
+msgid  "INBYTES"
+msgid  "INSEGS"
+msgid  "INUNORDERBYTES"
+msgid  "INUNORDERSEGS"
+msgid  "OUTBYTES"
+msgid  "OUTSEGS"
+msgid  "RETRANSBYTES"
+msgid  "RETRANSSEGS"
+msgid  "SUNA"
+msgid  "count"
+msgid  "SWND"
+msgid  "CWND"
+msgid  "RWND"
+msgid  "STATE"
+msgid  "CLOSED"
+msgid  "IDLE"
+msgid  "BOUND"
+msgid  "LISTEN"
+msgid  "SYN_SENT"
+msgid  "SYN_RCVD"
+msgid  "ESTABLISHED"
+msgid  "CLOSE_WAIT"
+msgid  "FIN_WAIT_1"
+msgid  "CLOSING"
+msgid  "LAST_ACK"
+msgid  "FIN_WAIT_2"
+msgid  "TIME_WAIT"
+msgid  "UNKNOWN(%d)"
+msgid  "/dev/arp"
+msgid  "putmsg"
+msgid  "getmsg"
+msgid  "malloc"
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat_main.c	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,565 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/varargs.h>
+#include <ofmt.h>
+#include <inet/tcp.h>
+#include <netinet/in.h>
+#include <inet/mib2.h>
+#include "connstat.h"
+#include "connstat_mib.h"
+#include "connstat_tcp.h"
+
+#define	DEFAULT_PROTO	"tcp"
+
+static const char *invalid_v4v6_msg =
+	"Invalid combination of IPv4 and IPv6 arguments\n";
+
+static const struct option longopts[] = {
+	{ "count",	required_argument,	0, 'c'	},
+	{ "established",	no_argument,	0, 'e'	},
+	{ "filter",	required_argument,	0, 'F'	},
+	{ "help",	no_argument,		0, 'h'	},
+	{ "interval",	required_argument,	0, 'i'	},
+	{ "ipv4",	no_argument,		0, '4'	},
+	{ "ipv6",	no_argument,		0, '6'	},
+	{ "no-loopback",	no_argument,	0, 'L'	},
+	{ "output",	required_argument,	0, 'o'	},
+	{ "parsable",	no_argument,		0, 'P'	},
+	{ "protocol",	required_argument,	0, 'p'	},
+	{ "timestamp",	required_argument,	0, 'T'	},
+	{ NULL, 0, 0, 0 }
+};
+
+static connstat_proto_t connstat_protos[] = {
+	CONNSTAT_TCP_PROTO,
+	{ NULL, 0, 0, 0, NULL }
+};
+
+typedef enum { NOTIMESTAMP, UTIMESTAMP, DTIMESTAMP } timestamp_fmt_t;
+
+static char *progname;
+
+static void	die(const char *, ...);
+static void	process_filter(char *, connstat_conn_attr_t *, uint_t *);
+static void	show_stats(connstat_proto_t *, ofmt_handle_t, uint_t,
+    connstat_conn_attr_t *, timestamp_fmt_t, uint_t, uint_t);
+
+static void
+usage(int code)
+{
+	static const char *opts[] = {
+		"-4, --ipv4             Only display IPv4 connections",
+		"-6, --ipv6             Only display IPv6 connections",
+		"-c, --count=COUNT      Only print COUNT reports",
+		"-e, --established      Only display established connections",
+		"-F, --filter=FILTER    Only display connections that match "
+		    "FILTER",
+		"-h, --help             Print this help",
+		"-i, --interval=SECONDS Report once every SECONDS seconds",
+		"-L, --no-loopback      Omit loopback connections",
+		"-o, --output=FIELDS    Restrict output to the comma-separated "
+		    "list of fields\n"
+		    "                         specified",
+		"-P, --parsable         Parsable output mode",
+		"-T, --timestamp=TYPE   Display a timestamp for each iteration",
+		NULL
+	};
+
+	(void) fprintf(stderr, gettext("usage: "));
+	(void) fprintf(stderr,
+	    gettext("%s [-eLP] [-4|-6] [-T d|u] [-F <filter>]\n"
+	    "               [-i <interval> [-c <count>]] [-o <field>[,...]]\n"),
+	    progname);
+
+	(void) fprintf(stderr, gettext("\nOptions:\n"));
+	for (const char **optp = opts; *optp != NULL; optp++) {
+		(void) fprintf(stderr, "  %s\n", gettext(*optp));
+	}
+
+	(void) fprintf(stderr, gettext("\nFilter:\n"));
+	(void) fprintf(stderr, gettext("  The FILTER argument for the -F "
+	    "option is of the form:\n"
+	    "    <field>=<value>,[<field>=<value>,...]\n"));
+	(void) fprintf(stderr, gettext("  Filterable fields are laddr, lport, "
+	    "raddr, rport, and state.\n"));
+
+	(void) fprintf(stderr, gettext("\nFields:\n"));
+	(void) fprintf(stderr, gettext(
+	    "  laddr           Local IP address\n"
+	    "  raddr           Remote IP address\n"
+	    "  lport           Local port\n"
+	    "  rport           Remote port\n"
+	    "  inbytes         Total bytes received\n"
+	    "  insegs          Total segments received\n"
+	    "  inunorderbytes  Bytes received out of order\n"
+	    "  inunordersegs   Segments received out of order\n"
+	    "  outbytes        Total bytes sent\n"
+	    "  outsegs         Total segments sent\n"
+	    "  retransbytes    Bytes retransmitted\n"
+	    "  retranssegs     Segments retransmitted\n"
+	    "  suna            Current unacknowledged bytes sent\n"
+	    "  unsent          Unsent bytes on the transmit queue\n"
+	    "  swnd            Send window size (peer's receive window)\n"
+	    "  cwnd            Congestion window size\n"
+	    "  rwnd            Receive window size\n"
+	    "  mss             Maximum segment size\n"
+	    "  rto             Retransmission timeout (ms)\n"
+	    "  rtt             Smoothed round-trip time (us)\n"
+	    "  rtts            Sum round-trip time (us)\n"
+	    "  rttc            Count of round-trip times\n"
+	    "  state           Connection state\n"));
+	exit(code);
+}
+
+static connstat_proto_t *
+getproto(const char *proto)
+{
+	for (connstat_proto_t *current = &connstat_protos[0];
+	    current->csp_proto != NULL; current++) {
+		if (strcasecmp(proto, current->csp_proto) == 0) {
+			return (current);
+		}
+	}
+	return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int option;
+	int count = 0;
+	int interval = 0;
+	char *fields = NULL;
+	char *filterstr = NULL;
+	connstat_conn_attr_t filter = {0};
+	char *protostr = DEFAULT_PROTO;
+	connstat_proto_t *proto;
+	ofmt_handle_t ofmt;
+	ofmt_status_t oferr;
+	char oferrbuf[OFMT_BUFSIZE];
+	uint_t ofmtflags = OFMT_RIGHTJUST|OFMT_NOHEADER;
+	uint_t flags = CS_LOOPBACK | CS_IPV4 | CS_IPV6;
+	timestamp_fmt_t timestamp_fmt = NOTIMESTAMP;
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	progname = argv[0];
+
+	while ((option = getopt_long(argc, argv, "c:eF:hi:Lo:Pp:T:46",
+	    longopts, NULL)) != -1) {
+		errno = 0;
+		switch (option) {
+		case 'c':
+			count = strtol(optarg, NULL, 10);
+			if (count == 0 && errno != 0) {
+				(void) fprintf(stderr, gettext(
+				    "error parsing -c argument (%s): %s\n"),
+				    optarg, strerror(errno));
+				usage(1);
+			}
+			if (count <= 0) {
+				die("count must be >= 0");
+			}
+			break;
+		case 'e':
+			flags |= CS_STATE;
+			filter.ca_state = TCPS_ESTABLISHED;
+			break;
+		case 'F':
+			filterstr = optarg;
+			break;
+		case 'i':
+			interval = strtol(optarg, NULL, 10);
+			if (interval == 0 && errno != 0) {
+				(void) fprintf(stderr, gettext(
+				    "error parsing -i argument (%s): %s\n"),
+				    optarg, strerror(errno));
+				usage(1);
+			}
+			if (interval <= 0) {
+				die("interval must be >= 0");
+			}
+			break;
+		case 'L':
+			flags &= ~CS_LOOPBACK;
+			break;
+		case 'o':
+			fields = optarg;
+			break;
+		case 'P':
+			ofmtflags |= OFMT_PARSABLE;
+			flags |= CS_PARSABLE;
+			break;
+		case 'p':
+			/*
+			 * -p is an undocumented flag whose only supported
+			 * argument is "tcp". The idea is to reserve this
+			 * flag for potential future use in case connstat
+			 * is extended to support stats for other protocols.
+			 */
+			protostr = optarg;
+			break;
+		case 'T':
+			if (*optarg == 'u') {
+				timestamp_fmt = UTIMESTAMP;
+			} else if (*optarg == 'd') {
+				timestamp_fmt = DTIMESTAMP;
+			} else {
+				usage(1);
+			}
+			break;
+		case '4':
+			if (!(flags & CS_IPV4)) {
+				(void) fprintf(stderr, gettext(
+				    invalid_v4v6_msg));
+				usage(1);
+			}
+			flags &= ~CS_IPV6;
+			break;
+		case '6':
+			if (!(flags & CS_IPV6)) {
+				(void) fprintf(stderr, gettext(
+				    invalid_v4v6_msg));
+				usage(1);
+			}
+			flags &= ~CS_IPV4;
+			break;
+		case '?':
+		default:
+			usage(1);
+			break;
+		}
+	}
+
+	if ((proto = getproto(protostr)) == NULL) {
+		(void) fprintf(stderr, gettext("unknown protocol: %s\n"),
+		    protostr);
+		usage(1);
+	}
+
+	if ((ofmtflags & OFMT_PARSABLE) && fields == NULL) {
+		die("parsable output requires \"-o\"");
+	}
+
+	if ((ofmtflags & OFMT_PARSABLE) && fields != NULL &&
+	    strcasecmp(fields, "all") == 0) {
+		die("\"-o all\" is invalid with parsable output");
+	}
+
+	if (fields == NULL) {
+		fields = proto->csp_default_fields;
+	}
+
+	/* If count is specified, then interval must also be specified. */
+	if (count != 0 && interval == 0) {
+		usage(1);
+	}
+
+	/* If interval is not specified, then the default count is 1. */
+	if (interval == 0 && count == 0) {
+		count = 1;
+	}
+
+	if (filterstr != NULL) {
+		process_filter(filterstr, &filter, &flags);
+	}
+
+	oferr = ofmt_open(fields, proto->csp_getfields(), ofmtflags, 0, &ofmt);
+	if (oferr != OFMT_SUCCESS) {
+		(void) ofmt_strerror(ofmt, oferr, oferrbuf, sizeof (oferrbuf));
+		die(oferrbuf);
+	}
+	ofmt_set_fs(ofmt, ',');
+
+	show_stats(proto, ofmt, flags, &filter, timestamp_fmt, interval, count);
+
+	ofmt_close(ofmt);
+	return (0);
+}
+
+/*
+ * Convert the input IP address literal to sockaddr of the appropriate address
+ * family. Preserves any potential port number that may have been set in the
+ * input sockaddr_storage structure.
+ */
+static void
+str2sockaddr(const char *addr, struct sockaddr_storage *ss)
+{
+	struct addrinfo hints, *res;
+	uint16_t port = ((struct sockaddr_in *)ss)->sin_port;
+
+	bzero(&hints, sizeof (hints));
+	hints.ai_flags = AI_NUMERICHOST;
+	if (getaddrinfo(addr, NULL, &hints, &res) != 0) {
+		die("invalid literal IP address: %s", addr);
+	}
+	bcopy(res->ai_addr, ss, res->ai_addrlen);
+	freeaddrinfo(res);
+	((struct sockaddr_in *)ss)->sin_port = port;
+}
+
+/*
+ * The filterstr argument is of the form: <attr>=<value>[,...]
+ * Possible attributes are laddr, raddr, lport, and rport. Parse this
+ * filter and store the results into the provided attribute structure.
+ */
+static void
+process_filter(char *filterstr, connstat_conn_attr_t *filter, uint_t *flags)
+{
+	int option;
+	char *val;
+	enum { F_LADDR, F_RADDR, F_LPORT, F_RPORT, F_STATE };
+	static char *filter_optstr[] =
+	    { "laddr", "raddr", "lport", "rport", "state" };
+	uint_t flag = 0;
+	struct sockaddr_storage *addrp = NULL;
+	uint16_t port;
+
+	while (*filterstr != '\0') {
+		option = getsubopt(&filterstr, filter_optstr, &val);
+		errno = 0;
+
+		switch (option) {
+		case F_LADDR:
+			flag = CS_LADDR;
+			addrp = &filter->ca_laddr;
+			break;
+		case F_RADDR:
+			flag = CS_RADDR;
+			addrp = &filter->ca_raddr;
+			break;
+		case F_LPORT:
+			flag = CS_LPORT;
+			addrp = &filter->ca_laddr;
+			break;
+		case F_RPORT:
+			flag = CS_RPORT;
+			addrp = &filter->ca_raddr;
+			break;
+		case F_STATE:
+			flag = CS_STATE;
+			break;
+		default:
+			usage(1);
+		}
+
+		if (*flags & flag) {
+			(void) fprintf(stderr, gettext(
+			    "Ambiguous filter provided. The \"%s\" field "
+			    "appears more than once.\n"),
+			    filter_optstr[option]);
+			usage(1);
+		}
+		*flags |= flag;
+
+		switch (flag) {
+		case CS_LADDR:
+		case CS_RADDR:
+			str2sockaddr(val, addrp);
+			if (addrp->ss_family == AF_INET) {
+				if (!(*flags & CS_IPV4)) {
+					(void) fprintf(stderr, gettext(
+					    invalid_v4v6_msg));
+					usage(1);
+				}
+				*flags &= ~CS_IPV6;
+			} else {
+				if (!(*flags & CS_IPV6)) {
+					(void) fprintf(stderr, gettext(
+					    invalid_v4v6_msg));
+					usage(1);
+				}
+				*flags &= ~CS_IPV4;
+			}
+			break;
+		case CS_LPORT:
+		case CS_RPORT:
+			port = strtol(val, NULL, 10);
+			if (port == 0 && errno != 0) {
+				(void) fprintf(stderr, gettext(
+				    "error parsing port (%s): %s\n"),
+				    val, strerror(errno));
+				usage(1);
+			}
+			((struct sockaddr_in *)addrp)->sin_port = htons(port);
+			break;
+		case CS_STATE:
+			filter->ca_state = tcp_str2state(val);
+			if (filter->ca_state < TCPS_CLOSED) {
+				(void) fprintf(stderr, gettext(
+				    "invalid TCP state: %s\n"), val);
+				usage(1);
+			}
+			break;
+		}
+	}
+
+	/* Make sure that laddr and raddr are at least in the same family. */
+	if ((*flags & (CS_LADDR|CS_RADDR)) == (CS_LADDR|CS_RADDR)) {
+		if (filter->ca_laddr.ss_family != filter->ca_raddr.ss_family) {
+			die("laddr and raddr must be of the same family.");
+		}
+	}
+}
+
+/*
+ * Print timestamp as decimal representation of time_t value (-T u was
+ * specified) or in date(1) format (-T d was specified).
+ */
+static void
+print_timestamp(timestamp_fmt_t timestamp_fmt, boolean_t parsable)
+{
+	time_t t = time(NULL);
+	char *pfx = parsable ? "= " : "";
+	static char *fmt = NULL;
+
+	/* We only need to retrieve this once per invocation */
+	if (fmt == NULL) {
+		fmt = nl_langinfo(_DATE_FMT);
+	}
+
+	if (timestamp_fmt == UTIMESTAMP) {
+		(void) printf("%s%ld\n", pfx, t);
+	} else if (timestamp_fmt == DTIMESTAMP) {
+		char dstr[64];
+		int len;
+
+		len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
+		if (len > 0) {
+			(void) printf("%s%s\n", pfx, dstr);
+		}
+	}
+}
+
+static void
+show_stats(connstat_proto_t *proto, ofmt_handle_t ofmt, uint_t flags,
+    connstat_conn_attr_t *filter, timestamp_fmt_t timestamp_fmt,
+    uint_t interval, uint_t count)
+{
+	boolean_t done = B_FALSE;
+	uint_t i = 0;
+	int mibfd;
+	conn_walk_state_t state;
+
+	state.cws_ofmt = ofmt;
+	state.cws_flags = flags;
+	state.cws_filter = *filter;
+
+	if ((mibfd = mibopen(proto->csp_proto)) == -1) {
+		die(strerror(errno));
+	}
+
+	do {
+		if (timestamp_fmt != NOTIMESTAMP) {
+			print_timestamp(timestamp_fmt, flags & CS_PARSABLE);
+		}
+		if (!(flags & CS_PARSABLE)) {
+			ofmt_print_header(ofmt);
+		}
+
+		conn_walk(mibfd, proto, &state);
+
+		if (count != 0 && ++i == count) {
+			done = B_TRUE;
+		} else {
+			(void) sleep(interval);
+		}
+	} while (!done);
+}
+
+/*
+ * ofmt callbacks for printing individual fields of various types.
+ */
+boolean_t
+print_string(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+	char *value;
+
+	value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
+	(void) strlcpy(buf, value, bufsize);
+	return (B_TRUE);
+}
+
+boolean_t
+print_uint16(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+	uint16_t value;
+
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	value = *(uint16_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
+	(void) snprintf(buf, bufsize, "%hu", value);
+	return (B_TRUE);
+}
+
+boolean_t
+print_uint32(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+	uint32_t value;
+
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	value = *(uint32_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
+	(void) snprintf(buf, bufsize, "%u", value);
+	return (B_TRUE);
+}
+
+boolean_t
+print_uint64(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+	uint64_t value;
+
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	value = *(uint64_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
+	(void) snprintf(buf, bufsize, "%llu", value);
+	return (B_TRUE);
+}
+
+/* PRINTFLIKE1 */
+static void
+die(const char *format, ...)
+{
+	va_list alist;
+
+	format = gettext(format);
+	(void) fprintf(stderr, "%s: ", progname);
+
+	va_start(alist, format);
+	(void) vfprintf(stderr, format, alist);
+	va_end(alist);
+
+	(void) putc('\n', stderr);
+
+	exit(1);
+}
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat_mib.c	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,137 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/tihdr.h>
+#include "connstat.h"
+
+int
+mibopen(const char *proto)
+{
+	int fd;
+
+	fd = open("/dev/arp", O_RDWR);
+	if (fd == -1) {
+		return (-1);
+	}
+
+	if (ioctl(fd, I_PUSH, proto) == -1) {
+		(void) close(fd);
+		return (-1);
+	}
+
+	return (fd);
+}
+
+void
+conn_walk(int fd, connstat_proto_t *proto, conn_walk_state_t *state)
+{
+	struct strbuf cbuf, dbuf;
+	struct opthdr *hdr;
+	int flags, r;
+	struct {
+		struct T_optmgmt_req req;
+		struct opthdr hdr;
+	} req;
+	union {
+		struct T_optmgmt_ack ack;
+		uint8_t space[sizeof (struct T_optmgmt_ack) +
+		    sizeof (struct opthdr) * 2];
+	} ack;
+
+	req.req.PRIM_type = T_OPTMGMT_REQ;
+	req.req.OPT_offset = (caddr_t)&req.hdr - (caddr_t)&req;
+	req.req.OPT_length = sizeof (req.hdr);
+	req.req.MGMT_flags = T_CURRENT;
+
+	req.hdr.level = proto->csp_miblevel;
+	req.hdr.name = 0;
+	req.hdr.len = 0;
+
+	cbuf.buf = (caddr_t)&req;
+	cbuf.len = sizeof (req);
+
+	if (putmsg(fd, &cbuf, NULL, 0) == -1) {
+		perror("putmsg");
+		return;
+	}
+
+	/*
+	 * Each reply consists of a control part for one fixed structure or
+	 * table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK
+	 * containing an opthdr structure.  The level and name identify the
+	 * entry, and len is the size of the data part of the message.
+	 */
+	for (;;) {
+		cbuf.buf = (caddr_t)&ack;
+		cbuf.maxlen = sizeof (ack);
+		flags = 0;
+
+		/*
+		 * We first do a getmsg() for the control part so that we
+		 * can allocate a properly sized buffer to read the data
+		 * part.
+		 */
+		if ((r = getmsg(fd, &cbuf, NULL, &flags)) < 0) {
+			perror("getmsg");
+			break;
+		}
+		if (r == 0) {
+			break;
+		}
+
+		if (cbuf.len < sizeof (struct T_optmgmt_ack) ||
+		    ack.ack.PRIM_type != T_OPTMGMT_ACK ||
+		    ack.ack.MGMT_flags != T_SUCCESS ||
+		    ack.ack.OPT_length < sizeof (struct opthdr)) {
+			(void) fprintf(stderr, "invalid message\n");
+			break;
+		}
+
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		hdr = (struct opthdr *)((caddr_t)&ack + ack.ack.OPT_offset);
+		if (hdr->level == 0 && hdr->name == 0)
+			break;
+
+		/* Allocate a buffer to hold the data portion of the message */
+		if ((dbuf.buf = malloc(hdr->len)) == NULL) {
+			perror("malloc");
+			break;
+		}
+		dbuf.maxlen = hdr->len;
+		dbuf.len = 0;
+		flags = 0;
+		r = getmsg(fd, NULL, &dbuf, &flags);
+
+		if ((state->cws_flags & CS_IPV4) &&
+		    hdr->name == proto->csp_mibv4name) {
+			proto->csp_v4walk(&dbuf, state);
+		} else if ((state->cws_flags & CS_IPV6) &&
+		    hdr->name == proto->csp_mibv6name) {
+			proto->csp_v6walk(&dbuf, state);
+		}
+
+		free(dbuf.buf);
+	}
+}
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat_mib.h	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,35 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015 by Delphix. All rights reserved.
+ */
+
+#ifndef	_CONNSTAT_MIB_H
+#define	_CONNSTAT_MIB_H
+
+#include "connstat.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+int mibopen(const char *);
+void conn_walk(int, connstat_proto_t *, conn_walk_state_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _CONNSTAT_MIB_H */
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat_tcp.c	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,384 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inet/mib2.h>
+#include <sys/stropts.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <inet/tcp.h>
+#include <arpa/inet.h>
+#include <ofmt.h>
+#include <sys/time.h>
+#include "connstat_mib.h"
+#include "connstat_tcp.h"
+
+typedef struct tcp_fields_buf_s {
+	char t_laddr[INET6_ADDRSTRLEN];
+	char t_raddr[INET6_ADDRSTRLEN];
+	uint16_t t_lport;
+	uint16_t t_rport;
+	uint64_t t_inbytes;
+	uint64_t t_insegs;
+	uint64_t t_inunorderbytes;
+	uint64_t t_inunordersegs;
+	uint64_t t_outbytes;
+	uint64_t t_outsegs;
+	uint64_t t_retransbytes;
+	uint64_t t_retranssegs;
+	uint32_t t_suna;
+	uint32_t t_unsent;
+	uint32_t t_swnd;
+	uint32_t t_cwnd;
+	uint32_t t_rwnd;
+	uint32_t t_mss;
+	uint32_t t_rto;
+	uint32_t t_rtt_cnt;
+	uint64_t t_rtt_sum;
+	int t_state;
+	uint64_t t_rtt;
+} tcp_fields_buf_t;
+
+static boolean_t print_tcp_state(ofmt_arg_t *, char *, uint_t);
+
+static ofmt_field_t tcp_fields[] = {
+	{ "LADDR",	15,
+		offsetof(tcp_fields_buf_t, t_laddr),	print_string },
+	{ "RADDR",	15,
+		offsetof(tcp_fields_buf_t, t_raddr),	print_string },
+	{ "LPORT",	6,
+		offsetof(tcp_fields_buf_t, t_lport),	print_uint16 },
+	{ "RPORT",	6,
+		offsetof(tcp_fields_buf_t, t_rport),	print_uint16 },
+	{ "INBYTES",	11,
+		offsetof(tcp_fields_buf_t, t_inbytes),	print_uint64 },
+	{ "INSEGS",	11,
+		offsetof(tcp_fields_buf_t, t_insegs),	print_uint64 },
+	{ "INUNORDERBYTES",	15,
+		offsetof(tcp_fields_buf_t, t_inunorderbytes),	print_uint64 },
+	{ "INUNORDERSEGS",	14,
+		offsetof(tcp_fields_buf_t, t_inunordersegs),	print_uint64 },
+	{ "OUTBYTES",	11,
+		offsetof(tcp_fields_buf_t, t_outbytes),	print_uint64 },
+	{ "OUTSEGS",	11,
+		offsetof(tcp_fields_buf_t, t_outsegs),	print_uint64 },
+	{ "RETRANSBYTES",	13,
+		offsetof(tcp_fields_buf_t, t_retransbytes),	print_uint64 },
+	{ "RETRANSSEGS",	12,
+		offsetof(tcp_fields_buf_t, t_retranssegs),	print_uint64 },
+	{ "SUNA",	11,
+		offsetof(tcp_fields_buf_t, t_suna),	print_uint32 },
+	{ "UNSENT",	11,
+		offsetof(tcp_fields_buf_t, t_unsent),	print_uint32 },
+	{ "SWND",	11,
+		offsetof(tcp_fields_buf_t, t_swnd),	print_uint32 },
+	{ "CWND",	11,
+		offsetof(tcp_fields_buf_t, t_cwnd),	print_uint32 },
+	{ "RWND",	11,
+		offsetof(tcp_fields_buf_t, t_rwnd),	print_uint32 },
+	{ "MSS",	6,
+		offsetof(tcp_fields_buf_t, t_mss),	print_uint32 },
+	{ "RTO",	8,
+		offsetof(tcp_fields_buf_t, t_rto),	print_uint32 },
+	{ "RTT",	8,
+		offsetof(tcp_fields_buf_t, t_rtt),	print_uint64 },
+	{ "RTTS",	8,
+		offsetof(tcp_fields_buf_t, t_rtt_sum),	print_uint64 },
+	{ "RTTC",	11,
+		offsetof(tcp_fields_buf_t, t_rtt_cnt),	print_uint32 },
+	{ "STATE",	12,
+		offsetof(tcp_fields_buf_t, t_state),	print_tcp_state },
+	{ NULL, 0, 0, NULL}
+};
+
+static tcp_fields_buf_t fields_buf;
+
+
+typedef struct tcp_state_info_s {
+	int tsi_state;
+	const char *tsi_string;
+} tcp_state_info_t;
+
+tcp_state_info_t tcp_state_info[] = {
+	{ TCPS_CLOSED, "CLOSED" },
+	{ TCPS_IDLE, "IDLE" },
+	{ TCPS_BOUND, "BOUND" },
+	{ TCPS_LISTEN, "LISTEN" },
+	{ TCPS_SYN_SENT, "SYN_SENT" },
+	{ TCPS_SYN_RCVD, "SYN_RCVD" },
+	{ TCPS_ESTABLISHED, "ESTABLISHED" },
+	{ TCPS_CLOSE_WAIT, "CLOSE_WAIT" },
+	{ TCPS_FIN_WAIT_1, "FIN_WAIT_1" },
+	{ TCPS_CLOSING, "CLOSING" },
+	{ TCPS_LAST_ACK, "LAST_ACK" },
+	{ TCPS_FIN_WAIT_2, "FIN_WAIT_2" },
+	{ TCPS_TIME_WAIT, "TIME_WAIT" },
+	{ TCPS_CLOSED - 1, NULL }
+};
+
+ofmt_field_t *
+tcp_get_fields()
+{
+	return (tcp_fields);
+}
+
+/*
+ * Extract information from the connection info structure into the global
+ * output buffer.
+ */
+static void
+tcp_ci2buf(struct tcpConnEntryInfo_s *ci)
+{
+	fields_buf.t_inbytes =
+	    ci->ce_in_data_inorder_bytes + ci->ce_in_data_unorder_bytes;
+	fields_buf.t_insegs =
+	    ci->ce_in_data_inorder_segs + ci->ce_in_data_unorder_segs;
+	fields_buf.t_inunorderbytes = ci->ce_in_data_unorder_bytes;
+	fields_buf.t_inunordersegs = ci->ce_in_data_unorder_segs;
+	fields_buf.t_outbytes = ci->ce_out_data_bytes;
+	fields_buf.t_outsegs = ci->ce_out_data_segs;
+	fields_buf.t_retransbytes = ci->ce_out_retrans_bytes;
+	fields_buf.t_retranssegs = ci->ce_out_retrans_segs;
+	fields_buf.t_suna = ci->ce_snxt - ci->ce_suna;
+	fields_buf.t_unsent = ci->ce_unsent;
+	fields_buf.t_swnd = ci->ce_swnd;
+	fields_buf.t_cwnd = ci->ce_cwnd;
+	fields_buf.t_rwnd = ci->ce_rwnd;
+	fields_buf.t_mss = ci->ce_mss;
+	fields_buf.t_rto = ci->ce_rto;
+	fields_buf.t_rtt = (ci->ce_out_data_segs == 0 ? 0 : ci->ce_rtt_sa);
+	fields_buf.t_rtt_sum = ci->ce_rtt_sum;
+	fields_buf.t_rtt_cnt = ci->ce_rtt_cnt;
+	fields_buf.t_state = ci->ce_state;
+}
+
+/*
+ * Extract information from the connection entry into the global output
+ * buffer.
+ */
+static void
+tcp_ipv4_ce2buf(mib2_tcpConnEntry_t *ce)
+{
+	(void) inet_ntop(AF_INET, (void *)&ce->tcpConnLocalAddress,
+	    fields_buf.t_laddr, sizeof (fields_buf.t_laddr));
+	(void) inet_ntop(AF_INET, (void *)&ce->tcpConnRemAddress,
+	    fields_buf.t_raddr, sizeof (fields_buf.t_raddr));
+
+	fields_buf.t_lport = ce->tcpConnLocalPort;
+	fields_buf.t_rport = ce->tcpConnRemPort;
+
+	tcp_ci2buf(&ce->tcpConnEntryInfo);
+}
+
+static void
+tcp_ipv6_ce2buf(mib2_tcp6ConnEntry_t *ce)
+{
+	(void) inet_ntop(AF_INET6, (void *)&ce->tcp6ConnLocalAddress,
+	    fields_buf.t_laddr, sizeof (fields_buf.t_laddr));
+	(void) inet_ntop(AF_INET6, (void *)&ce->tcp6ConnRemAddress,
+	    fields_buf.t_raddr, sizeof (fields_buf.t_raddr));
+
+	fields_buf.t_lport = ce->tcp6ConnLocalPort;
+	fields_buf.t_rport = ce->tcp6ConnRemPort;
+
+	tcp_ci2buf(&ce->tcp6ConnEntryInfo);
+}
+
+/*
+ * Print a single IPv4 connection entry, taking into account possible
+ * filters that have been set in state.
+ */
+static void
+tcp_ipv4_print(mib2_tcpConnEntry_t *ce, conn_walk_state_t *state)
+{
+	if (!(state->cws_flags & CS_LOOPBACK) &&
+	    ntohl(ce->tcpConnLocalAddress) == INADDR_LOOPBACK) {
+		return;
+	}
+
+	if (state->cws_flags & CS_LADDR) {
+		struct sockaddr_in *sin =
+		    (struct sockaddr_in *)&state->cws_filter.ca_laddr;
+		if (ce->tcpConnLocalAddress != sin->sin_addr.s_addr) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_RADDR) {
+		struct sockaddr_in *sin =
+		    (struct sockaddr_in *)&state->cws_filter.ca_raddr;
+		if (ce->tcpConnRemAddress != sin->sin_addr.s_addr) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_LPORT) {
+		struct sockaddr_in *sin =
+		    (struct sockaddr_in *)&state->cws_filter.ca_laddr;
+		if (ce->tcpConnLocalPort != ntohs(sin->sin_port)) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_RPORT) {
+		struct sockaddr_in *sin =
+		    (struct sockaddr_in *)&state->cws_filter.ca_raddr;
+		if (ce->tcpConnRemPort != ntohs(sin->sin_port)) {
+			return;
+		}
+	}
+
+	if ((state->cws_flags & CS_STATE) &&
+	    ce->tcpConnEntryInfo.ce_state != state->cws_filter.ca_state) {
+		return;
+	}
+
+	tcp_ipv4_ce2buf(ce);
+	ofmt_print(state->cws_ofmt, &fields_buf);
+}
+
+/*
+ * Print a single IPv6 connection entry, taking into account possible
+ * filters that have been set in state.
+ */
+static void
+tcp_ipv6_print(mib2_tcp6ConnEntry_t *ce, conn_walk_state_t *state)
+{
+	if (!(state->cws_flags & CS_LOOPBACK) &&
+	    IN6_IS_ADDR_LOOPBACK(
+	    (struct in6_addr *)&ce->tcp6ConnLocalAddress)) {
+		return;
+	}
+
+	if (state->cws_flags & CS_LADDR) {
+		struct sockaddr_in6 *sin6 =
+		    (struct sockaddr_in6 *)&state->cws_filter.ca_laddr;
+		if (!IN6_ARE_ADDR_EQUAL(
+		    (struct in6_addr *)&ce->tcp6ConnLocalAddress,
+		    &sin6->sin6_addr)) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_RADDR) {
+		struct sockaddr_in6 *sin6 =
+		    (struct sockaddr_in6 *)&state->cws_filter.ca_raddr;
+		if (!IN6_ARE_ADDR_EQUAL(
+		    (struct in6_addr *)&ce->tcp6ConnRemAddress,
+		    &sin6->sin6_addr)) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_LPORT) {
+		struct sockaddr_in6 *sin6 =
+		    (struct sockaddr_in6 *)&state->cws_filter.ca_laddr;
+		if (ce->tcp6ConnLocalPort != ntohs(sin6->sin6_port)) {
+			return;
+		}
+	}
+	if (state->cws_flags & CS_RPORT) {
+		struct sockaddr_in6 *sin6 =
+		    (struct sockaddr_in6 *)&state->cws_filter.ca_raddr;
+		if (ce->tcp6ConnRemPort != ntohs(sin6->sin6_port)) {
+			return;
+		}
+	}
+
+	if ((state->cws_flags & CS_STATE) &&
+	    ce->tcp6ConnEntryInfo.ce_state != state->cws_filter.ca_state) {
+		return;
+	}
+
+	tcp_ipv6_ce2buf(ce);
+	ofmt_print(state->cws_ofmt, &fields_buf);
+}
+
+void
+tcp_walk_ipv4(struct strbuf *dbuf, conn_walk_state_t *state)
+{
+	uint_t nconns = (dbuf->len / sizeof (mib2_tcpConnEntry_t));
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	mib2_tcpConnEntry_t *ce = (mib2_tcpConnEntry_t *)dbuf->buf;
+
+	for (; nconns > 0; ce++, nconns--) {
+		tcp_ipv4_print(ce, state);
+	}
+}
+
+void
+tcp_walk_ipv6(struct strbuf *dbuf, conn_walk_state_t *state)
+{
+	uint_t nconns = (dbuf->len / sizeof (mib2_tcp6ConnEntry_t));
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	mib2_tcp6ConnEntry_t *ce = (mib2_tcp6ConnEntry_t *)dbuf->buf;
+
+	for (; nconns > 0; ce++, nconns--) {
+		tcp_ipv6_print(ce, state);
+	}
+}
+
+static tcp_state_info_t *tcp_stateinfobystate(int state) {
+	tcp_state_info_t *sip;
+
+	for (sip = tcp_state_info; sip->tsi_string != NULL; sip++) {
+		if (sip->tsi_state == state) {
+			return (sip);
+		}
+	}
+	return (NULL);
+}
+
+static tcp_state_info_t *tcp_stateinfobystr(const char *statestr) {
+	tcp_state_info_t *sip;
+
+	for (sip = tcp_state_info; sip->tsi_string != NULL; sip++) {
+		if (strncasecmp(statestr, sip->tsi_string,
+		    strlen(sip->tsi_string)) == 0) {
+			return (sip);
+		}
+	}
+	return (NULL);
+}
+int
+tcp_str2state(const char *statestr)
+{
+	tcp_state_info_t *sip = tcp_stateinfobystr(statestr);
+	return (sip == NULL ? TCPS_CLOSED - 1 : sip->tsi_state);
+}
+
+static const char *
+tcp_state2str(int state)
+{
+	tcp_state_info_t *sip = tcp_stateinfobystate(state);
+	return (sip == NULL ? NULL : sip->tsi_string);
+}
+
+static boolean_t
+print_tcp_state(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	int state = *(int *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
+	const char *statestr = tcp_state2str(state);
+
+	if (statestr != NULL) {
+		(void) strlcpy(buf, statestr, bufsize);
+	} else {
+		(void) snprintf(buf, bufsize, "UNKNOWN(%d)", state);
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Mar  1 14:54:12 2018
+++ new/usr/src/cmd/connstat/connstat_tcp.h	Thu Mar  1 14:54:12 2018
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ */
+
+#ifndef	_CONNSTAT_TCP_H
+#define	_CONNSTAT_TCP_H
+
+#include <stddef.h>
+#include "connstat.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+int tcp_str2state(const char *state);
+connstat_getfieldsfunc_t tcp_get_fields;
+connstat_walkfunc_t tcp_walk_ipv4, tcp_walk_ipv6;
+
+/*
+ * Keep the default output to < 80 columns. For most interactive workflows,
+ * the user will run the command without arguments to get an idea of what
+ * connections exist before narrowing down the investigation to a single
+ * connection (with filtering) and specifying additional fields to output
+ * depending on what the user is interested in.
+ */
+#define	TCP_DEFAULT_FIELDS	"laddr,lport,raddr,rport,state"
+
+#define	CONNSTAT_TCP_PROTO \
+	{ "tcp", TCP_DEFAULT_FIELDS, MIB2_TCP, MIB2_TCP_CONN, MIB2_TCP6_CONN, \
+	tcp_get_fields, tcp_walk_ipv4, tcp_walk_ipv6 }
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _CONNSTAT_TCP_H */
--- old/usr/src/lib/libinetutil/common/mapfile-vers	Thu Mar  1 14:54:12 2018
+++ new/usr/src/lib/libinetutil/common/mapfile-vers	Thu Mar  1 14:54:12 2018
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015 by Delphix. All rights reserved.
 # Copyright 2017 Joyent, Inc.
 #
 
@@ -67,7 +68,9 @@
 	ofmt_open		{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
 	ofmt_close		{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
 	ofmt_print		{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
+	ofmt_print_header	{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
 	ofmt_update_winsize	{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
+	ofmt_set_fs		{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
 	ofmt_strerror		{ TYPE = FUNCTION; FILTER = libofmt.so.1 };
 	mask2plen;
 	plen2mask;
--- old/usr/src/lib/libipadm/common/ipadm_prop.c	Thu Mar  1 14:54:12 2018
+++ new/usr/src/lib/libipadm/common/ipadm_prop.c	Thu Mar  1 14:54:12 2018
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2013, 2017 by Delphix. All rights reserved.
  */
 
 /*
@@ -40,6 +40,8 @@
 #include <fcntl.h>
 #include <strings.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/sockio.h>
@@ -67,7 +69,8 @@
 static ipadm_pd_getf_t	i_ipadm_get_prop, i_ipadm_get_ifprop_flags,
 			i_ipadm_get_mtu, i_ipadm_get_metric,
 			i_ipadm_get_usesrc, i_ipadm_get_forwarding,
-			i_ipadm_get_ecnsack, i_ipadm_get_hostmodel;
+			i_ipadm_get_ecnsack, i_ipadm_get_hostmodel,
+			i_ipadm_get_cc;
 
 /*
  * Callback function to set property values. These functions translate the
@@ -153,6 +156,9 @@
 
 /* Supported TCP protocol properties */
 static ipadm_prop_desc_t ipadm_tcp_prop_table[] = {
+	{ "congestion_control", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
+	    i_ipadm_set_prop, i_ipadm_get_cc, i_ipadm_get_prop },
+
 	{ "ecn", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
 	    i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
 
@@ -812,6 +818,42 @@
 	return (status);
 }
 
+/*
+ * Retrieves the list of possible congestion control algorithms by enumerating
+ * the modules in /kernel/cc.
+ */
+/* ARGSUSED */
+ipadm_status_t
+i_ipadm_get_cc(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp,
+    char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
+{
+	DIR *dir;
+	struct dirent *ent;
+	boolean_t first = B_TRUE;
+	uint_t bytes = 0;
+
+	assert(valtype == MOD_PROP_POSSIBLE);
+
+	/* We assume that all platforms have the same algorithms installed. */
+	if ((dir = opendir("/kernel/cc")) != NULL) {
+		while ((ent = readdir(dir)) != NULL) {
+			/* By convention, modules are named cc_<algo>. */
+			if (strstr(ent->d_name, "cc_") != NULL) {
+				bytes += snprintf(buf + bytes,
+				    bytes < *bufsize ? *bufsize - bytes : 0,
+				    "%s%s", first ? "" : ",", ent->d_name + 3);
+				first = B_FALSE;
+			}
+		}
+		(void) closedir(dir);
+	}
+	if (bytes >= *bufsize) {
+		*bufsize = bytes + 1;
+		return (IPADM_NO_BUFS);
+	}
+	return (IPADM_SUCCESS);
+}
+
 /* ARGSUSED */
 static ipadm_status_t
 i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg,
--- old/usr/src/lib/libofmt/common/mapfile-vers	Thu Mar  1 14:54:13 2018
+++ new/usr/src/lib/libofmt/common/mapfile-vers	Thu Mar  1 14:54:13 2018
@@ -32,12 +32,14 @@
 
 SYMBOL_VERSION ILLUMOSprivate {
     global:
+	ofmt_check;
 	ofmt_close;
 	ofmt_open;
 	ofmt_print;
+	ofmt_print_header;
+	ofmt_set_fs;
 	ofmt_strerror;
 	ofmt_update_winsize;
-	ofmt_check;
 
     local:
 	*;
--- old/usr/src/lib/libofmt/common/ofmt.c	Thu Mar  1 14:54:13 2018
+++ new/usr/src/lib/libofmt/common/ofmt.c	Thu Mar  1 14:54:13 2018
@@ -25,6 +25,7 @@
  */
 
 /*
+ * Copyright (c) 2015 by Delphix. All rights reserved.
  * Copyright 2017 Joyent, Inc.
  */
 
@@ -53,6 +54,8 @@
 } split_t;
 
 static void splitfree(split_t *);
+static split_t *split_str(const char *, uint_t);
+static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
 
 /*
  * The state of the output is tracked in a ofmt_state_t structure.
@@ -71,6 +74,7 @@
 	int		os_nbad;
 	char		**os_badfields;
 	int		os_maxnamelen;	/* longest name (f. multiline) */
+	char		os_fs;		/* field seperator */
 } ofmt_state_t;
 /*
  * A B_TRUE return value from the callback function will print out the contents
@@ -82,12 +86,13 @@
 #define	OFMT_VAL_UNDEF		"--"
 #define	OFMT_VAL_UNKNOWN	"?"
 
+#define	OFMT_DEFAULT_FS		':'
+
 /*
  * The maximum number of rows supported by the OFMT_WRAP option.
  */
 #define	OFMT_MAX_ROWS		128
 
-static void ofmt_print_header(ofmt_state_t *);
 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
     boolean_t);
 
@@ -127,30 +132,26 @@
 }
 
 /*
- * Split a template into its maximum number of fields (capped by the maxcols
- * if it's non-zero).  Return a pointer to a split_t containing the split
- * fields, or NULL on failure.  Invoked when all fields are implicitly
- * selected at handle creation.
+ * Split `fields' into at most `maxfields' fields. Return a pointer to
+ * a split_t containing the split fields, or NULL on failure. Invoked
+ * when all fields are implicitly selected at handle creation by
+ * passing in a NULL fields_str
  */
 static split_t *
-split_max(const ofmt_field_t *template, uint_t maxcols)
+split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
 {
-	const ofmt_field_t *ofp;
 	split_t	*sp;
-	int i, cols, nfields = 0;
+	int i, cols;
 
 	sp = calloc(sizeof (split_t), 1);
 	if (sp == NULL)
 		return (NULL);
 
-	for (ofp = template; ofp->of_name != NULL; ofp++)
-		nfields++;
-
-	sp->s_fields = malloc(sizeof (char *) * nfields);
+	sp->s_fields = malloc(sizeof (char *) * maxfields);
 	if (sp->s_fields == NULL)
 		goto fail;
 	cols = 0;
-	for (i = 0; i < nfields; i++) {
+	for (i = 0; i < maxfields; i++) {
 		cols += template[i].of_width;
 		/*
 		 * If all fields are implied without explicitly passing
@@ -188,10 +189,11 @@
     uint_t maxcols, ofmt_handle_t *ofmt)
 {
 	split_t		*sp;
-	uint_t		i, of_index;
+	uint_t		i, j, of_index;
 	const ofmt_field_t *ofp;
 	ofmt_field_t	*of;
 	ofmt_state_t	*os = NULL;
+	int		nfields = 0;
 	ofmt_status_t	error = OFMT_SUCCESS;
 	boolean_t	parsable = (flags & OFMT_PARSABLE);
 	boolean_t	wrap = (flags & OFMT_WRAP);
@@ -215,29 +217,18 @@
 	}
 	if (template == NULL)
 		return (OFMT_ENOTEMPLATE);
-
+	for (ofp = template; ofp->of_name != NULL; ofp++)
+		nfields++;
 	/*
 	 * split str into the columns selected, or construct the
 	 * full set of columns (equivalent to -o all).
 	 */
 	if (str != NULL && strcasecmp(str, "all") != 0) {
-		const char *c;
-		int nfields = 1;
-
-		/*
-		 * Get an upper bound on the number of fields by counting
-		 * the commas.
-		 */
-		for (c = str; *c != '\0'; c++) {
-			if (*c == ',')
-				nfields++;
-		}
-
 		sp = split_str(str, nfields);
 	} else {
 		if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
 			maxcols = 0;
-		sp = split_max(template, maxcols);
+		sp = split_fields(template, nfields, maxcols);
 	}
 	if (sp == NULL)
 		goto nomem;
@@ -249,6 +240,7 @@
 	*ofmt = os;
 	os->os_fields = (ofmt_field_t *)&os[1];
 	os->os_flags = flags;
+	os->os_fs = OFMT_DEFAULT_FS;
 
 	of = os->os_fields;
 	of_index = 0;
@@ -257,12 +249,13 @@
 	 * nfields is the number of fields in template.
 	 */
 	for (i = 0; i < sp->s_nfields; i++) {
-		for (ofp = template; ofp->of_name != NULL; ofp++) {
-			if (strcasecmp(sp->s_fields[i], ofp->of_name) == 0)
+		for (j = 0; j < nfields; j++) {
+			if (strcasecmp(sp->s_fields[i],
+			    template[j].of_name) == 0) {
 				break;
+			}
 		}
-
-		if (ofp->of_name == NULL) {
+		if (j == nfields) {
 			int nbad = os->os_nbad++;
 
 			error = OFMT_EBADFIELDS;
@@ -277,7 +270,7 @@
 				goto nomem;
 			continue;
 		}
-		of[of_index].of_name = strdup(ofp->of_name);
+		of[of_index].of_name = strdup(template[j].of_name);
 		if (of[of_index].of_name == NULL)
 			goto nomem;
 		if (multiline) {
@@ -285,9 +278,9 @@
 
 			os->os_maxnamelen = MAX(n, os->os_maxnamelen);
 		}
-		of[of_index].of_width = ofp->of_width;
-		of[of_index].of_id = ofp->of_id;
-		of[of_index].of_cb = ofp->of_cb;
+		of[of_index].of_width = template[j].of_width;
+		of[of_index].of_id = template[j].of_id;
+		of[of_index].of_cb = template[j].of_cb;
 		of_index++;
 	}
 	splitfree(sp);
@@ -305,6 +298,12 @@
 	return (error);
 }
 
+void
+ofmt_set_fs(ofmt_handle_t ofmt, char fs)
+{
+	((ofmt_state_t *)ofmt)->os_fs = fs;
+}
+
 /*
  * free resources associated with the ofmt_handle_t
  */
@@ -341,8 +340,8 @@
 	char	c;
 
 	/*
-	 * Parsable fields are separated by ':'. If such a field contains
-	 * a ':' or '\', this character is prefixed by a '\'.
+	 * Parsable fields are separated by os_fs. os_fs and '\' are escaped
+	 * (prefixed by a) '\'.
 	 */
 	if (parsable) {
 		if (os->os_nfields == 1) {
@@ -350,12 +349,12 @@
 			return;
 		}
 		while ((c = *value++) != '\0') {
-			if (escsep && ((c == ':' || c == '\\')))
+			if (escsep && ((c == os->os_fs || c == '\\')))
 				(void) putchar('\\');
 			(void) putchar(c);
 		}
 		if (!os->os_lastfield)
-			(void) putchar(':');
+			(void) putchar(os->os_fs);
 	} else if (multiline) {
 		if (value[0] == '\0')
 			value = OFMT_VAL_UNDEF;
@@ -455,7 +454,8 @@
 
 	if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
 	    !parsable && !multiline) {
-		ofmt_print_header(os);
+		if (!(os->os_flags & OFMT_NOHEADER))
+		    ofmt_print_header(os);
 		os->os_nrow++;
 	}
 
@@ -521,9 +521,10 @@
 /*
  * Print the field headers
  */
-static void
-ofmt_print_header(ofmt_state_t *os)
+void
+ofmt_print_header(ofmt_handle_t ofmt)
 {
+	ofmt_state_t *os = ofmt;
 	int i;
 	ofmt_field_t *of = os->os_fields;
 	boolean_t escsep = (os->os_nfields > 1);
@@ -647,7 +648,7 @@
 
 	/*
 	 * All errors are considered fatal in parsable mode.  OFMT_ENOMEM and
-	 * OFMT_ENOFIELDS errors are always fatal, regardless of mode.  For
+	 * OFMT_ENOFIELDS errors are always fatal, regardless of mode.	For
 	 * other errors, we print diagnostics in human-readable mode and
 	 * processs what we can.
 	 */
--- old/usr/src/lib/libofmt/common/ofmt.h	Thu Mar  1 14:54:13 2018
+++ new/usr/src/lib/libofmt/common/ofmt.h	Thu Mar  1 14:54:13 2018
@@ -25,6 +25,7 @@
  */
 
 /*
+ * Copyright (c) 2015 by Delphix. All rights reserved.
  * Copyright 2017 Joyent, Inc.
  */
 
@@ -127,12 +128,12 @@
  * caller), the function ofmt_update_winsize(handle) may be called.
  */
 
-#include <sys/types.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#include <sys/types.h>
+
 /*
  * Recommended buffer size for buffers passed, for example, to ofmt_strerror().
  */
@@ -187,6 +188,7 @@
 #define	OFMT_WRAP	0x00000002 /* wrap output if field width is exceeded */
 #define	OFMT_MULTILINE	0x00000004 /* "long" output: "name: value" lines */
 #define	OFMT_RIGHTJUST	0x00000008 /* right justified output */
+#define	OFMT_NOHEADER	0x00000010 /* do not automatically print header lines */
 
 /*
  * ofmt_close() must be called to free resources associated
@@ -195,11 +197,22 @@
 extern void ofmt_close(ofmt_handle_t);
 
 /*
+ * Set the field separator used in parsable output (default is ':').
+ */
+extern void ofmt_set_fs(ofmt_handle_t, char);
+
+/*
  * ofmt_print() emits one row of output
  */
 extern void ofmt_print(ofmt_handle_t, void *);
 
 /*
+ * ofmt_print_header() prints the header line. It can be used with
+ * OFMT_NOHEADER to control exactly when the header gets printed.
+ */
+extern void ofmt_print_header(ofmt_handle_t);
+
+/*
  * ofmt_update_winsize() updates the window size information for ofmt_handle_t
  */
 extern void ofmt_update_winsize(ofmt_handle_t);
--- old/usr/src/man/man1m/Makefile	Thu Mar  1 14:54:13 2018
+++ new/usr/src/man/man1m/Makefile	Thu Mar  1 14:54:13 2018
@@ -76,6 +76,7 @@
 		clear_locks.1m		\
 		clinfo.1m		\
 		clri.1m			\
+		connstat.1m		\
 		consadm.1m		\
 		conv_lp.1m		\
 		conv_lpd.1m		\
--- /dev/null	Thu Mar  1 14:54:13 2018
+++ new/usr/src/man/man1m/connstat.1m	Thu Mar  1 14:54:13 2018
@@ -0,0 +1,387 @@
+.\"
+.\" CDDL HEADER START
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\" CDDL HEADER END
+.\"
+.\"
+.\" Copyright (c) 2016 by Delphix. All rights reserved.
+.\"
+.Dd July 5, 2016
+.Dt CONNSTAT 1M
+.Os
+.Sh NAME
+.Nm connstat
+.Nd report TCP connection statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl eLP
+.Op Fl 4 Ns | Ns Fl 6
+.Op Fl T Sy d Ns | Ns Sy u
+.Op Fl F Ar filter
+.Op Fl i Ar interval
+.Op Fl c Ar count
+.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ...
+.Sh DESCRIPTION
+The
+.Nm
+command reports TCP connection statistics in tabular form.
+Each row of the table represents the activity of one connection.
+The
+.Nm
+command adds virtually no overhead to run as it is aggregating statistics that
+are always collected in the kernel.
+.Pp
+With no arguments,
+.Nm
+prints a single report containing all TCP connections, and includes a basic
+set of fields representing IP address and port information, as well as connection
+state.
+The
+.Fl o
+flag can be used to specify which fields to display, and other arguments to
+filter the set of connections included in the output.
+.Pp
+The arguments are as folows:
+.Bl -tag -width ""
+.It Fl 4 , Fl -ipv4
+Only displays IPv4 connections.
+.It Fl 6 , Fl -ipv6
+Only displays IPv6 connections
+.It Fl c Ar count , Fl -count Ns = Ns Ar count
+Print a specified number of reports before exiting.
+This is used in conjuction with
+.Fl i .
+.It Fl e , Fl -established
+Only display connections that are in state ESTABLISHED.
+This is equivalent to including
+.Sy state=ESTABLISHED
+in the filter argument to the
+.Fl F
+option.
+.It Fl F Ar filter , Fl -filter Ns = Ns Ar filter
+Only display connections that match the filter argument provided.
+The format of the filter is:
+.Pp
+.Ar field Ns = Ns Ar value Ns Oo , Ns Ar field Ns = Ns Ar value Oc Ns ...
+.Pp
+Fields that can currently be filtered are
+.Ar laddr , Ar lport , Ar raddr , Ar rport , and Ar state .
+See the
+.Sx Fields
+section for a description of these fields.
+The filter matches a connection if all of the filter elements match, and a
+field must only appears once in the filter.
+.It Fl i Ar interval , Fl -interval Ns = Ns Ar interval
+Specify an output interval in seconds.
+For each interval, a report containing all connections appropriate given other
+command-line options is printed.
+.It Fl L , Fl -no-loopback
+Exclude connections to the loopback address.
+.It Fl o Ar fields , Fl -output Ns = Ns Ar fields
+Restrict the output to the specified comma-delimited list of field names.
+See the
+.Sx Fields
+section for information about possible fields.
+.It Fl P , Fl -parsable
+Display using a stable machine-parsable output format.
+With this option, each line of output consists of comma (,) delimited fields,
+and the fields displayed are specified using the
+.Fl o
+option.
+See
+.Sx Example 4
+for an example of how to process parsable output.
+.It Fl T Sy d Ns | Ns Sy u , Fl -timestamp Ns = Ns Sy d Ns | Ns Sy u
+Print a timestamp before each block of output.
+.Pp
+Specify
+.Sy u
+for a printed representation of the internal representation of time (see
+.Xr time 2 Ns ).
+Specify
+.Sy d
+for standard date format (see
+.Xr date 1 Ns ).
+.El
+.Ss Fields
+The following fields are supported.
+Field names are case insensitive.
+Unless otherwise indicated, the values of fields that represent a count (e.g.
+bytes or segments) are cumulative since the connection was established.
+Some of these fields refer to data segments, which are segments that contain
+non-zero amount of data.
+All sizes are in bytes.
+.Bl -tag -width "inunorderbytes"
+.It Sy cwnd
+The size of the local TCP congestion window at this instant.
+.It Sy inbytes
+The number of data bytes received.
+This does not include duplicate bytes received.
+.It Sy insegs
+The number of data segments received.
+This does not include duplicate segments received.
+.It Sy inunorderbytes
+The number of data bytes that were received out of order.
+.It Sy inunordersegs
+The number of data segments that were received out of order.
+.It Sy laddr
+The local IP address.
+.It Sy lport
+The local TCP port.
+.It Sy mss
+The maximum TCP segment size for this connection.
+.It Sy outbytes
+The number of data bytes sent.
+This does not include retransmitted bytes counted by
+.Sy retransbytes .
+.It Sy outsegs
+The number of data segments sent.
+This does not include segments containing retransmitted bytes counted by
+.Sy retranssegs .
+.It Sy raddr
+The remote IP address.
+.It Sy retransbytes
+The number of data bytes retransmitted.
+.It Sy retranssegs
+The number of data segments sent that contained retransmitted bytes.
+.It Sy rport
+The remote TCP port.
+.It Sy rto
+The current retransmission timeout in milliseconds.
+.It Sy rtt
+The current smoothed round-trip time to the peer in microseconds.
+The smoothed RTT average algorithm used is as described in RFC 6298.
+.It Sy rttc
+The number of times that a round-trip sample was added to
+.Sy rtts .
+See
+.Sy rtts
+for a description of how these two fields can be used together to calculate the
+average round-trip over a given period.
+.It Sy rtts
+The sum of all round-trip samples taken over the lifetime of the connection in
+microseconds.
+Each time TCP updates the value of
+.Sy rtt
+with a new sample, that sample's value is added to
+.Sy rtts .
+To calculate the average round-trip over a given period (e.g. between T1 and T2),
+take samples of
+.Sy rtts
+and
+.Sy rttc
+at T1 and T2, and calculate
+.br
+((
+.Sy rtts Ns
+_T2 -
+.Sy rtts Ns
+_T1 ) / (
+.Sy rttc Ns
+_T2 -
+.Sy rttc Ns
+_T1 )).
+.br
+See
+.Sx Example 4
+for an example of how this can be done programatically from a shell script.
+.It Sy rwnd
+The size of the local TCP receive window at this instant.
+.It Sy state
+The TCP connection state.
+Possible values are:
+.Bl -tag -width "SYN_RECEIVED"
+.It Sy BOUND
+Bound, ready to connect or listen.
+.It Sy CLOSED
+Closed.
+The local endpoint (e.g. socket) is not being used.
+.It Sy CLOSING
+Closed, but still waiting for a termination acknowledgment from the peer.
+.It Sy CLOSE_WAIT
+The peer has shutdown; waiting for the local endpoint to close.
+.It Sy ESTABLISHED
+Connection has been established and data can be transfered.
+.It Sy FIN_WAIT_1
+Local endpoint is closed, but waiting for termination acknowledgment from the
+peer.
+.It Sy FIN_WAIT_2
+Local endpoint is closed, but waiting for a termination request from the peer.
+.It Sy IDLE
+The local endpoint (e.g. socket) has been opened, but is not bound.
+.It Sy LAST_ACK
+The remote endpoint has terminated, and the local endpoint has sent a termination
+request.
+The acknowledgment for this request has not been received.
+.It Sy LISTEN
+Listening for incoming connections.
+.It Sy SYN_RECEIVED
+Initial connection request has been received and acknowledged, and a connection
+request has been sent but not yet acknowledged.
+.It Sy SYN_SENT
+A connection establishment request has been sent but not yet acknowledged.
+.It Sy TIME_WAIT
+Waiting for time to pass after having sent an acknowledgment for the peer's
+connection termination request.
+.El
+.Pp
+See RFC 793 for a more complete understanding of the TCP protocol and TCP
+connection states.
+.It Sy suna
+The number of unacknowledged bytes outstanding at this instant.
+.It Sy swnd
+The size of the local TCP send window (the peer's receive window) at this
+instant.
+.It Sy unsent
+The number of unsent bytes in the local TCP transmit queue at this instant.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, or 1 if an error occurs.
+.Sh EXAMPLES
+.Bl -tag -width ""
+.It Sy Example 1 List established connections.
+By default, connstat lists basic connection details.
+Using the
+.Fl e
+option allows the user to get a quick glance of established connections.
+.Bd -literal
+$ connstat -e
+          LADDR  LPORT           RADDR  RPORT        STATE
+   10.43.37.172  51275    172.16.105.4    389  ESTABLISHED
+   10.43.37.172     22    172.16.98.16  62270  ESTABLISHED
+   10.43.37.172   1020  172.16.100.162   2049  ESTABLISHED
+   10.43.37.172   1019     10.43.11.64   2049  ESTABLISHED
+   10.43.37.172     22    172.16.98.16  61520  ESTABLISHED
+   10.43.37.172     80    10.43.16.132  59467  ESTABLISHED
+.Ed
+.It Sy Example 2 Show one connection's I/O stats every second
+The
+.Fl F
+option is used to filter a specific connection,
+.Fl o
+is used to output specific fields, and
+.Fl i
+to provide the output interval in seconds.
+.Bd -literal
+$ connstat -F lport=22,rport=49675,raddr=172.16.168.30 \e
+  -o inbytes,outbytes -i 1
+    INBYTES    OUTBYTES
+       9589       18101
+    INBYTES    OUTBYTES
+       9589       18341
+    INBYTES    OUTBYTES
+       9589       18501
+    INBYTES    OUTBYTES
+       9589       18661
+    ...
+.Ed
+.It Sy Example 3 Understanding the bottleneck for a given connection
+Understanding the transmit bottleneck for a connection requires knowing the
+size of the congestion window, whether the window is full, and the round-trip
+time to the peer.
+The congestion window is full when
+.Sy suna
+is equal to
+.Sy cwnd .
+If the window is full, then the throughput is limited by the size of the window
+and the round-trip time.
+In that case, knowing these two values is critical.
+Either the window is small because of retransmissions, or the the round-trip
+latency is high, or both.
+In the example below, the window is small due to high congestion or an
+unreliable network.
+.Bd -literal
+$ connstat -F lport=41934,rport=50001 \e
+  -o outbytes,suna,cwnd,unsent,retransbytes,rtt -T d -i 1
+July  7, 2016 11:04:40 AM EDT
+   OUTBYTES        SUNA        CWND      UNSENT  RETRANSBYTES      RTT
+ 1647048093       47784       47784     3017352       3701844      495
+July  7, 2016 11:04:41 AM EDT
+   OUTBYTES        SUNA        CWND      UNSENT  RETRANSBYTES      RTT
+ 1660720109       41992       41992     1535032       3765556      673
+July  7, 2016 11:04:42 AM EDT
+   OUTBYTES        SUNA        CWND      UNSENT  RETRANSBYTES      RTT
+ 1661875613       26064       26064     4311688       3829268      571
+July  7, 2016 11:04:43 AM EDT
+   OUTBYTES        SUNA        CWND      UNSENT  RETRANSBYTES      RTT
+ 1681478637       41992       41992      437304       3932076     1471
+July  7, 2016 11:04:44 AM EDT
+   OUTBYTES        SUNA        CWND      UNSENT  RETRANSBYTES      RTT
+ 1692028765       44888       44888     1945800       4014612      921
+\&...
+.Ed
+.It Sy Example 4 Calculating average RTT over intervals
+As described in the
+.Sx Fields
+section, the
+.Sy rtts
+and
+.Sy rttc
+fields can be used to calculate average RTT over a period of time.
+The following example combines machine parsable output with these fields to do
+this programatically.
+The script:
+.Bd -literal
+#!/bin/bash
+
+i=0
+connstat -P -F lport=41934,rport=50001 -o rttc,rtts -i 1 | \e
+    while IFS=, read rttc[$i] rtts[$i]; do
+        if [[ $i != 0 ]]; then
+                let rtt="(${rtts[$i]} - ${rtts[$i - 1]}) / \e
+                    (${rttc[$i]} - ${rttc[$i - 1]})"
+                print "avg rtt = ${rtt}us"
+        fi
+        ((i++))
+done
+.Ed
+.Pp
+The output:
+.Bd -literal
+\&...
+avg rtt = 992us
+avg rtt = 829us
+avg rtt = 712us
+avg rtt = 869us
+\&...
+.Ed
+.It Sy Example 5 Show HTTP server connections in TIME_WAIT state
+Connections accumulating in TIME_WAIT state can sometimes be an issue, as these
+connections linger and take up port number space while their time wait timer
+is ticking.
+.Bd -literal
+$ connstat -F state=time_wait,lport=80
+          LADDR  LPORT           RADDR  RPORT        STATE
+   10.43.37.172     80   172.16.168.30  56067    TIME_WAIT
+   10.43.37.172     80   172.16.168.30  56068    TIME_WAIT
+   10.43.37.172     80   172.16.168.30  56070    TIME_WAIT
+.Ed
+.El
+.Sh INTERFACE STABILITY
+.Sy Commited .
+.Sh SEE ALSO
+.Xr netstat 1M
+.Rs
+.%A J. Postel
+.%B Transmission Control Protocol, STD 7, RFC 793
+.%D September 1981
+.Re
+.Rs
+.%A V. Paxson
+.%A M. Allman
+.%A J. Chu
+.%A M. Sargent
+.%B Computing TCP's Retransmission Timer, RFC 6298
+.%D June 2011
+.Re
--- old/usr/src/man/man1m/ipadm.1m	Thu Mar  1 14:54:13 2018
+++ new/usr/src/man/man1m/ipadm.1m	Thu Mar  1 14:54:13 2018
@@ -10,7 +10,7 @@
 .\"
 .\"
 .\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved
-.\" Copyright (c) 2013 by Delphix. All rights reserved.
+.\" Copyright (c) 2013, 2017 by Delphix. All rights reserved.
 .\" Copyright 2018 Nexenta Systems, Inc.
 .\" Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
 .\"
@@ -776,6 +776,9 @@
 property.
 The property name can be one of the following:
 .Bl -tag -compact -width "smallest_nonpriv_port"
+.It Cm congestion_control
+The default congestion-control algorithm to be used for new connections
+.Pq TCP .
 .It Cm ecn
 Explicit congestion control
 .Pq Cm never Ns / Ns Cm passive Ns / Ns Cm active
--- old/usr/src/pkg/manifests/SUNWcs.man1m.inc	Thu Mar  1 14:54:13 2018
+++ new/usr/src/pkg/manifests/SUNWcs.man1m.inc	Thu Mar  1 14:54:13 2018
@@ -14,6 +14,7 @@
 # Copyright 2015 Nexenta Systems, Inc. All rights reserved.
 # Copyright 2016 Toomas Soome <tsoome@me.com>
 # Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
+# Copyright (c) 2016 by Delphix. All rights reserved.
 #
 
 file path=usr/share/man/man1m/6to4relay.1m
@@ -48,6 +49,7 @@
 file path=usr/share/man/man1m/clear_locks.1m
 file path=usr/share/man/man1m/clinfo.1m
 file path=usr/share/man/man1m/clri.1m
+file path=usr/share/man/man1m/connstat.1m
 file path=usr/share/man/man1m/consadm.1m
 file path=usr/share/man/man1m/coreadm.1m
 file path=usr/share/man/man1m/cron.1m
--- old/usr/src/pkg/manifests/SUNWcs.mf	Thu Mar  1 14:54:13 2018
+++ new/usr/src/pkg/manifests/SUNWcs.mf	Thu Mar  1 14:54:13 2018
@@ -25,6 +25,7 @@
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2015 Nexenta Systems, Inc. All rights reserved.
 # Copyright 2017 Toomas Soome <tsoome@me.com>
+# Copyright (c) 2011, 2015 by Delphix. All rights reserved.
 #
 
 <include SUNWcs.man1.inc>
@@ -711,6 +712,7 @@
 file path=usr/bin/ckuid mode=0555
 file path=usr/bin/ckyorn mode=0555
 file path=usr/bin/clear mode=0555
+file path=usr/bin/connstat mode=0555
 file path=usr/bin/coreadm mode=0555
 file path=usr/bin/cp mode=0555
 file path=usr/bin/cpio mode=0555
--- old/usr/src/pkg/manifests/system-header.mf	Thu Mar  1 14:54:13 2018
+++ new/usr/src/pkg/manifests/system-header.mf	Thu Mar  1 14:54:13 2018
@@ -21,7 +21,7 @@
 
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2012 by Delphix. All rights reserved.
+# Copyright (c) 2012, 2017 by Delphix. All rights reserved.
 # Copyright 2013 Saso Kiselkov. All rights reserved.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2017 Nexenta Systems, Inc.
@@ -406,6 +406,7 @@
 file path=usr/include/ieeefp.h
 file path=usr/include/ifaddrs.h
 file path=usr/include/inet/arp.h
+file path=usr/include/inet/cc.h
 file path=usr/include/inet/common.h
 file path=usr/include/inet/ip.h
 file path=usr/include/inet/ip6.h
--- old/usr/src/pkg/manifests/system-kernel.mf	Thu Mar  1 14:54:14 2018
+++ new/usr/src/pkg/manifests/system-kernel.mf	Thu Mar  1 14:54:14 2018
@@ -21,6 +21,7 @@
 
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2017 by Delphix. All rights reserved.
 # Copyright 2013 Saso Kiselkov. All rights reserved.
 # Copyright 2015 Nexenta Systems, Inc. All rights reserved.
 # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
@@ -63,6 +64,8 @@
 dir path=etc/sock2path.d group=sys
 dir path=kernel group=sys
 $(i386_ONLY)dir path=kernel/$(ARCH64) group=sys
+dir path=kernel/cc group=sys
+dir path=kernel/cc/$(ARCH64) group=sys
 dir path=kernel/crypto group=sys
 dir path=kernel/crypto/$(ARCH64) group=sys
 dir path=kernel/dacf group=sys
@@ -309,6 +312,9 @@
 file path=etc/sock2path.d/system%2Fkernel group=sys
 file path=etc/system group=sys original_name=SUNWckr:etc/system preserve=true
 $(i386_ONLY)file path=kernel/$(ARCH64)/genunix group=sys mode=0755
+file path=kernel/cc/$(ARCH64)/cc_cubic group=sys mode=0755
+file path=kernel/cc/$(ARCH64)/cc_newreno group=sys mode=0755
+file path=kernel/cc/$(ARCH64)/cc_sunreno group=sys mode=0755
 file path=kernel/crypto/$(ARCH64)/aes group=sys mode=0755
 file path=kernel/crypto/$(ARCH64)/arcfour group=sys mode=0755
 file path=kernel/crypto/$(ARCH64)/blowfish group=sys mode=0755
--- old/usr/src/uts/Makefile.targ	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/Makefile.targ	Thu Mar  1 14:54:14 2018
@@ -23,6 +23,7 @@
 # Copyright 2015, Joyent, Inc.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+# Copyright (c) 2017 by Delphix. All rights reserved.
 #
 #	This Makefiles contains the common targets and definitions for
 #	all kernels. It is to be included in the Makefiles for specific
@@ -178,6 +179,9 @@
 $(ROOT_BRAND_DIR)/%:	$(OBJS_DIR)/% $(ROOT_BRAND_DIR) FRC
 	$(INS.file)
 
+$(ROOT_CC_DIR)/%:	$(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_CC_DIR) FRC
+	$(INS.file)
+
 $(ROOT_CRYPTO_DIR)/%:	$(OBJS_DIR)/% $(ROOT_CRYPTO_DIR) FRC
 	$(INS.file)
 
--- old/usr/src/uts/Makefile.uts	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/Makefile.uts	Thu Mar  1 14:54:14 2018
@@ -22,7 +22,7 @@
 #
 # Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2011 Bayard G. Bell. All rights reserved.
-# Copyright (c) 2011 by Delphix. All rights reserved.
+# Copyright (c) 2011,2017 by Delphix. All rights reserved.
 # Copyright (c) 2013 Andrew Stormont.  All rights reserved.
 # Copyright 2015 Joyent, Inc.
 # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
@@ -407,6 +407,7 @@
 ROOT_DACF_DIR_32	= $(ROOT_MOD_DIR)/dacf
 ROOT_CRYPTO_DIR_32	= $(ROOT_MOD_DIR)/crypto
 ROOT_MAC_DIR_32		= $(ROOT_MOD_DIR)/mac
+ROOT_CC_DIR_32		= $(ROOT_MOD_DIR)/cc
 ROOT_OVERLAY_DIR_32	= $(ROOT_MOD_DIR)/overlay
 ROOT_KICONV_DIR_32	= $(ROOT_MOD_DIR)/kiconv
 
@@ -435,6 +436,7 @@
 ROOT_DACF_DIR_64	= $(ROOT_MOD_DIR)/dacf/$(SUBDIR64)
 ROOT_CRYPTO_DIR_64	= $(ROOT_MOD_DIR)/crypto/$(SUBDIR64)
 ROOT_MAC_DIR_64		= $(ROOT_MOD_DIR)/mac/$(SUBDIR64)
+ROOT_CC_DIR_64		= $(ROOT_MOD_DIR)/cc/$(SUBDIR64)
 ROOT_OVERLAY_DIR_64	= $(ROOT_MOD_DIR)/overlay/$(SUBDIR64)
 ROOT_KICONV_DIR_64	= $(ROOT_MOD_DIR)/kiconv/$(SUBDIR64)
 
@@ -463,6 +465,7 @@
 ROOT_DACF_DIR		= $(ROOT_DACF_DIR_$(CLASS))
 ROOT_CRYPTO_DIR		= $(ROOT_CRYPTO_DIR_$(CLASS))
 ROOT_MAC_DIR		= $(ROOT_MAC_DIR_$(CLASS))
+ROOT_CC_DIR		= $(ROOT_CC_DIR_$(CLASS))
 ROOT_OVERLAY_DIR	= $(ROOT_OVERLAY_DIR_$(CLASS))
 ROOT_KICONV_DIR		= $(ROOT_KICONV_DIR_$(CLASS))
 ROOT_FIRMWARE_DIR	= $(ROOT_MOD_DIR)/firmware
@@ -482,6 +485,7 @@
 ROOT_MOD_DIRS_32	+= $(ROOT_CPU_DIR_32) $(ROOT_FONT_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_TOD_DIR_32) $(ROOT_DACF_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_CRYPTO_DIR_32) $(ROOT_MAC_DIR_32)
+ROOT_MOD_DIRS_32	+= $(ROOT_CC_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_OVERLAY_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_KICONV_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_FIRMWARE_DIR)
@@ -576,7 +580,7 @@
 		 $(MMU_KMODS) $(DACF_KMODS) $(EXPORT_KMODS) $(IPP_KMODS) \
 		 $(CRYPTO_KMODS) $(PCBE_KMODS) \
 		 $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS) \
-		 $(BRAND_KMODS) $(KICONV_KMODS) $(OVERLAY_KMODS) \
+		 $(BRAND_KMODS) $(KICONV_KMODS) $(OVERLAY_KMODS) $(CC_KMODS) \
 		 $(SOCKET_KMODS)
 
 KMODS = $(GENUNIX_KMODS) $(PARALLEL_KMODS)
@@ -588,7 +592,7 @@
 	  $(MACH_KMODS) $(GSS_KMODS) $(DACF_KMODS) $(IPP_KMODS) \
 	  $(CRYPTO_KMODS) $(PCBE_KMODS) \
 	  $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS) \
-	  $(BRAND_KMODS) $(KICONV_KMODS) $(SOCKET_KMODS)
+	  $(BRAND_KMODS) $(KICONV_KMODS) $(CC_KMODS) $(SOCKET_KMODS)
 
 #
 #	Files to be compiled with -xa, to generate basic block execution
--- old/usr/src/uts/common/Makefile.files	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/Makefile.files	Thu Mar  1 14:54:14 2018
@@ -119,6 +119,7 @@
 		bz2huffman.o	\
 		callb.o		\
 		callout.o	\
+		cc.o		\
 		chdir.o		\
 		chmod.o		\
 		chown.o		\
@@ -488,6 +489,12 @@
 
 CARDBUS_OBJS += cardbus.o cardbus_hp.o cardbus_cfg.o
 
+CC_CUBIC_OBJS += cc_cubic.o
+
+CC_NEWRENO_OBJS += cc_newreno.o
+
+CC_SUNRENO_OBJS += cc_sunreno.o
+
 CONSKBD_OBJS += conskbd.o
 
 CONSMS_OBJS +=	consms.o
--- old/usr/src/uts/common/Makefile.rules	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/Makefile.rules	Thu Mar  1 14:54:14 2018
@@ -25,7 +25,7 @@
 # Copyright 2013 Saso Kiselkov. All rights reserved.
 # Copyright 2017 Joyent, Inc.
 # Copyright 2016 Nexenta Systems, Inc.
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2017 by Delphix. All rights reserved.
 #
 
 #
@@ -566,6 +566,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/inet/cc/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -2063,6 +2067,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/inet/dlpistub/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/inet/cc/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- old/usr/src/uts/common/inet/Makefile	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/Makefile	Thu Mar  1 14:54:14 2018
@@ -23,12 +23,14 @@
 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright (c) 2014, 2017 by Delphix. All rights reserved.
+#
 # uts/common/inet/Makefile
 #
 # include global definitions
 include ../../../Makefile.master
 
-HDRS=	arp.h common.h ipclassifier.h ip.h ip6.h ipdrop.h ipnet.h \
+HDRS=	arp.h cc.h common.h ipclassifier.h ip.h ip6.h ipdrop.h ipnet.h \
 	ipsecah.h ipsecesp.h ipsec_info.h iptun.h ip6_asp.h ip_if.h ip_ire.h \
 	ip_multi.h ip_netinfo.h ip_ndp.h ip_rts.h ipsec_impl.h keysock.h \
 	led.h mi.h mib2.h nd.h optcom.h sadb.h sctp_itf.h snmpcom.h tcp.h \
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc.h	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2007-2008
+ * 	Swinburne University of Technology, Melbourne, Australia.
+ * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University of Technology, by Lawrence Stewart and
+ * James Healy, made possible in part by a grant from the Cisco University
+ * Research Program Fund at Community Foundation Silicon Valley.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This software was first released in 2007 by James Healy and Lawrence Stewart
+ * whilst working on the NewTCP research project at Swinburne University of
+ * Technology's Centre for Advanced Internet Architectures, Melbourne,
+ * Australia, which was made possible in part by a grant from the Cisco
+ * University Research Program Fund at Community Foundation Silicon Valley.
+ * More details are available at:
+ *   http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#ifndef _NETINET_CC_H_
+#define	_NETINET_CC_H_
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <netinet/tcp.h>
+#include <sys/queue.h>
+#include <sys/rwlock.h>
+
+#define	CC_ALGO_NAME_MAX	16	/* max congestion control name length */
+
+#define	CC_DEFAULT_ALGO_NAME	"sunreno"
+
+struct tcp_s;
+struct sctp_s;
+
+/* CC housekeeping functions. */
+void	cc_init(void);
+struct cc_algo *cc_load_algo(const char *name);
+int	cc_register_algo(struct cc_algo *add_cc);
+int	cc_deregister_algo(struct cc_algo *remove_cc);
+
+/*
+ * Wrapper around transport structs that contain same-named congestion
+ * control variables. Allows algos to be shared amongst multiple CC aware
+ * transports.
+ *
+ * In theory, this code (from FreeBSD) can be used to support pluggable
+ * congestion control for sctp as well as tcp.  However, the support for sctp
+ * in FreeBSD is incomplete, and in practice "type" is ignored.  cc_module.h
+ * provides a CCV macro which implementations can use to get a variable out of
+ * the protocol-appropriate structure.
+ *
+ * If FreeBSD eventually does extend support for pluggable congestion control
+ * to sctp, we'll need to make sure we're setting "type" appropriately or use
+ * a definition of CCV that ignores it.
+ */
+struct cc_var {
+	void		*cc_data; /* Per-connection private algorithm data. */
+	int		bytes_this_ack; /* # bytes acked by the current ACK. */
+	int		t_bytes_acked; /* # bytes acked during current RTT */
+	tcp_seq		curack; /* Most recent ACK. */
+	uint32_t	flags; /* Flags for cc_var (see below) */
+	int		type; /* Indicates which ptr is valid in ccvc. */
+	union ccv_container {
+		struct tcp_s	*tcp;
+		struct sctp_s	*sctp;
+	} ccvc;
+	uint16_t	nsegs; /* # segments coalesced into current chain. */
+};
+
+/*
+ * cc_var flags.
+ *
+ * CCF_ABC_SENTAWND is set when a full congestion window of data has been ACKed
+ *   according to the Appropriate Byte Counting spec, defined in RFC 3465.
+ */
+#define	CCF_ABC_SENTAWND	0x0001	/* ABC counted cwnd worth of bytes? */
+#define	CCF_CWND_LIMITED	0x0002	/* Are we currently cwnd limited? */
+#define	CCF_FASTRECOVERY	0x0004	/* in NewReno Fast Recovery */
+#define	CCF_WASFRECOVERY	0x0008	/* was in NewReno Fast Recovery */
+#define	CCF_CONGRECOVERY	0x0010	/* congestion recovery mode */
+#define	CCF_WASCRECOVERY	0x0020	/* was in congestion recovery */
+/*
+ * In slow-start due to a retransmission timeout. This flag is enabled for the
+ * duration of the slow-start phase.
+ */
+#define	CCF_RTO			0x0040	/* in slow-start due to timeout */
+
+#define	IN_FASTRECOVERY(flags)		(flags & CCF_FASTRECOVERY)
+#define	ENTER_FASTRECOVERY(flags)	flags |= CCF_FASTRECOVERY
+#define	EXIT_FASTRECOVERY(flags)	flags &= ~CCF_FASTRECOVERY
+
+#define	IN_CONGRECOVERY(flags)		(flags & CCF_CONGRECOVERY)
+#define	ENTER_CONGRECOVERY(flags)	flags |= CCF_CONGRECOVERY
+#define	EXIT_CONGRECOVERY(flags)	flags &= ~CCF_CONGRECOVERY
+
+#define	IN_RECOVERY(flags) (flags & (CCF_CONGRECOVERY | CCF_FASTRECOVERY))
+#define	ENTER_RECOVERY(flags) flags |= (CCF_CONGRECOVERY | CCF_FASTRECOVERY)
+#define	EXIT_RECOVERY(flags) flags &= ~(CCF_CONGRECOVERY | CCF_FASTRECOVERY)
+
+/*
+ * ACK types passed to the ack_received() hook.
+ *
+ * CC_ACK is passed when an ACK acknowledges previously unACKed data.
+ * CC_DUPACK is passed when a duplicate ACK is received.  The conditions under
+ *   which an ACK is considered a duplicate ACK are defined in RFC 5681.
+ */
+#define	CC_ACK		0x0001	/* Regular in sequence ACK. */
+#define	CC_DUPACK	0x0002	/* Duplicate ACK. */
+#define	CC_PARTIALACK	0x0004	/* Not yet. */
+#define	CC_SACK		0x0008	/* Not yet. */
+
+/*
+ * Congestion signal types passed to the cong_signal() hook. The highest order 8
+ * bits (0x01000000 - 0x80000000) are reserved for CC algos to declare their own
+ * congestion signal types.
+ *
+ * The congestion signals defined here cover the following situations:
+ * CC_ECN: A packet with an Explicit Congestion Notification was received
+ *   See RFC 3168.
+ * CC_RTO: A round-trip timeout occured.
+ * CC_RTO_ERR: An ACK was received for a sequence number after we fired an RTO
+ *   for that sequence number
+ * CC_NDUPACK: Trigger fast retransmit based on the assumption that receiving
+ *   N duplicate ACKs indicates packet loss rather than reordering.  Fast
+ *   retransmit is followed by fast recovery.  Fast retransmit and recovery
+ *   were originally described in RFC 2581 and were updated by RFC3782
+ *   (NewReno).  In both RFC2581 and RFC3782, N is 3.
+ */
+#define	CC_ECN		0x00000001	/* ECN marked packet received. */
+#define	CC_RTO		0x00000002	/* RTO fired. */
+#define	CC_RTO_ERR	0x00000004	/* RTO fired in error. */
+#define	CC_NDUPACK	0x00000008	/* Threshold of dupack's reached. */
+
+#define	CC_SIGPRIVMASK	0xFF000000	/* Mask to check if sig is private. */
+
+/*
+ * Structure to hold data and function pointers that together represent a
+ * congestion control algorithm.
+ */
+struct cc_algo {
+	char	name[CC_ALGO_NAME_MAX];
+
+	/* Init CC state for a new control block. */
+	int	(*cb_init)(struct cc_var *ccv);
+
+	/* Cleanup CC state for a terminating control block. */
+	void	(*cb_destroy)(struct cc_var *ccv);
+
+	/* Init variables for a newly established connection. */
+	void	(*conn_init)(struct cc_var *ccv);
+
+	/* Called on receipt of an ack. */
+	void	(*ack_received)(struct cc_var *ccv, uint16_t type);
+
+	/* Called on detection of a congestion signal. */
+	void	(*cong_signal)(struct cc_var *ccv, uint32_t type);
+
+	/* Called after exiting congestion recovery. */
+	void	(*post_recovery)(struct cc_var *ccv);
+
+	/* Called when data transfer resumes after an idle period. */
+	void	(*after_idle)(struct cc_var *ccv);
+
+	STAILQ_ENTRY(cc_algo) entries;
+};
+
+/* Macro to obtain the CC algo's struct ptr. */
+#define	CC_ALGO(tp)	((tp)->tcp_cc_algo)
+
+/* Macro to obtain the CC algo's data ptr. */
+#define	CC_DATA(tp)	((tp)->tcp_ccv->cc_data)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _NETINET_CC_H_ */
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc.c	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2007-2008
+ *	Swinburne University of Technology, Melbourne, Australia.
+ * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University of Technology, by Lawrence Stewart and
+ * James Healy, made possible in part by a grant from the Cisco University
+ * Research Program Fund at Community Foundation Silicon Valley.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This software was first released in 2007 by James Healy and Lawrence Stewart
+ * whilst working on the NewTCP research project at Swinburne University of
+ * Technology's Centre for Advanced Internet Architectures, Melbourne,
+ * Australia, which was made possible in part by a grant from the Cisco
+ * University Research Program Fund at Community Foundation Silicon Valley.
+ * More details are available at:
+ *   http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <inet/cc.h>
+#include <inet/tcp.h>
+#include <sys/sdt.h>
+
+#define	CC_KMODDIR	"cc"
+
+/*
+ * List of available cc algorithms on the current system. Access is
+ * synchronized using cc_list_lock.
+ */
+static STAILQ_HEAD(cc_head, cc_algo) cc_list = STAILQ_HEAD_INITIALIZER(cc_list);
+static kmutex_t cc_list_lock;
+
+/*
+ * Initialise CC subsystem on system boot.
+ */
+void
+cc_init(void)
+{
+	STAILQ_INIT(&cc_list);
+}
+
+struct cc_algo *
+cc_load_algo(const char *name)
+{
+	struct cc_algo *algo;
+	boolean_t tried_modload = B_FALSE, found = B_FALSE;
+	char modname[CC_ALGO_NAME_MAX + 3] = "cc_"; /* cc_<name> */
+
+find_registered_algo:
+	mutex_enter(&cc_list_lock);
+	STAILQ_FOREACH(algo, &cc_list, entries) {
+		if (strncmp(algo->name, name, CC_ALGO_NAME_MAX) == 0) {
+			found = B_TRUE;
+			break;
+		}
+	}
+	mutex_exit(&cc_list_lock);
+
+	if (!found) {
+		algo = NULL;
+		if (!tried_modload) {
+			/*
+			 * If the module has not yet been loaded, then attempt
+			 * to load it now.  If modload() succeeds, the
+			 * algorithm should have registered using
+			 * cc_register_algo(), in which case we can go back and
+			 * attempt to find it again.
+			 */
+			(void) strlcat(modname, name, sizeof (modname));
+			if (modload(CC_KMODDIR, modname) == -1) {
+				cmn_err(CE_WARN, "failed to load TCP congestion"
+				    "control algorithm: %s", modname);
+			} else {
+				tried_modload = B_TRUE;
+				goto find_registered_algo;
+			}
+		}
+	}
+
+	return (algo);
+}
+
+/*
+ * Returns non-zero on success, 0 on failure.
+ */
+int
+cc_deregister_algo(struct cc_algo *remove_cc)
+{
+	struct cc_algo *funcs, *tmpfuncs;
+	int err = ENOENT;
+
+	mutex_enter(&cc_list_lock);
+	STAILQ_FOREACH_SAFE(funcs, &cc_list, entries, tmpfuncs) {
+		if (funcs == remove_cc) {
+			STAILQ_REMOVE(&cc_list, funcs, cc_algo, entries);
+			err = 0;
+			break;
+		}
+	}
+	mutex_exit(&cc_list_lock);
+	return (err);
+}
+
+/*
+ * Returns 0 on success, non-zero on failure.
+ */
+int
+cc_register_algo(struct cc_algo *add_cc)
+{
+	struct cc_algo *funcs;
+	int err = 0;
+
+	/*
+	 * Iterate over list of registered CC algorithms and make sure
+	 * we're not trying to add a duplicate.
+	 */
+	mutex_enter(&cc_list_lock);
+	STAILQ_FOREACH(funcs, &cc_list, entries) {
+		if (strncmp(funcs->name, add_cc->name, CC_ALGO_NAME_MAX) == 0)
+			err = EEXIST;
+	}
+
+	if (err == 0)
+		STAILQ_INSERT_TAIL(&cc_list, add_cc, entries);
+
+	mutex_exit(&cc_list_lock);
+
+	return (err);
+}
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc_cubic.c	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2008-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed by Lawrence Stewart while studying at the Centre
+ * for Advanced Internet Architectures, Swinburne University of Technology, made
+ * possible in part by a grant from the Cisco University Research Program Fund
+ * at Community Foundation Silicon Valley.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * An implementation of the CUBIC congestion control algorithm for FreeBSD,
+ * based on the Internet Draft "draft-rhee-tcpm-cubic-02" by Rhee, Xu and Ha.
+ * Originally released as part of the NewTCP research project at Swinburne
+ * University of Technology's Centre for Advanced Internet Architectures,
+ * Melbourne, Australia, which was made possible in part by a grant from the
+ * Cisco University Research Program Fund at Community Foundation Silicon
+ * Valley. More details are available at:
+ *   http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+
+#include <inet/tcp_impl.h>
+#include <inet/cc.h>
+#include <inet/cc/cc_cubic.h>
+#include <inet/cc/cc_module.h>
+
+static struct modlmisc cc_cubic_modlmisc = {
+	&mod_miscops,
+	"Cubic Congestion Control"
+};
+
+static struct modlinkage cc_cubic_modlinkage = {
+	MODREV_1,
+	&cc_cubic_modlmisc,
+	NULL
+};
+
+/*
+ * cubic uses the NewReno implementation of after_idle and uses NewReno's
+ * ack_received callback during slow start.
+ */
+static struct cc_algo *newreno_cc_algo;
+
+static void	cubic_ack_received(struct cc_var *ccv, uint16_t type);
+static void	cubic_cb_destroy(struct cc_var *ccv);
+static int	cubic_cb_init(struct cc_var *ccv);
+static void	cubic_cong_signal(struct cc_var *ccv, uint32_t type);
+static void	cubic_conn_init(struct cc_var *ccv);
+static void	cubic_post_recovery(struct cc_var *ccv);
+static void	cubic_record_rtt(struct cc_var *ccv);
+static void	cubic_ssthresh_update(struct cc_var *ccv);
+
+struct cubic {
+	/* Cubic K in fixed point form with CUBIC_SHIFT worth of precision. */
+	int64_t		K;
+	/* Sum of RTT samples across an epoch in ticks. */
+	int64_t		sum_rtt_ticks;
+	/* cwnd at the most recent congestion event. */
+	uint32_t	max_cwnd;
+	/* cwnd at the previous congestion event. */
+	uint32_t	prev_max_cwnd;
+	/* Number of congestion events. */
+	uint32_t	num_cong_events;
+	/* Minimum observed rtt in ticks. */
+	int		min_rtt_ticks;
+	/* Mean observed rtt between congestion epochs. */
+	int		mean_rtt_ticks;
+	/* ACKs since last congestion event. */
+	int		epoch_ack_count;
+	/* Time of last congestion event in ticks. */
+	clock_t		t_last_cong;
+};
+
+struct cc_algo cubic_cc_algo = {
+	.name = "cubic",
+	.ack_received = cubic_ack_received,
+	.cb_destroy = cubic_cb_destroy,
+	.cb_init = cubic_cb_init,
+	.cong_signal = cubic_cong_signal,
+	.conn_init = cubic_conn_init,
+	.post_recovery = cubic_post_recovery,
+};
+
+int
+_init(void)
+{
+	int err;
+
+	if ((newreno_cc_algo = cc_load_algo("newreno")) == NULL)
+		return (EINVAL);
+
+	if ((err = cc_register_algo(&cubic_cc_algo)) == 0) {
+		if ((err = mod_install(&cc_cubic_modlinkage)) != 0)
+			(void) cc_deregister_algo(&cubic_cc_algo);
+	}
+	cubic_cc_algo.after_idle = newreno_cc_algo->after_idle;
+	return (err);
+}
+
+int
+_fini(void)
+{
+	/* XXX Not unloadable for now */
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&cc_cubic_modlinkage, modinfop));
+}
+
+static void
+cubic_ack_received(struct cc_var *ccv, uint16_t type)
+{
+	struct cubic *cubic_data;
+	uint32_t w_tf, w_cubic_next;
+	int ticks_since_cong;
+
+	cubic_data = ccv->cc_data;
+	cubic_record_rtt(ccv);
+
+	/*
+	 * Regular ACK and we're not in cong/fast recovery and we're cwnd
+	 * limited and we're either not doing ABC or are slow starting or are
+	 * doing ABC and we've sent a cwnd's worth of bytes.
+	 */
+	if (type == CC_ACK && !IN_RECOVERY(ccv->flags) &&
+	    (ccv->flags & CCF_CWND_LIMITED) && (!CC_ABC(ccv) ||
+	    CCV(ccv, tcp_cwnd) <= CCV(ccv, tcp_cwnd_ssthresh) ||
+	    (CC_ABC(ccv) && (ccv->flags & CCF_ABC_SENTAWND)))) {
+		/* Use the logic in NewReno ack_received() for slow start. */
+		if (CCV(ccv, tcp_cwnd) <= CCV(ccv, tcp_cwnd_ssthresh) ||
+		    cubic_data->min_rtt_ticks == TCPTV_SRTTBASE)
+			newreno_cc_algo->ack_received(ccv, type);
+		else {
+			ticks_since_cong = ddi_get_lbolt() -
+			    cubic_data->t_last_cong;
+
+			/*
+			 * The mean RTT is used to best reflect the equations in
+			 * the I-D. Using min_rtt in the tf_cwnd calculation
+			 * causes w_tf to grow much faster than it should if the
+			 * RTT is dominated by network buffering rather than
+			 * propagation delay.
+			 */
+			w_tf = tf_cwnd(ticks_since_cong,
+			    cubic_data->mean_rtt_ticks, cubic_data->max_cwnd,
+			    CCV(ccv, tcp_mss));
+
+			w_cubic_next = cubic_cwnd(ticks_since_cong +
+			    cubic_data->mean_rtt_ticks, cubic_data->max_cwnd,
+			    CCV(ccv, tcp_mss), cubic_data->K);
+
+			ccv->flags &= ~CCF_ABC_SENTAWND;
+
+			if (w_cubic_next < w_tf)
+				/*
+				 * TCP-friendly region, follow tf
+				 * cwnd growth.
+				 */
+				CCV(ccv, tcp_cwnd) = w_tf;
+
+			else if (CCV(ccv, tcp_cwnd) < w_cubic_next) {
+				/*
+				 * Concave or convex region, follow CUBIC
+				 * cwnd growth.
+				 */
+				if (CC_ABC(ccv))
+					CCV(ccv, tcp_cwnd) = w_cubic_next;
+				else
+					CCV(ccv, tcp_cwnd) += ((w_cubic_next -
+					    CCV(ccv, tcp_cwnd)) *
+					    CCV(ccv, tcp_mss)) /
+					    CCV(ccv, tcp_cwnd);
+			}
+
+			/*
+			 * If we're not in slow start and we're probing for a
+			 * new cwnd limit at the start of a connection
+			 * (happens when hostcache has a relevant entry),
+			 * keep updating our current estimate of the
+			 * max_cwnd.
+			 */
+			if (cubic_data->num_cong_events == 0 &&
+			    cubic_data->max_cwnd < CCV(ccv, tcp_cwnd))
+				cubic_data->max_cwnd = CCV(ccv, tcp_cwnd);
+		}
+	}
+}
+
+static void
+cubic_cb_destroy(struct cc_var *ccv)
+{
+
+	if (ccv->cc_data != NULL)
+		kmem_free(ccv->cc_data, sizeof (struct cubic));
+}
+
+static int
+cubic_cb_init(struct cc_var *ccv)
+{
+	struct cubic *cubic_data;
+
+	cubic_data = kmem_alloc(sizeof (struct cubic), KM_NOSLEEP);
+
+	if (cubic_data == NULL)
+		return (ENOMEM);
+
+	/* Init some key variables with sensible defaults. */
+	cubic_data->t_last_cong = ddi_get_lbolt();
+	cubic_data->min_rtt_ticks = TCPTV_SRTTBASE;
+	cubic_data->mean_rtt_ticks = 1;
+
+	ccv->cc_data = cubic_data;
+
+	return (0);
+}
+
+/*
+ * Perform any necessary tasks before we enter congestion recovery.
+ */
+static void
+cubic_cong_signal(struct cc_var *ccv, uint32_t type)
+{
+	struct cubic *cubic_data;
+	uint32_t cwin;
+	uint32_t mss;
+
+	cubic_data = ccv->cc_data;
+	cwin = CCV(ccv, tcp_cwnd);
+	mss = CCV(ccv, tcp_mss);
+
+	switch (type) {
+	case CC_NDUPACK:
+		if (!IN_FASTRECOVERY(ccv->flags)) {
+			if (!IN_CONGRECOVERY(ccv->flags)) {
+				cubic_ssthresh_update(ccv);
+				cubic_data->num_cong_events++;
+				cubic_data->prev_max_cwnd =
+				    cubic_data->max_cwnd;
+				cubic_data->max_cwnd = cwin;
+				CCV(ccv, tcp_cwnd) =
+				    CCV(ccv, tcp_cwnd_ssthresh);
+			}
+			ENTER_RECOVERY(ccv->flags);
+		}
+		break;
+
+	case CC_ECN:
+		if (!IN_CONGRECOVERY(ccv->flags)) {
+			cubic_ssthresh_update(ccv);
+			cubic_data->num_cong_events++;
+			cubic_data->prev_max_cwnd = cubic_data->max_cwnd;
+			cubic_data->max_cwnd = cwin;
+			cubic_data->t_last_cong = ddi_get_lbolt();
+			CCV(ccv, tcp_cwnd) = CCV(ccv, tcp_cwnd_ssthresh);
+			ENTER_CONGRECOVERY(ccv->flags);
+		}
+		break;
+
+	case CC_RTO:
+		/*
+		 * Grab the current time and record it so we know when the
+		 * most recent congestion event was. Only record it when the
+		 * timeout has fired more than once, as there is a reasonable
+		 * chance the first one is a false alarm and may not indicate
+		 * congestion.
+		 */
+		if (CCV(ccv, tcp_timer_backoff) >= 2) {
+			cubic_data->num_cong_events++;
+			cubic_data->t_last_cong = ddi_get_lbolt();
+			cubic_ssthresh_update(ccv);
+			cubic_data->max_cwnd = cwin;
+			CCV(ccv, tcp_cwnd) = mss;
+		}
+		break;
+	}
+}
+
+static void
+cubic_conn_init(struct cc_var *ccv)
+{
+	struct cubic *cubic_data;
+
+	cubic_data = ccv->cc_data;
+
+	/*
+	 * Ensure we have a sane initial value for max_cwnd recorded. Without
+	 * this here bad things happen when entries from the TCP hostcache
+	 * get used.
+	 */
+	cubic_data->max_cwnd = CCV(ccv, tcp_cwnd);
+}
+
+/*
+ * Perform any necessary tasks before we exit congestion recovery.
+ */
+static void
+cubic_post_recovery(struct cc_var *ccv)
+{
+	struct cubic *cubic_data;
+
+	cubic_data = ccv->cc_data;
+
+	/* Fast convergence heuristic. */
+	if (cubic_data->max_cwnd < cubic_data->prev_max_cwnd)
+		cubic_data->max_cwnd = (cubic_data->max_cwnd * CUBIC_FC_FACTOR)
+		    >> CUBIC_SHIFT;
+
+	if (IN_FASTRECOVERY(ccv->flags)) {
+		/* Update cwnd based on beta and adjusted max_cwnd. */
+		CCV(ccv, tcp_cwnd) = max(1, ((CUBIC_BETA *
+		    cubic_data->max_cwnd) >> CUBIC_SHIFT));
+	}
+	cubic_data->t_last_cong = ddi_get_lbolt();
+
+	/* Calculate the average RTT between congestion epochs. */
+	if (cubic_data->epoch_ack_count > 0 &&
+	    cubic_data->sum_rtt_ticks >= cubic_data->epoch_ack_count) {
+		cubic_data->mean_rtt_ticks = (int)(cubic_data->sum_rtt_ticks /
+		    cubic_data->epoch_ack_count);
+	}
+
+	cubic_data->epoch_ack_count = 0;
+	cubic_data->sum_rtt_ticks = 0;
+	cubic_data->K = cubic_k(cubic_data->max_cwnd / CCV(ccv, tcp_mss));
+}
+
+/*
+ * Record the min RTT and sum samples for the epoch average RTT calculation.
+ */
+static void
+cubic_record_rtt(struct cc_var *ccv)
+{
+	struct cubic *cubic_data;
+	int t_srtt_ticks;
+
+	/* Ignore srtt until a min number of samples have been taken. */
+	if (CCV(ccv, tcp_rtt_update) >= CUBIC_MIN_RTT_SAMPLES) {
+		cubic_data = ccv->cc_data;
+		/* tcp_rtt_sa is 8 * smoothed RTT in nanoseconds */
+		t_srtt_ticks = NSEC_TO_TICK(CCV(ccv, tcp_rtt_sa >> 3));
+
+		/*
+		 * Record the current SRTT as our minrtt if it's the smallest
+		 * we've seen or minrtt is currently equal to its initialized
+		 * value.
+		 *
+		 * XXXLAS: Should there be some hysteresis for minrtt?
+		 */
+		if ((t_srtt_ticks < cubic_data->min_rtt_ticks ||
+		    cubic_data->min_rtt_ticks == TCPTV_SRTTBASE)) {
+			cubic_data->min_rtt_ticks = max(1, t_srtt_ticks);
+
+			/*
+			 * If the connection is within its first congestion
+			 * epoch, ensure we prime mean_rtt_ticks with a
+			 * reasonable value until the epoch average RTT is
+			 * calculated in cubic_post_recovery().
+			 */
+			if (cubic_data->min_rtt_ticks >
+			    cubic_data->mean_rtt_ticks)
+				cubic_data->mean_rtt_ticks =
+				    cubic_data->min_rtt_ticks;
+		}
+
+		/* Sum samples for epoch average RTT calculation. */
+		cubic_data->sum_rtt_ticks += t_srtt_ticks;
+		cubic_data->epoch_ack_count++;
+	}
+}
+
+/*
+ * Update the ssthresh in the event of congestion.
+ */
+static void
+cubic_ssthresh_update(struct cc_var *ccv)
+{
+	struct cubic *cubic_data;
+
+	cubic_data = ccv->cc_data;
+
+	/*
+	 * On the first congestion event, set ssthresh to cwnd * 0.5, on
+	 * subsequent congestion events, set it to cwnd * beta.
+	 */
+	if (cubic_data->num_cong_events == 0)
+		CCV(ccv, tcp_cwnd_ssthresh) = CCV(ccv, tcp_cwnd) >> 1;
+	else
+		CCV(ccv, tcp_cwnd_ssthresh) =
+		    (CCV(ccv, tcp_cwnd) * CUBIC_BETA) >> CUBIC_SHIFT;
+}
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc_cubic.h	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2008-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed by Lawrence Stewart while studying at the Centre
+ * for Advanced Internet Architectures, Swinburne University of Technology, made
+ * possible in part by a grant from the Cisco University Research Program Fund
+ * at Community Foundation Silicon Valley.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_CC_CUBIC_H_
+#define	_NETINET_CC_CUBIC_H_
+
+/* Number of bits of precision for fixed point math calcs. */
+#define	CUBIC_SHIFT		8
+
+#define	CUBIC_SHIFT_4		32
+
+/* 0.5 << CUBIC_SHIFT. */
+#define	RENO_BETA		128
+
+/* ~0.8 << CUBIC_SHIFT. */
+#define	CUBIC_BETA		204
+
+/* ~0.2 << CUBIC_SHIFT. */
+#define	ONE_SUB_CUBIC_BETA	51
+
+/* 3 * ONE_SUB_CUBIC_BETA. */
+#define	THREE_X_PT2		153
+
+/* (2 << CUBIC_SHIFT) - ONE_SUB_CUBIC_BETA. */
+#define	TWO_SUB_PT2		461
+
+/* ~0.4 << CUBIC_SHIFT. */
+#define	CUBIC_C_FACTOR		102
+
+/* CUBIC fast convergence factor: ~0.9 << CUBIC_SHIFT. */
+#define	CUBIC_FC_FACTOR		230
+
+/* Don't trust s_rtt until this many rtt samples have been taken. */
+#define	CUBIC_MIN_RTT_SAMPLES	8
+
+/* Userland only bits. */
+#ifndef _KERNEL
+
+extern int hz;
+
+/*
+ * Implementation based on the formulae found in the CUBIC Internet Draft
+ * "draft-rhee-tcpm-cubic-02".
+ *
+ * Note BETA used in cc_cubic is equal to (1-beta) in the I-D
+ */
+
+static __inline float
+theoretical_cubic_k(double wmax_pkts)
+{
+	double C;
+
+	C = 0.4;
+
+	return (pow((wmax_pkts * 0.2) / C, (1.0 / 3.0)) * pow(2, CUBIC_SHIFT));
+}
+
+static __inline uint32_t
+theoretical_cubic_cwnd(int ticks_since_cong, uint32_t wmax, uint32_t smss)
+{
+	double C, wmax_pkts;
+
+	C = 0.4;
+	wmax_pkts = wmax / (double)smss;
+
+	return (smss * (wmax_pkts +
+	    (C * pow(ticks_since_cong / (double)hz -
+	    theoretical_cubic_k(wmax_pkts) / pow(2, CUBIC_SHIFT), 3.0))));
+}
+
+static __inline uint32_t
+theoretical_reno_cwnd(int ticks_since_cong, int rtt_ticks, uint32_t wmax,
+    uint32_t smss)
+{
+
+	return ((wmax * 0.5) + ((ticks_since_cong / (float)rtt_ticks) * smss));
+}
+
+static __inline uint32_t
+theoretical_tf_cwnd(int ticks_since_cong, int rtt_ticks, unsigned long wmax,
+    uint32_t smss)
+{
+
+	return ((wmax * 0.8) + ((3 * 0.2) / (2 - 0.2) *
+	    (ticks_since_cong / (float)rtt_ticks) * smss));
+}
+
+#endif /* !_KERNEL */
+
+/*
+ * Compute the CUBIC K value used in the cwnd calculation, using an
+ * implementation of eqn 2 in the I-D. The method used
+ * here is adapted from Apple Computer Technical Report #KT-32.
+ */
+static __inline int64_t
+cubic_k(uint32_t wmax_pkts)
+{
+	int64_t s, K;
+	uint16_t p;
+
+	K = s = 0;
+	p = 0;
+
+	/* (wmax * beta)/C with CUBIC_SHIFT worth of precision. */
+	s = ((wmax_pkts * ONE_SUB_CUBIC_BETA) << CUBIC_SHIFT) / CUBIC_C_FACTOR;
+
+	/* Rebase s to be between 1 and 1/8 with a shift of CUBIC_SHIFT. */
+	while (s >= 256) {
+		s >>= 3;
+		p++;
+	}
+
+	/*
+	 * Some magic constants taken from the Apple TR with appropriate
+	 * shifts: 275 == 1.072302 << CUBIC_SHIFT, 98 == 0.3812513 <<
+	 * CUBIC_SHIFT, 120 == 0.46946116 << CUBIC_SHIFT.
+	 */
+	K = (((s * 275) >> CUBIC_SHIFT) + 98) -
+	    (((s * s * 120) >> CUBIC_SHIFT) >> CUBIC_SHIFT);
+
+	/* Multiply by 2^p to undo the rebasing of s from above. */
+	return (K <<= p);
+}
+
+/*
+ * Compute the new cwnd value using an implementation of eqn 1 from the I-D.
+ * Thanks to Kip Macy for help debugging this function.
+ *
+ * XXXLAS: Characterise bounds for overflow.
+ */
+static __inline uint32_t
+cubic_cwnd(int ticks_since_cong, uint32_t wmax, uint32_t smss, int64_t K)
+{
+	int64_t cwnd;
+
+	/* K is in fixed point form with CUBIC_SHIFT worth of precision. */
+
+	/* t - K, with CUBIC_SHIFT worth of precision. */
+	cwnd = ((int64_t)(ticks_since_cong << CUBIC_SHIFT) - (K * hz)) / hz;
+
+	/* (t - K)^3, with CUBIC_SHIFT^3 worth of precision. */
+	cwnd *= (cwnd * cwnd);
+
+	/*
+	 * C(t - K)^3 + wmax
+	 * The down shift by CUBIC_SHIFT_4 is because cwnd has 4 lots of
+	 * CUBIC_SHIFT included in the value. 3 from the cubing of cwnd above,
+	 * and an extra from multiplying through by CUBIC_C_FACTOR.
+	 */
+	cwnd = ((cwnd * CUBIC_C_FACTOR * smss) >> CUBIC_SHIFT_4) + wmax;
+
+	return ((uint32_t)cwnd);
+}
+
+/*
+ * Compute an approximation of the "TCP friendly" cwnd some number of ticks
+ * after a congestion event that is designed to yield the same average cwnd as
+ * NewReno while using CUBIC's beta of 0.8. RTT should be the average RTT
+ * estimate for the path measured over the previous congestion epoch and wmax is
+ * the value of cwnd at the last congestion event.
+ */
+static __inline uint32_t
+tf_cwnd(int ticks_since_cong, int rtt_ticks, uint32_t wmax,
+    uint32_t smss)
+{
+
+	/* Equation 4 of I-D. */
+	return (((wmax * CUBIC_BETA) + (((THREE_X_PT2 * ticks_since_cong *
+	    smss) << CUBIC_SHIFT) / TWO_SUB_PT2 / rtt_ticks)) >> CUBIC_SHIFT);
+}
+
+#endif /* _NETINET_CC_CUBIC_H_ */
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc_module.h	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed by Lawrence Stewart while studying at the Centre
+ * for Advanced Internet Architectures, Swinburne University of Technology, made
+ * possible in part by a grant from the Cisco University Research Program Fund
+ * at Community Foundation Silicon Valley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This software was first released in 2009 by Lawrence Stewart as part of the
+ * NewTCP research project at Swinburne University of Technology's Centre for
+ * Advanced Internet Architectures, Melbourne, Australia, which was made
+ * possible in part by a grant from the Cisco University Research Program Fund
+ * at Community Foundation Silicon Valley. More details are available at:
+ *   http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#ifndef _NETINET_CC_MODULE_H_
+#define	_NETINET_CC_MODULE_H_
+
+#define	CCV(ccv, what) (ccv)->ccvc.tcp->what
+#define	CCSV(ccv, what) (ccv)->ccvc.tcp->tcp_tcps->what
+#define	CCV_PROTO(ccv) (ccv)->ccvc.tcp
+
+#define	CC_ABC(ccv) (ccv)->ccvc.tcp->tcp_tcps->tcps_abc
+#define	CC_ABC_L_VAR(ccv) (ccv)->ccvc.tcp->tcp_tcps->tcps_abc_l_var
+
+#define	TCPTV_SRTTBASE 0
+
+#endif /* _NETINET_CC_MODULE_H_ */
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc_newreno.c	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
+ *	The Regents of the University of California.
+ * Copyright (c) 2007-2008,2010
+ *	Swinburne University of Technology, Melbourne, Australia.
+ * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University of Technology, by Lawrence Stewart, James
+ * Healy and David Hayes, made possible in part by a grant from the Cisco
+ * University Research Program Fund at Community Foundation Silicon Valley.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This software was first released in 2007 by James Healy and Lawrence Stewart
+ * whilst working on the NewTCP research project at Swinburne University of
+ * Technology's Centre for Advanced Internet Architectures, Melbourne,
+ * Australia, which was made possible in part by a grant from the Cisco
+ * University Research Program Fund at Community Foundation Silicon Valley.
+ * More details are available at:
+ *   http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#include <sys/errno.h>
+#include <inet/tcp.h>
+#include <inet/tcp_impl.h>
+#include <inet/cc.h>
+#include <inet/cc/cc_module.h>
+
+static void	newreno_ack_received(struct cc_var *ccv, uint16_t type);
+static void	newreno_after_idle(struct cc_var *ccv);
+static void	newreno_cong_signal(struct cc_var *ccv, uint32_t type);
+static void	newreno_post_recovery(struct cc_var *ccv);
+
+static struct modlmisc cc_newreno_modlmisc = {
+	&mod_miscops,
+	"New Reno Congestion Control"
+};
+
+static struct modlinkage cc_newreno_modlinkage = {
+	MODREV_1,
+	&cc_newreno_modlmisc,
+	NULL
+};
+
+struct cc_algo newreno_cc_algo = {
+	.name = "newreno",
+	.ack_received = newreno_ack_received,
+	.after_idle = newreno_after_idle,
+	.cong_signal = newreno_cong_signal,
+	.post_recovery = newreno_post_recovery,
+};
+
+int
+_init(void)
+{
+	int err;
+
+	if ((err = cc_register_algo(&newreno_cc_algo)) == 0) {
+		if ((err = mod_install(&cc_newreno_modlinkage)) != 0)
+			(void) cc_deregister_algo(&newreno_cc_algo);
+	}
+	return (err);
+}
+
+int
+_fini(void)
+{
+	/* XXX Not unloadable for now */
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&cc_newreno_modlinkage, modinfop));
+}
+
+static void
+newreno_ack_received(struct cc_var *ccv, uint16_t type)
+{
+	if (type == CC_ACK && !IN_RECOVERY(ccv->flags) &&
+	    (ccv->flags & CCF_CWND_LIMITED)) {
+		uint_t cw = CCV(ccv, tcp_cwnd);
+		uint_t incr = CCV(ccv, tcp_mss);
+
+		/*
+		 * Regular in-order ACK, open the congestion window.
+		 * Method depends on which congestion control state we're
+		 * in (slow start or cong avoid) and if ABC (RFC 3465) is
+		 * enabled.
+		 *
+		 * slow start: cwnd <= ssthresh
+		 * cong avoid: cwnd > ssthresh
+		 *
+		 * slow start and ABC (RFC 3465):
+		 *   Grow cwnd exponentially by the amount of data
+		 *   ACKed capping the max increment per ACK to
+		 *   (abc_l_var * maxseg) bytes.
+		 *
+		 * slow start without ABC (RFC 5681):
+		 *   Grow cwnd exponentially by maxseg per ACK.
+		 *
+		 * cong avoid and ABC (RFC 3465):
+		 *   Grow cwnd linearly by maxseg per RTT for each
+		 *   cwnd worth of ACKed data.
+		 *
+		 * cong avoid without ABC (RFC 5681):
+		 *   Grow cwnd linearly by approximately maxseg per RTT using
+		 *   maxseg^2 / cwnd per ACK as the increment.
+		 *   If cwnd > maxseg^2, fix the cwnd increment at 1 byte to
+		 *   avoid capping cwnd.
+		 */
+		if (cw > CCV(ccv, tcp_cwnd_ssthresh)) {
+			if (CC_ABC(ccv)) {
+				if (ccv->flags & CCF_ABC_SENTAWND)
+					ccv->flags &= ~CCF_ABC_SENTAWND;
+				else
+					incr = 0;
+			} else
+				incr = max((incr * incr / cw), 1);
+		} else if (CC_ABC(ccv)) {
+			/*
+			 * In slow-start with ABC enabled and no RTO in sight?
+			 * (Must not use abc_l_var > 1 if slow starting after
+			 * an RTO.
+			 */
+			if (ccv->flags & CCF_RTO) {
+				incr = min(ccv->bytes_this_ack,
+				    CCV(ccv, tcp_mss));
+			} else {
+				incr = min(ccv->bytes_this_ack,
+				    CC_ABC_L_VAR(ccv) * CCV(ccv, tcp_mss));
+			}
+
+		}
+		/* ABC is on by default, so incr equals 0 frequently. */
+		if (incr > 0)
+			CCV(ccv, tcp_cwnd) = min(cw + incr,
+			    TCP_MAXWIN << CCV(ccv, tcp_snd_ws));
+	}
+}
+
+static void
+newreno_after_idle(struct cc_var *ccv)
+{
+	int rw;
+
+	/*
+	 * If we've been idle for more than one retransmit timeout the old
+	 * congestion window is no longer current and we have to reduce it to
+	 * the restart window before we can transmit again.
+	 *
+	 * The restart window is the initial window or the last CWND, whichever
+	 * is smaller.
+	 *
+	 * This is done to prevent us from flooding the path with a full CWND at
+	 * wirespeed, overloading router and switch buffers along the way.
+	 *
+	 * See RFC5681 Section 4.1. "Restarting Idle Connections".
+	 */
+	if (CCV(ccv, tcp_init_cwnd) != 0) {
+		/*
+		 * The TCP_INIT_CWND socket option was used to override the
+		 * default.
+		 */
+		rw = CCV(ccv, tcp_init_cwnd) * CCV(ccv, tcp_mss);
+	} else if (CCSV(ccv, tcps_slow_start_initial) != 0) {
+		/* The _slow_start_initial tunable was explicitly set. */
+		rw = min(TCP_MAX_INIT_CWND, CCSV(ccv, tcps_slow_start_initial))
+		    * CCV(ccv, tcp_mss);
+	} else {
+		/* Do RFC 3390 */
+		rw = min(4 * CCV(ccv, tcp_mss),
+		    max(2 * CCV(ccv, tcp_mss), 4380));
+	}
+
+	CCV(ccv, tcp_cwnd) = min(rw, CCV(ccv, tcp_cwnd));
+}
+
+/*
+ * Perform any necessary tasks before we enter congestion recovery.
+ */
+static void
+newreno_cong_signal(struct cc_var *ccv, uint32_t type)
+{
+	uint32_t cwin, ssthresh_on_loss;
+	uint32_t mss;
+
+	cwin = CCV(ccv, tcp_cwnd);
+	mss = CCV(ccv, tcp_mss);
+	ssthresh_on_loss =
+	    max((CCV(ccv, tcp_snxt) - CCV(ccv, tcp_suna)) / 2 / mss, 2)
+	    * mss;
+
+	/* Catch algos which mistakenly leak private signal types. */
+	ASSERT((type & CC_SIGPRIVMASK) == 0);
+
+	cwin = max(cwin / 2 / mss, 2) * mss;
+
+	switch (type) {
+	case CC_NDUPACK:
+		if (!IN_FASTRECOVERY(ccv->flags)) {
+			if (!IN_CONGRECOVERY(ccv->flags)) {
+				CCV(ccv, tcp_cwnd_ssthresh) = ssthresh_on_loss;
+				CCV(ccv, tcp_cwnd) = cwin;
+			}
+			ENTER_RECOVERY(ccv->flags);
+		}
+		break;
+	case CC_ECN:
+		if (!IN_CONGRECOVERY(ccv->flags)) {
+			CCV(ccv, tcp_cwnd_ssthresh) = ssthresh_on_loss;
+			CCV(ccv, tcp_cwnd) = cwin;
+			ENTER_CONGRECOVERY(ccv->flags);
+		}
+		break;
+	case CC_RTO:
+		CCV(ccv, tcp_cwnd_ssthresh) = ssthresh_on_loss;
+		CCV(ccv, tcp_cwnd) = mss;
+		break;
+	}
+}
+
+/*
+ * Perform any necessary tasks before we exit congestion recovery.
+ */
+static void
+newreno_post_recovery(struct cc_var *ccv)
+{
+	if (IN_FASTRECOVERY(ccv->flags)) {
+		/*
+		 * Fast recovery will conclude after returning from this
+		 * function.
+		 */
+		if (CCV(ccv, tcp_cwnd) > CCV(ccv, tcp_cwnd_ssthresh)) {
+			CCV(ccv, tcp_cwnd) = CCV(ccv, tcp_cwnd_ssthresh);
+		}
+	}
+}
--- /dev/null	Thu Mar  1 14:54:14 2018
+++ new/usr/src/uts/common/inet/cc/cc_sunreno.c	Thu Mar  1 14:54:14 2018
@@ -0,0 +1,223 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 (c) 2017 by Delphix. All rights reserved.
+ */
+
+/*
+ * The TCP congestion control algorithm extracted from the pre-framework
+ * implementation of TCP congestion control.
+ */
+
+#include <sys/errno.h>
+#include <inet/tcp.h>
+#include <inet/tcp_impl.h>
+#include <inet/cc.h>
+#include <inet/cc/cc_module.h>
+
+
+#define	CC_SUNRENO_ALGO_NAME "sunreno"
+
+static struct modlmisc cc_sunreno_modlmisc = {
+	&mod_miscops,
+	"SUNReno Congestion Control"
+};
+
+static struct modlinkage cc_sunreno_modlinkage = {
+	MODREV_1,
+	&cc_sunreno_modlmisc,
+	NULL
+};
+
+static void	sunreno_ack_received(struct cc_var *ccv, uint16_t type);
+static void	sunreno_after_idle(struct cc_var *ccv);
+static void	sunreno_cong_signal(struct cc_var *ccv, uint32_t type);
+static void	sunreno_post_recovery(struct cc_var *ccv);
+
+struct cc_algo sunreno_cc_algo = {
+	.name = CC_SUNRENO_ALGO_NAME,
+	.ack_received = sunreno_ack_received,
+	.after_idle = sunreno_after_idle,
+	.cong_signal = sunreno_cong_signal,
+	.post_recovery = sunreno_post_recovery,
+};
+
+int
+_init(void)
+{
+	int err;
+
+	if ((err = cc_register_algo(&sunreno_cc_algo)) == 0) {
+		if ((err = mod_install(&cc_sunreno_modlinkage)) != 0)
+			(void) cc_deregister_algo(&sunreno_cc_algo);
+	}
+	return (err);
+}
+
+int
+_fini(void)
+{
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&cc_sunreno_modlinkage, modinfop));
+}
+
+static void
+sunreno_ack_received(struct cc_var *ccv, uint16_t type)
+{
+	uint32_t add;
+	uint32_t cwnd;
+	int mss;
+
+	if (type == CC_ACK && !IN_RECOVERY(ccv->flags)) {
+		mss = CCV(ccv, tcp_mss);
+		cwnd = CCV(ccv, tcp_cwnd);
+		add = mss;
+
+		if (cwnd >= CCV(ccv, tcp_cwnd_ssthresh)) {
+			/*
+			 * This is to prevent an increase of less than 1 MSS of
+			 * tcp_cwnd.  With partial increase, tcp_wput_data()
+			 * may send out tinygrams in order to preserve mblk
+			 * boundaries.
+			 *
+			 * By initializing tcp_cwnd_cnt to new tcp_cwnd and
+			 * decrementing it by 1 MSS for every ACKs, tcp_cwnd is
+			 * increased by 1 MSS for every RTTs.
+			 */
+			if (CCV(ccv, tcp_cwnd_cnt) <= 0) {
+				CCV(ccv, tcp_cwnd_cnt) = cwnd + add;
+			} else {
+				CCV(ccv, tcp_cwnd_cnt) -= add;
+				add = 0;
+			}
+		}
+		CCV(ccv, tcp_cwnd) = MIN(cwnd + add, CCV(ccv, tcp_cwnd_max));
+	}
+}
+
+static void
+sunreno_after_idle(struct cc_var *ccv)
+{
+	int32_t	num_sack_blk = 0;
+	int mss;
+
+	if (CCV(ccv, tcp_snd_sack_ok) && CCV(ccv, tcp_num_sack_blk) > 0) {
+		int32_t	opt_len;
+
+		num_sack_blk = MIN(CCV(ccv, tcp_max_sack_blk),
+		    CCV(ccv, tcp_num_sack_blk));
+		opt_len = num_sack_blk * sizeof (sack_blk_t) + TCPOPT_NOP_LEN *
+		    2 + TCPOPT_HEADER_LEN;
+		mss = CCV(ccv, tcp_mss) - opt_len;
+	} else {
+		mss = CCV(ccv, tcp_mss);
+	}
+
+	TCP_SET_INIT_CWND(CCV_PROTO(ccv), mss,
+	    CCSV(ccv, tcps_slow_start_after_idle));
+}
+
+/*
+ * Perform any necessary tasks before we enter congestion recovery.
+ */
+static void
+sunreno_cong_signal(struct cc_var *ccv, uint32_t type)
+{
+	int npkt;
+	int mss;
+
+	/* Catch algos which mistakenly leak private signal types. */
+	ASSERT((type & CC_SIGPRIVMASK) == 0);
+
+	mss = CCV(ccv, tcp_mss);
+	npkt = ((CCV(ccv, tcp_snxt) - CCV(ccv, tcp_suna)) >> 1) / mss;
+
+	switch (type) {
+	case CC_NDUPACK:
+		if (!IN_FASTRECOVERY(ccv->flags)) {
+			if (!IN_CONGRECOVERY(ccv->flags)) {
+				CCV(ccv, tcp_cwnd_ssthresh) = MAX(npkt, 2) *
+				    mss;
+				CCV(ccv, tcp_cwnd) = (npkt +
+				    CCV(ccv, tcp_dupack_cnt)) * mss;
+			}
+			ENTER_RECOVERY(ccv->flags);
+		}
+		break;
+	case CC_ECN:
+		if (!IN_CONGRECOVERY(ccv->flags) && !CCV(ccv, tcp_cwr)) {
+			CCV(ccv, tcp_cwnd_ssthresh) = MAX(npkt, 2) * mss;
+			CCV(ccv, tcp_cwnd) = npkt * mss;
+			if (CCV(ccv, tcp_cwnd) == 0) {
+				/*
+				 * This makes sure that when the ACK comes
+				 * back, we will increase tcp_cwnd by 1 MSS.
+				 */
+				CCV(ccv, tcp_cwnd_cnt) = 0;
+			}
+			ENTER_CONGRECOVERY(ccv->flags);
+		}
+		break;
+	case CC_RTO:
+		/*
+		 * After retransmission, we need to do slow start.  Set the
+		 * ssthresh to one half of current effective window and cwnd to
+		 * one MSS.  Also reset tcp_cwnd_cnt.
+		 *
+		 * Note that if tcp_ssthresh is reduced because of ECN, do not
+		 * reduce it again unless it is already one window of data away
+		 * (tcp_cwr should then be cleared) or this is a timeout for a
+		 * retransmitted segment.
+		 */
+		if (!CCV(ccv, tcp_cwr) || CCV(ccv, tcp_rexmit)) {
+			if (CCV(ccv, tcp_timer_backoff) != 0)
+				npkt = CCV(ccv, tcp_cwnd_ssthresh) / 2 / mss;
+			CCV(ccv, tcp_cwnd_ssthresh) = MAX(npkt, 2) * mss;
+		}
+		CCV(ccv, tcp_cwnd) = mss;
+		CCV(ccv, tcp_cwnd_cnt) = 0;
+		break;
+	}
+}
+
+/*
+ * Perform any necessary tasks before we exit congestion recovery.
+ */
+static void
+sunreno_post_recovery(struct cc_var *ccv)
+{
+	/*
+	 * Restore the congestion window back to ssthresh as per RFC 5681
+	 * section 3.2.
+	 */
+	if (IN_FASTRECOVERY(ccv->flags)) {
+		if (CCV(ccv, tcp_cwnd) > CCV(ccv, tcp_cwnd_ssthresh)) {
+			CCV(ccv, tcp_cwnd) = CCV(ccv, tcp_cwnd_ssthresh);
+		}
+	}
+	CCV(ccv, tcp_cwnd_cnt) = 0;
+}
--- old/usr/src/uts/common/inet/ip/ip.c	Thu Mar  1 14:54:15 2018
+++ new/usr/src/uts/common/inet/ip/ip.c	Thu Mar  1 14:54:15 2018
@@ -95,6 +95,7 @@
 #include <netinet/igmp.h>
 #include <netinet/ip_mroute.h>
 #include <inet/ipp_common.h>
+#include <inet/cc.h>
 
 #include <net/pfkeyv2.h>
 #include <inet/sadb.h>
@@ -4567,6 +4568,8 @@
 #endif
 	ip_poll_normal_ticks = MSEC_TO_TICK_ROUNDUP(ip_poll_normal_ms);
 
+	cc_init();
+
 	ipcl_g_init();
 	ip_ire_g_init();
 	ip_net_g_init();
@@ -9638,6 +9641,9 @@
 		if ((mpctl = udp_snmp_get(q, mpctl, legacy_req)) == NULL) {
 			return (1);
 		}
+		if (level == MIB2_UDP) {
+			goto done;
+		}
 	}
 
 	if (level != MIB2_UDP) {
@@ -9644,6 +9650,9 @@
 		if ((mpctl = tcp_snmp_get(q, mpctl, legacy_req)) == NULL) {
 			return (1);
 		}
+		if (level == MIB2_TCP) {
+			goto done;
+		}
 	}
 
 	if ((mpctl = ip_snmp_get_mib2_ip_traffic_stats(q, mpctl,
@@ -9720,6 +9729,7 @@
 	if ((mpctl = ip_snmp_get_mib2_ip_dce(q, mpctl, ipst)) == NULL) {
 		return (1);
 	}
+done:
 	freemsg(mpctl);
 	return (1);
 }
--- old/usr/src/uts/common/inet/mib2.h	Thu Mar  1 14:54:15 2018
+++ new/usr/src/uts/common/inet/mib2.h	Thu Mar  1 14:54:15 2018
@@ -20,7 +20,10 @@
  *
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-/* Copyright (c) 1990 Mentat Inc. */
+/*
+ * Copyright (c) 1990 Mentat Inc.
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ */
 
 #ifndef	_INET_MIB2_H
 #define	_INET_MIB2_H
@@ -1354,25 +1357,46 @@
 		/* remote port for this connection	{ tcpConnEntry 5 } */
 	int		tcpConnRemPort;		/* In host byte order */
 	struct tcpConnEntryInfo_s {
-			/* seq # of next segment to send */
+		Counter64	ce_in_data_inorder_bytes;
+		Counter64	ce_in_data_inorder_segs;
+		Counter64	ce_in_data_unorder_bytes;
+		Counter64	ce_in_data_unorder_segs;
+		Counter64	ce_in_zwnd_probes;
+
+		Counter64	ce_out_data_bytes;
+		Counter64	ce_out_data_segs;
+		Counter64	ce_out_retrans_bytes;
+		Counter64	ce_out_retrans_segs;
+		Counter64	ce_out_zwnd_probes;
+		Counter64	ce_rtt_sum;
+
+				/* seq # of next segment to send */
 		Gauge		ce_snxt;
 				/* seq # of of last segment unacknowledged */
 		Gauge		ce_suna;
-				/* currect send window size */
+				/* current send window size */
 		Gauge		ce_swnd;
+				/* current congestion window size */
+		Gauge		ce_cwnd;
 				/* seq # of next expected segment */
 		Gauge		ce_rnxt;
 				/* seq # of last ack'd segment */
 		Gauge		ce_rack;
-				/* currenct receive window size */
+				/* # of unsent bytes in the xmit queue */
+		Gauge		ce_unsent;
+				/* current receive window size */
 		Gauge		ce_rwnd;
-					/* current rto (retransmit timeout) */
+				/* round-trip time smoothed average (us) */
+		Gauge		ce_rtt_sa;
+				/* current rto (retransmit timeout) */
 		Gauge		ce_rto;
-					/* current max segment size */
+				/* round-trip time count */
+		Gauge		ce_rtt_cnt;
+				/* current max segment size */
 		Gauge		ce_mss;
 				/* actual internal state */
 		int		ce_state;
-	} 		tcpConnEntryInfo;
+	}		tcpConnEntryInfo;
 
 	/* pid of the processes that created this connection */
 	uint32_t	tcpConnCreationProcess;
@@ -1408,26 +1432,7 @@
 	DeviceIndex	tcp6ConnIfIndex;
 	/* state of tcp6 connection		{ ipv6TcpConnEntry 6 } RW */
 	int		tcp6ConnState;
-	struct tcp6ConnEntryInfo_s {
-			/* seq # of next segment to send */
-		Gauge		ce_snxt;
-				/* seq # of of last segment unacknowledged */
-		Gauge		ce_suna;
-				/* currect send window size */
-		Gauge		ce_swnd;
-				/* seq # of next expected segment */
-		Gauge		ce_rnxt;
-				/* seq # of last ack'd segment */
-		Gauge		ce_rack;
-				/* currenct receive window size */
-		Gauge		ce_rwnd;
-					/* current rto (retransmit timeout) */
-		Gauge		ce_rto;
-					/* current max segment size */
-		Gauge		ce_mss;
-				/* actual internal state */
-		int		ce_state;
-	} 		tcp6ConnEntryInfo;
+	struct tcpConnEntryInfo_s tcp6ConnEntryInfo;
 
 	/* pid of the processes that created this connection */
 	uint32_t	tcp6ConnCreationProcess;
--- old/usr/src/uts/common/inet/tcp.h	Thu Mar  1 14:54:15 2018
+++ new/usr/src/uts/common/inet/tcp.h	Thu Mar  1 14:54:15 2018
@@ -22,7 +22,7 @@
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2015 Joyent, Inc.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2014, 2017 by Delphix. All rights reserved.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
@@ -46,6 +46,7 @@
 #include <inet/mib2.h>
 #include <inet/tcp_stack.h>
 #include <inet/tcp_sack.h>
+#include <inet/cc.h>
 
 /* TCP states */
 #define	TCPS_CLOSED		-6
@@ -153,6 +154,9 @@
 	struct conn_s	*tcp_connp;	/* back pointer to conn_t */
 	tcp_stack_t	*tcp_tcps;	/* back pointer to tcp_stack_t */
 
+	struct cc_algo	*tcp_cc_algo;	/* congestion control algorithm */
+	struct cc_var	tcp_ccv;	/* congestion control specific vars */
+
 	int32_t	tcp_state;
 	int32_t	tcp_rcv_ws;		/* My window scale power */
 	int32_t	tcp_snd_ws;		/* Sender's window scale power */
@@ -178,16 +182,11 @@
 	mblk_t	*tcp_xmit_tail;		/* Last data sent */
 	uint32_t tcp_unsent;		/* # of bytes in hand that are unsent */
 	uint32_t tcp_xmit_tail_unsent;	/* # of unsent bytes in xmit_tail */
-
 	uint32_t tcp_suna;		/* Sender unacknowledged */
 	uint32_t tcp_rexmit_nxt;	/* Next rexmit seq num */
 	uint32_t tcp_rexmit_max;	/* Max retran seq num */
 	uint32_t tcp_cwnd;		/* Congestion window */
 	int32_t tcp_cwnd_cnt;		/* cwnd cnt in congestion avoidance */
-
-	uint32_t tcp_ibsegs;		/* Inbound segments on this stream */
-	uint32_t tcp_obsegs;		/* Outbound segments on this stream */
-
 	uint32_t tcp_naglim;		/* Tunable nagle limit */
 	uint32_t	tcp_valid_bits;
 #define	TCP_ISS_VALID	0x1	/* Is the tcp_iss seq num active? */
@@ -195,8 +194,6 @@
 #define	TCP_URG_VALID	0x4	/* Is the tcp_urg seq num active? */
 #define	TCP_OFO_FIN_VALID 0x8	/* Has TCP received an out of order FIN? */
 
-
-
 	timeout_id_t	tcp_timer_tid;	/* Control block for timer service */
 	uchar_t	tcp_timer_backoff;	/* Backoff shift count. */
 	int64_t tcp_last_recv_time;	/* Last time we receive a segment. */
@@ -283,9 +280,11 @@
 	uint32_t tcp_cwnd_max;
 	uint32_t tcp_csuna;		/* Clear (no rexmits in window) suna */
 
-	clock_t	tcp_rtt_sa;		/* Round trip smoothed average */
-	clock_t	tcp_rtt_sd;		/* Round trip smoothed deviation */
-	clock_t	tcp_rtt_update;		/* Round trip update(s) */
+	hrtime_t tcp_rtt_sum;		/* Round trip sum */
+	uint32_t tcp_rtt_cnt;		/* Round trip count (non_dup ACKs) */
+	hrtime_t tcp_rtt_sa;		/* Round trip smoothed average */
+	hrtime_t tcp_rtt_sd;		/* Round trip smoothed deviation */
+	uint32_t tcp_rtt_update;	/* Round trip update(s) */
 	clock_t tcp_ms_we_have_waited;	/* Total retrans time */
 
 	uint32_t tcp_swl1;		/* These help us avoid using stale */
@@ -501,6 +500,8 @@
 	/* FIN-WAIT-2 flush timeout */
 	uint32_t		tcp_fin_wait_2_flush_interval;
 
+	tcp_conn_stats_t	tcp_cs;
+
 #ifdef DEBUG
 	pc_t			tcmp_stk[15];
 #endif
@@ -514,10 +515,10 @@
 #endif
 
 extern void	tcp_conn_reclaim(void *);
-extern void 	tcp_free(tcp_t *tcp);
+extern void	tcp_free(tcp_t *tcp);
 extern void	tcp_ddi_g_init(void);
 extern void	tcp_ddi_g_destroy(void);
-extern void 	*tcp_get_conn(void *arg, tcp_stack_t *);
+extern conn_t	*tcp_get_conn(void *arg, tcp_stack_t *);
 extern mblk_t	*tcp_snmp_get(queue_t *, mblk_t *, boolean_t);
 extern int	tcp_snmp_set(queue_t *, int, int, uchar_t *, int len);
 
--- old/usr/src/uts/common/inet/tcp/tcp.c	Thu Mar  1 14:54:15 2018
+++ new/usr/src/uts/common/inet/tcp/tcp.c	Thu Mar  1 14:54:15 2018
@@ -23,7 +23,7 @@
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2017 Joyent, Inc.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2013,2014 by Delphix. All rights reserved.
+ * Copyright (c) 2013, 2017 by Delphix. All rights reserved.
  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
  */
 /* Copyright (c) 1990 Mentat Inc. */
@@ -74,6 +74,7 @@
 #include <inet/ipsec_impl.h>
 
 #include <inet/common.h>
+#include <inet/cc.h>
 #include <inet/ip.h>
 #include <inet/ip_impl.h>
 #include <inet/ip6.h>
@@ -266,8 +267,6 @@
 /*
  * Functions called directly via squeue having a prototype of edesc_t.
  */
-void		tcp_input_listener(void *arg, mblk_t *mp, void *arg2,
-    ip_recv_attr_t *ira);
 void		tcp_input_data(void *arg, mblk_t *mp, void *arg2,
     ip_recv_attr_t *ira);
 static void	tcp_linger_interrupted(void *arg, mblk_t *mp, void *arg2,
@@ -576,7 +575,37 @@
 	ASSERT(connp->conn_ref == 1);
 }
 
+#pragma inline(tcp_calculate_rto)
+
 /*
+ * RTO = average estimates (sa / 8) + 4 * deviation estimates (sd)
+ *
+ * Add tcp_rexmit_interval extra in case of extreme environment where the
+ * algorithm fails to work.  The default value of tcp_rexmit_interval_extra
+ * should be 0.
+ *
+ * As we use a finer grained clock than BSD and update RTO for every ACKs, add
+ * in another .25 of RTT to the deviation of RTO to accommodate burstiness of
+ * 1/4 of window size.
+ */
+clock_t
+tcp_calculate_rto(tcp_t *tcp, tcp_stack_t *tcps)
+{
+	clock_t rto;
+
+	rto = NSEC2MSEC((tcp->tcp_rtt_sa >> 3) + (tcp->tcp_rtt_sa >> 5) +
+	    tcp->tcp_rtt_sd) + tcps->tcps_rexmit_interval_extra +
+	    tcps->tcps_conn_grace_period;
+
+	if (rto < tcp->tcp_rto_min)
+		rto = tcp->tcp_rto_min;
+	else if (rto > tcp->tcp_rto_max)
+		rto = tcp->tcp_rto_max;
+
+	return (rto);
+}
+
+/*
  * Adapt to the information, such as rtt and rtt_sd, provided from the
  * DCE and IRE maintained by IP.
  *
@@ -640,15 +669,9 @@
 	tcp->tcp_localnet = uinfo.iulp_localnet;
 
 	if (uinfo.iulp_rtt != 0) {
-		clock_t	rto;
-
-		tcp->tcp_rtt_sa = uinfo.iulp_rtt;
-		tcp->tcp_rtt_sd = uinfo.iulp_rtt_sd;
-		rto = (tcp->tcp_rtt_sa >> 3) + tcp->tcp_rtt_sd +
-		    tcps->tcps_rexmit_interval_extra +
-		    (tcp->tcp_rtt_sa >> 5);
-
-		TCP_SET_RTO(tcp, rto);
+		tcp->tcp_rtt_sa = MSEC2NSEC(uinfo.iulp_rtt);
+		tcp->tcp_rtt_sd = MSEC2NSEC(uinfo.iulp_rtt_sd);
+		tcp->tcp_rto = tcp_calculate_rto(tcp, tcps);
 	}
 	if (uinfo.iulp_ssthresh != 0)
 		tcp->tcp_cwnd_ssthresh = uinfo.iulp_ssthresh;
@@ -1238,11 +1261,6 @@
 	if (!TCP_IS_SOCKET(tcp))
 		tcp_acceptor_hash_remove(tcp);
 
-	TCPS_UPDATE_MIB(tcps, tcpHCInSegs, tcp->tcp_ibsegs);
-	tcp->tcp_ibsegs = 0;
-	TCPS_UPDATE_MIB(tcps, tcpHCOutSegs, tcp->tcp_obsegs);
-	tcp->tcp_obsegs = 0;
-
 	/*
 	 * This can be called via tcp_time_wait_processing() if TCP gets a
 	 * SYN with sequence number outside the TIME-WAIT connection's
@@ -1421,6 +1439,10 @@
 	 */
 	tcp_close_mpp(&tcp->tcp_conn.tcp_eager_conn_ind);
 
+	/* Allow the CC algorithm to clean up after itself. */
+	if (tcp->tcp_cc_algo != NULL && tcp->tcp_cc_algo->cb_destroy != NULL)
+		tcp->tcp_cc_algo->cb_destroy(&tcp->tcp_ccv);
+
 	/*
 	 * Destroy any association with SO_REUSEPORT group.
 	 */
@@ -1482,7 +1504,7 @@
  * collector will free up the freelist is the connection ends up sitting
  * there for too long.
  */
-void *
+conn_t *
 tcp_get_conn(void *arg, tcp_stack_t *tcps)
 {
 	tcp_t			*tcp = NULL;
@@ -1521,7 +1543,7 @@
 		connp->conn_recv = tcp_input_data;
 		ASSERT(connp->conn_recvicmp == tcp_icmp_input);
 		ASSERT(connp->conn_verifyicmp == tcp_verifyicmp);
-		return ((void *)connp);
+		return (connp);
 	}
 	mutex_exit(&tcp_time_wait->tcp_time_wait_lock);
 	/*
@@ -1556,7 +1578,7 @@
 	connp->conn_ixa->ixa_notify = tcp_notify;
 	connp->conn_ixa->ixa_notify_cookie = tcp;
 
-	return ((void *)connp);
+	return (connp);
 }
 
 /*
@@ -1926,15 +1948,6 @@
 	/* Cancel outstanding timers */
 	tcp_timers_stop(tcp);
 
-	/*
-	 * Reset everything in the state vector, after updating global
-	 * MIB data from instance counters.
-	 */
-	TCPS_UPDATE_MIB(tcps, tcpHCInSegs, tcp->tcp_ibsegs);
-	tcp->tcp_ibsegs = 0;
-	TCPS_UPDATE_MIB(tcps, tcpHCOutSegs, tcp->tcp_obsegs);
-	tcp->tcp_obsegs = 0;
-
 	tcp_close_mpp(&tcp->tcp_xmit_head);
 	if (tcp->tcp_snd_zcopy_aware)
 		tcp_zcopy_notify(tcp);
@@ -2106,9 +2119,6 @@
 	tcp->tcp_swnd = 0;
 	DONTCARE(tcp->tcp_cwnd);	/* Init in tcp_process_options */
 
-	ASSERT(tcp->tcp_ibsegs == 0);
-	ASSERT(tcp->tcp_obsegs == 0);
-
 	if (connp->conn_ht_iphc != NULL) {
 		kmem_free(connp->conn_ht_iphc, connp->conn_ht_iphc_allocated);
 		connp->conn_ht_iphc = NULL;
@@ -2200,6 +2210,8 @@
 	DONTCARE(tcp->tcp_rtt_sa);		/* Init in tcp_init_values */
 	DONTCARE(tcp->tcp_rtt_sd);		/* Init in tcp_init_values */
 	tcp->tcp_rtt_update = 0;
+	tcp->tcp_rtt_sum = 0;
+	tcp->tcp_rtt_cnt = 0;
 
 	DONTCARE(tcp->tcp_swl1); /* Init in case TCPS_LISTEN/TCPS_SYN_SENT */
 	DONTCARE(tcp->tcp_swl2); /* Init in case TCPS_LISTEN/TCPS_SYN_SENT */
@@ -2335,6 +2347,11 @@
 	ASSERT(tcp->tcp_listen_cnt == NULL);
 	ASSERT(tcp->tcp_reass_tid == 0);
 
+	/* Allow the CC algorithm to clean up after itself. */
+	if (tcp->tcp_cc_algo->cb_destroy != NULL)
+		tcp->tcp_cc_algo->cb_destroy(&tcp->tcp_ccv);
+	tcp->tcp_cc_algo = NULL;
+
 #undef	DONTCARE
 #undef	PRESERVE
 }
@@ -2348,7 +2365,6 @@
 {
 	tcp_stack_t	*tcps = tcp->tcp_tcps;
 	conn_t		*connp = tcp->tcp_connp;
-	clock_t		rto;
 
 	ASSERT((connp->conn_family == AF_INET &&
 	    connp->conn_ipversion == IPV4_VERSION) ||
@@ -2356,7 +2372,12 @@
 	    (connp->conn_ipversion == IPV4_VERSION ||
 	    connp->conn_ipversion == IPV6_VERSION)));
 
+	tcp->tcp_ccv.type = IPPROTO_TCP;
+	tcp->tcp_ccv.ccvc.tcp = tcp;
+
 	if (parent == NULL) {
+		tcp->tcp_cc_algo = tcps->tcps_default_cc_algo;
+
 		tcp->tcp_naglim = tcps->tcps_naglim_def;
 
 		tcp->tcp_rto_initial = tcps->tcps_rexmit_interval_initial;
@@ -2384,6 +2405,8 @@
 		 */
 	} else {
 		/* Inherit various TCP parameters from the parent. */
+		tcp->tcp_cc_algo = parent->tcp_cc_algo;
+
 		tcp->tcp_naglim = parent->tcp_naglim;
 
 		tcp->tcp_rto_initial = parent->tcp_rto_initial;
@@ -2410,6 +2433,9 @@
 		tcp->tcp_init_cwnd = parent->tcp_init_cwnd;
 	}
 
+	if (tcp->tcp_cc_algo->cb_init != NULL)
+		VERIFY(tcp->tcp_cc_algo->cb_init(&tcp->tcp_ccv) == 0);
+
 	/*
 	 * Initialize tcp_rtt_sa and tcp_rtt_sd so that the calculated RTO
 	 * will be close to tcp_rexmit_interval_initial.  By doing this, we
@@ -2417,12 +2443,9 @@
 	 * during first few transmissions of a connection as seen in slow
 	 * links.
 	 */
-	tcp->tcp_rtt_sa = tcp->tcp_rto_initial << 2;
-	tcp->tcp_rtt_sd = tcp->tcp_rto_initial >> 1;
-	rto = (tcp->tcp_rtt_sa >> 3) + tcp->tcp_rtt_sd +
-	    tcps->tcps_rexmit_interval_extra + (tcp->tcp_rtt_sa >> 5) +
-	    tcps->tcps_conn_grace_period;
-	TCP_SET_RTO(tcp, rto);
+	tcp->tcp_rtt_sa = MSEC2NSEC(tcp->tcp_rto_initial) << 2;
+	tcp->tcp_rtt_sd = MSEC2NSEC(tcp->tcp_rto_initial) >> 1;
+	tcp->tcp_rto = tcp_calculate_rto(tcp, tcps);
 
 	tcp->tcp_timer_backoff = 0;
 	tcp->tcp_ms_we_have_waited = 0;
@@ -2659,7 +2682,7 @@
 	}
 
 	sqp = IP_SQUEUE_GET((uint_t)gethrtime());
-	connp = (conn_t *)tcp_get_conn(sqp, tcps);
+	connp = tcp_get_conn(sqp, tcps);
 	/*
 	 * Both tcp_get_conn and netstack_find_by_cred incremented refcnt,
 	 * so we drop it by one.
@@ -3847,6 +3870,9 @@
 	list_create(&tcps->tcps_listener_conf, sizeof (tcp_listener_t),
 	    offsetof(tcp_listener_t, tl_link));
 
+	tcps->tcps_default_cc_algo = cc_load_algo(CC_DEFAULT_ALGO_NAME);
+	ASSERT3P(tcps->tcps_default_cc_algo, !=, NULL);
+
 	return (tcps);
 }
 
--- old/usr/src/uts/common/inet/tcp/tcp_fusion.c	Thu Mar  1 14:54:16 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_fusion.c	Thu Mar  1 14:54:16 2018
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -645,15 +646,17 @@
 	peer_tcp->tcp_rack = peer_tcp->tcp_rnxt;
 
 	TCPS_BUMP_MIB(tcps, tcpOutDataSegs);
+	TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 	TCPS_UPDATE_MIB(tcps, tcpOutDataBytes, send_size);
+	tcp->tcp_cs.tcp_out_data_bytes += send_size;
+	tcp->tcp_cs.tcp_out_data_segs++;
 
 	TCPS_BUMP_MIB(tcps, tcpHCInSegs);
 	TCPS_BUMP_MIB(tcps, tcpInDataInorderSegs);
 	TCPS_UPDATE_MIB(tcps, tcpInDataInorderBytes, send_size);
+	peer_tcp->tcp_cs.tcp_in_data_inorder_bytes += send_size;
+	peer_tcp->tcp_cs.tcp_in_data_inorder_segs++;
 
-	BUMP_LOCAL(tcp->tcp_obsegs);
-	BUMP_LOCAL(peer_tcp->tcp_ibsegs);
-
 	DTRACE_TCP5(send, void, NULL, ip_xmit_attr_t *, connp->conn_ixa,
 	    __dtrace_tcp_void_ip_t *, NULL, tcp_t *, tcp,
 	    __dtrace_tcp_tcph_t *, NULL);
--- old/usr/src/uts/common/inet/tcp/tcp_input.c	Thu Mar  1 14:54:16 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_input.c	Thu Mar  1 14:54:16 2018
@@ -23,7 +23,7 @@
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright 2017 Joyent, Inc.
- * Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
  */
 
 /* This file contains all TCP input processing functions. */
@@ -166,10 +166,137 @@
 static mblk_t	*tcp_reass(tcp_t *, mblk_t *, uint32_t);
 static void	tcp_reass_elim_overlap(tcp_t *, mblk_t *);
 static void	tcp_rsrv_input(void *, mblk_t *, void *, ip_recv_attr_t *);
-static void	tcp_set_rto(tcp_t *, time_t);
+static void	tcp_set_rto(tcp_t *, hrtime_t);
 static void	tcp_setcred_data(mblk_t *, ip_recv_attr_t *);
 
 /*
+ * CC wrapper hook functions
+ */
+static void
+cc_ack_received(tcp_t *tcp, uint32_t seg_ack, int32_t bytes_acked,
+    uint16_t type)
+{
+	uint32_t old_cwnd = tcp->tcp_cwnd;
+
+	tcp->tcp_ccv.bytes_this_ack = bytes_acked;
+	if (tcp->tcp_cwnd <= tcp->tcp_swnd)
+		tcp->tcp_ccv.flags |= CCF_CWND_LIMITED;
+	else
+		tcp->tcp_ccv.flags &= ~CCF_CWND_LIMITED;
+
+	if (type == CC_ACK) {
+		if (tcp->tcp_cwnd > tcp->tcp_cwnd_ssthresh) {
+			if (tcp->tcp_ccv.flags & CCF_RTO)
+				tcp->tcp_ccv.flags &= ~CCF_RTO;
+
+			tcp->tcp_ccv.t_bytes_acked +=
+			    min(tcp->tcp_ccv.bytes_this_ack,
+			    tcp->tcp_tcps->tcps_abc_l_var * tcp->tcp_mss);
+			if (tcp->tcp_ccv.t_bytes_acked >= tcp->tcp_cwnd) {
+				tcp->tcp_ccv.t_bytes_acked -= tcp->tcp_cwnd;
+				tcp->tcp_ccv.flags |= CCF_ABC_SENTAWND;
+			}
+		} else {
+			tcp->tcp_ccv.flags &= ~CCF_ABC_SENTAWND;
+			tcp->tcp_ccv.t_bytes_acked = 0;
+		}
+	}
+
+	if (CC_ALGO(tcp)->ack_received != NULL) {
+		/*
+		 * The FreeBSD code where this originated had a comment "Find
+		 * a way to live without this" in several places where curack
+		 * got set.  If they eventually dump curack from from the cc
+		 * variables, we'll need to adapt our code.
+		 */
+		tcp->tcp_ccv.curack = seg_ack;
+		CC_ALGO(tcp)->ack_received(&tcp->tcp_ccv, type);
+	}
+
+	DTRACE_PROBE3(cwnd__cc__ack__received, tcp_t *, tcp, uint32_t, old_cwnd,
+	    uint32_t, tcp->tcp_cwnd);
+}
+
+void
+cc_cong_signal(tcp_t *tcp, uint32_t seg_ack, uint32_t type)
+{
+	uint32_t old_cwnd = tcp->tcp_cwnd;
+	uint32_t old_cwnd_ssthresh = tcp->tcp_cwnd_ssthresh;
+	switch (type) {
+	case CC_NDUPACK:
+		if (!IN_FASTRECOVERY(tcp->tcp_ccv.flags)) {
+			tcp->tcp_rexmit_max = tcp->tcp_snxt;
+			if (tcp->tcp_ecn_ok) {
+				tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
+				tcp->tcp_cwr = B_TRUE;
+				tcp->tcp_ecn_cwr_sent = B_FALSE;
+			}
+		}
+		break;
+	case CC_ECN:
+		if (!IN_CONGRECOVERY(tcp->tcp_ccv.flags)) {
+			tcp->tcp_rexmit_max = tcp->tcp_snxt;
+			if (tcp->tcp_ecn_ok) {
+				tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
+				tcp->tcp_cwr = B_TRUE;
+				tcp->tcp_ecn_cwr_sent = B_FALSE;
+			}
+		}
+		break;
+	case CC_RTO:
+		tcp->tcp_ccv.flags |= CCF_RTO;
+		tcp->tcp_dupack_cnt = 0;
+		tcp->tcp_ccv.t_bytes_acked = 0;
+		/*
+		 * Give up on fast recovery and congestion recovery if we were
+		 * attempting either.
+		 */
+		EXIT_RECOVERY(tcp->tcp_ccv.flags);
+		if (CC_ALGO(tcp)->cong_signal == NULL) {
+			/*
+			 * RFC5681 Section 3.1
+			 * ssthresh = max (FlightSize / 2, 2*SMSS) eq (4)
+			 */
+			tcp->tcp_cwnd_ssthresh = max(
+			    (tcp->tcp_snxt - tcp->tcp_suna) / 2 / tcp->tcp_mss,
+			    2) * tcp->tcp_mss;
+			tcp->tcp_cwnd = tcp->tcp_mss;
+		}
+
+		if (tcp->tcp_ecn_ok) {
+			tcp->tcp_cwr = B_TRUE;
+			tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
+			tcp->tcp_ecn_cwr_sent = B_FALSE;
+		}
+		break;
+	}
+
+	if (CC_ALGO(tcp)->cong_signal != NULL) {
+		tcp->tcp_ccv.curack = seg_ack;
+		CC_ALGO(tcp)->cong_signal(&tcp->tcp_ccv, type);
+	}
+
+	DTRACE_PROBE6(cwnd__cc__cong__signal, tcp_t *, tcp, uint32_t, old_cwnd,
+	    uint32_t, tcp->tcp_cwnd, uint32_t, old_cwnd_ssthresh,
+	    uint32_t, tcp->tcp_cwnd_ssthresh, uint32_t, type);
+}
+
+static void
+cc_post_recovery(tcp_t *tcp, uint32_t seg_ack)
+{
+	uint32_t old_cwnd = tcp->tcp_cwnd;
+
+	if (CC_ALGO(tcp)->post_recovery != NULL) {
+		tcp->tcp_ccv.curack = seg_ack;
+		CC_ALGO(tcp)->post_recovery(&tcp->tcp_ccv);
+	}
+	tcp->tcp_ccv.t_bytes_acked = 0;
+
+	DTRACE_PROBE3(cwnd__cc__post__recovery, tcp_t *, tcp,
+	    uint32_t, old_cwnd, uint32_t, tcp->tcp_cwnd);
+}
+
+/*
  * Set the MSS associated with a particular tcp based on its current value,
  * and a new one passed in. Observe minimums and maximums, and reset other
  * state variables that we want to view as multiples of MSS.
@@ -548,6 +675,9 @@
 	 * updated properly.
 	 */
 	TCP_SET_INIT_CWND(tcp, tcp->tcp_mss, tcps->tcps_slow_start_initial);
+
+	if (tcp->tcp_cc_algo->conn_init != NULL)
+		tcp->tcp_cc_algo->conn_init(&tcp->tcp_ccv);
 }
 
 /*
@@ -559,7 +689,7 @@
 static mblk_t *
 tcp_reass(tcp_t *tcp, mblk_t *mp, uint32_t start)
 {
-	uint32_t	end;
+	uint32_t	end, bytes;
 	mblk_t		*mp1;
 	mblk_t		*mp2;
 	mblk_t		*next_mp;
@@ -578,28 +708,28 @@
 			freeb(mp);
 			continue;
 		}
+		bytes = end - start;
 		mp->b_cont = NULL;
 		TCP_REASS_SET_SEQ(mp, start);
 		TCP_REASS_SET_END(mp, end);
 		mp1 = tcp->tcp_reass_tail;
-		if (!mp1) {
+		if (mp1 == NULL || SEQ_GEQ(start, TCP_REASS_END(mp1))) {
+			if (mp1 != NULL) {
+				/*
+				 * New stuff is beyond the tail; link it on the
+				 * end.
+				 */
+				mp1->b_cont = mp;
+			} else {
+				tcp->tcp_reass_head = mp;
+			}
 			tcp->tcp_reass_tail = mp;
-			tcp->tcp_reass_head = mp;
 			TCPS_BUMP_MIB(tcps, tcpInDataUnorderSegs);
-			TCPS_UPDATE_MIB(tcps, tcpInDataUnorderBytes,
-			    end - start);
+			TCPS_UPDATE_MIB(tcps, tcpInDataUnorderBytes, bytes);
+			tcp->tcp_cs.tcp_in_data_unorder_segs++;
+			tcp->tcp_cs.tcp_in_data_unorder_bytes += bytes;
 			continue;
 		}
-		/* New stuff completely beyond tail? */
-		if (SEQ_GEQ(start, TCP_REASS_END(mp1))) {
-			/* Link it on end. */
-			mp1->b_cont = mp;
-			tcp->tcp_reass_tail = mp;
-			TCPS_BUMP_MIB(tcps, tcpInDataUnorderSegs);
-			TCPS_UPDATE_MIB(tcps, tcpInDataUnorderBytes,
-			    end - start);
-			continue;
-		}
 		mp1 = tcp->tcp_reass_head;
 		u1 = TCP_REASS_SEQ(mp1);
 		/* New stuff at the front? */
@@ -2324,8 +2454,6 @@
 	ip_pkt_t	ipp;
 	boolean_t	ofo_seg = B_FALSE; /* Out of order segment */
 	uint32_t	cwnd;
-	uint32_t	add;
-	int		npkt;
 	int		mss;
 	conn_t		*connp = (conn_t *)arg;
 	squeue_t	*sqp = (squeue_t *)arg2;
@@ -2414,7 +2542,7 @@
 
 	flags = (unsigned int)tcpha->tha_flags & 0xFF;
 
-	BUMP_LOCAL(tcp->tcp_ibsegs);
+	TCPS_BUMP_MIB(tcps, tcpHCInSegs);
 	DTRACE_PROBE2(tcp__trace__recv, mblk_t *, mp, tcp_t *, tcp);
 
 	if ((flags & TH_URG) && sqp != NULL) {
@@ -2601,6 +2729,9 @@
 				 * draft-floyd-incr-init-win-01.txt,
 				 * Increasing TCP's Initial Window.
 				 */
+				DTRACE_PROBE3(cwnd__retransmitted__syn,
+				    tcp_t *, tcp, uint32_t, tcp->tcp_cwnd,
+				    uint32_t, tcp->tcp_mss);
 				tcp->tcp_cwnd = tcp->tcp_mss;
 			}
 
@@ -2659,7 +2790,7 @@
 						tcp->tcp_ack_tid = 0;
 					}
 					tcp_send_data(tcp, ack_mp);
-					BUMP_LOCAL(tcp->tcp_obsegs);
+					TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 					TCPS_BUMP_MIB(tcps, tcpOutAck);
 
 					if (!IPCL_IS_NONSTR(connp)) {
@@ -3048,6 +3179,7 @@
 
 		if (tcp->tcp_rwnd == 0) {
 			TCPS_BUMP_MIB(tcps, tcpInWinProbe);
+			tcp->tcp_cs.tcp_in_zwnd_probes++;
 		} else {
 			TCPS_BUMP_MIB(tcps, tcpInDataPastWinSegs);
 			TCPS_UPDATE_MIB(tcps, tcpInDataPastWinBytes, -rgap);
@@ -3297,6 +3429,9 @@
 	} else if (seg_len > 0) {
 		TCPS_BUMP_MIB(tcps, tcpInDataInorderSegs);
 		TCPS_UPDATE_MIB(tcps, tcpInDataInorderBytes, seg_len);
+		tcp->tcp_cs.tcp_in_data_inorder_segs++;
+		tcp->tcp_cs.tcp_in_data_inorder_bytes += seg_len;
+
 		/*
 		 * If an out of order FIN was received before, and the seq
 		 * num and len of the new segment match that of the FIN,
@@ -3362,7 +3497,7 @@
 	 * and TCP_OLD_URP_INTERPRETATION is set. This implies that the urgent
 	 * byte was at seg_seq - 1, in which case we ignore the urgent flag.
 	 */
-	if (flags & TH_URG && urp >= 0) {
+	if ((flags & TH_URG) && urp >= 0) {
 		if (!tcp->tcp_urp_last_valid ||
 		    SEQ_GT(urp + seg_seq, tcp->tcp_urp_last)) {
 			/*
@@ -3819,6 +3954,9 @@
 			tcp->tcp_rexmit_nxt = tcp->tcp_snxt;
 			tcp->tcp_rexmit_max = tcp->tcp_snxt;
 			tcp->tcp_ms_we_have_waited = 0;
+			DTRACE_PROBE3(cwnd__retransmitted__syn,
+			    tcp_t *, tcp, uint32_t, tcp->tcp_cwnd,
+			    uint32_t, tcp->tcp_mss);
 			tcp->tcp_cwnd = mss;
 		}
 
@@ -3862,33 +4000,22 @@
 	 */
 	if (tcp->tcp_cwr && SEQ_GT(seg_ack, tcp->tcp_cwr_snd_max))
 		tcp->tcp_cwr = B_FALSE;
-	if (tcp->tcp_ecn_ok && (flags & TH_ECE)) {
-		if (!tcp->tcp_cwr) {
-			npkt = ((tcp->tcp_snxt - tcp->tcp_suna) >> 1) / mss;
-			tcp->tcp_cwnd_ssthresh = MAX(npkt, 2) * mss;
-			tcp->tcp_cwnd = npkt * mss;
-			/*
-			 * If the cwnd is 0, use the timer to clock out
-			 * new segments.  This is required by the ECN spec.
-			 */
-			if (npkt == 0) {
-				TCP_TIMER_RESTART(tcp, tcp->tcp_rto);
-				/*
-				 * This makes sure that when the ACK comes
-				 * back, we will increase tcp_cwnd by 1 MSS.
-				 */
-				tcp->tcp_cwnd_cnt = 0;
-			}
-			tcp->tcp_cwr = B_TRUE;
-			/*
-			 * This marks the end of the current window of in
-			 * flight data.  That is why we don't use
-			 * tcp_suna + tcp_swnd.  Only data in flight can
-			 * provide ECN info.
-			 */
-			tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
-			tcp->tcp_ecn_cwr_sent = B_FALSE;
-		}
+	if (tcp->tcp_ecn_ok && (flags & TH_ECE) && !tcp->tcp_cwr) {
+		cc_cong_signal(tcp, seg_ack, CC_ECN);
+		/*
+		 * If the cwnd is 0, use the timer to clock out
+		 * new segments.  This is required by the ECN spec.
+		 */
+		if (tcp->tcp_cwnd == 0)
+			TCP_TIMER_RESTART(tcp, tcp->tcp_rto);
+		tcp->tcp_cwr = B_TRUE;
+		/*
+		 * This marks the end of the current window of in
+		 * flight data.  That is why we don't use
+		 * tcp_suna + tcp_swnd.  Only data in flight can
+		 * provide ECN info.
+		 */
+		tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
 	}
 
 	mp1 = tcp->tcp_xmit_head;
@@ -3910,6 +4037,8 @@
 				/* Do Limited Transmit */
 				if ((dupack_cnt = ++tcp->tcp_dupack_cnt) <
 				    tcps->tcps_dupack_fast_retransmit) {
+					cc_ack_received(tcp, seg_ack,
+					    bytes_acked, CC_DUPACK);
 					/*
 					 * RFC 3042
 					 *
@@ -3956,12 +4085,10 @@
 				 * dropped (due to congestion.)
 				 */
 				if (!tcp->tcp_cwr) {
-					npkt = ((tcp->tcp_snxt -
-					    tcp->tcp_suna) >> 1) / mss;
-					tcp->tcp_cwnd_ssthresh = MAX(npkt, 2) *
-					    mss;
-					tcp->tcp_cwnd = (npkt +
-					    tcp->tcp_dupack_cnt) * mss;
+					cc_cong_signal(tcp, seg_ack,
+					    CC_NDUPACK);
+					cc_ack_received(tcp, seg_ack,
+					    bytes_acked, CC_DUPACK);
 				}
 				if (tcp->tcp_ecn_ok) {
 					tcp->tcp_cwr = B_TRUE;
@@ -4023,6 +4150,8 @@
 				} /* tcp_snd_sack_ok */
 
 				} else {
+					cc_ack_received(tcp, seg_ack,
+					    bytes_acked, CC_DUPACK);
 					/*
 					 * Here we perform congestion
 					 * avoidance, but NOT slow start.
@@ -4044,6 +4173,10 @@
 					cwnd = tcp->tcp_cwnd + mss;
 					if (cwnd > tcp->tcp_cwnd_max)
 						cwnd = tcp->tcp_cwnd_max;
+					DTRACE_PROBE3(cwnd__fast__recovery,
+					    tcp_t *, tcp,
+					    uint32_t, tcp->tcp_cwnd,
+					    uint32_t, cwnd);
 					tcp->tcp_cwnd = cwnd;
 					if (tcp->tcp_unsent > 0)
 						flags |= TH_XMIT_NEEDED;
@@ -4146,7 +4279,7 @@
 			}
 			mp = tcp_ack_mp(tcp);
 			if (mp != NULL) {
-				BUMP_LOCAL(tcp->tcp_obsegs);
+				TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 				TCPS_BUMP_MIB(tcps, tcpOutAck);
 				tcp_send_data(tcp, mp);
 			}
@@ -4176,15 +4309,10 @@
 		ASSERT(tcp->tcp_rexmit == B_FALSE);
 		if (SEQ_GEQ(seg_ack, tcp->tcp_rexmit_max)) {
 			tcp->tcp_dupack_cnt = 0;
-			/*
-			 * Restore the orig tcp_cwnd_ssthresh after
-			 * fast retransmit phase.
-			 */
-			if (tcp->tcp_cwnd > tcp->tcp_cwnd_ssthresh) {
-				tcp->tcp_cwnd = tcp->tcp_cwnd_ssthresh;
-			}
+
+			cc_post_recovery(tcp, seg_ack);
+
 			tcp->tcp_rexmit_max = seg_ack;
-			tcp->tcp_cwnd_cnt = 0;
 
 			/*
 			 * Remove all notsack info to avoid confusion with
@@ -4213,8 +4341,12 @@
 				 * aggressive behaviour in sending new
 				 * segments.
 				 */
-				tcp->tcp_cwnd = tcp->tcp_cwnd_ssthresh +
+				cwnd = tcp->tcp_cwnd_ssthresh +
 				    tcps->tcps_dupack_fast_retransmit * mss;
+				DTRACE_PROBE3(cwnd__fast__retransmit__part__ack,
+				    tcp_t *, tcp, uint32_t, tcp->tcp_cwnd,
+				    uint32_t, cwnd);
+				tcp->tcp_cwnd = cwnd;
 				tcp->tcp_cwnd_cnt = tcp->tcp_cwnd;
 				flags |= TH_REXMIT_NEEDED;
 			}
@@ -4275,28 +4407,10 @@
 	 * usual.
 	 */
 	if (!tcp->tcp_ecn_ok || !(flags & TH_ECE)) {
-		cwnd = tcp->tcp_cwnd;
-		add = mss;
-
-		if (cwnd >= tcp->tcp_cwnd_ssthresh) {
-			/*
-			 * This is to prevent an increase of less than 1 MSS of
-			 * tcp_cwnd.  With partial increase, tcp_wput_data()
-			 * may send out tinygrams in order to preserve mblk
-			 * boundaries.
-			 *
-			 * By initializing tcp_cwnd_cnt to new tcp_cwnd and
-			 * decrementing it by 1 MSS for every ACKs, tcp_cwnd is
-			 * increased by 1 MSS for every RTTs.
-			 */
-			if (tcp->tcp_cwnd_cnt <= 0) {
-				tcp->tcp_cwnd_cnt = cwnd + add;
-			} else {
-				tcp->tcp_cwnd_cnt -= add;
-				add = 0;
-			}
+		if (IN_RECOVERY(tcp->tcp_ccv.flags)) {
+			EXIT_RECOVERY(tcp->tcp_ccv.flags);
 		}
-		tcp->tcp_cwnd = MIN(cwnd + add, tcp->tcp_cwnd_max);
+		cc_ack_received(tcp, seg_ack, bytes_acked, CC_ACK);
 	}
 
 	/* See if the latest urgent data has been acknowledged */
@@ -4304,36 +4418,34 @@
 	    SEQ_GT(seg_ack, tcp->tcp_urg))
 		tcp->tcp_valid_bits &= ~TCP_URG_VALID;
 
-	/* Can we update the RTT estimates? */
-	if (tcp->tcp_snd_ts_ok) {
-		/* Ignore zero timestamp echo-reply. */
-		if (tcpopt.tcp_opt_ts_ecr != 0) {
-			tcp_set_rto(tcp, (int32_t)LBOLT_FASTPATH -
-			    (int32_t)tcpopt.tcp_opt_ts_ecr);
-		}
-
-		/* If needed, restart the timer. */
-		if (tcp->tcp_set_timer == 1) {
-			TCP_TIMER_RESTART(tcp, tcp->tcp_rto);
-			tcp->tcp_set_timer = 0;
-		}
+	/*
+	 * Update the RTT estimates. Note that we don't use the TCP
+	 * timestamp option to calculate RTT even if one is present. This is
+	 * because the timestamp option's resolution (CPU tick) is
+	 * too coarse to measure modern datacenter networks' microsecond
+	 * latencies. The timestamp field's resolution is limited by its
+	 * 4-byte width (see RFC1323), and since we always store a
+	 * high-resolution nanosecond presision timestamp along with the data,
+	 * there is no point to ever using the timestamp option.
+	 */
+	if (SEQ_GT(seg_ack, tcp->tcp_csuna)) {
 		/*
-		 * Update tcp_csuna in case the other side stops sending
-		 * us timestamps.
-		 */
-		tcp->tcp_csuna = tcp->tcp_snxt;
-	} else if (SEQ_GT(seg_ack, tcp->tcp_csuna)) {
-		/*
 		 * An ACK sequence we haven't seen before, so get the RTT
 		 * and update the RTO. But first check if the timestamp is
 		 * valid to use.
 		 */
 		if ((mp1->b_next != NULL) &&
-		    SEQ_GT(seg_ack, (uint32_t)(uintptr_t)(mp1->b_next)))
+		    SEQ_GT(seg_ack, (uint32_t)(uintptr_t)(mp1->b_next))) {
+#ifdef KERNEL_32
 			tcp_set_rto(tcp, (int32_t)LBOLT_FASTPATH -
 			    (int32_t)(intptr_t)mp1->b_prev);
-		else
+#else
+			tcp_set_rto(tcp, gethrtime() -
+			    (hrtime_t)(intptr_t)mp1->b_prev);
+#endif
+		} else {
 			TCPS_BUMP_MIB(tcps, tcpRttNoUpdate);
+		}
 
 		/* Remeber the last sequence to be ACKed */
 		tcp->tcp_csuna = seg_ack;
@@ -4361,8 +4473,13 @@
 			 */
 			if (SEQ_GT(seg_ack,
 			    (uint32_t)(uintptr_t)(mp1->b_next))) {
+#ifdef KERNEL_32
 				mp1->b_prev =
 				    (mblk_t *)(uintptr_t)LBOLT_FASTPATH;
+#else
+				mp1->b_prev =
+				    (mblk_t *)(intptr_t)gethrtime();
+#endif
 				mp1->b_next = NULL;
 			}
 			break;
@@ -4838,12 +4955,19 @@
 			    B_TRUE);
 
 			if (mp1 != NULL) {
+#ifdef KERNEL_32
 				tcp->tcp_xmit_head->b_prev =
 				    (mblk_t *)LBOLT_FASTPATH;
+#else
+				tcp->tcp_xmit_head->b_prev =
+				    (mblk_t *)(intptr_t)gethrtime();
+#endif
 				tcp->tcp_csuna = tcp->tcp_snxt;
 				TCPS_BUMP_MIB(tcps, tcpRetransSegs);
 				TCPS_UPDATE_MIB(tcps, tcpRetransBytes,
 				    snd_size);
+				tcp->tcp_cs.tcp_out_retrans_segs++;
+				tcp->tcp_cs.tcp_out_retrans_bytes += snd_size;
 				tcp_send_data(tcp, mp1);
 			}
 		}
@@ -4873,9 +4997,15 @@
 			 * timer is used to avoid a timeout before the
 			 * limited transmitted segment's ACK gets back.
 			 */
-			if (tcp->tcp_xmit_head != NULL)
+			if (tcp->tcp_xmit_head != NULL) {
+#ifdef KERNEL_32
 				tcp->tcp_xmit_head->b_prev =
 				    (mblk_t *)LBOLT_FASTPATH;
+#else
+				tcp->tcp_xmit_head->b_prev =
+				    (mblk_t *)(intptr_t)gethrtime();
+#endif
+			}
 		}
 
 		/* Anything more to do? */
@@ -4918,7 +5048,7 @@
 
 		if (mp1 != NULL) {
 			tcp_send_data(tcp, mp1);
-			BUMP_LOCAL(tcp->tcp_obsegs);
+			TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 			TCPS_BUMP_MIB(tcps, tcpOutAck);
 		}
 		if (tcp->tcp_ack_tid != 0) {
@@ -5211,13 +5341,14 @@
 	return (mp);
 }
 
-/* The minimum of smoothed mean deviation in RTO calculation. */
-#define	TCP_SD_MIN	400
+/* The minimum of smoothed mean deviation in RTO calculation (nsec). */
+#define	TCP_SD_MIN	400000000
 
 /*
- * Set RTO for this connection.  The formula is from Jacobson and Karels'
- * "Congestion Avoidance and Control" in SIGCOMM '88.  The variable names
- * are the same as those in Appendix A.2 of that paper.
+ * Set RTO for this connection based on a new round-trip time measurement.
+ * The formula is from Jacobson and Karels' "Congestion Avoidance and Control"
+ * in SIGCOMM '88.  The variable names are the same as those in Appendix A.2
+ * of that paper.
  *
  * m = new measurement
  * sa = smoothed RTT average (8 * average estimates).
@@ -5224,25 +5355,36 @@
  * sv = smoothed mean deviation (mdev) of RTT (4 * deviation estimates).
  */
 static void
-tcp_set_rto(tcp_t *tcp, clock_t rtt)
+tcp_set_rto(tcp_t *tcp, hrtime_t rtt)
 {
-	long m = TICK_TO_MSEC(rtt);
-	clock_t sa = tcp->tcp_rtt_sa;
-	clock_t sv = tcp->tcp_rtt_sd;
-	clock_t rto;
-	tcp_stack_t	*tcps = tcp->tcp_tcps;
+	hrtime_t m = rtt;
+	hrtime_t sa = tcp->tcp_rtt_sa;
+	hrtime_t sv = tcp->tcp_rtt_sd;
+	tcp_stack_t *tcps = tcp->tcp_tcps;
 
 	TCPS_BUMP_MIB(tcps, tcpRttUpdate);
 	tcp->tcp_rtt_update++;
+	tcp->tcp_rtt_sum += m;
+	tcp->tcp_rtt_cnt++;
 
 	/* tcp_rtt_sa is not 0 means this is a new sample. */
 	if (sa != 0) {
 		/*
-		 * Update average estimator:
-		 *	new rtt = 7/8 old rtt + 1/8 Error
+		 * Update average estimator (see section 2.3 of RFC6298):
+		 *	SRTT = 7/8 SRTT + 1/8 rtt
+		 *
+		 * We maintain tcp_rtt_sa as 8 * SRTT, so this reduces to:
+		 *	tcp_rtt_sa = 7 SRTT + rtt
+		 *	tcp_rtt_sa = tcp_rtt_sa - 1/8 tcp_rtt_sa + rtt
+		 *	tcp_rtt_sa = tcp_rtt_sa + (rtt - 1/8 tcp_rtt_sa)
+		 *
+		 * (rtt - 1/8 tcp_rtt_sa) is simply the difference
+		 * between the new rtt measurement and the existing smoothed
+		 * RTT average. This is referred to as "Error" in subsequent
+		 * calculations.
 		 */
 
-		/* m is now Error in estimate. */
+		/* m is now Error. */
 		m -= sa >> 3;
 		if ((sa += m) <= 0) {
 			/*
@@ -5255,7 +5397,11 @@
 
 		/*
 		 * Update deviation estimator:
-		 *	new mdev = 3/4 old mdev + 1/4 (abs(Error) - old mdev)
+		 *	mdev = 3/4 mdev + 1/4 abs(Error)
+		 *
+		 * We maintain tcp_rtt_sd as 4 * mdev, so this reduces to:
+		 *	tcp_rtt_sd = 3 mdev + abs(Error)
+		 *	tcp_rtt_sd = tcp_rtt_sd - 1/4 tcp_rtt_sd + abs(Error)
 		 */
 		if (m < 0)
 			m = -m;
@@ -5281,27 +5427,13 @@
 		 * to be TCP_SD_MIN, which is default to 400 ms, twice
 		 * of BSD DATO.  That means the minimum of mean
 		 * deviation is 100 ms.
-		 *
 		 */
 		sv = TCP_SD_MIN;
 	}
 	tcp->tcp_rtt_sa = sa;
 	tcp->tcp_rtt_sd = sv;
-	/*
-	 * RTO = average estimates (sa / 8) + 4 * deviation estimates (sv)
-	 *
-	 * Add tcp_rexmit_interval extra in case of extreme environment
-	 * where the algorithm fails to work.  The default value of
-	 * tcp_rexmit_interval_extra should be 0.
-	 *
-	 * As we use a finer grained clock than BSD and update
-	 * RTO for every ACKs, add in another .25 of RTT to the
-	 * deviation of RTO to accomodate burstiness of 1/4 of
-	 * window size.
-	 */
-	rto = (sa >> 3) + sv + tcps->tcps_rexmit_interval_extra + (sa >> 5);
 
-	TCP_SET_RTO(tcp, rto);
+	tcp->tcp_rto = tcp_calculate_rto(tcp, tcps);
 
 	/* Now, we can reset tcp_timer_backoff to use the new RTO... */
 	tcp->tcp_timer_backoff = 0;
@@ -5626,6 +5758,10 @@
 			npkt = ((tcp->tcp_snxt - tcp->tcp_suna) >> 1) /
 			    tcp->tcp_mss;
 			tcp->tcp_cwnd_ssthresh = MAX(npkt, 2) * tcp->tcp_mss;
+
+			DTRACE_PROBE3(cwnd__source__quench, tcp_t *, tcp,
+			    uint32_t, tcp->tcp_cwnd,
+			    uint32_t, tcp->tcp_mss);
 			tcp->tcp_cwnd = tcp->tcp_mss;
 			tcp->tcp_cwnd_cnt = 0;
 		}
--- old/usr/src/uts/common/inet/tcp/tcp_opt_data.c	Thu Mar  1 14:54:16 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_opt_data.c	Thu Mar  1 14:54:16 2018
@@ -22,6 +22,7 @@
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright 2016 Joyent, Inc.
+ * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -974,9 +975,7 @@
 				tcp->tcp_cork = onoff;
 			}
 			break;
-		case TCP_RTO_INITIAL: {
-			clock_t rto;
-
+		case TCP_RTO_INITIAL:
 			if (checkonly || val == 0)
 				break;
 
@@ -1006,15 +1005,10 @@
 			if (tcp->tcp_state >= TCPS_SYN_SENT)
 				break;
 
-			tcp->tcp_rtt_sa = tcp->tcp_rto_initial << 2;
-			tcp->tcp_rtt_sd = tcp->tcp_rto_initial >> 1;
-			rto = (tcp->tcp_rtt_sa >> 3) + tcp->tcp_rtt_sd +
-			    tcps->tcps_rexmit_interval_extra +
-			    (tcp->tcp_rtt_sa >> 5) +
-			    tcps->tcps_conn_grace_period;
-			TCP_SET_RTO(tcp, rto);
+			tcp->tcp_rtt_sa = MSEC2NSEC(tcp->tcp_rto_initial) << 2;
+			tcp->tcp_rtt_sd = MSEC2NSEC(tcp->tcp_rto_initial) >> 1;
+			tcp->tcp_rto = tcp_calculate_rto(tcp, tcps);
 			break;
-		}
 		case TCP_RTO_MIN:
 			if (checkonly || val == 0)
 				break;
--- old/usr/src/uts/common/inet/tcp/tcp_output.c	Thu Mar  1 14:54:16 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_output.c	Thu Mar  1 14:54:16 2018
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2014, 2017 by Delphix. All rights reserved.
  */
 
 /* This file contains all TCP output processing functions. */
@@ -63,7 +63,7 @@
 		    int, ip_recv_attr_t *, ip_stack_t *, conn_t *);
 static boolean_t	tcp_send_rst_chk(tcp_stack_t *);
 static void	tcp_process_shrunk_swnd(tcp_t *, uint32_t);
-static void	tcp_fill_header(tcp_t *, uchar_t *, clock_t, int);
+static void	tcp_fill_header(tcp_t *, uchar_t *, int);
 
 /*
  * Functions called directly via squeue having a prototype of edesc_t.
@@ -80,6 +80,18 @@
  */
 static int tcp_tx_pull_len = 16;
 
+static void
+cc_after_idle(tcp_t *tcp)
+{
+	uint32_t old_cwnd = tcp->tcp_cwnd;
+
+	if (CC_ALGO(tcp)->after_idle != NULL)
+		CC_ALGO(tcp)->after_idle(&tcp->tcp_ccv);
+
+	DTRACE_PROBE3(cwnd__cc__after__idle, tcp_t *, tcp, uint32_t, old_cwnd,
+	    uint32_t, tcp->tcp_cwnd);
+}
+
 void
 tcp_wput(queue_t *q, mblk_t *mp)
 {
@@ -217,7 +229,6 @@
 	int32_t		total_hdr_len;
 	int32_t		tcp_hdr_len;
 	int		rc;
-	tcp_stack_t	*tcps = tcp->tcp_tcps;
 	conn_t		*connp = tcp->tcp_connp;
 	clock_t		now = LBOLT_FASTPATH;
 
@@ -372,7 +383,7 @@
 
 	if ((tcp->tcp_suna == snxt) && !tcp->tcp_localnet &&
 	    (TICK_TO_MSEC(now - tcp->tcp_last_recv_time) >= tcp->tcp_rto)) {
-		TCP_SET_INIT_CWND(tcp, mss, tcps->tcps_slow_start_after_idle);
+		cc_after_idle(tcp);
 	}
 	if (tcpstate == TCPS_SYN_RCVD) {
 		/*
@@ -453,7 +464,11 @@
 		}
 	}
 
+#ifdef KERNEL_32
 	local_time = (mblk_t *)now;
+#else
+	local_time = (mblk_t *)(intptr_t)gethrtime();
+#endif
 
 	/*
 	 * "Our" Nagle Algorithm.  This is not the same as in the old
@@ -1190,7 +1205,7 @@
 	now = LBOLT_FASTPATH;
 	if ((tcp->tcp_suna == snxt) && !tcp->tcp_localnet &&
 	    (TICK_TO_MSEC(now - tcp->tcp_last_recv_time) >= tcp->tcp_rto)) {
-		TCP_SET_INIT_CWND(tcp, mss, tcps->tcps_slow_start_after_idle);
+		cc_after_idle(tcp);
 	}
 
 	usable = tcp->tcp_swnd;		/* tcp window size */
@@ -1253,7 +1268,11 @@
 
 	if ((mp1 = dupb(mp)) == 0)
 		goto no_memory;
+#ifdef KERNEL_32
 	mp->b_prev = (mblk_t *)(uintptr_t)now;
+#else
+	mp->b_prev = (mblk_t *)(intptr_t)gethrtime();
+#endif
 	mp->b_next = (mblk_t *)(uintptr_t)snxt;
 
 	/* adjust tcp header information */
@@ -1268,7 +1287,9 @@
 
 	TCPS_BUMP_MIB(tcps, tcpOutDataSegs);
 	TCPS_UPDATE_MIB(tcps, tcpOutDataBytes, len);
-	BUMP_LOCAL(tcp->tcp_obsegs);
+	TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
+	tcp->tcp_cs.tcp_out_data_segs++;
+	tcp->tcp_cs.tcp_out_data_bytes += len;
 
 	/* Update the latest receive window size in TCP header. */
 	tcpha->tha_win = htons(tcp->tcp_rwnd >> tcp->tcp_rcv_ws);
@@ -1308,12 +1329,10 @@
 
 	/* Fill in the timestamp option. */
 	if (tcp->tcp_snd_ts_ok) {
-		uint32_t llbolt = (uint32_t)LBOLT_FASTPATH;
-
-		U32_TO_BE32(llbolt,
-		    (char *)tcpha + TCP_MIN_HEADER_LENGTH+4);
+		U32_TO_BE32(now,
+		    (char *)tcpha + TCP_MIN_HEADER_LENGTH + 4);
 		U32_TO_BE32(tcp->tcp_ts_recent,
-		    (char *)tcpha + TCP_MIN_HEADER_LENGTH+8);
+		    (char *)tcpha + TCP_MIN_HEADER_LENGTH + 8);
 	} else {
 		ASSERT(connp->conn_ht_ulp_len == TCP_MIN_HEADER_LENGTH);
 	}
@@ -1957,16 +1976,21 @@
 			}
 			*snxt += len;
 			*tail_unsent = (*xmit_tail)->b_wptr - mp1->b_wptr;
-			BUMP_LOCAL(tcp->tcp_obsegs);
+			TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 			TCPS_BUMP_MIB(tcps, tcpOutDataSegs);
 			TCPS_UPDATE_MIB(tcps, tcpOutDataBytes, len);
+			tcp->tcp_cs.tcp_out_data_segs++;
+			tcp->tcp_cs.tcp_out_data_bytes += len;
 			tcp_send_data(tcp, mp);
 			continue;
 		}
 
 		*snxt += len;	/* Adjust later if we don't send all of len */
+		TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 		TCPS_BUMP_MIB(tcps, tcpOutDataSegs);
 		TCPS_UPDATE_MIB(tcps, tcpOutDataBytes, len);
+		tcp->tcp_cs.tcp_out_data_segs++;
+		tcp->tcp_cs.tcp_out_data_bytes += len;
 
 		if (*tail_unsent) {
 			/* Are the bytes above us in flight? */
@@ -2063,7 +2087,7 @@
 		 * Fill in the header using the template header, and add
 		 * options such as time-stamp, ECN and/or SACK, as needed.
 		 */
-		tcp_fill_header(tcp, rptr, (clock_t)local_time, num_sack_blk);
+		tcp_fill_header(tcp, rptr, num_sack_blk);
 
 		mp->b_rptr = rptr;
 
@@ -2142,6 +2166,7 @@
 				*snxt += spill;
 				tcp->tcp_last_sent_len += spill;
 				TCPS_UPDATE_MIB(tcps, tcpOutDataBytes, spill);
+				tcp->tcp_cs.tcp_out_data_bytes += spill;
 				/*
 				 * Adjust the checksum
 				 */
@@ -2190,7 +2215,7 @@
 			 */
 			ixa->ixa_fragsize = ixa->ixa_pmtu;
 			ixa->ixa_extra_ident = 0;
-			tcp->tcp_obsegs += num_lso_seg;
+			TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 			TCP_STAT(tcps, tcp_lso_times);
 			TCP_STAT_UPDATE(tcps, tcp_lso_pkt_out, num_lso_seg);
 		} else {
@@ -2201,7 +2226,7 @@
 			 */
 			lso_info_cleanup(mp);
 			tcp_send_data(tcp, mp);
-			BUMP_LOCAL(tcp->tcp_obsegs);
+			TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 		}
 	}
 
@@ -2281,8 +2306,8 @@
 	 * So don't do any update.
 	 */
 	bzero(&uinfo, sizeof (uinfo));
-	uinfo.iulp_rtt = tcp->tcp_rtt_sa;
-	uinfo.iulp_rtt_sd = tcp->tcp_rtt_sd;
+	uinfo.iulp_rtt = NSEC2MSEC(tcp->tcp_rtt_sa);
+	uinfo.iulp_rtt_sd = NSEC2MSEC(tcp->tcp_rtt_sd);
 
 	/*
 	 * Note that uinfo is kept for conn_faddr in the DCE. Could update even
@@ -2417,7 +2442,7 @@
 		tcp->tcp_rack_cnt = 0;
 		TCPS_BUMP_MIB(tcps, tcpOutAck);
 	}
-	BUMP_LOCAL(tcp->tcp_obsegs);
+	TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 	tcpha->tha_seq = htonl(seq);
 	tcpha->tha_ack = htonl(ack);
 	/*
@@ -3386,11 +3411,17 @@
 		/*
 		 * Update the send timestamp to avoid false retransmission.
 		 */
+#ifdef KERNEL_32
 		snxt_mp->b_prev = (mblk_t *)ddi_get_lbolt();
+#else
+		snxt_mp->b_prev = (mblk_t *)(intptr_t)gethrtime();
+#endif
 
 		TCPS_BUMP_MIB(tcps, tcpRetransSegs);
 		TCPS_UPDATE_MIB(tcps, tcpRetransBytes, seg_len);
 		TCPS_BUMP_MIB(tcps, tcpOutSackRetransSegs);
+		tcp->tcp_cs.tcp_out_retrans_segs++;
+		tcp->tcp_cs.tcp_out_retrans_bytes += seg_len;
 		/*
 		 * Update tcp_rexmit_max to extend this SACK recovery phase.
 		 * This happens when new data sent during fast recovery is
@@ -3458,9 +3489,15 @@
 			 * Update the send timestamp to avoid false
 			 * retransmission.
 			 */
+#ifdef KERNEL_32
 			old_snxt_mp->b_prev = (mblk_t *)ddi_get_lbolt();
+#else
+			old_snxt_mp->b_prev = (mblk_t *)(intptr_t)gethrtime();
+#endif
 			TCPS_BUMP_MIB(tcps, tcpRetransSegs);
 			TCPS_UPDATE_MIB(tcps, tcpRetransBytes, cnt);
+			tcp->tcp_cs.tcp_out_retrans_segs++;
+			tcp->tcp_cs.tcp_out_retrans_bytes += cnt;
 
 			tcp->tcp_rexmit_nxt = snxt;
 		}
@@ -3618,7 +3655,7 @@
  * ECN and/or SACK.
  */
 static void
-tcp_fill_header(tcp_t *tcp, uchar_t *rptr, clock_t now, int num_sack_blk)
+tcp_fill_header(tcp_t *tcp, uchar_t *rptr, int num_sack_blk)
 {
 	tcpha_t *tcp_tmpl, *tcpha;
 	uint32_t *dst, *src;
@@ -3640,7 +3677,7 @@
 
 	/* Fill time-stamp option if needed */
 	if (tcp->tcp_snd_ts_ok) {
-		U32_TO_BE32((uint32_t)now,
+		U32_TO_BE32(LBOLT_FASTPATH,
 		    (char *)tcp_tmpl + TCP_MIN_HEADER_LENGTH + 4);
 		U32_TO_BE32(tcp->tcp_ts_recent,
 		    (char *)tcp_tmpl + TCP_MIN_HEADER_LENGTH + 8);
--- old/usr/src/uts/common/inet/tcp/tcp_stats.c	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_stats.c	Thu Mar  1 14:54:17 2018
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, Joyent Inc. All rights reserved.
+ * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -86,6 +87,50 @@
 	}
 }
 
+static void
+tcp_set_conninfo(tcp_t *tcp, struct tcpConnEntryInfo_s *tcei, boolean_t ispriv)
+{
+	/* Don't want just anybody seeing these... */
+	if (ispriv) {
+		tcei->ce_snxt = tcp->tcp_snxt;
+		tcei->ce_suna = tcp->tcp_suna;
+		tcei->ce_rnxt = tcp->tcp_rnxt;
+		tcei->ce_rack = tcp->tcp_rack;
+	} else {
+		/*
+		 * Netstat, unfortunately, uses this to get send/receive queue
+		 * sizes.  How to fix? Why not compute the difference only?
+		 */
+		tcei->ce_snxt = tcp->tcp_snxt - tcp->tcp_suna;
+		tcei->ce_suna = 0;
+		tcei->ce_rnxt = tcp->tcp_rnxt - tcp->tcp_rack;
+		tcei->ce_rack = 0;
+	}
+
+	tcei->ce_in_data_inorder_bytes = tcp->tcp_cs.tcp_in_data_inorder_bytes;
+	tcei->ce_in_data_inorder_segs = tcp->tcp_cs.tcp_in_data_inorder_segs;
+	tcei->ce_in_data_unorder_bytes = tcp->tcp_cs.tcp_in_data_unorder_bytes;
+	tcei->ce_in_data_unorder_segs = tcp->tcp_cs.tcp_in_data_unorder_segs;
+	tcei->ce_in_zwnd_probes = tcp->tcp_cs.tcp_in_zwnd_probes;
+
+	tcei->ce_out_data_bytes = tcp->tcp_cs.tcp_out_data_bytes;
+	tcei->ce_out_data_segs = tcp->tcp_cs.tcp_out_data_segs;
+	tcei->ce_out_retrans_bytes = tcp->tcp_cs.tcp_out_retrans_bytes;
+	tcei->ce_out_retrans_segs = tcp->tcp_cs.tcp_out_retrans_segs;
+	tcei->ce_out_zwnd_probes = tcp->tcp_cs.tcp_out_zwnd_probes;
+
+	tcei->ce_unsent = tcp->tcp_unsent;
+	tcei->ce_swnd = tcp->tcp_swnd;
+	tcei->ce_cwnd = tcp->tcp_cwnd;
+	tcei->ce_rwnd = tcp->tcp_rwnd;
+	tcei->ce_rto =  tcp->tcp_rto;
+	tcei->ce_mss =  tcp->tcp_mss;
+	tcei->ce_state = tcp->tcp_state;
+	tcei->ce_rtt_sa = NSEC2USEC(tcp->tcp_rtt_sa >> 3);
+	tcei->ce_rtt_sum = NSEC2USEC(tcp->tcp_rtt_sum);
+	tcei->ce_rtt_cnt = tcp->tcp_rtt_cnt;
+}
+
 /*
  * Return SNMP stuff in buffer in mpdata.
  */
@@ -183,11 +228,6 @@
 				continue;	/* not in this zone */
 
 			tcp = connp->conn_tcp;
-			TCPS_UPDATE_MIB(tcps, tcpHCInSegs, tcp->tcp_ibsegs);
-			tcp->tcp_ibsegs = 0;
-			TCPS_UPDATE_MIB(tcps, tcpHCOutSegs, tcp->tcp_obsegs);
-			tcp->tcp_obsegs = 0;
-
 			tce6.tcp6ConnState = tce.tcpConnState =
 			    tcp_snmp_state(tcp);
 			if (tce.tcpConnState == MIB2_TCP_established ||
@@ -243,35 +283,9 @@
 			} else {
 				tce6.tcp6ConnIfIndex = connp->conn_bound_if;
 			}
-			/* Don't want just anybody seeing these... */
-			if (ispriv) {
-				tce6.tcp6ConnEntryInfo.ce_snxt =
-				    tcp->tcp_snxt;
-				tce6.tcp6ConnEntryInfo.ce_suna =
-				    tcp->tcp_suna;
-				tce6.tcp6ConnEntryInfo.ce_rnxt =
-				    tcp->tcp_rnxt;
-				tce6.tcp6ConnEntryInfo.ce_rack =
-				    tcp->tcp_rack;
-			} else {
-				/*
-				 * Netstat, unfortunately, uses this to
-				 * get send/receive queue sizes.  How to fix?
-				 * Why not compute the difference only?
-				 */
-				tce6.tcp6ConnEntryInfo.ce_snxt =
-				    tcp->tcp_snxt - tcp->tcp_suna;
-				tce6.tcp6ConnEntryInfo.ce_suna = 0;
-				tce6.tcp6ConnEntryInfo.ce_rnxt =
-				    tcp->tcp_rnxt - tcp->tcp_rack;
-				tce6.tcp6ConnEntryInfo.ce_rack = 0;
-			}
 
-			tce6.tcp6ConnEntryInfo.ce_swnd = tcp->tcp_swnd;
-			tce6.tcp6ConnEntryInfo.ce_rwnd = tcp->tcp_rwnd;
-			tce6.tcp6ConnEntryInfo.ce_rto =  tcp->tcp_rto;
-			tce6.tcp6ConnEntryInfo.ce_mss =  tcp->tcp_mss;
-			tce6.tcp6ConnEntryInfo.ce_state = tcp->tcp_state;
+			tcp_set_conninfo(tcp, &tce6.tcp6ConnEntryInfo,
+			    ispriv);
 
 			tce6.tcp6ConnCreationProcess =
 			    (connp->conn_cpid < 0) ? MIB2_UNKNOWN_PROCESS :
@@ -307,37 +321,9 @@
 				}
 				tce.tcpConnLocalPort = ntohs(connp->conn_lport);
 				tce.tcpConnRemPort = ntohs(connp->conn_fport);
-				/* Don't want just anybody seeing these... */
-				if (ispriv) {
-					tce.tcpConnEntryInfo.ce_snxt =
-					    tcp->tcp_snxt;
-					tce.tcpConnEntryInfo.ce_suna =
-					    tcp->tcp_suna;
-					tce.tcpConnEntryInfo.ce_rnxt =
-					    tcp->tcp_rnxt;
-					tce.tcpConnEntryInfo.ce_rack =
-					    tcp->tcp_rack;
-				} else {
-					/*
-					 * Netstat, unfortunately, uses this to
-					 * get send/receive queue sizes.  How
-					 * to fix?
-					 * Why not compute the difference only?
-					 */
-					tce.tcpConnEntryInfo.ce_snxt =
-					    tcp->tcp_snxt - tcp->tcp_suna;
-					tce.tcpConnEntryInfo.ce_suna = 0;
-					tce.tcpConnEntryInfo.ce_rnxt =
-					    tcp->tcp_rnxt - tcp->tcp_rack;
-					tce.tcpConnEntryInfo.ce_rack = 0;
-				}
 
-				tce.tcpConnEntryInfo.ce_swnd = tcp->tcp_swnd;
-				tce.tcpConnEntryInfo.ce_rwnd = tcp->tcp_rwnd;
-				tce.tcpConnEntryInfo.ce_rto =  tcp->tcp_rto;
-				tce.tcpConnEntryInfo.ce_mss =  tcp->tcp_mss;
-				tce.tcpConnEntryInfo.ce_state =
-				    tcp->tcp_state;
+				tcp_set_conninfo(tcp, &tce.tcpConnEntryInfo,
+				    ispriv);
 
 				tce.tcpConnCreationProcess =
 				    (connp->conn_cpid < 0) ?
--- old/usr/src/uts/common/inet/tcp/tcp_time_wait.c	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_time_wait.c	Thu Mar  1 14:54:17 2018
@@ -608,7 +608,7 @@
 	conn_t		*connp = tcp->tcp_connp;
 	tcp_stack_t	*tcps = tcp->tcp_tcps;
 
-	BUMP_LOCAL(tcp->tcp_ibsegs);
+	TCPS_BUMP_MIB(tcps, tcpHCInSegs);
 	DTRACE_PROBE2(tcp__trace__recv, mblk_t *, mp, tcp_t *, tcp);
 
 	flags = (unsigned int)tcpha->tha_flags & 0xFF;
@@ -794,6 +794,8 @@
 		TCPS_BUMP_MIB(tcps, tcpInClosed);
 		TCPS_BUMP_MIB(tcps, tcpInDataInorderSegs);
 		TCPS_UPDATE_MIB(tcps, tcpInDataInorderBytes, seg_len);
+		tcp->tcp_cs.tcp_in_data_inorder_segs++;
+		tcp->tcp_cs.tcp_in_data_inorder_bytes += seg_len;
 	}
 	if (flags & TH_RST) {
 		(void) tcp_clean_death(tcp, 0);
--- old/usr/src/uts/common/inet/tcp/tcp_timers.c	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_timers.c	Thu Mar  1 14:54:17 2018
@@ -23,7 +23,7 @@
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright 2011 Joyent, Inc.  All rights reserved.
- * Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2014, 2017 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -594,7 +594,7 @@
 	mp = tcp_ack_mp(tcp);
 
 	if (mp != NULL) {
-		BUMP_LOCAL(tcp->tcp_obsegs);
+		TCPS_BUMP_MIB(tcps, tcpHCOutSegs);
 		TCPS_BUMP_MIB(tcps, tcpOutAck);
 		TCPS_BUMP_MIB(tcps, tcpOutAckDelayed);
 		tcp_send_data(tcp, mp);
@@ -751,15 +751,19 @@
 	case TCPS_LAST_ACK:
 		/* If we have data to rexmit */
 		if (tcp->tcp_suna != tcp->tcp_snxt) {
-			clock_t	time_to_wait;
+			clock_t time_to_wait;
 
 			TCPS_BUMP_MIB(tcps, tcpTimRetrans);
 			if (!tcp->tcp_xmit_head)
 				break;
-			time_to_wait = ddi_get_lbolt() -
-			    (clock_t)tcp->tcp_xmit_head->b_prev;
-			time_to_wait = tcp->tcp_rto -
-			    TICK_TO_MSEC(time_to_wait);
+#ifdef KERNEL_32
+			time_to_wait = TICK_TO_MSEC(ddi_get_lbolt() -
+			    (clock_t)tcp->tcp_xmit_head->b_prev);
+#else
+			time_to_wait = NSEC2MSEC(gethrtime() -
+			    (hrtime_t)(intptr_t)tcp->tcp_xmit_head->b_prev);
+#endif
+			time_to_wait = tcp->tcp_rto - time_to_wait;
 			/*
 			 * If the timer fires too early, 1 clock tick earlier,
 			 * restart the timer.
@@ -785,36 +789,7 @@
 					    SL_TRACE, "tcp_timer: zero win");
 				}
 			} else {
-				/*
-				 * After retransmission, we need to do
-				 * slow start.  Set the ssthresh to one
-				 * half of current effective window and
-				 * cwnd to one MSS.  Also reset
-				 * tcp_cwnd_cnt.
-				 *
-				 * Note that if tcp_ssthresh is reduced because
-				 * of ECN, do not reduce it again unless it is
-				 * already one window of data away (tcp_cwr
-				 * should then be cleared) or this is a
-				 * timeout for a retransmitted segment.
-				 */
-				uint32_t npkt;
-
-				if (!tcp->tcp_cwr || tcp->tcp_rexmit) {
-					npkt = ((tcp->tcp_timer_backoff ?
-					    tcp->tcp_cwnd_ssthresh :
-					    tcp->tcp_snxt -
-					    tcp->tcp_suna) >> 1) / tcp->tcp_mss;
-					tcp->tcp_cwnd_ssthresh = MAX(npkt, 2) *
-					    tcp->tcp_mss;
-				}
-				tcp->tcp_cwnd = tcp->tcp_mss;
-				tcp->tcp_cwnd_cnt = 0;
-				if (tcp->tcp_ecn_ok) {
-					tcp->tcp_cwr = B_TRUE;
-					tcp->tcp_cwr_snd_max = tcp->tcp_snxt;
-					tcp->tcp_ecn_cwr_sent = B_FALSE;
-				}
+				cc_cong_signal(tcp, NULL, CC_RTO);
 			}
 			break;
 		}
@@ -854,6 +829,7 @@
 				tcp->tcp_swnd++;
 				tcp->tcp_zero_win_probe = B_TRUE;
 				TCPS_BUMP_MIB(tcps, tcpOutWinProbe);
+				tcp->tcp_cs.tcp_out_zwnd_probes++;
 			} else {
 				/*
 				 * Handle timeout from sender SWS avoidance.
@@ -1012,8 +988,8 @@
 		 * window probe.
 		 */
 		if (tcp->tcp_rtt_sa != 0 && tcp->tcp_zero_win_probe == 0) {
-			tcp->tcp_rtt_sd += (tcp->tcp_rtt_sa >> 3) +
-			    (tcp->tcp_rtt_sa >> 5);
+			tcp->tcp_rtt_sd += tcp->tcp_rtt_sa >> 3 +
+			    tcp->tcp_rtt_sa >> 5;
 			tcp->tcp_rtt_sa = 0;
 			tcp_ip_notify(tcp);
 			tcp->tcp_rtt_update = 0;
@@ -1022,9 +998,7 @@
 
 timer_rexmit:
 	tcp->tcp_timer_backoff++;
-	if ((ms = (tcp->tcp_rtt_sa >> 3) + tcp->tcp_rtt_sd +
-	    tcps->tcps_rexmit_interval_extra + (tcp->tcp_rtt_sa >> 5)) <
-	    tcp->tcp_rto_min) {
+	if ((ms = tcp_calculate_rto(tcp, tcps)) < tcp->tcp_rto_min) {
 		/*
 		 * This means the original RTO is tcp_rexmit_interval_min.
 		 * So we will use tcp_rexmit_interval_min as the RTO value
@@ -1059,8 +1033,13 @@
 	if (mss > tcp->tcp_swnd && tcp->tcp_swnd != 0)
 		mss = tcp->tcp_swnd;
 
-	if ((mp = tcp->tcp_xmit_head) != NULL)
+	if ((mp = tcp->tcp_xmit_head) != NULL) {
+#ifdef KERNEL_32
 		mp->b_prev = (mblk_t *)ddi_get_lbolt();
+#else
+		mp->b_prev = (mblk_t *)(intptr_t)gethrtime();
+#endif
+	}
 	mp = tcp_xmit_mp(tcp, mp, mss, NULL, NULL, tcp->tcp_suna, B_TRUE, &mss,
 	    B_TRUE);
 
@@ -1091,6 +1070,8 @@
 	tcp->tcp_csuna = tcp->tcp_snxt;
 	TCPS_BUMP_MIB(tcps, tcpRetransSegs);
 	TCPS_UPDATE_MIB(tcps, tcpRetransBytes, mss);
+	tcp->tcp_cs.tcp_out_retrans_segs++;
+	tcp->tcp_cs.tcp_out_retrans_bytes += mss;
 	tcp_send_data(tcp, mp);
 
 }
--- old/usr/src/uts/common/inet/tcp/tcp_tunables.c	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp/tcp_tunables.c	Thu Mar  1 14:54:17 2018
@@ -22,12 +22,13 @@
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2016 Joyent, Inc.
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
 #include <inet/ip.h>
 #include <inet/tcp_impl.h>
+#include <inet/cc.h>
 #include <sys/multidata.h>
 #include <sys/sunddi.h>
 
@@ -239,6 +240,48 @@
 	return (0);
 }
 
+/* ARGSUSED */
+static int
+tcp_set_cc_algorithm(netstack_t *stack, cred_t *cr, mod_prop_info_t *pinfo,
+    const char *ifname, const void *pval, uint_t flags)
+{
+	tcp_stack_t *tcps = stack->netstack_tcp;
+	char *name = (flags & MOD_PROP_DEFAULT) ?
+	    CC_DEFAULT_ALGO_NAME : (char *)pval;
+	struct cc_algo *algo = cc_load_algo(name);
+
+	if (algo == NULL)
+		return (EINVAL);
+
+	tcps->tcps_default_cc_algo = algo;
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+tcp_get_cc_algorithm(netstack_t *stack, mod_prop_info_t *pinfo,
+    const char *ifname, void *pval, uint_t psize, uint_t flags)
+{
+	size_t nbytes;
+
+	if (flags & MOD_PROP_POSSIBLE) {
+		/* libipadm doesn't call down for possible values. */
+		return (ENOTSUP);
+	}
+
+	if (flags & MOD_PROP_PERM) {
+		nbytes = snprintf(pval, psize, "%u", MOD_PROP_PERM_RW);
+	} else if (flags & MOD_PROP_DEFAULT) {
+		nbytes = snprintf(pval, psize, "%s", CC_DEFAULT_ALGO_NAME);
+	} else {
+		nbytes = snprintf(pval, psize, "%s",
+		    stack->netstack_tcp->tcps_default_cc_algo->name);
+	}
+	if (nbytes >= psize)
+		return (ENOBUFS);
+	return (0);
+}
+
 /*
  * All of these are alterable, within the min/max values given, at run time.
  *
@@ -527,6 +570,17 @@
 	    {1, ISS_INCR, ISS_INCR},
 	    {ISS_INCR} },
 
+	{ "congestion_control", MOD_PROTO_TCP,
+	    tcp_set_cc_algorithm, tcp_get_cc_algorithm, {0}, {0} },
+
+	/* RFC 3465 - TCP Congestion Control with Appropriate Byte Counting */
+	{ "_abc", MOD_PROTO_TCP,
+	    mod_set_boolean, mod_get_boolean, {B_TRUE}, {B_TRUE} },
+
+	/* "L" value from RFC 3465 */
+	{ "_abc_l_var", MOD_PROTO_TCP,
+	    mod_set_uint32, mod_get_uint32, {1, UINT32_MAX, 2}, {2} },
+
 	{ "?", MOD_PROTO_TCP, NULL, mod_get_allprop, {0}, {0} },
 
 	{ NULL, 0, NULL, NULL, {0}, {0} }
--- old/usr/src/uts/common/inet/tcp_impl.h	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp_impl.h	Thu Mar  1 14:54:17 2018
@@ -22,7 +22,7 @@
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2017 Joyent, Inc.
  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2013, 2017 by Delphix. All rights reserved.
  */
 
 #ifndef	_INET_TCP_IMPL_H
@@ -300,17 +300,6 @@
 	}
 
 /*
- * Set tcp_rto with boundary checking.
- */
-#define	TCP_SET_RTO(tcp, rto) \
-	if ((rto) < (tcp)->tcp_rto_min)			\
-		(tcp)->tcp_rto = (tcp)->tcp_rto_min;	\
-	else if ((rto) > (tcp)->tcp_rto_max)		\
-		(tcp)->tcp_rto = (tcp)->tcp_rto_max;	\
-	else						\
-		(tcp)->tcp_rto = (rto);
-
-/*
  * TCP options struct returned from tcp_parse_options.
  */
 typedef struct tcp_opt_s {
@@ -589,6 +578,8 @@
 #define	tcps_dev_flow_ctl		tcps_propinfo_tbl[58].prop_cur_bval
 #define	tcps_reass_timeout		tcps_propinfo_tbl[59].prop_cur_uval
 #define	tcps_iss_incr			tcps_propinfo_tbl[65].prop_cur_uval
+#define	tcps_abc			tcps_propinfo_tbl[67].prop_cur_bval
+#define	tcps_abc_l_var			tcps_propinfo_tbl[68].prop_cur_uval
 
 extern struct qinit tcp_rinitv4, tcp_rinitv6;
 extern boolean_t do_tcp_fusion;
@@ -613,6 +604,7 @@
 extern void	tcp_acceptor_hash_remove(tcp_t *);
 extern mblk_t	*tcp_ack_mp(tcp_t *);
 extern int	tcp_build_hdrs(tcp_t *);
+extern clock_t	tcp_calculate_rto(tcp_t *, tcp_stack_t *);
 extern void	tcp_cleanup(tcp_t *);
 extern int	tcp_clean_death(tcp_t *, int);
 extern void	tcp_clean_death_wrapper(void *, mblk_t *, void *,
@@ -709,6 +701,7 @@
 /*
  * Input related functions in tcp_input.c.
  */
+extern void	cc_cong_signal(tcp_t *, uint32_t, uint32_t);
 extern void	tcp_icmp_input(void *, mblk_t *, void *, ip_recv_attr_t *);
 extern void	tcp_input_data(void *, mblk_t *, void *, ip_recv_attr_t *);
 extern void	tcp_input_listener_unbound(void *, mblk_t *, void *,
--- old/usr/src/uts/common/inet/tcp_stack.h	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp_stack.h	Thu Mar  1 14:54:17 2018
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017 by Delphix. All rights reserved.
  */
 
 #ifndef	_INET_TCP_STACK_H
@@ -114,6 +115,8 @@
 	kmutex_t	tcps_listener_conf_lock;
 	list_t		tcps_listener_conf;
 
+	struct cc_algo	*tcps_default_cc_algo;
+
 	/*
 	 * Per CPU stats
 	 *
--- old/usr/src/uts/common/inet/tcp_stats.h	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/inet/tcp_stats.h	Thu Mar  1 14:54:17 2018
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015 by Delphix. All rights reserved.
  */
 
 #ifndef	_INET_TCP_STATS_H
@@ -205,6 +206,26 @@
 	tcp_stat_counter_t	tcp_sc_stats;
 } tcp_stats_cpu_t;
 
+/*
+ * Per-connection statistics. Some of these are also kept globally in the
+ * per-cpu tcp_sc_mib entry (see tcp_stats_cpu_t above). We need not maintain
+ * per-cpu versions of these stats since a connection is typically processed
+ * on the same CPU.
+ */
+typedef struct tcp_conn_stats {
+	uint64_t	tcp_in_data_inorder_bytes;
+	uint64_t	tcp_in_data_inorder_segs;
+	uint64_t	tcp_in_data_unorder_bytes;
+	uint64_t	tcp_in_data_unorder_segs;
+	uint64_t	tcp_in_zwnd_probes;
+
+	uint64_t	tcp_out_data_bytes;
+	uint64_t	tcp_out_data_segs;
+	uint64_t	tcp_out_retrans_bytes;
+	uint64_t	tcp_out_retrans_segs;
+	uint64_t	tcp_out_zwnd_probes;
+} tcp_conn_stats_t;
+
 #define	TCPS_BUMP_MIB(tcps, x) \
 	BUMP_MIB(&(tcps)->tcps_sc[CPU->cpu_seqid]->tcp_sc_mib, x)
 
--- old/usr/src/uts/common/sys/queue.h	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/sys/queue.h	Thu Mar  1 14:54:17 2018
@@ -322,11 +322,6 @@
 	_NOTE(CONSTCOND)						\
 } while (0)
 
-#define	STAILQ_FOREACH(var, head, field)				\
-	for ((var) = ((head)->stqh_first);				\
-		(var);							\
-		(var) = ((var)->field.stqe_next))
-
 /*
  * Singly-linked Tail queue access methods.
  */
@@ -334,7 +329,17 @@
 #define	STAILQ_FIRST(head)	((head)->stqh_first)
 #define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
 
+#define	STAILQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->stqh_first);				\
+		(var);							\
+		(var) = ((var)->field.stqe_next))
 
+#define	STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
+	for ((var) = STAILQ_FIRST((head));                              \
+		(var) && ((tvar) = STAILQ_NEXT((var), field), 1);	\
+		(var) = (tvar))
+
+
 /*
  * Simple queue definitions.
  */
--- old/usr/src/uts/common/sys/time.h	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/common/sys/time.h	Thu Mar  1 14:54:17 2018
@@ -19,7 +19,7 @@
  */
 
 /*
- * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2013, 2016 by Delphix. All rights reserved.
  */
 
 #ifndef _SYS_TIME_H
@@ -248,8 +248,8 @@
 #define	MSEC2NSEC(m)	((hrtime_t)(m) * (NANOSEC / MILLISEC))
 #define	NSEC2MSEC(n)	((n) / (NANOSEC / MILLISEC))
 
-#define	USEC2NSEC(m)    ((hrtime_t)(m) * (NANOSEC / MICROSEC))
-#define	NSEC2USEC(n)    ((n) / (NANOSEC / MICROSEC))
+#define	USEC2NSEC(m)	((hrtime_t)(m) * (NANOSEC / MICROSEC))
+#define	NSEC2USEC(n)	((n) / (NANOSEC / MICROSEC))
 
 #define	NSEC2SEC(n)	((n) / (NANOSEC / SEC))
 #define	SEC2NSEC(m)	((hrtime_t)(m) * (NANOSEC / SEC))
--- old/usr/src/uts/intel/Makefile.intel	Thu Mar  1 14:54:17 2018
+++ new/usr/src/uts/intel/Makefile.intel	Thu Mar  1 14:54:17 2018
@@ -21,6 +21,7 @@
 #
 # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2013 Andrew Stormont.  All rights reserved.
+# Copyright (c) 2014 by Delphix. All rights reserved.
 # Copyright 2016 Joyent, Inc.
 # Copyright 2016 Garrett D'Amore <garrett@damore.org>
 # Copyright 2017 Nexenta Systems, Inc.
@@ -175,6 +176,11 @@
 ALL_DEFS	 = $(DEBUG_DEFS) $(OPTION_DEFS)
 
 #
+#	TCP congestion control modules (/kernel/cc)
+#
+CC_KMODS       += cc_newreno cc_cubic cc_sunreno
+
+#
 #	The kernels modules which are "implementation architecture"
 #	specific for this machine are enumerated below. Note that most
 #	of these modules must exist (in one form or another) for each
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/intel/cc_cubic/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_cubic
+OBJECTS		= $(CC_CUBIC_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_CUBIC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/intel/cc_newreno/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_newreno
+OBJECTS		= $(CC_NEWRENO_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_NEWRENO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/intel/cc_sunreno/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_sunreno
+OBJECTS		= $(CC_SUNRENO_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_SUNRENO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- old/usr/src/uts/sparc/Makefile.sparc	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/sparc/Makefile.sparc	Thu Mar  1 14:54:18 2018
@@ -22,6 +22,7 @@
 #
 # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2013 Andrew Stormont.  All rights reserved.
+# Copyright (c) 2015, 2017 by Delphix. All rights reserved.
 # Copyright (c) 2015, Joyent, Inc. All rights reserved.
 # Copyright 2016 Gary Mills
 # Copyright 2016 Nexenta Systems, Inc.
@@ -201,8 +202,14 @@
 #	files.
 #
 ALL_DEFS	 = $(MACHINE_DEFS) $(DEBUG_DEFS) $(OPTION_DEFS)
+
 #
+#	TCP congestion control modules (/kernel/cc)
 #
+CC_KMODS       += cc_newreno cc_cubic cc_sunreno
+
+#
+#
 #	The kernels modules which are "implementation architecture"
 #	specific for this machine are enumerated below. Note that most
 #	of these modules must exist (in one form or another) for each
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/sparc/cc_cubic/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_cubic
+OBJECTS		= $(CC_CUBIC_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_CUBIC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/sparc/cc_newreno/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_newreno
+OBJECTS		= $(CC_NEWRENO_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_NEWRENO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- /dev/null	Thu Mar  1 14:54:18 2018
+++ new/usr/src/uts/sparc/cc_sunreno/Makefile	Thu Mar  1 14:54:18 2018
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2017 by Delphix. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cc_sunreno
+OBJECTS		= $(CC_SUNRENO_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CC_SUNRENO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_CC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
