summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Zezeski <rpz@joyent.com>2019-01-18 15:43:50 -0700
committerRyan Zezeski <rpz@joyent.com>2019-06-20 06:05:27 -0600
commit87738edeea3a17bfc0f19c6e1c3a597f3970e943 (patch)
tree8fa16e1e88ba990e21a5e03bdd7743459a6498dd
parent285d665c1bfb19b7a0d31074cbb554aae649ca56 (diff)
downloadillumos-joyent-87738edeea3a17bfc0f19c6e1c3a597f3970e943.tar.gz
OS-7520 OS-6778 broke IPv4 forwarding
OS-6878 mac_fix_cksum is incomplete OS-7806 cannot move link from NGZ to GZ Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Robert Mustacchi <rm@joyent.com>
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_door.c18
-rw-r--r--usr/src/test/Makefile2
-rw-r--r--usr/src/test/net-tests/Makefile20
-rw-r--r--usr/src/test/net-tests/cmd/Makefile36
-rw-r--r--usr/src/test/net-tests/cmd/nettest.ksh52
-rw-r--r--usr/src/test/net-tests/config/Makefile38
-rw-r--r--usr/src/test/net-tests/config/ip_forwarding.config22
-rw-r--r--usr/src/test/net-tests/runfiles/Makefile38
-rw-r--r--usr/src/test/net-tests/runfiles/default.run29
-rw-r--r--usr/src/test/net-tests/tests/Makefile42
-rw-r--r--usr/src/test/net-tests/tests/forwarding/Makefile51
-rw-r--r--usr/src/test/net-tests/tests/forwarding/README124
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_forwarding.ksh444
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum.ksh38
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum_lso.ksh38
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_fwd_no_cksum.ksh38
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum.ksh38
-rw-r--r--usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum_lso.ksh38
-rw-r--r--usr/src/test/net-tests/tests/net_common.ksh631
-rw-r--r--usr/src/uts/common/inet/ip/ip6.c98
-rw-r--r--usr/src/uts/common/inet/ip/ip6_input.c12
-rw-r--r--usr/src/uts/common/inet/ip/ip_input.c45
-rw-r--r--usr/src/uts/common/inet/ip6.h3
-rw-r--r--usr/src/uts/common/io/bridge.c14
-rw-r--r--usr/src/uts/common/io/fcoe/fcoe_fc.c5
-rw-r--r--usr/src/uts/common/io/mac/mac.c60
-rw-r--r--usr/src/uts/common/io/mac/mac_bcast.c3
-rw-r--r--usr/src/uts/common/io/mac/mac_client.c76
-rw-r--r--usr/src/uts/common/io/mac/mac_provider.c37
-rw-r--r--usr/src/uts/common/io/mac/mac_sched.c6
-rw-r--r--usr/src/uts/common/io/mac/mac_util.c379
-rw-r--r--usr/src/uts/common/io/simnet/simnet.c444
-rw-r--r--usr/src/uts/common/io/simnet/simnet_impl.h11
-rw-r--r--usr/src/uts/common/os/ip_cksum.c108
-rw-r--r--usr/src/uts/common/sys/mac_impl.h55
35 files changed, 2690 insertions, 403 deletions
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c
index 2bebaa24c3..647b919864 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_door.c
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -1248,21 +1248,6 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
}
if (oldzoneid != GLOBAL_ZONEID) {
- if (newzoneid == GLOBAL_ZONEID && linkp->ll_onloan) {
- /*
- * In this case we are attempting to assign a
- * loaned datalink from an NGZ to the GZ. We
- * can only reassign a loaned VNIC back to the
- * GZ when the zone is shutting down --
- * because otherwise the VNIC is in use by the
- * zone and will be busy. Leave the VNIC
- * loaned to the zone and return EBUSY to the
- * caller.
- */
- err = EBUSY;
- goto done;
- }
-
if (zone_remove_datalink(oldzoneid, linkid) != 0) {
err = errno;
dlmgmt_log(LOG_WARNING, "unable to remove link %d from "
@@ -1272,6 +1257,7 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
linkp->ll_onloan = B_FALSE;
}
+
if (newzoneid != GLOBAL_ZONEID) {
if (zone_add_datalink(newzoneid, linkid) != 0) {
err = errno;
diff --git a/usr/src/test/Makefile b/usr/src/test/Makefile
index fa57d36772..9756f02ef7 100644
--- a/usr/src/test/Makefile
+++ b/usr/src/test/Makefile
@@ -12,6 +12,7 @@
#
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2019 Joyent, Inc.
#
.PARALLEL: $(SUBDIRS)
@@ -20,6 +21,7 @@ SUBDIRS = \
crypto-tests \
elf-tests \
libc-tests \
+ net-tests \
os-tests \
smbclient-tests \
test-runner \
diff --git a/usr/src/test/net-tests/Makefile b/usr/src/test/net-tests/Makefile
new file mode 100644
index 0000000000..6536e70c59
--- /dev/null
+++ b/usr/src/test/net-tests/Makefile
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019, Joyent Inc.
+#
+
+.PARALLEL: $(SUBDIRS)
+
+SUBDIRS = cmd config runfiles tests
+
+include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/net-tests/cmd/Makefile b/usr/src/test/net-tests/cmd/Makefile
new file mode 100644
index 0000000000..b2770c84c6
--- /dev/null
+++ b/usr/src/test/net-tests/cmd/Makefile
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/net-tests
+ROOTBIN = $(ROOTOPTPKG)/bin
+PROGS = nettest
+CMDS = $(PROGS:%=$(ROOTBIN)/%)
+$(CMDS) := FILEMODE = 0555
+
+include $(SRC)/test/Makefile.com
+
+install: $(CMDS)
+
+clobber: clean
+ $(RM) $(CMDS)
+
+$(CMDS): $(ROOTBIN)
+
+$(ROOTBIN):
+ $(INS.dir)
+
+$(ROOTBIN)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/net-tests/cmd/nettest.ksh b/usr/src/test/net-tests/cmd/nettest.ksh
new file mode 100644
index 0000000000..e7d0e78865
--- /dev/null
+++ b/usr/src/test/net-tests/cmd/nettest.ksh
@@ -0,0 +1,52 @@
+#!/usr/bin/ksh
+
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+export NET_TESTS="/opt/net-tests"
+runner="/opt/test-runner/bin/run"
+
+function fail
+{
+ echo $1 >&2
+ exit ${2:-1}
+}
+
+function find_runfile
+{
+ typeset distro=
+ if [[ -f $NET_TESTS/runfiles/default.run ]]; then
+ distro=default
+ fi
+
+ [[ -n $distro ]] && echo $NET_TESTS/runfiles/$distro.run
+}
+
+while getopts c: c; do
+ case $c in
+ 'c')
+ runfile=$OPTARG
+ [[ -f $runfile ]] || fail "Cannot read file: $runfile"
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+[[ -z $runfile ]] && runfile=$(find_runfile)
+[[ -z $runfile ]] && fail "Couldn't determine distro"
+
+$runner -c $runfile
+
+exit $?
diff --git a/usr/src/test/net-tests/config/Makefile b/usr/src/test/net-tests/config/Makefile
new file mode 100644
index 0000000000..c1d3f8c285
--- /dev/null
+++ b/usr/src/test/net-tests/config/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+CFGS = ip_forwarding.config
+ROOTOPTPKG = $(ROOT)/opt/net-tests
+ROOTOPTPKGCFG = $(ROOT)/opt/net-tests/config
+ROOTOPTPKGDIRS = $(ROOTOPTPKG) $(ROOTOPTPKGCFG)
+FILES = $(CFGS:%=$(ROOTOPTPKGCFG)/%)
+$(FILES) := FILEMODE = 0555
+
+include $(SRC)/test/Makefile.com
+
+all: $(CFGS)
+
+install: $(ROOTOPTPKG) $(ROOTOPTPKGCFG) $(FILES)
+
+clobber: clean
+ $(RM) $(FILES)
+
+$(ROOTOPTPKGDIRS):
+ $(INS.dir)
+
+$(ROOTOPTPKGCFG)/%: % $(ROOTOPTPKGDIRS)
+ $(INS.file)
diff --git a/usr/src/test/net-tests/config/ip_forwarding.config b/usr/src/test/net-tests/config/ip_forwarding.config
new file mode 100644
index 0000000000..4a839cd49d
--- /dev/null
+++ b/usr/src/test/net-tests/config/ip_forwarding.config
@@ -0,0 +1,22 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+#
+# See the tests/forwarding/README file for information about how to
+# configure and run the tests.
+#
+export NT_CLIENT=client_zone_name
+export NT_ROUTER=router_zone_name
+export NT_SERVER=server_zone_name
diff --git a/usr/src/test/net-tests/runfiles/Makefile b/usr/src/test/net-tests/runfiles/Makefile
new file mode 100644
index 0000000000..d50a8deebf
--- /dev/null
+++ b/usr/src/test/net-tests/runfiles/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+include $(SRC)/Makefile.master
+
+SRCS = default.run
+ROOTOPTPKG = $(ROOT)/opt/net-tests
+RUNFILES = $(ROOTOPTPKG)/runfiles
+CMDS = $(SRCS:%=$(RUNFILES)/%)
+$(CMDS) := FILEMODE = 0444
+
+include $(SRC)/test/Makefile.com
+
+all: $(SRCS)
+
+install: $(CMDS)
+
+clobber: clean
+ $(RM) $(CMDS)
+
+$(CMDS): $(RUNFILES) $(SRCS)
+
+$(RUNFILES):
+ $(INS.dir)
+
+$(RUNFILES)/%: %
+ $(INS.file)
diff --git a/usr/src/test/net-tests/runfiles/default.run b/usr/src/test/net-tests/runfiles/default.run
new file mode 100644
index 0000000000..ff5a3cac6e
--- /dev/null
+++ b/usr/src/test/net-tests/runfiles/default.run
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+[DEFAULT]
+outputdir = /var/tmp/test_results
+quiet = False
+timeout = 60
+
+[/opt/net-tests/tests/forwarding]
+tests = [
+ 'ip_fwd_no_cksum',
+ 'ip_fwd_partial_cksum',
+ 'ip_fwd_full_cksum',
+ 'ip_fwd_partial_cksum_lso',
+ 'ip_fwd_full_cksum_lso'
+ ]
+user = root
diff --git a/usr/src/test/net-tests/tests/Makefile b/usr/src/test/net-tests/tests/Makefile
new file mode 100644
index 0000000000..2712d62751
--- /dev/null
+++ b/usr/src/test/net-tests/tests/Makefile
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = forwarding
+SCRIPTS = net_common
+ROOTOPTPKG = $(ROOT)/opt/net-tests
+TESTDIR = $(ROOTOPTPKG)/tests
+CMDS = $(SCRIPTS:%=$(TESTDIR)/%)
+FILEMODE=0444
+$(CMDS) := FILEMODE = 0555
+
+include $(SRC)/test/Makefile.com
+
+install: $(CMDS)
+
+clobber: clean
+ $(RM) $(CMDS)
+
+$(CMDS): $(TESTDIR)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/net-tests/tests/forwarding/Makefile b/usr/src/test/net-tests/tests/forwarding/Makefile
new file mode 100644
index 0000000000..30a95b7fe9
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/Makefile
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+
+ROOTOPTPKG = $(ROOT)/opt/net-tests
+TESTDIR = $(ROOTOPTPKG)/tests/forwarding
+
+PROG = \
+ ip_forwarding \
+ ip_fwd_no_cksum \
+ ip_fwd_partial_cksum \
+ ip_fwd_full_cksum \
+ ip_fwd_partial_cksum_lso \
+ ip_fwd_full_cksum_lso
+
+DOC = $(TESTDIR)/README
+
+CMDS = $(PROG:%=$(TESTDIR)/%)
+FILEMODE=0444
+$(CMDS) := FILEMODE = 0555
+
+include $(SRC)/test/Makefile.com
+
+install: $(CMDS) $(DOC)
+
+clobber: clean
+ $(RM) $(CMDS) $(DOC)
+
+$(CMDS) $(DOC): $(TESTDIR)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/net-tests/tests/forwarding/README b/usr/src/test/net-tests/tests/forwarding/README
new file mode 100644
index 0000000000..626dbe55e4
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/README
@@ -0,0 +1,124 @@
+Running
+-------
+
+* Create three native zones and start them.
+
+* Edit config/ip_forwarding.config, entering the names of the zones
+ you created.
+
+* Run /opt/net-tests/bin/nettest.
+
+Overview
+--------
+
+The tests in this directory test the IP forwarding path under several
+different variations. All tests require three zones. The tests use
+these three zones, along with the simnet driver, to emulate a real IP
+forwarding scenario involving multiple hosts. All tests verify that
+TCP, UDP, ICMP, IPv4/IPv6, and fragmented IPv4/IPv6 traffic can cross
+the IP forwarding datapath. All tests send traffic across both "the
+wire" (simnet) and the special MAC-loopback path. Each test differs in
+its emulation of various hardware offload features (which would
+typically be presented by real NICs). There is no emulation of Rx
+checksum offload; all packets will be software checksummed by IP
+input. In the future we may want to add variations where Rx checksum
+offload is in play as that's a typical feature provided by NICs.
+
+The diagram below gives a visual representation of the situation we
+are testing and shows how the test components relate to each other.
+
++----------------------------+ +----------------------------+
+|client host (ipft_nic0) | |server host (ipft_nic1) |
+| | | +------------------------+ |
+| +----------------------+ | | |router zone | |
+| |ipft_client0 | | | | | |
+| |192.168.77.2 |<-+----+ | |+----------------------+| |
+| |fd00:0:1:4d::2 | | | | ||ipft_client_r0 || |
+| +----------------------+ | Wire --+->|192.168.77.1 || |
++----------------------------+ | ||fd00:0:1:4d::1 || |
+ | |+----------------------+| |
+ | | ^ | |
+ | | | | |
+ | | IP | | |
+ | | forwarding | | |
+ | | | | |
+ | | v | |
+ | |+----------------------+| |
+ | ||ipft_server_r0 || |
+ | ||VLAN 5 || |
+ +-----+->|192.168.88.1 || |
+ | | ||fd00:0:1:58::1 || |
+ | | |+----------------------+| |
+ | | +------------------------+ |
+ MAC-loopback | | |
+ | | +------------------------+ |
+ | | |server zone | |
+ | | | | |
+ | | |+----------------------+| |
+ | | ||ipft_server0 || |
+ | | ||VLAN 5 || |
+ +-----+->|192.168.88.2 || |
+ | ||fd00:0:1:58::2 || |
+ | |+----------------------+| |
+ | +------------------------+ |
+ +----------------------------+
+
+Requirements
+------------
+
+* These tests are currently SmartOS specific as they rely on simnet
+ extensions only found in illumos-joyent.
+
+* The client and server zones must provide `/usr/bin/socat`. It would
+ be nice to use netcat but our native version is missing features
+ like connection timeout.
+
+* The user must both create and start the three required zones.
+
+* All three zones should be native zones.
+
+* You must edit the ip_forwarding.config file; providing it with the
+ names of the zones you have created.
+
+Files
+-----
+
+ip_fowarding
+
+ The main test script; it provides the logic for all the tests
+ below. The different test variations are controlled by options
+ and it takes the three zones as arguments. This script is also
+ easily run by hand if you want to bypass the test-runner
+ framework. The rest of the test sripts are meant for
+ consumption by the test-runner framework and are wrappers
+ around this script.
+
+ip_fwd_no_cksum
+
+ Tests IP forwarding with no hardware offloads on any of the
+ interfaces.
+
+ip_fwd_partial_cksum
+
+ Tests IP forwarding with partial Tx checksum offload and IPv4
+ header checksum offload enabled on both interfaces.
+
+ip_fwd_full_cksum
+
+ Tests IP forwarding with full Tx checksum offload and IPv4
+ header checksum offload enabled on both interfaces.
+
+ip_fwd_partial_cksum_lso
+
+ Tests IP forwarding with partial Tx checksum offload, IPv4
+ header checksum offload, and TCP LSO on both interfaces.
+
+ip_fwd_full_cksum_lso
+
+ Tests IP forwarding with full Tx checksum offload, IPv4 header
+ checksum offload, and TCP LSO on both interfaces.
+
+config/ip_forwarding.config
+
+ This file must be modified to contain the names of the zones
+ the user crated for running these tests. \ No newline at end of file
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_forwarding.ksh b/usr/src/test/net-tests/tests/forwarding/ip_forwarding.ksh
new file mode 100644
index 0000000000..74784fb494
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_forwarding.ksh
@@ -0,0 +1,444 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+#
+# Usage:
+#
+# ip_forwarding.ksh -flnpvu <client> <router> <server>
+#
+# Where client, router, and server are the UUIDs of three native
+# zones. The user must create and start these zones; but other
+# than that there is no special configuration required for them.
+#
+# -c Run cleanup only.
+#
+# -f Full ULP hardware checksum.
+#
+# -l Hardware TCP LSO.
+#
+# -n No cleanup: the various artifacts created by this script will
+# remain after execution.
+#
+# -p Partial ULP hardware checksum.
+#
+# -u Run UDP tests.
+#
+# -v Vebose mode.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+
+function cleanup
+{
+ rm -rf ${nt_tdirprefix}*
+ zlogin $nt_client rm -rf ${nt_tdirprefix}*
+ zlogin $nt_server rm -rf ${nt_tdirprefix}*
+
+ rm_route $nt_client $nt_server_ip $nt_server_subnet $nt_client_router_ip
+ rm_route $nt_server $nt_client_ip $nt_client_subnet $nt_server_router_ip
+ rm_route6 $nt_client $nt_server_ip6 $nt_server_subnet6 \
+ $nt_client_router_ip6
+ rm_route6 $nt_server $nt_client_ip6 $nt_client_subnet6 \
+ $nt_server_router_ip6
+
+ ip_fwd_disable $nt_router
+
+ delete_addr $nt_client ipft_client0 v4
+ delete_addr $nt_router ipft_client_r0 v4
+ delete_addr $nt_router ipft_server_r0 v4
+ delete_addr $nt_server ipft_server0 v4
+
+ delete_addr $nt_client ipft_client0 v6
+ delete_addr $nt_router ipft_client_r0 v6
+ delete_addr $nt_router ipft_server_r0 v6
+ delete_addr $nt_server ipft_server0 v6
+
+ delete_if $nt_client ipft_client0
+ delete_if $nt_router ipft_client_r0
+ delete_if $nt_router ipft_server_r0
+ delete_if $nt_server ipft_server0
+
+ delete_vnic ipft_client0 ipft_nic0 0 $nt_client
+ delete_vnic ipft_client_r0 ipft_nic1 0 $nt_router
+ delete_vnic ipft_server_r0 ipft_nic1 5 $nt_router
+ delete_vnic ipft_server0 ipft_nic1 5 $nt_server
+
+ delete_simnet ipft_nic0
+ delete_simnet ipft_nic1
+}
+
+function usage
+{
+ echo "$nt_tname -cflnpuv <client> <router> <server>" >&2
+}
+
+#
+# Set test defaults.
+#
+nt_tname=${NT_TNAME:-$(basename $0)}
+nt_ulp_full=0
+nt_ulp_partial=0
+nt_tcp_lso=0
+nt_udp=0
+nt_cleanup=1
+nt_cleanup_only=0
+
+nt_tdirprefix=/tmp/${nt_tname}
+nt_tdir=${nt_tdirprefix}.$$
+nt_dfile=${nt_tdir}/${nt_tname}.data
+nt_efile=${nt_tdir}/${nt_tname}-expected-sha1
+nt_rfile=${nt_tdir}/${nt_tname}-received-sha1
+nt_ofile=${nt_tdir}/${nt_tname}-received
+nt_client_subnet=192.168.77.0/24
+nt_client_ip=192.168.77.2
+nt_client_router_ip=192.168.77.1
+nt_server_subnet=192.168.88.0/24
+nt_server_ip=192.168.88.2
+nt_server_router_ip=192.168.88.1
+nt_port=7774
+nt_client_subnet6=fd00:0:1:4d::2/64
+nt_client_ip6=fd00:0:1:4d::2
+nt_client_router_ip6=fd00:0:1:4d::1
+nt_server_subnet6=fd00:0:1:58::/64
+nt_server_router_ip6=fd00:0:1:58::1
+nt_server_ip6=fd00:0:1:58::2
+nt_port6=7776
+
+while getopts "cflnpuv" opt; do
+ case $opt in
+ c)
+ nt_cleanup_only=1
+ ;;
+ f)
+ nt_ulp_full=1
+ ;;
+ l)
+ nt_tcp_lso=1
+ ;;
+ n)
+ nt_cleanup=0
+ ;;
+ p)
+ nt_ulp_partial=1
+ ;;
+ u)
+ nt_udp=1
+ ;;
+ v)
+ DEBUG=1
+ ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+if ((nt_ulp_partial == 1)) && ((nt_ulp_full == 1)); then
+ fail "both partial and full checksum enabled"
+fi
+
+if (( $# != 3 )); then
+ usage
+ fail "wrong number of arguments"
+fi
+
+nt_client=$1
+nt_router=$2
+nt_server=$3
+
+if [[ "$nt_client" == "$nt_router" || "$nt_router" == "$nt_server" ||
+ "$nt_client" == "$nt_server" ]]; then
+ fail "all zones must be unique"
+fi
+
+dbg "client zone: $nt_client"
+dbg "router zone: $nt_router"
+dbg "server zone: $nt_server"
+
+BAIL=1
+zone_exists $nt_client || fail "zone $nt_client not found"
+zone_exists $nt_router || fail "zone $nt_router not found"
+zone_exists $nt_server || fail "zone $nt_server not found"
+
+zone_running $nt_client
+zone_running $nt_router
+zone_running $nt_server
+
+if ! zlogin $nt_client ls /usr/bin/socat > /dev/null; then
+ fail "zone $nt_client missing socat"
+fi
+
+if ! zlogin $nt_server ls /usr/bin/socat > /dev/null; then
+ fail "zone $nt_client missing socat"
+fi
+
+#
+# Make a best effort to cleanup artifacts from a previous run.
+#
+if ((nt_cleanup_only == 1)); then
+ dbg "performing cleanup only"
+ BAIL=0
+ cleanup
+ BAIL=1
+ exit 0
+fi
+
+mkdir $nt_tdir
+zlogin $nt_client mkdir $nt_tdir
+zlogin $nt_server mkdir $nt_tdir
+
+create_simnet ipft_nic0
+create_simnet ipft_nic1
+link_simnets ipft_nic0 ipft_nic1
+
+if ((nt_ulp_partial == 1)); then
+ set_linkprop ipft_nic0 _tx_ulp_cksum partial
+ set_linkprop ipft_nic1 _tx_ulp_cksum partial
+fi
+
+if ((nt_ulp_full == 1)); then
+ set_linkprop ipft_nic0 _tx_ulp_cksum fullv4
+ set_linkprop ipft_nic1 _tx_ulp_cksum fullv4
+fi
+
+if ((nt_ulp_full == 1)) || ((nt_ulp_partial == 1)); then
+ set_linkprop ipft_nic0 _tx_ipv4_cksum on
+ set_linkprop ipft_nic1 _tx_ipv4_cksum on
+fi
+
+if ((nt_tcp_lso == 1)); then
+ set_linkprop ipft_nic0 _lso on
+ set_linkprop ipft_nic1 _lso on
+fi
+
+create_vnic ipft_client0 ipft_nic0 0 $nt_client
+create_vnic ipft_client_r0 ipft_nic1 0 $nt_router
+create_vnic ipft_server_r0 ipft_nic1 5 $nt_router
+create_vnic ipft_server0 ipft_nic1 5 $nt_server
+
+ip_fwd_enable $nt_router
+
+create_addr $nt_client ipft_client0 $nt_client_ip/24
+create_addr $nt_router ipft_client_r0 $nt_client_router_ip/24
+create_addr $nt_router ipft_server_r0 $nt_server_router_ip/24
+create_addr $nt_server ipft_server0 $nt_server_ip/24
+
+add_route $nt_client $nt_server_ip $nt_server_subnet $nt_client_router_ip
+add_route $nt_server $nt_client_ip $nt_client_subnet $nt_server_router_ip
+
+create_addr6 $nt_client ipft_client0 $nt_client_ip6
+create_addr6 $nt_router ipft_client_r0 $nt_client_router_ip6
+create_addr6 $nt_router ipft_server_r0 $nt_server_router_ip6
+create_addr6 $nt_server ipft_server0 $nt_server_ip6
+
+add_route6 $nt_client $nt_server_ip6 $nt_server_subnet6 $nt_client_router_ip6
+add_route6 $nt_server $nt_client_ip6 $nt_client_subnet6 $nt_server_router_ip6
+
+dd if=/dev/urandom of=$nt_dfile bs=1024 count=1024 > /dev/null 2>&1
+if (($? != 0)); then
+ fail "failed to create data file: $nt_dfile"
+else
+ dbg "created data file: $nt_dfile"
+fi
+
+digest -a sha1 $nt_dfile > $nt_efile
+
+# ================================================================
+# client -> server
+# ================================================================
+ping $nt_client $nt_client_ip $nt_server_ip
+ping $nt_client $nt_client_ip6 $nt_server_ip6
+
+start_server $nt_server TCP4 $nt_server_ip $nt_port $nt_ofile
+nt_listener_ppid=$!
+
+# Give the server time to start.
+sleep 1
+
+dbg "sending 1M $nt_client ($nt_client_ip) -> $nt_server ($nt_server_ip)"
+zlogin $nt_client /usr/bin/socat -b 8192 STDIN \
+ TCP4:$nt_server_ip:$nt_port,connect-timeout=5 < $nt_dfile
+
+if (($? != 0)); then
+ pkill -TERM -P $nt_listener_ppid
+ fail "failed to run socat client"
+else
+ dbg "sent 1M $nt_client ($nt_client_ip) -> $nt_server ($nt_server_ip)"
+fi
+
+#
+# The client may have exited but make sure to give the server time to
+# exit and finish computing the SHA1.
+#
+dbg "waiting for listener $nt_listener_ppid"
+wait_for_pid $nt_listener_ppid 5
+dbg "listener $nt_listener_ppid exited"
+
+digest -a sha1 /zones/$nt_server/root/$nt_ofile > $nt_rfile
+
+if ! diff $nt_efile $nt_rfile; then
+ fail "SHA1 comparison failed"
+else
+ dbg "SHA1 comparison passed"
+fi
+
+start_server $nt_server TCP6 $nt_server_ip6 $nt_port6 $nt_rfile
+listener_ppid=$!
+
+# Give the server time to start.
+sleep 1
+
+zlogin $nt_client /usr/bin/socat -b 8192 STDIN \
+ TCP6:[${nt_server_ip6}]:$nt_port6,connect-timeout=5 < $nt_dfile
+
+if (($? != 0)); then
+ pkill -TERM -P $nt_listener_ppid
+ fail "failed to run socat client IPv6"
+else
+ dbg "sent 1M $nt_client ($nt_client_ip6)" \
+ "-> $nt_server ($nt_server_ip6) IPv6"
+fi
+
+#
+# The client may have exited but make sure to give the server time to
+# exit and finish computing the SHA1.
+#
+dbg "waiting for listener $nt_listener_ppid"
+wait_for_pid $nt_listener_ppid 5
+dbg "listener $nt_listener_ppid exited"
+
+digest -a sha1 /zones/$nt_server/root/$nt_ofile > $nt_rfile
+
+if ! diff $nt_efile $nt_rfile; then
+ fail "SHA1 comparison failed"
+else
+ dbg "SHA1 comparison passed"
+fi
+
+if ((nt_udp == 1)); then
+ ping_udp $nt_client $nt_client_ip $nt_server_ip 256 3
+ ping_udp $nt_client $nt_client_ip6 $nt_server_ip6 256 3
+
+ #
+ # Test IP fragmentation by sending a larger-than-MTU datagram.
+ # You can verify fragmentation is happening by dtracing the
+ # various "Frag" and "Reasm" mibs.
+ #
+ dbg "test IP fragmentation $nt_client_ip -> $nt_server_ip"
+ ping_udp $nt_client $nt_client_ip $nt_server_ip $((1024 * 16)) 3
+
+ dbg "test IPv6 fragmentation $nt_client_ip6 -> $nt_server_ip6"
+ ping_udp $nt_client $nt_client_ip6 $nt_server_ip6 $((1024 * 16)) 3
+fi
+
+# ================================================================
+# server -> client
+# ================================================================
+ping $nt_server $nt_server_ip $nt_client_ip
+ping $nt_server $nt_server_ip6 $nt_client_ip6
+
+start_server $nt_client TCP4 $nt_client_ip $nt_port $nt_ofile
+nt_listener_ppid=$!
+
+# Give the listener time to start.
+sleep 1
+
+zlogin $nt_server /usr/bin/socat -b 8192 STDIN \
+ TCP4:$nt_client_ip:$nt_port,bind=$nt_server_ip,connect-timeout=5 \
+ < $nt_dfile
+
+if (($? != 0)); then
+ pkill -TERM -P $nt_listener_ppid
+ fail "failed to run socat client"
+else
+ dbg "sent 1M $nt_server ($nt_server_ip) -> $nt_client ($nt_client_ip)"
+fi
+
+#
+# The client may have exited but make sure to give the server time to
+# exit and finish computing the SHA1.
+#
+dbg "waiting for listener $nt_listener_ppid"
+wait_for_pid $nt_listener_ppid 5
+dbg "listener $nt_listener_ppid exited"
+
+digest -a sha1 /zones/$nt_client/root/$nt_ofile > $nt_rfile
+
+if ! diff $nt_efile $nt_rfile; then
+ fail "SHA1 comparison failed"
+else
+ dbg "SHA1 comparison passed"
+fi
+
+start_server $nt_client TCP6 $nt_client_ip6 $nt_port6 $nt_ofile
+nt_listener_ppid=$!
+
+# Give the listener time to start.
+sleep 1
+
+zlogin $nt_server /usr/bin/socat -b 8192 STDIN \
+ TCP6:[$nt_client_ip6]:$nt_port6,connect-timeout=5 < $nt_dfile
+
+if (($? != 0)); then
+ pkill -TERM -P $nt_listener_ppid
+ fail "failed to run socat client IPv6"
+else
+ dbg "sent 1M $nt_server ($nt_server_ip6) -> $nt_client ($nt_client_ip6)"
+fi
+
+#
+# The client may have exited but make sure to give the server time to
+# exit and finish computing the SHA1.
+#
+dbg "waiting for listener $nt_listener_ppid"
+wait_for_pid $nt_listener_ppid 5
+dbg "server $nt_listener_ppid exited"
+
+digest -a sha1 /zones/$nt_client/root/$nt_ofile > $nt_rfile
+
+if ! diff $nt_efile $nt_rfile; then
+ fail "SHA1 comparison failed"
+else
+ dbg "SHA1 comparison passed"
+fi
+
+if ((nt_udp == 1)); then
+ ping_udp $nt_server $nt_server_ip $nt_client_ip 256 3
+ ping_udp $nt_server $nt_server_ip6 $nt_client_ip6 256 3
+
+ #
+ # Test IP fragmentation by sending a larger-than-MTU datagram.
+ # You can verify fragmentation is happening by dtracing the
+ # various "Frag" and "Reasm" mibs.
+ #
+ dbg "test IP fragmentation $nt_server_ip -> $nt_client_ip"
+ ping_udp $nt_server $nt_server_ip $nt_client_ip $((1024 * 16)) 3
+
+ dbg "test IPv6 fragmentation $nt_server_ip6 -> $nt_client_ip6"
+ ping_udp $nt_server $nt_server_ip6 $nt_client_ip6 $((1024 * 16)) 3
+fi
+
+if ((nt_cleanup == 0)); then
+ dbg "skipping cleanup"
+ echo "PASS [$nt_tname]"
+ exit 0
+fi
+
+cleanup
+echo "PASS [$nt_tname]"
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum.ksh b/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum.ksh
new file mode 100644
index 0000000000..57c9e2d271
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum.ksh
@@ -0,0 +1,38 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+. $NET_TESTS/config/ip_forwarding.config
+
+if [[ -z "$NT_CLIENT" ]]; then
+ fail "NT_CLIENT must be set"
+fi
+
+if [[ -z "$NT_ROUTER" ]]; then
+ fail "NT_ROUTER must be set"
+fi
+
+if [[ -z "$NT_SERVER" ]]; then
+ fail "NT_SERVER must be set"
+fi
+
+export NT_TNAME=$(basename $0)
+$NET_TESTS/tests/forwarding/ip_forwarding -fuv $NT_CLIENT $NT_ROUTER $NT_SERVER
+exit $?
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum_lso.ksh b/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum_lso.ksh
new file mode 100644
index 0000000000..a9b6f98a46
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_fwd_full_cksum_lso.ksh
@@ -0,0 +1,38 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+. $NET_TESTS/config/ip_forwarding.config
+
+if [[ -z "$NT_CLIENT" ]]; then
+ fail "NT_CLIENT must be set"
+fi
+
+if [[ -z "$NT_ROUTER" ]]; then
+ fail "NT_ROUTER must be set"
+fi
+
+if [[ -z "$NT_SERVER" ]]; then
+ fail "NT_SERVER must be set"
+fi
+
+export NT_TNAME=$(basename $0)
+$NET_TESTS/tests/forwarding/ip_forwarding -fluv $NT_CLIENT $NT_ROUTER $NT_SERVER
+exit $?
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_fwd_no_cksum.ksh b/usr/src/test/net-tests/tests/forwarding/ip_fwd_no_cksum.ksh
new file mode 100644
index 0000000000..ad593568d0
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_fwd_no_cksum.ksh
@@ -0,0 +1,38 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+. $NET_TESTS/config/ip_forwarding.config
+
+if [[ -z "$NT_CLIENT" ]]; then
+ fail "NT_CLIENT must be set"
+fi
+
+if [[ -z "$NT_ROUTER" ]]; then
+ fail "NT_ROUTER must be set"
+fi
+
+if [[ -z "$NT_SERVER" ]]; then
+ fail "NT_SERVER must be set"
+fi
+
+export NT_TNAME=$(basename $0)
+$NET_TESTS/tests/forwarding/ip_forwarding -uv $NT_CLIENT $NT_ROUTER $NT_SERVER
+exit $?
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum.ksh b/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum.ksh
new file mode 100644
index 0000000000..b7d51ab224
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum.ksh
@@ -0,0 +1,38 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+. $NET_TESTS/config/ip_forwarding.config
+
+if [[ -z "$NT_CLIENT" ]]; then
+ fail "NT_CLIENT must be set"
+fi
+
+if [[ -z "$NT_ROUTER" ]]; then
+ fail "NT_ROUTER must be set"
+fi
+
+if [[ -z "$NT_SERVER" ]]; then
+ fail "NT_SERVER must be set"
+fi
+
+export NT_TNAME=$(basename $0)
+$NET_TESTS/tests/forwarding/ip_forwarding -puv $NT_CLIENT $NT_ROUTER $NT_SERVER
+exit $?
diff --git a/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum_lso.ksh b/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum_lso.ksh
new file mode 100644
index 0000000000..426c6d3614
--- /dev/null
+++ b/usr/src/test/net-tests/tests/forwarding/ip_fwd_partial_cksum_lso.ksh
@@ -0,0 +1,38 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+if [[ -z $NET_TESTS ]]; then
+ echo "NET_TESTS not set" >&2
+ exit 1
+fi
+
+. $NET_TESTS/tests/net_common
+. $NET_TESTS/config/ip_forwarding.config
+
+if [[ -z "$NT_CLIENT" ]]; then
+ fail "NT_CLIENT must be set"
+fi
+
+if [[ -z "$NT_ROUTER" ]]; then
+ fail "NT_ROUTER must be set"
+fi
+
+if [[ -z "$NT_SERVER" ]]; then
+ fail "NT_SERVER must be set"
+fi
+
+export NT_TNAME=$(basename $0)
+$NET_TESTS/tests/forwarding/ip_forwarding -pluv $NT_CLIENT $NT_ROUTER $NT_SERVER
+exit $?
diff --git a/usr/src/test/net-tests/tests/net_common.ksh b/usr/src/test/net-tests/tests/net_common.ksh
new file mode 100644
index 0000000000..8465ead088
--- /dev/null
+++ b/usr/src/test/net-tests/tests/net_common.ksh
@@ -0,0 +1,631 @@
+#!/usr/bin/ksh
+#
+# 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.
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+#
+# Functions shared across the network tests.
+#
+
+DEBUG=0
+
+function dbg
+{
+ typeset msg="$*"
+ if (($DEBUG == 1)); then
+ echo "DBG [$nt_tname]: $msg"
+ fi
+}
+
+function fail
+{
+ typeset msg="$*"
+ echo "FAIL [$nt_tname]: $msg" >&2
+ exit 1
+}
+
+function maybe_fail
+{
+ typeset msg=$1
+
+ if ((BAIL == 1)); then
+ fail "$msg"
+ else
+ dbg "$msg"
+ return 1
+ fi
+}
+
+function zone_exists
+{
+ typeset name=$1
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "checking for existence of zone: $name"
+ if zoneadm -z $name list > /dev/null 2>&1; then
+ dbg "found zone: $name"
+ return 0
+ else
+ dbg "zone not found: $name"
+ return 1
+ fi
+}
+
+function zone_running
+{
+ typeset name=$1
+ typeset state=$(zoneadm -z $name list -p | awk -F: '{ print $3 }')
+ typeset err="zone $name is not running"
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "check if zone $name is running"
+ dbg "state of zone $name: $state"
+ if [[ "$state" == "running" ]]; then
+ dbg "zone $name is running"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function simnet_exists
+{
+ typeset name=$1
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if dladm show-simnet $name > /dev/null 2>&1; then
+ dbg "simnet $name found"
+ return 0
+ else
+ dbg "simnet $name not found"
+ return 1
+ fi
+}
+
+function create_simnet
+{
+ typeset name=$1
+ typeset err="failed to create simnet $name"
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "creating simnet $name"
+ if simnet_exists $name; then
+ dbg "simnet $name already exists"
+ maybe_fail "$err"
+ return 1
+ fi
+
+ if dladm create-simnet > /dev/null $name; then
+ dbg "created simnet $name"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function delete_simnet
+{
+ typeset name=$1
+ typeset err="failed to delete simnet $name"
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "deleting simnet $name"
+ if ! simnet_exists $name; then
+ dbg "simnet $name doesn't exist"
+ return 1
+ fi
+
+ if dladm delete-simnet $name; then
+ dbg "simnet $name deleted"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function link_simnets
+{
+ typeset sim1=$1
+ typeset sim2=$2
+ typeset err="failed to link simnet $sim1 to $sim2"
+
+ if (($# != 2)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "linking simnet $sim1 to $sim2"
+ if dladm modify-simnet -p $sim2 $sim1 > /dev/null; then
+ dbg "linked simnet $sim1 to $sim2"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function vnic_exists
+{
+ typeset name=$1
+ typeset vid=$2
+ typeset over=$3
+ typeset zone=$4
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if dladm show-vnic -z $zone $name > /dev/null 2>&1; then
+ typeset avid=$(dladm show-vnic -z $zone -p -o vid $name)
+ typeset aover=$(dladm show-vnic -z $zone -p -o over $name)
+ if (($avid == $vid)) && [ $aover == $over ]; then
+ return 0
+ else
+ return 1
+ fi
+ else
+ return 1
+ fi
+}
+
+function create_vnic
+{
+ typeset name=$1
+ typeset over=$2
+ typeset vid=$3
+ typeset zone=$4
+ typeset r=1
+ typeset vid_opt=""
+ typeset vnic_info="$name, vid: $vid, over: $over, zone: $zone"
+ typeset err="failed to create VNIC: $vnic_info"
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if ((vid != 0)); then
+ vid_opt="-v $vid"
+ fi
+
+ dbg "creating VNIC: $vnic_info"
+ if dladm create-vnic -t -p zone=$zone -l $over \
+ $vid_opt $name > /dev/null 2>&1
+ then
+ dbg "created VNIC: $vnic_info"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function delete_vnic
+{
+ typeset name=$1
+ typeset over=$2
+ typeset vid=$3
+ typeset zone=$4
+ typeset vnic_info="$name, vid: $vid, over: $over, zone: $zone"
+ typeset err1="failed to assign VNIC $name from $zone to GZ"
+ typeset err2="failed to delete VNIC: $vnic_info"
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if ! vnic_exists $name $vid $over $zone; then
+ dbg "VNIC doesn't exist: $vnic_info"
+ return 0
+ fi
+
+ dbg "assigning VNIC $name from $zone to GZ"
+ if ! dladm set-linkprop -t -z $zone -p zone=global $name; then
+ maybe_fail "$err1"
+ return 1
+ fi
+
+ dbg "deleting VNIC: $vnic_info"
+ if dladm delete-vnic $name > /dev/null; then
+ dbg "deleted VNIC: $vnic_info"
+ return 0
+ fi
+
+ maybe_fail "$err2"
+}
+
+function create_addr
+{
+ typeset zone=$1
+ typeset vnic=$2
+ typeset ip=$3
+ typeset ipname=${vnic}/v4
+
+ if (($# != 3)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone ipadm create-addr -t -T static -a $ip \
+ $ipname > /dev/null
+ then
+ dbg "created addr $ipname ($ip) in zone $zone"
+ return 0
+ fi
+
+ maybe_fail "failed to create addr $ipname ($ip) in zone $zone"
+}
+
+function create_addr6
+{
+ typeset zone=$1
+ typeset vnic=$2
+ typeset ip=$3
+ typeset ll_name=${vnic}/v6
+ typeset uni_name=${vnic}/v6add
+ typeset err1="failed to create link-local addr $ll_name in zone $zone"
+ typeset err2="failed to create unicast addr $uni_name in zone $zone"
+
+ if (($# != 3)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone ipadm create-addr -t -T addrconf $ll_name; then
+ dbg "created link-local addr $ll_name in zone $zone"
+ else
+ maybe_fail "$err1"
+ return 1
+ fi
+
+ if zlogin $zone ipadm create-addr -t -T static -a $ip/64 $uni_name; then
+ dbg "created unicast addr $uni_name in zone $zone"
+ else
+ maybe_fail "$err2"
+ fi
+}
+
+function delete_addr
+{
+ typeset zone=$1
+ typeset ifname=$2
+ typeset version=$3
+ typeset ipname=$ifname/$version
+
+ if (($# != 3)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone ipadm show-addr $ipname > /dev/null 2>&1; then
+ if zlogin $zone ipadm delete-addr $ipname > /dev/null; then
+ dbg "deleted addr $ipname in zone $zone"
+ else
+ maybe_fail "failed to delete addr $ipname in zone $zone"
+ return 1
+ fi
+ else
+ dbg "addr $ipname doesn't exist in zone $zone"
+ fi
+
+ if [[ "v6" == "$version" ]]; then
+ typeset ipname=$ifname/v6add
+ typeset err="failed to delete addr $ipname in zone $zone"
+
+ if zlogin $zone ipadm show-addr $ipname > /dev/null 2>&1; then
+ if zlogin $zone ipadm delete-addr $ipname > /dev/null
+ then
+ dbg "deleted addr $ipname in zone $zone"
+ else
+ maybe_fail "$err"
+ fi
+ else
+ dbg "addr $ipname doesn't exist in zone $zone"
+ fi
+ fi
+}
+
+function delete_if
+{
+ typeset zone=$1
+ typeset ifname=$2
+ typeset err="failed to delete interface $ifname in zone $zone"
+
+ if (($# != 2)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone ipadm show-if $ifname > /dev/null 2>&1; then
+ if zlogin $zone ipadm delete-if $ifname > /dev/null; then
+ dbg "deleted interface $ifname in zone $zone"
+ else
+ maybe_fail "$err"
+ fi
+ else
+ dbg "interface $ifname doesn't exist in zone $zone"
+ fi
+}
+
+function ip_fwd_enable
+{
+ typeset zone=$1
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone routeadm -p ipv4-forwarding | \
+ egrep 'current=enabled' > /dev/null
+ then
+ dbg "IPv4 forwarding already enabled for $zone"
+ else
+ if zlogin $zone routeadm -ue ipv4-forwarding; then
+ dbg "enabled IPv4 forwarding for $zone"
+ else
+ maybe_fail "failed to enable IPv4 forwarding for $zone"
+ return 1
+ fi
+ fi
+
+ if zlogin $zone routeadm -p ipv6-forwarding | \
+ egrep 'current=enabled' > /dev/null
+ then
+ dbg "IPv6 forwarding already enabled for $zone"
+ else
+ if zlogin $zone routeadm -ue ipv6-forwarding; then
+ dbg "enabled IPv6 forwarding for $zone"
+ else
+ maybe_fail "failed to enable IPv6 forwarding for $zone"
+ fi
+ fi
+}
+
+function ip_fwd_disable
+{
+ typeset zone=$1
+
+ if (($# != 1)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone routeadm -p ipv4-forwarding | \
+ egrep 'current=disabled' > /dev/null
+ then
+ dbg "IPv4 forwarding already disabled for $zone"
+ else
+ if zlogin $zone routeadm -ud ipv4-forwarding; then
+ dbg "disabled IPv4 forwarding in $zone"
+ else
+ maybe_fail "failed to disable IPv4 forwarding in $zone"
+ return 1
+ fi
+ fi
+
+ if zlogin $zone routeadm -p ipv6-forwarding | \
+ egrep 'current=disabled' > /dev/null
+ then
+ dbg "IPv6 forwarding already disabled for $zone"
+ else
+ if zlogin $zone routeadm -ud ipv6-forwarding; then
+ dbg "disabled IPv6 forwarding in $zone"
+ else
+ maybe_fail "failed to disable IPv6 forwarding in $zone"
+ fi
+ fi
+}
+
+function add_route
+{
+ typeset zone=$1
+ typeset dest=$2
+ typeset net=$3
+ typeset gateway=$4
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone route -n add $net $gateway > /dev/null; then
+ dbg "added route $gateway => $net to $zone"
+ return 0
+ fi
+
+ maybe_fail "failed to add route $gateway => $net to $zone"
+}
+
+function add_route6
+{
+ typeset zone=$1
+ typeset dest=$2
+ typeset net=$3
+ typeset gateway=$4
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if zlogin $zone route -n add -inet6 $net $gateway > /dev/null
+ then
+ dbg "added route $gateway => $net to $zone"
+ return 0
+ fi
+
+ maybe_fail "failed to add route $gateway => $net to $zone"
+}
+
+function rm_route
+{
+ typeset zone=$1
+ typeset dest=$2
+ typeset net=$3
+ typeset gateway=$4
+ typeset gw=$(zlogin $zone route -n get $dest | \
+ grep gateway | awk '{ print $2 }')
+ typeset err="failed to remove route $gateway => $net from $zone"
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if [[ "$gw" == "$gateway" ]]; then
+ if zlogin $zone route -n delete $net $gateway > /dev/null
+ then
+ dbg "removed route $gateway => $net from $zone"
+ else
+ maybe_fail "$err"
+ fi
+ else
+ dbg "$zone already lacked route $gateway => $net"
+ fi
+}
+
+function rm_route6
+{
+ typeset zone=$1
+ typeset dest=$2
+ typeset net=$3
+ typeset gateway=$4
+ typeset gw=$(zlogin $zone route -n get -inet6 $dest | \
+ grep gateway | awk '{ print $2 }')
+ typeset err="failed to remove route $gateway => $net from $zone"
+
+ if (($# != 4)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ if [[ "$gw" == "$gateway" ]]; then
+ if zlogin $zone route -n delete -inet6 $net $gateway > /dev/null
+ then
+ dbg "removed route $gateway => $net from $zone"
+ else
+ maybe_fail "$err"
+ fi
+ else
+ dbg "$zone already lacked route $gateway => $net"
+ fi
+}
+
+function set_linkprop
+{
+ typeset link=$1
+ typeset prop=$2
+ typeset val=$3
+ typeset err="failed to set $link prop: $prop=$val"
+
+ if (($# != 3)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "attempt to set $link prop: $prop=$val"
+ if dladm set-linkprop -p $prop=$val $link; then
+ dbg "set $link prop: $prop=$val"
+ return 0
+ fi
+
+ maybe_fail "$err"
+}
+
+function ping
+{
+ typeset zone=$1
+ typeset src=$2
+ typeset dst=$3
+ typeset info="$src -> $dst"
+
+ if (($# != 3)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "ping: $info"
+ if zlogin $zone ping $dst > /dev/null 2>&1; then
+ dbg "successful ping: $info"
+ return 0
+ fi
+
+ maybe_fail "could not ping: $info"
+}
+
+function ping_udp
+{
+ typeset client=$1
+ typeset client_ip=$2
+ typeset server_ip=$3
+ typeset size=$4
+ typeset num=$5
+ typeset info="$client_ip -> $server_ip (size: $size)"
+
+ if (($# != 5)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "UDP ping: $info"
+ if zlogin $client ping -ns -U $server_ip $size $num > /dev/null; then
+ dbg "UDP ping passed: $info"
+ return 0
+ fi
+
+ maybe_fail "UDP ping failed: $info"
+}
+
+function start_server
+{
+ typeset zone=$1
+ typeset type=$2
+ typeset ip=$3
+ typeset port=$4
+ typeset ofile=$5
+
+ if (($# != 5)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ dbg "start server $rfile"
+ zlogin $zone \
+ /usr/bin/socat -u ${type}-LISTEN:$port,bind=[$ip],reuseaddr \
+ CREATE:$ofile &
+ listener_ppid=$!
+ dbg "listener PPID: $listener_ppid, zone $zone"
+}
+
+function wait_for_pid
+{
+ typeset pid=$1
+ typeset seconds=$2
+ typeset s=0
+
+ if (($# != 2)); then
+ fail "$0: incorrect number of args provided"
+ fi
+
+ while true; do
+ if kill -0 $pid > /dev/null 2>&1; then
+ if ((seconds == s)); then
+ maybe_fail "timed out waiting for pid $pid"
+ return 1
+ fi
+ dbg "waiting for pid $pid"
+ sleep 1
+ ((s++))
+ else
+ return 0
+ fi
+ done
+}
diff --git a/usr/src/uts/common/inet/ip/ip6.c b/usr/src/uts/common/inet/ip/ip6.c
index 9c7f6b4000..afaf01024f 100644
--- a/usr/src/uts/common/inet/ip/ip6.c
+++ b/usr/src/uts/common/inet/ip/ip6.c
@@ -22,6 +22,7 @@
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1990 Mentat Inc.
* Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/types.h>
@@ -2730,108 +2731,15 @@ done:
}
/*
- * Try to determine where and what are the IPv6 header length and
- * pointer to nexthdr value for the upper layer protocol (or an
- * unknown next hdr).
- *
- * Parameters returns a pointer to the nexthdr value;
- * Must handle malformed packets of various sorts.
- * Function returns failure for malformed cases.
- */
-boolean_t
-ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr,
- uint8_t **nexthdrpp)
-{
- uint16_t length;
- uint_t ehdrlen;
- uint8_t *nexthdrp;
- uint8_t *whereptr;
- uint8_t *endptr;
- ip6_dest_t *desthdr;
- ip6_rthdr_t *rthdr;
- ip6_frag_t *fraghdr;
-
- ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
- length = IPV6_HDR_LEN;
- whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
- endptr = mp->b_wptr;
-
- nexthdrp = &ip6h->ip6_nxt;
- while (whereptr < endptr) {
- /* Is there enough left for len + nexthdr? */
- if (whereptr + MIN_EHDR_LEN > endptr)
- break;
-
- switch (*nexthdrp) {
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- /* Assumes the headers are identical for hbh and dst */
- desthdr = (ip6_dest_t *)whereptr;
- ehdrlen = 8 * (desthdr->ip6d_len + 1);
- if ((uchar_t *)desthdr + ehdrlen > endptr)
- return (B_FALSE);
- nexthdrp = &desthdr->ip6d_nxt;
- break;
- case IPPROTO_ROUTING:
- rthdr = (ip6_rthdr_t *)whereptr;
- ehdrlen = 8 * (rthdr->ip6r_len + 1);
- if ((uchar_t *)rthdr + ehdrlen > endptr)
- return (B_FALSE);
- nexthdrp = &rthdr->ip6r_nxt;
- break;
- case IPPROTO_FRAGMENT:
- fraghdr = (ip6_frag_t *)whereptr;
- ehdrlen = sizeof (ip6_frag_t);
- if ((uchar_t *)&fraghdr[1] > endptr)
- return (B_FALSE);
- nexthdrp = &fraghdr->ip6f_nxt;
- break;
- case IPPROTO_NONE:
- /* No next header means we're finished */
- default:
- *hdr_length_ptr = length;
- *nexthdrpp = nexthdrp;
- return (B_TRUE);
- }
- length += ehdrlen;
- whereptr += ehdrlen;
- *hdr_length_ptr = length;
- *nexthdrpp = nexthdrp;
- }
- switch (*nexthdrp) {
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- case IPPROTO_ROUTING:
- case IPPROTO_FRAGMENT:
- /*
- * If any know extension headers are still to be processed,
- * the packet's malformed (or at least all the IP header(s) are
- * not in the same mblk - and that should never happen.
- */
- return (B_FALSE);
-
- default:
- /*
- * If we get here, we know that all of the IP headers were in
- * the same mblk, even if the ULP header is in the next mblk.
- */
- *hdr_length_ptr = length;
- *nexthdrpp = nexthdrp;
- return (B_TRUE);
- }
-}
-
-/*
* Return the length of the IPv6 related headers (including extension headers)
* Returns a length even if the packet is malformed.
*/
-int
+uint16_t
ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h)
{
uint16_t hdr_len;
- uint8_t *nexthdrp;
- (void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, &nexthdrp);
+ (void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, NULL);
return (hdr_len);
}
diff --git a/usr/src/uts/common/inet/ip/ip6_input.c b/usr/src/uts/common/inet/ip/ip6_input.c
index 96cc281da5..284eab8a70 100644
--- a/usr/src/uts/common/inet/ip/ip6_input.c
+++ b/usr/src/uts/common/inet/ip/ip6_input.c
@@ -977,9 +977,6 @@ ire_recv_forward_v6(ire_t *ire, mblk_t *mp, void *iph_arg, ip_recv_attr_t *ira)
ira->ira_pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
}
- /* Packet is being forwarded. Turning off hwcksum flag. */
- DB_CKSUMFLAGS(mp) = 0;
-
/*
* Per RFC 3513 section 2.5.2, we must not forward packets with
* an unspecified source address.
@@ -1101,7 +1098,14 @@ ip_forward_xmit_v6(nce_t *nce, mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira,
BUMP_MIB(dst_ill->ill_ip_mib, ipIfStatsHCOutForwDatagrams);
- if (pkt_len > mtu) {
+ /*
+ * If the packet arrived via MAC-loopback then it might be an
+ * LSO packet; in this case the MAC layer will take care to
+ * segment it. Otherwise, we have a normal packet that is
+ * being forwarded from a source interface with an MTU larger
+ * than the destination's; in this case IP must fragment it.
+ */
+ if (pkt_len > mtu && (DB_CKSUMFLAGS(mp) & HW_LSO) == 0) {
BUMP_MIB(dst_ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("ipIfStatsOutFragFails", mp, dst_ill);
if (iraflags & IRAF_SYSTEM_LABELED) {
diff --git a/usr/src/uts/common/inet/ip/ip_input.c b/usr/src/uts/common/inet/ip/ip_input.c
index e2e7dca22c..7cfffbc84b 100644
--- a/usr/src/uts/common/inet/ip/ip_input.c
+++ b/usr/src/uts/common/inet/ip/ip_input.c
@@ -1003,8 +1003,26 @@ ire_recv_forward_v4(ire_t *ire, mblk_t *mp, void *iph_arg, ip_recv_attr_t *ira)
ira->ira_pktlen = ntohs(ipha->ipha_length);
}
- /* Packet is being forwarded. Turning off hwcksum flag. */
- DB_CKSUMFLAGS(mp) = 0;
+ /*
+ * The packet came here via MAC-loopback so we must reinstate
+ * its hardware IP header checksum request.
+ *
+ * IP input clears the HCK_IPV4_HDRCKSUM_OK flag. Since
+ * HCK_IPV4_HDRCKSUM_OK and HCK_IPV4_HDRCKSUM use the same
+ * value, we end up clearing the client's request to use
+ * hardware IP header checksum offload. We check for
+ * HW_LOCAL_MAC and an IP header checksum value of zero as an
+ * indicator that this packet requires reinstatement of the
+ * HCK_IPV4_HDRCKSUM flag. That said, zero is a valid
+ * checksum, and given hardware that doesn't support IP header
+ * checksum offload, this could result in the checksum being
+ * computed twice. This is fine because the checksum value is
+ * zero, and thus will retain its value on recalculation.
+ */
+ if (((DB_CKSUMFLAGS(mp) & HW_LOCAL_MAC) != 0) &&
+ ipha->ipha_hdr_checksum == 0) {
+ DB_CKSUMFLAGS(mp) |= HCK_IPV4_HDRCKSUM;
+ }
/*
* Martian Address Filtering [RFC 1812, Section 5.3.7]
@@ -1138,9 +1156,17 @@ ip_forward_xmit_v4(nce_t *nce, ill_t *ill, mblk_t *mp, ipha_t *ipha,
return;
}
ipha->ipha_ttl--;
- /* Adjust the checksum to reflect the ttl decrement. */
- sum = (int)ipha->ipha_hdr_checksum + IP_HDR_CSUM_TTL_ADJUST;
- ipha->ipha_hdr_checksum = (uint16_t)(sum + (sum >> 16));
+ /*
+ * Adjust the checksum to reflect the TTL decrement unless
+ * the packet expects IP header checksum offload; in which
+ * case we delay its calculation until later. Such a packet
+ * occurs when it travels via MAC-loopback over a link
+ * exposing HCKSUM_IPHDRCKSUM.
+ */
+ if ((DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM) == 0) {
+ sum = (int)ipha->ipha_hdr_checksum + IP_HDR_CSUM_TTL_ADJUST;
+ ipha->ipha_hdr_checksum = (uint16_t)(sum + (sum >> 16));
+ }
/* Check if there are options to update */
if (iraflags & IRAF_IPV4_OPTIONS) {
@@ -1175,7 +1201,14 @@ ip_forward_xmit_v4(nce_t *nce, ill_t *ill, mblk_t *mp, ipha_t *ipha,
ixaflags = IXAF_IS_IPV4 | IXAF_NO_DEV_FLOW_CTL;
- if (pkt_len > mtu) {
+ /*
+ * If the packet arrived via MAC-loopback, then it might be an
+ * LSO packet; in this case the MAC layer will take care to
+ * segment it. Otherwise, we have a normal packet that is
+ * being forwarded from a source interface with an MTU larger
+ * than the desination's; in this case IP must fragment it.
+ */
+ if (pkt_len > mtu && (DB_CKSUMFLAGS(mp) & HW_LSO) == 0) {
/*
* It needs fragging on its way out. If we haven't
* verified the header checksum yet we do it now since
diff --git a/usr/src/uts/common/inet/ip6.h b/usr/src/uts/common/inet/ip6.h
index 4f5b81c12f..01c25b52b5 100644
--- a/usr/src/uts/common/inet/ip6.h
+++ b/usr/src/uts/common/inet/ip6.h
@@ -23,6 +23,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _INET_IP6_H
@@ -255,7 +256,7 @@ extern in6_addr_t ip_get_dst_v6(ip6_t *, const mblk_t *, boolean_t *);
extern ip6_rthdr_t *ip_find_rthdr_v6(ip6_t *, uint8_t *);
extern boolean_t ip_hdr_length_nexthdr_v6(mblk_t *, ip6_t *,
uint16_t *, uint8_t **);
-extern int ip_hdr_length_v6(mblk_t *, ip6_t *);
+extern uint16_t ip_hdr_length_v6(mblk_t *, ip6_t *);
extern uint32_t ip_massage_options_v6(ip6_t *, ip6_rthdr_t *, netstack_t *);
extern void ip_forward_xmit_v6(nce_t *, mblk_t *, ip6_t *, ip_recv_attr_t *,
uint32_t, uint32_t);
diff --git a/usr/src/uts/common/io/bridge.c b/usr/src/uts/common/io/bridge.c
index 375d166972..276ad238f2 100644
--- a/usr/src/uts/common/io/bridge.c
+++ b/usr/src/uts/common/io/bridge.c
@@ -23,7 +23,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016 by Delphix. All rights reserved.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -2033,7 +2033,7 @@ done:
KIINCR(bki_forwards);
KLPINCR(blpsend, bkl_xmit);
- MAC_RING_TX(blpsend->bl_mh, NULL, mpsend,
+ mpsend = mac_ring_tx(blpsend->bl_mh, NULL,
mpsend);
freemsg(mpsend);
mpsend = next;
@@ -2189,7 +2189,7 @@ done:
mpcopy);
}
- MAC_RING_TX(blpsend->bl_mh, NULL, mpsend,
+ mpsend = mac_ring_tx(blpsend->bl_mh, NULL,
mpsend);
freemsg(mpsend);
mpsend = next;
@@ -2571,7 +2571,7 @@ bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext)
(blp->bl_flags & BLF_SDUFAIL)))) {
KIINCR(bki_sent);
KLINCR(bkl_xmit);
- MAC_RING_TX(blp->bl_mh, rh, mpnext, mp);
+ mp = mac_ring_tx(blp->bl_mh, rh, mpnext);
return (mp);
}
@@ -2629,7 +2629,7 @@ bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext)
B_FALSE, B_TRUE);
}
if (mp != NULL) {
- MAC_RING_TX(blp->bl_mh, rh, mp, mp);
+ mp = mac_ring_tx(blp->bl_mh, rh, mp);
if (mp == NULL) {
KIINCR(bki_sent);
KLINCR(bkl_xmit);
@@ -2695,7 +2695,7 @@ bridge_trill_decaps(bridge_link_t *blp, mblk_t *mp, uint16_t ingress_nick)
/* Deliver a copy locally as well */
if ((mpcopy = copymsg(mp)) != NULL)
mac_rx_common(blp->bl_mh, NULL, mpcopy);
- MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
+ mp = mac_ring_tx(blp->bl_mh, NULL, mp);
}
if (mp == NULL) {
KIINCR(bki_sent);
@@ -2716,7 +2716,7 @@ bridge_trill_output(bridge_link_t *blp, mblk_t *mp)
bridge_inst_t *bip = blp->bl_inst; /* used by macros */
mac_trill_snoop(blp->bl_mh, mp);
- MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
+ mp = mac_ring_tx(blp->bl_mh, NULL, mp);
if (mp == NULL) {
KIINCR(bki_sent);
KLINCR(bkl_xmit);
diff --git a/usr/src/uts/common/io/fcoe/fcoe_fc.c b/usr/src/uts/common/io/fcoe/fcoe_fc.c
index 42764e48d6..54402b027f 100644
--- a/usr/src/uts/common/io/fcoe/fcoe_fc.c
+++ b/usr/src/uts/common/io/fcoe/fcoe_fc.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -39,6 +40,7 @@
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/mac_client.h>
+#include <sys/strsubr.h>
/*
* FCoE header files
@@ -209,6 +211,7 @@ tx_frame:
ret_cookie = mac_tx(mac->fm_cli_handle, FRM2MBLK(frm), 0,
MAC_TX_NO_ENQUEUE, &ret_mblk);
if (ret_cookie != (mac_tx_cookie_t)NULL) {
+ frm->frm_netb = ret_mblk;
mutex_enter(&mac->fm_mutex);
(void) cv_reltimedwait(&mac->fm_tx_cv, &mac->fm_mutex,
drv_usectohz(100000), TR_CLOCK_TICK);
@@ -265,7 +268,7 @@ fcoe_alloc_netb(fcoe_port_t *eport, uint32_t fc_frame_size, uint8_t **ppfc)
static void
fcoe_free_netb(void *netb)
{
- freeb((mblk_t *)netb);
+ freemsgchain((mblk_t *)netb);
}
fcoe_frame_t *
diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c
index 12d1e9809c..e748e6d57b 100644
--- a/usr/src/uts/common/io/mac/mac.c
+++ b/usr/src/uts/common/io/mac/mac.c
@@ -1935,8 +1935,7 @@ mac_hwring_send_priv(mac_client_handle_t mch, mac_ring_handle_t rh, mblk_t *mp)
mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
mac_impl_t *mip = mcip->mci_mip;
- MAC_TX(mip, rh, mp, mcip);
- return (mp);
+ return (mac_provider_tx(mip, rh, mp, mcip));
}
/*
@@ -4713,9 +4712,9 @@ mac_group_remmac(mac_group_t *group, const uint8_t *addr)
}
/*
- * This is the entry point for packets transmitted through the bridging code.
- * If no bridge is in place, MAC_RING_TX transmits using tx ring. The 'rh'
- * pointer may be NULL to select the default ring.
+ * This is the entry point for packets transmitted through the bridge
+ * code. If no bridge is in place, mac_ring_tx() transmits via the tx
+ * ring. The 'rh' pointer may be NULL to select the default ring.
*/
mblk_t *
mac_bridge_tx(mac_impl_t *mip, mac_ring_handle_t rh, mblk_t *mp)
@@ -4732,7 +4731,7 @@ mac_bridge_tx(mac_impl_t *mip, mac_ring_handle_t rh, mblk_t *mp)
mac_bridge_ref_cb(mh, B_TRUE);
mutex_exit(&mip->mi_bridge_lock);
if (mh == NULL) {
- MAC_RING_TX(mip, rh, mp, mp);
+ mp = mac_ring_tx((mac_handle_t)mip, rh, mp);
} else {
mp = mac_bridge_tx_cb(mh, rh, mp);
mac_bridge_ref_cb(mh, B_FALSE);
@@ -8786,3 +8785,52 @@ mac_led_set(mac_handle_t mh, mac_led_mode_t desired)
return (ret);
}
+
+/*
+ * Send packets through the Tx ring ('mrh') or through the default
+ * handler if no ring is specified. Before passing the packet down to
+ * the MAC provider, emulate any hardware offloads which have been
+ * requested but are not supported by the provider.
+ */
+mblk_t *
+mac_ring_tx(mac_handle_t mh, mac_ring_handle_t mrh, mblk_t *mp)
+{
+ mac_impl_t *mip = (mac_impl_t *)mh;
+
+ if (mrh == NULL)
+ mrh = mip->mi_default_tx_ring;
+
+ if (mrh == NULL)
+ return (mip->mi_tx(mip->mi_driver, mp));
+ else
+ return (mac_hwring_tx(mrh, mp));
+}
+
+/*
+ * This is the final stop before reaching the underlying MAC provider.
+ * This is also where the bridging hook is inserted. Packets that are
+ * bridged will return through mac_bridge_tx(), with rh nulled out if
+ * the bridge chooses to send output on a different link due to
+ * forwarding.
+ */
+mblk_t *
+mac_provider_tx(mac_impl_t *mip, mac_ring_handle_t rh, mblk_t *mp,
+ mac_client_impl_t *mcip)
+{
+ /*
+ * If there is a bound Hybrid I/O share, send packets through
+ * the default tx ring. When there's a bound Hybrid I/O share,
+ * the tx rings of this client are mapped in the guest domain
+ * and not accessible from here.
+ */
+ if (mcip->mci_state_flags & MCIS_SHARE_BOUND)
+ rh = mip->mi_default_tx_ring;
+
+ if (mip->mi_promisc_list != NULL)
+ mac_promisc_dispatch(mip, mp, mcip);
+
+ if (mip->mi_bridge_link == NULL)
+ return (mac_ring_tx((mac_handle_t)mip, rh, mp));
+ else
+ return (mac_bridge_tx(mip, rh, mp));
+}
diff --git a/usr/src/uts/common/io/mac/mac_bcast.c b/usr/src/uts/common/io/mac/mac_bcast.c
index 3b674be1d0..5302b89196 100644
--- a/usr/src/uts/common/io/mac/mac_bcast.c
+++ b/usr/src/uts/common/io/mac/mac_bcast.c
@@ -240,7 +240,8 @@ mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback)
MCIP_STAT_UPDATE(src_mcip, brdcstxmt, 1);
MCIP_STAT_UPDATE(src_mcip, brdcstxmtbytes, msgdsize(mp_chain));
- MAC_TX(mip, mip->mi_default_tx_ring, mp_chain, src_mcip);
+ mp_chain = mac_provider_tx(mip, mip->mi_default_tx_ring,
+ mp_chain, src_mcip);
if (mp_chain != NULL)
freemsgchain(mp_chain);
} else {
diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c
index 50316bb81e..d2444094a9 100644
--- a/usr/src/uts/common/io/mac/mac_client.c
+++ b/usr/src/uts/common/io/mac/mac_client.c
@@ -3582,7 +3582,9 @@ mac_tx(mac_client_handle_t mch, mblk_t *mp_chain, uintptr_t hint,
srs_tx = &srs->srs_tx;
if (srs_tx->st_mode == SRS_TX_DEFAULT &&
(srs->srs_state & SRS_ENQUEUED) == 0 &&
- mip->mi_nactiveclients == 1 && mp_chain->b_next == NULL) {
+ mip->mi_nactiveclients == 1 &&
+ mp_chain->b_next == NULL &&
+ (DB_CKSUMFLAGS(mp_chain) & HW_LSO) == 0) {
uint64_t obytes;
/*
@@ -3623,8 +3625,9 @@ mac_tx(mac_client_handle_t mch, mblk_t *mp_chain, uintptr_t hint,
* receiver -- mark the packet accordingly.
*/
DB_CKSUMFLAGS(mp_chain) |= HW_LOCAL_MAC;
+ mp_chain = mac_provider_tx(mip, srs_tx->st_arg2, mp_chain,
+ mcip);
- MAC_TX(mip, srs_tx->st_arg2, mp_chain, mcip);
if (mp_chain == NULL) {
cookie = 0;
SRS_TX_STAT_UPDATE(srs, opackets, 1);
@@ -3636,7 +3639,74 @@ mac_tx(mac_client_handle_t mch, mblk_t *mp_chain, uintptr_t hint,
mutex_exit(&srs->srs_lock);
}
} else {
- cookie = srs_tx->st_func(srs, mp_chain, hint, flag, ret_mp);
+ mblk_t *mp = mp_chain;
+ mblk_t *new_head = NULL;
+ mblk_t *new_tail = NULL;
+
+ /*
+ * There are occasions where the packets arriving here
+ * may request hardware offloads that are not
+ * available from the underlying MAC provider. This
+ * currently only happens when a packet is sent across
+ * the MAC-loopback path of one MAC and then forwarded
+ * (via IP) to another MAC that lacks one or more of
+ * the hardware offloads provided by the first one.
+ * However, in the future, we may choose to pretend
+ * all MAC providers support all offloads, performing
+ * emulation on Tx as needed.
+ *
+ * We iterate each mblk in-turn, emulating hardware
+ * offloads as required. From this process, we create
+ * a new chain. The new chain may be the same as the
+ * original chain (no hardware emulation needed), a
+ * collection of new mblks (hardware emulation
+ * needed), or a mix. At this point, the chain is safe
+ * for consumption by the underlying MAC provider and
+ * is passed down to the SRS.
+ */
+ while (mp != NULL) {
+ mblk_t *next = mp->b_next;
+ mblk_t *tail = NULL;
+ const uint16_t needed =
+ (DB_CKSUMFLAGS(mp) ^ mip->mi_tx_cksum_flags) &
+ DB_CKSUMFLAGS(mp);
+
+ mp->b_next = NULL;
+
+ if (needed != 0) {
+ mac_emul_t emul = 0;
+
+ if (needed & HCK_IPV4_HDRCKSUM)
+ emul |= MAC_IPCKSUM_EMUL;
+ if (needed & (HCK_PARTIALCKSUM | HCK_FULLCKSUM))
+ emul |= MAC_HWCKSUM_EMUL;
+ if (needed & HW_LSO)
+ emul = MAC_LSO_EMUL;
+
+ mac_hw_emul(&mp, &tail, NULL, emul);
+
+ if (mp == NULL) {
+ mp = next;
+ continue;
+ }
+ }
+
+ if (new_head == NULL) {
+ new_head = mp;
+ } else {
+ new_tail->b_next = mp;
+ }
+
+ new_tail = (tail == NULL) ? mp : tail;
+ mp = next;
+ }
+
+ if (new_head == NULL) {
+ cookie = 0;
+ goto done;
+ }
+
+ cookie = srs_tx->st_func(srs, new_head, hint, flag, ret_mp);
}
done:
diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c
index cb1a76aef6..99f2f3431e 100644
--- a/usr/src/uts/common/io/mac/mac_provider.c
+++ b/usr/src/uts/common/io/mac/mac_provider.c
@@ -112,6 +112,37 @@ mac_free(mac_register_t *mregp)
}
/*
+ * Convert a MAC's offload features into the equivalent DB_CKSUMFLAGS
+ * value.
+ */
+static uint16_t
+mac_features_to_flags(mac_handle_t mh)
+{
+ uint16_t flags = 0;
+ uint32_t cap_sum = 0;
+ mac_capab_lso_t cap_lso;
+
+ if (mac_capab_get(mh, MAC_CAPAB_HCKSUM, &cap_sum)) {
+ if (cap_sum & HCKSUM_IPHDRCKSUM)
+ flags |= HCK_IPV4_HDRCKSUM;
+
+ if (cap_sum & HCKSUM_INET_PARTIAL)
+ flags |= HCK_PARTIALCKSUM;
+ else if (cap_sum & (HCKSUM_INET_FULL_V4 | HCKSUM_INET_FULL_V6))
+ flags |= HCK_FULLCKSUM;
+ }
+
+ /*
+ * We don't need the information stored in 'cap_lso', but we
+ * need to pass a non-NULL pointer to appease the driver.
+ */
+ if (mac_capab_get(mh, MAC_CAPAB_LSO, &cap_lso))
+ flags |= HW_LSO;
+
+ return (flags);
+}
+
+/*
* mac_register() is how drivers register new MACs with the GLDv3
* framework. The mregp argument is allocated by drivers using the
* mac_alloc() function, and can be freed using mac_free() immediately upon
@@ -341,9 +372,13 @@ mac_register(mac_register_t *mregp, mac_handle_t *mhp)
mip, 0, &p0, TS_RUN, minclsyspri);
/*
- * Initialize the capabilities
+ * Cache the DB_CKSUMFLAGS that this MAC supports.
*/
+ mip->mi_tx_cksum_flags = mac_features_to_flags((mac_handle_t)mip);
+ /*
+ * Initialize the capabilities
+ */
bzero(&mip->mi_rx_rings_cap, sizeof (mac_capab_rings_t));
bzero(&mip->mi_tx_rings_cap, sizeof (mac_capab_rings_t));
diff --git a/usr/src/uts/common/io/mac/mac_sched.c b/usr/src/uts/common/io/mac/mac_sched.c
index 0e62f828a9..c4086d6bb6 100644
--- a/usr/src/uts/common/io/mac/mac_sched.c
+++ b/usr/src/uts/common/io/mac/mac_sched.c
@@ -1348,7 +1348,7 @@ int mac_srs_worker_wakeup_ticks = 0;
mutex_exit(&srs->srs_bw->mac_bw_lock);
#define MAC_TX_SRS_DROP_MESSAGE(srs, chain, cookie, s) { \
- mac_drop_pkt((chain), (s)); \
+ mac_drop_chain((chain), (s)); \
/* increment freed stats */ \
(srs)->srs_tx.st_stat.mts_sdrops++; \
(cookie) = (mac_tx_cookie_t)(srs); \
@@ -4355,7 +4355,7 @@ mac_tx_send(mac_client_handle_t mch, mac_ring_handle_t ring, mblk_t *mp_chain,
*/
DB_CKSUMFLAGS(mp) |= HW_LOCAL_MAC;
CHECK_VID_AND_ADD_TAG(mp);
- MAC_TX(mip, ring, mp, src_mcip);
+ mp = mac_provider_tx(mip, ring, mp, src_mcip);
/*
* If the driver is out of descriptors and does a
@@ -4467,7 +4467,7 @@ mac_tx_send(mac_client_handle_t mch, mac_ring_handle_t ring, mblk_t *mp_chain,
* Unknown destination, send via the underlying
* NIC.
*/
- MAC_TX(mip, ring, mp, src_mcip);
+ mp = mac_provider_tx(mip, ring, mp, src_mcip);
if (mp != NULL) {
/*
* Adjust for the last packet that
diff --git a/usr/src/uts/common/io/mac/mac_util.c b/usr/src/uts/common/io/mac/mac_util.c
index 334d1d034b..61671a8886 100644
--- a/usr/src/uts/common/io/mac/mac_util.c
+++ b/usr/src/uts/common/io/mac/mac_util.c
@@ -50,6 +50,7 @@
#include <inet/ipsecah.h>
#include <inet/tcp.h>
#include <inet/udp_impl.h>
+#include <inet/sctp_ip.h>
/*
* The next two functions are used for dropping packets or chains of
@@ -157,6 +158,208 @@ mac_copymsgchain_cksum(mblk_t *mp)
}
/*
+ * Calculate the ULP checksum for IPv4. Return true if the calculation
+ * was successful, or false if an error occurred. If the later, place
+ * an error message into '*err'.
+ */
+static boolean_t
+mac_sw_cksum_ipv4(mblk_t *mp, uint32_t ip_hdr_offset, ipha_t *ipha,
+ const char **err)
+{
+ const uint8_t proto = ipha->ipha_protocol;
+ size_t len;
+ const uint32_t ip_hdr_sz = IPH_HDR_LENGTH(ipha);
+ /* ULP offset from start of L2. */
+ const uint32_t ulp_offset = ip_hdr_offset + ip_hdr_sz;
+ ipaddr_t src, dst;
+ uint32_t cksum;
+ uint16_t *up;
+
+ /*
+ * We need a pointer to the ULP checksum. We're assuming the
+ * ULP checksum pointer resides in the first mblk. Our native
+ * TCP stack should always put the headers in the first mblk,
+ * but currently we have no way to guarantee that other
+ * clients don't spread headers (or even header fields) across
+ * mblks.
+ */
+ switch (proto) {
+ case IPPROTO_TCP:
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (tcph_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (tcph_t))) {
+ *err = "mblk doesn't contain TCP header";
+ goto bail;
+ }
+
+ up = IPH_TCPH_CHECKSUMP(ipha, ip_hdr_sz);
+ cksum = IP_TCP_CSUM_COMP;
+ break;
+
+ case IPPROTO_UDP:
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (udpha_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (udpha_t))) {
+ *err = "mblk doesn't contain UDP header";
+ goto bail;
+ }
+
+ up = IPH_UDPH_CHECKSUMP(ipha, ip_hdr_sz);
+ cksum = IP_UDP_CSUM_COMP;
+ break;
+
+ case IPPROTO_SCTP: {
+ sctp_hdr_t *sctph;
+
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (sctp_hdr_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (sctp_hdr_t))) {
+ *err = "mblk doesn't contain SCTP header";
+ goto bail;
+ }
+
+ sctph = (sctp_hdr_t *)(mp->b_rptr + ulp_offset);
+ sctph->sh_chksum = 0;
+ sctph->sh_chksum = sctp_cksum(mp, ulp_offset);
+ return (B_TRUE);
+ }
+
+ default:
+ *err = "unexpected protocol";
+ goto bail;
+
+ }
+
+ /* Pseudo-header checksum. */
+ src = ipha->ipha_src;
+ dst = ipha->ipha_dst;
+ len = ntohs(ipha->ipha_length) - ip_hdr_sz;
+
+ cksum += (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF);
+ cksum += htons(len);
+
+ /*
+ * We have already accounted for the pseudo checksum above.
+ * Make sure the ULP checksum field is zero before computing
+ * the rest.
+ */
+ *up = 0;
+ cksum = IP_CSUM(mp, ulp_offset, cksum);
+ *up = (uint16_t)(cksum ? cksum : ~cksum);
+
+ return (B_TRUE);
+
+bail:
+ return (B_FALSE);
+}
+
+/*
+ * Calculate the ULP checksum for IPv6. Return true if the calculation
+ * was successful, or false if an error occurred. If the later, place
+ * an error message into '*err'.
+ */
+static boolean_t
+mac_sw_cksum_ipv6(mblk_t *mp, uint32_t ip_hdr_offset, const char **err)
+{
+ ip6_t* ip6h = (ip6_t *)(mp->b_rptr + ip_hdr_offset);
+ const uint8_t proto = ip6h->ip6_nxt;
+ const uint16_t *iphs = (uint16_t *)ip6h;
+ /* ULP offset from start of L2. */
+ uint32_t ulp_offset;
+ size_t len;
+ uint32_t cksum;
+ uint16_t *up;
+ uint16_t ip_hdr_sz;
+
+ if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ip_hdr_sz, NULL)) {
+ *err = "malformed IPv6 header";
+ goto bail;
+ }
+
+ ulp_offset = ip_hdr_offset + ip_hdr_sz;
+
+ /*
+ * We need a pointer to the ULP checksum. We're assuming the
+ * ULP checksum pointer resides in the first mblk. Our native
+ * TCP stack should always put the headers in the first mblk,
+ * but currently we have no way to guarantee that other
+ * clients don't spread headers (or even header fields) across
+ * mblks.
+ */
+ switch (proto) {
+ case IPPROTO_TCP:
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (tcph_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (tcph_t))) {
+ *err = "mblk doesn't contain TCP header";
+ goto bail;
+ }
+
+ up = IPH_TCPH_CHECKSUMP(ip6h, ip_hdr_sz);
+ cksum = IP_TCP_CSUM_COMP;
+ break;
+
+ case IPPROTO_UDP:
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (udpha_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (udpha_t))) {
+ *err = "mblk doesn't contain UDP header";
+ goto bail;
+ }
+
+ up = IPH_UDPH_CHECKSUMP(ip6h, ip_hdr_sz);
+ cksum = IP_UDP_CSUM_COMP;
+ break;
+
+ case IPPROTO_SCTP: {
+ sctp_hdr_t *sctph;
+
+ ASSERT3U(MBLKL(mp), >=, (ulp_offset + sizeof (sctp_hdr_t)));
+ if (MBLKL(mp) < (ulp_offset + sizeof (sctp_hdr_t))) {
+ *err = "mblk doesn't contain SCTP header";
+ goto bail;
+ }
+
+ sctph = (sctp_hdr_t *)(mp->b_rptr + ulp_offset);
+ /*
+ * Zero out the checksum field to ensure proper
+ * checksum calculation.
+ */
+ sctph->sh_chksum = 0;
+ sctph->sh_chksum = sctp_cksum(mp, ulp_offset);
+ return (B_TRUE);
+ }
+
+ default:
+ *err = "unexpected protocol";
+ goto bail;
+ }
+
+ /*
+ * The payload length includes the payload and the IPv6
+ * extension headers; the idea is to subtract the extension
+ * header length to get the real payload length.
+ */
+ len = ntohs(ip6h->ip6_plen) - (ip_hdr_sz - IPV6_HDR_LEN);
+ cksum += len;
+
+ /*
+ * We accumulate the pseudo header checksum in cksum; then we
+ * call IP_CSUM to compute the checksum over the payload.
+ */
+ cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] + iphs[8] + iphs[9] +
+ iphs[10] + iphs[11] + iphs[12] + iphs[13] + iphs[14] + iphs[15] +
+ iphs[16] + iphs[17] + iphs[18] + iphs[19];
+ cksum = IP_CSUM(mp, ulp_offset, cksum);
+
+ /* For UDP/IPv6 a zero UDP checksum is not allowed. Change to 0xffff */
+ if (proto == IPPROTO_UDP && cksum == 0)
+ cksum = ~cksum;
+
+ *up = (uint16_t)cksum;
+
+ return (B_TRUE);
+
+bail:
+ return (B_FALSE);
+}
+
+/*
* Perform software checksum on a single message, if needed. The
* emulation performed is determined by an intersection of the mblk's
* flags and the emul flags requested. The emul flags are documented
@@ -167,12 +370,10 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
{
mblk_t *skipped_hdr = NULL;
uint32_t flags, start, stuff, end, value;
- uint16_t len;
- uint32_t offset;
+ uint32_t ip_hdr_offset;
uint16_t etype;
+ size_t ip_hdr_sz;
struct ether_header *ehp;
- ipha_t *ipha;
- uint8_t proto;
const char *err = "";
/*
@@ -202,21 +403,31 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
evhp = (struct ether_vlan_header *)mp->b_rptr;
etype = ntohs(evhp->ether_type);
- offset = sizeof (struct ether_vlan_header);
+ ip_hdr_offset = sizeof (struct ether_vlan_header);
} else {
etype = ntohs(ehp->ether_type);
- offset = sizeof (struct ether_header);
+ ip_hdr_offset = sizeof (struct ether_header);
}
/*
- * If this packet isn't IPv4, then leave it alone. We still
- * need to add IPv6 support and we don't want to affect non-IP
- * traffic like ARP.
+ * If this packet isn't IP, then leave it alone. We don't want
+ * to affect non-IP traffic like ARP. Assume the IP header
+ * doesn't include any options, for now. We will use the
+ * correct size later after we know there are enough bytes to
+ * at least fill out the basic header.
*/
- if (etype != ETHERTYPE_IP)
+ switch (etype) {
+ case ETHERTYPE_IP:
+ ip_hdr_sz = sizeof (ipha_t);
+ break;
+ case ETHERTYPE_IPV6:
+ ip_hdr_sz = sizeof (ip6_t);
+ break;
+ default:
return (mp);
+ }
- ASSERT3U(MBLKL(mp), >=, offset);
+ ASSERT3U(MBLKL(mp), >=, ip_hdr_offset);
/*
* If the first mblk of this packet contains only the ethernet
@@ -224,8 +435,8 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
* contained in only a single mblk can then use the fastpaths
* tuned to that possibility.
*/
- if (MBLKL(mp) == offset) {
- offset -= MBLKL(mp);
+ if (MBLKL(mp) == ip_hdr_offset) {
+ ip_hdr_offset -= MBLKL(mp);
/* This is guaranteed by mac_hw_emul(). */
ASSERT3P(mp->b_cont, !=, NULL);
skipped_hdr = mp;
@@ -238,8 +449,8 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
* this assumption but it's prudent to guard our future
* clients that might not honor this contract.
*/
- ASSERT3U(MBLKL(mp), >=, offset + sizeof (ipha_t));
- if (MBLKL(mp) < (offset + sizeof (ipha_t))) {
+ ASSERT3U(MBLKL(mp), >=, ip_hdr_offset + ip_hdr_sz);
+ if (MBLKL(mp) < (ip_hdr_offset + ip_hdr_sz)) {
err = "mblk doesn't contain IP header";
goto bail;
}
@@ -268,86 +479,12 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
mp = tmp;
}
- ipha = (ipha_t *)(mp->b_rptr + offset);
+ if (etype == ETHERTYPE_IP) {
+ ipha_t *ipha = (ipha_t *)(mp->b_rptr + ip_hdr_offset);
- /*
- * This code assumes a "simple" IP header (20 bytes, no
- * options). IPv4 options are mostly a historic artifact. The
- * one slight exception is Router Alert, but we don't expect
- * such a packet to land here.
- */
- proto = ipha->ipha_protocol;
- ASSERT(ipha->ipha_version_and_hdr_length == IP_SIMPLE_HDR_VERSION);
- if (ipha->ipha_version_and_hdr_length != IP_SIMPLE_HDR_VERSION) {
- err = "not simple IP header";
- goto bail;
- }
-
- switch (proto) {
- case IPPROTO_TCP:
- ASSERT3U(MBLKL(mp), >=,
- (offset + sizeof (ipha_t) + sizeof (tcph_t)));
- if (MBLKL(mp) < (offset + sizeof (ipha_t) + sizeof (tcph_t))) {
- err = "mblk doesn't contain TCP header";
- goto bail;
- }
- break;
-
- case IPPROTO_UDP:
- ASSERT3U(MBLKL(mp), >=,
- (offset + sizeof (ipha_t) + sizeof (udpha_t)));
- if (MBLKL(mp) < (offset + sizeof (ipha_t) + sizeof (udpha_t))) {
- err = "mblk doesn't contain UDP header";
- goto bail;
- }
- break;
-
- default:
- err = "unexpected protocol";
- goto bail;
- }
-
- if (flags & (HCK_FULLCKSUM | HCK_IPV4_HDRCKSUM)) {
if ((flags & HCK_FULLCKSUM) && (emul & MAC_HWCKSUM_EMUL)) {
- ipaddr_t src, dst;
- uint32_t cksum;
- uint16_t *up;
-
- /* Get a pointer to the ULP checksum. */
- switch (proto) {
- case IPPROTO_TCP:
- /* LINTED: improper alignment cast */
- up = IPH_TCPH_CHECKSUMP(ipha,
- IP_SIMPLE_HDR_LENGTH);
- break;
-
- case IPPROTO_UDP:
- /* LINTED: improper alignment cast */
- up = IPH_UDPH_CHECKSUMP(ipha,
- IP_SIMPLE_HDR_LENGTH);
- break;
- }
-
- /* Pseudo-header checksum. */
- src = ipha->ipha_src;
- dst = ipha->ipha_dst;
- len = ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH;
-
- cksum = (dst >> 16) + (dst & 0xFFFF) +
- (src >> 16) + (src & 0xFFFF);
- cksum += htons(len);
-
- /*
- * The checksum value stored in the packet
- * needs to be correct. Compute it here.
- */
- *up = 0;
- cksum += (((proto) == IPPROTO_UDP) ?
- IP_UDP_CSUM_COMP : IP_TCP_CSUM_COMP);
- cksum = IP_CSUM(mp, IP_SIMPLE_HDR_LENGTH +
- offset, cksum);
- *(up) = (uint16_t)(cksum ? cksum : ~cksum);
-
+ if (!mac_sw_cksum_ipv4(mp, ip_hdr_offset, ipha, &err))
+ goto bail;
}
/* We always update the ULP checksum flags. */
@@ -358,47 +495,54 @@ mac_sw_cksum(mblk_t *mp, mac_emul_t emul)
}
/*
- * Out of paranoia, and for the sake of correctness,
- * we won't calulate the IP header checksum if it's
- * already populated. While unlikely, it's possible to
- * write code that might end up calling mac_sw_cksum()
- * twice on the same mblk (performing both LSO and
- * checksum emualtion in a single mblk chain loop --
- * the LSO emulation inserts a new chain into the
- * existing chain and then the loop iterates back over
- * the new segments and emulates the checksum a second
- * time). Normally this wouldn't be a problem, because
- * the HCK_*_OK flags are supposed to indicate that we
+ * While unlikely, it's possible to write code that
+ * might end up calling mac_sw_cksum() twice on the
+ * same mblk (performing both LSO and checksum
+ * emualtion in a single mblk chain loop -- the LSO
+ * emulation inserts a new chain into the existing
+ * chain and then the loop iterates back over the new
+ * segments and emulates the checksum a second time).
+ * Normally this wouldn't be a problem, because the
+ * HCK_*_OK flags are supposed to indicate that we
* don't need to do peform the work. But
* HCK_IPV4_HDRCKSUM and HCK_IPV4_HDRCKSUM_OK have the
* same value; so we cannot use these flags to
* determine if the IP header checksum has already
- * been calculated or not. Luckily, if IP requests
- * HCK_IPV4_HDRCKSUM, then the IP header checksum will
- * be zero. So this test works just as well as
- * checking the flag. However, in the future, we
+ * been calculated or not. For this reason, we zero
+ * out the the checksum first. In the future, we
* should fix the HCK_* flags.
*/
- if ((flags & HCK_IPV4_HDRCKSUM) && (emul & MAC_HWCKSUM_EMULS) &&
- ipha->ipha_hdr_checksum == 0) {
+ if ((flags & HCK_IPV4_HDRCKSUM) && (emul & MAC_HWCKSUM_EMULS)) {
+ ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
flags &= ~HCK_IPV4_HDRCKSUM;
flags |= HCK_IPV4_HDRCKSUM_OK;
}
+ } else if (etype == ETHERTYPE_IPV6) {
+ /* There is no IP header checksum for IPv6. */
+ if ((flags & HCK_FULLCKSUM) && (emul & MAC_HWCKSUM_EMUL)) {
+ if (!mac_sw_cksum_ipv6(mp, ip_hdr_offset, &err))
+ goto bail;
+ flags &= ~HCK_FULLCKSUM;
+ flags |= HCK_FULLCKSUM_OK;
+ value = 0;
+ }
}
+ /*
+ * Partial checksum is the same for both IPv4 and IPv6.
+ */
if ((flags & HCK_PARTIALCKSUM) && (emul & MAC_HWCKSUM_EMUL)) {
uint16_t *up, partial, cksum;
uchar_t *ipp; /* ptr to beginning of IP header */
- ipp = mp->b_rptr + offset;
- /* LINTED: cast may result in improper alignment */
+ ipp = mp->b_rptr + ip_hdr_offset;
up = (uint16_t *)((uchar_t *)ipp + stuff);
partial = *up;
*up = 0;
ASSERT3S(end, >, start);
- cksum = ~IP_CSUM_PARTIAL(mp, offset + start, partial);
+ cksum = ~IP_CSUM_PARTIAL(mp, ip_hdr_offset + start, partial);
*up = cksum != 0 ? cksum : ~cksum;
}
@@ -1169,13 +1313,8 @@ fail:
* can't directly handle LSO packets or packets without fully
* calculated checksums.
*
- * 2. A way for MAC providers (drivers) to offer LSO even when the
- * underlying HW can't or won't supply LSO offload.
- *
- * At the time of this writing no provider is making use of this
- * function. However, the plan for the future is to always assume LSO
- * is available and then add SW LSO emulation to all providers that
- * don't support it in HW.
+ * 2. A way for MAC to offer hardware offloads when the underlying
+ * hardware can't or won't.
*/
void
mac_hw_emul(mblk_t **mp_chain, mblk_t **otail, uint_t *ocount, mac_emul_t emul)
diff --git a/usr/src/uts/common/io/simnet/simnet.c b/usr/src/uts/common/io/simnet/simnet.c
index 9bfe2fe7cf..8459a02d46 100644
--- a/usr/src/uts/common/io/simnet/simnet.c
+++ b/usr/src/uts/common/io/simnet/simnet.c
@@ -22,7 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -53,6 +53,7 @@
#include <sys/atomic.h>
#include <sys/mac_wifi.h>
#include <sys/mac_impl.h>
+#include <sys/pattr.h>
#include <inet/wifi_ioctl.h>
#include <sys/thread.h>
#include <sys/synch.h>
@@ -109,14 +110,15 @@ static int simnet_m_stat(void *, uint_t, uint64_t *);
static void simnet_m_ioctl(void *, queue_t *, mblk_t *);
static mblk_t *simnet_m_tx(void *, mblk_t *);
static int simnet_m_setprop(void *, const char *, mac_prop_id_t,
- uint_t, const void *);
+ const uint_t, const void *);
static int simnet_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static void simnet_m_propinfo(void *, const char *, mac_prop_id_t,
mac_prop_info_handle_t);
+static boolean_t simnet_m_getcapab(void *, mac_capab_t, void *);
static mac_callbacks_t simnet_m_callbacks = {
- (MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO),
+ (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO),
simnet_m_stat,
simnet_m_start,
simnet_m_stop,
@@ -126,7 +128,7 @@ static mac_callbacks_t simnet_m_callbacks = {
simnet_m_tx,
NULL,
simnet_m_ioctl,
- NULL,
+ simnet_m_getcapab,
NULL,
NULL,
simnet_m_setprop,
@@ -673,6 +675,13 @@ simnet_thread_unref(simnet_dev_t *sdev)
mutex_exit(&sdev->sd_instlock);
}
+/*
+ * TODO: Add properties to set Rx checksum flag behavior.
+ *
+ * o HCK_IPV4_HDRCKSUM_OK.
+ * o HCK_PARTIALCKSUM.
+ * o HCK_FULLCKSUM_OK.
+ */
static void
simnet_rx(void *arg)
{
@@ -685,7 +694,7 @@ simnet_rx(void *arg)
/* Check for valid packet header */
if (mac_header_info(sdev->sd_mh, mp, &hdr_info) != 0) {
- freemsg(mp);
+ mac_drop_pkt(mp, "invalid L2 header");
sdev->sd_stats.recv_errors++;
goto rx_done;
}
@@ -727,13 +736,13 @@ simnet_m_tx(void *arg, mblk_t *mp_chain)
simnet_dev_t *sdev = arg;
simnet_dev_t *sdev_rx;
mblk_t *mpnext = mp_chain;
- mblk_t *mp;
+ mblk_t *mp, *nmp;
rw_enter(&simnet_dev_lock, RW_READER);
if ((sdev_rx = sdev->sd_peer_dev) == NULL) {
/* Discard packets when no peer exists */
rw_exit(&simnet_dev_lock);
- freemsgchain(mp_chain);
+ mac_drop_chain(mp_chain, "no peer");
return (NULL);
}
@@ -750,20 +759,20 @@ simnet_m_tx(void *arg, mblk_t *mp_chain)
*/
if (!simnet_thread_ref(sdev_rx)) {
rw_exit(&simnet_dev_lock);
- freemsgchain(mp_chain);
+ mac_drop_chain(mp_chain, "simnet peer dev not ready");
return (NULL);
}
rw_exit(&simnet_dev_lock);
if (!simnet_thread_ref(sdev)) {
simnet_thread_unref(sdev_rx);
- freemsgchain(mp_chain);
+ mac_drop_chain(mp_chain, "simnet dev not ready");
return (NULL);
}
while ((mp = mpnext) != NULL) {
- int len;
- int size;
+ size_t len;
+ size_t size;
mblk_t *mp_new;
mblk_t *mp_tmp;
@@ -777,7 +786,7 @@ simnet_m_tx(void *arg, mblk_t *mp_chain)
mp_new = allocb(size, BPRI_HI);
if (mp_new == NULL) {
sdev->sd_stats.xmit_errors++;
- freemsg(mp);
+ mac_drop_pkt(mp, "allocb failed");
continue;
}
bzero(mp_new->b_wptr, size);
@@ -791,19 +800,36 @@ simnet_m_tx(void *arg, mblk_t *mp_chain)
}
/* Pullup packet into a single mblk */
- if (!pullupmsg(mp, -1)) {
+ if ((nmp = msgpullup(mp, -1)) == NULL) {
sdev->sd_stats.xmit_errors++;
- freemsg(mp);
+ mac_drop_pkt(mp, "msgpullup failed");
continue;
+ } else {
+ mac_hcksum_clone(mp, nmp);
+ freemsg(mp);
+ mp = nmp;
}
/* Hold reference for taskq receive processing per-pkt */
if (!simnet_thread_ref(sdev_rx)) {
- freemsg(mp);
- freemsgchain(mpnext);
+ mac_drop_pkt(mp, "failed to get thread ref");
+ mac_drop_chain(mpnext, "failed to get thread ref");
break;
}
+ mac_hw_emul(&mp, NULL, NULL,
+ MAC_IPCKSUM_EMUL | MAC_HWCKSUM_EMUL | MAC_LSO_EMUL);
+ if (mp == NULL) {
+ sdev->sd_stats.xmit_errors++;
+ continue;
+ }
+
+ /*
+ * Remember, we are emulating a real NIC here; the
+ * checksum flags can't make the trip across the link.
+ */
+ DB_CKSUMFLAGS(mp) = 0;
+
/* Use taskq for pkt receive to avoid kernel stack explosion */
mp->b_next = (mblk_t *)sdev_rx;
if (ddi_taskq_dispatch(simnet_rxq, simnet_rx, mp,
@@ -882,6 +908,43 @@ simnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
miocack(q, mp, msgdsize(mp1), rc);
}
+static boolean_t
+simnet_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
+{
+ simnet_dev_t *sdev = arg;
+ const uint_t tcp_cksums = HCKSUM_INET_FULL_V4 | HCKSUM_INET_PARTIAL;
+
+ switch (cap) {
+ case MAC_CAPAB_HCKSUM: {
+ uint32_t *tx_cksum_flags = cap_data;
+ *tx_cksum_flags = sdev->sd_tx_cksum;
+ break;
+ }
+ case MAC_CAPAB_LSO: {
+ mac_capab_lso_t *cap_lso = cap_data;
+
+ if (sdev->sd_lso &&
+ (sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0 &&
+ (sdev->sd_tx_cksum & tcp_cksums) != 0) {
+ /*
+ * The LSO configuration is hardwried for now,
+ * but there's no reason we couldn't also make
+ * this configurable in the future.
+ */
+ cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
+ cap_lso->lso_basic_tcp_ipv4.lso_max = SD_LSO_MAXLEN;
+ break;
+ } else {
+ return (B_FALSE);
+ }
+ }
+ default:
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
static int
simnet_m_stat(void *arg, uint_t stat, uint64_t *val)
{
@@ -1138,20 +1201,20 @@ set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize,
}
static int
-simnet_set_priv_prop(simnet_dev_t *sdev, const char *pr_name,
- uint_t pr_valsize, const void *pr_val)
+simnet_set_priv_prop_wifi(simnet_dev_t *sdev, const char *name,
+ const uint_t len, const void *val)
{
simnet_wifidev_t *wdev = sdev->sd_wifidev;
long result;
- if (strcmp(pr_name, "_wl_esslist") == 0) {
- if (pr_val == NULL)
+ if (strcmp(name, "_wl_esslist") == 0) {
+ if (val == NULL)
return (EINVAL);
- return (set_wl_esslist_priv_prop(wdev, pr_valsize, pr_val));
- } else if (strcmp(pr_name, "_wl_connected") == 0) {
- if (pr_val == NULL)
+ return (set_wl_esslist_priv_prop(wdev, len, val));
+ } else if (strcmp(name, "_wl_connected") == 0) {
+ if (val == NULL)
return (EINVAL);
- (void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
+ (void) ddi_strtol(val, (char **)NULL, 0, &result);
wdev->swd_linkstatus = ((result == 1) ?
WL_CONNECTED:WL_NOTCONNECTED);
return (0);
@@ -1160,37 +1223,76 @@ simnet_set_priv_prop(simnet_dev_t *sdev, const char *pr_name,
return (EINVAL);
}
+/* ARGSUSED */
static int
-simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
- uint_t wldp_length, const void *wldp_buf)
+simnet_set_priv_prop_ether(simnet_dev_t *sdev, const char *name,
+ const uint_t len, const void *val)
{
- simnet_dev_t *sdev = arg;
- simnet_wifidev_t *wdev = sdev->sd_wifidev;
- int err = 0;
- uint32_t mtu;
+ if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
+ if (val == NULL)
+ return (EINVAL);
- switch (wldp_pr_num) {
- case MAC_PROP_MTU:
- (void) memcpy(&mtu, wldp_buf, sizeof (mtu));
- if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU)
- return (mac_maxsdu_update(sdev->sd_mh, mtu));
- else
+ /*
+ * Remember, full and partial checksum are mutually
+ * exclusive.
+ */
+ if (strcmp(val, "none") == 0) {
+ sdev->sd_tx_cksum &= ~HCKSUM_INET_FULL_V4;
+ } else if (strcmp(val, "fullv4") == 0) {
+ sdev->sd_tx_cksum &= ~HCKSUM_INET_PARTIAL;
+ sdev->sd_tx_cksum |= HCKSUM_INET_FULL_V4;
+ } else if (strcmp(val, "partial") == 0) {
+ sdev->sd_tx_cksum &= HCKSUM_INET_FULL_V4;
+ sdev->sd_tx_cksum |= HCKSUM_INET_PARTIAL;
+ } else {
return (EINVAL);
- default:
- break;
+ }
+
+ return (0);
+ } else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
+ if (val == NULL)
+ return (EINVAL);
+
+ if (strcmp(val, "off") == 0) {
+ sdev->sd_tx_cksum &= ~HCKSUM_IPHDRCKSUM;
+ } else if (strcmp(val, "on") == 0) {
+ sdev->sd_tx_cksum |= HCKSUM_IPHDRCKSUM;
+ } else {
+ return (EINVAL);
+ }
+
+ return (0);
+ } else if (strcmp(name, SD_PROP_LSO) == 0) {
+ if (val == NULL)
+ return (EINVAL);
+
+ if (strcmp(val, "off") == 0) {
+ sdev->sd_lso = B_FALSE;
+ } else if (strcmp(val, "on") == 0) {
+ sdev->sd_lso = B_TRUE;
+ } else {
+ return (EINVAL);
+ }
+
+ return (0);
}
- if (sdev->sd_type == DL_ETHER)
- return (ENOTSUP);
+ return (ENOTSUP);
+}
+
+static int
+simnet_setprop_wifi(simnet_dev_t *sdev, const char *name,
+ const mac_prop_id_t num, const uint_t len, const void *val)
+{
+ int err = 0;
+ simnet_wifidev_t *wdev = sdev->sd_wifidev;
- /* mac_prop_id */
- switch (wldp_pr_num) {
+ switch (num) {
case MAC_PROP_WL_ESSID: {
int i;
wl_ess_conf_t *wls;
- (void) memcpy(&wdev->swd_essid, wldp_buf,
- sizeof (wl_essid_t));
+ (void) memcpy(&wdev->swd_essid, val, sizeof (wl_essid_t));
wdev->swd_linkstatus = WL_CONNECTED;
/* Lookup the signal strength of the connected ESSID */
@@ -1205,8 +1307,7 @@ simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
break;
}
case MAC_PROP_WL_BSSID: {
- (void) memcpy(&wdev->swd_bssid, wldp_buf,
- sizeof (wl_bssid_t));
+ (void) memcpy(&wdev->swd_bssid, val, sizeof (wl_bssid_t));
break;
}
case MAC_PROP_WL_PHY_CONFIG:
@@ -1217,10 +1318,10 @@ simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
case MAC_PROP_WL_DESIRED_RATES:
break;
case MAC_PROP_PRIVATE:
- err = simnet_set_priv_prop(sdev, pr_name,
- wldp_length, wldp_buf);
+ err = simnet_set_priv_prop_wifi(sdev, name, len, val);
break;
default:
+ err = EINVAL;
break;
}
@@ -1228,66 +1329,147 @@ simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
}
static int
-simnet_get_priv_prop(simnet_dev_t *sdev, const char *pr_name,
- uint_t pr_valsize, void *pr_val)
+simnet_setprop_ether(simnet_dev_t *sdev, const char *name,
+ const mac_prop_id_t num, const uint_t len, const void *val)
{
- simnet_wifidev_t *wdev = sdev->sd_wifidev;
int err = 0;
- int value;
- if (strcmp(pr_name, "_wl_esslist") == 0) {
- /* Returns num of _wl_ess_conf_t that have been set */
- value = wdev->swd_esslist_num;
- } else if (strcmp(pr_name, "_wl_connected") == 0) {
- value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0);
- } else {
- err = ENOTSUP;
+ switch (num) {
+ case MAC_PROP_PRIVATE:
+ err = simnet_set_priv_prop_ether(sdev, name, len, val);
+ break;
+ default:
+ err = EINVAL;
+ break;
}
- if (err == 0)
- (void) snprintf(pr_val, pr_valsize, "%d", value);
return (err);
}
static int
-simnet_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
- uint_t wldp_length, void *wldp_buf)
+simnet_m_setprop(void *arg, const char *name, mac_prop_id_t num,
+ const uint_t len, const void *val)
{
simnet_dev_t *sdev = arg;
- simnet_wifidev_t *wdev = sdev->sd_wifidev;
int err = 0;
- int i;
+ uint32_t mtu;
- if (sdev->sd_type == DL_ETHER)
+ switch (num) {
+ case MAC_PROP_MTU:
+ (void) memcpy(&mtu, val, sizeof (mtu));
+ if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU)
+ return (mac_maxsdu_update(sdev->sd_mh, mtu));
+ else
+ return (EINVAL);
+ default:
+ break;
+ }
+
+ switch (sdev->sd_type) {
+ case DL_ETHER:
+ err = simnet_setprop_ether(sdev, name, num, len, val);
+ break;
+ case DL_WIFI:
+ err = simnet_setprop_wifi(sdev, name, num, len, val);
+ break;
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ return (err);
+}
+
+static int
+simnet_get_priv_prop_wifi(const simnet_dev_t *sdev, const char *name,
+ const uint_t len, void *val)
+{
+ simnet_wifidev_t *wdev = sdev->sd_wifidev;
+ int ret, value;
+
+ if (strcmp(name, "_wl_esslist") == 0) {
+ /* Returns num of _wl_ess_conf_t that have been set */
+ value = wdev->swd_esslist_num;
+ } else if (strcmp(name, "_wl_connected") == 0) {
+ value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0);
+ } else {
return (ENOTSUP);
+ }
+
+ ret = snprintf(val, len, "%d", value);
- /* mac_prop_id */
- switch (wldp_pr_num) {
+ if (ret < 0 || ret >= len)
+ return (EOVERFLOW);
+
+ return (0);
+}
+
+static int
+simnet_get_priv_prop_ether(const simnet_dev_t *sdev, const char *name,
+ const uint_t len, void *val)
+{
+ int ret;
+ char *value;
+
+ if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
+ if ((sdev->sd_tx_cksum & HCKSUM_INET_FULL_V4) != 0) {
+ value = "fullv4";
+ } else if ((sdev->sd_tx_cksum & HCKSUM_INET_PARTIAL) != 0) {
+ value = "partial";
+ } else {
+ value = "none";
+ }
+ } else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
+ if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
+ value = "on";
+ } else {
+ value = "off";
+ }
+ } else if (strcmp(name, SD_PROP_LSO) == 0) {
+ value = sdev->sd_lso ? "on" : "off";
+ } else {
+ return (ENOTSUP);
+ }
+
+ ret = snprintf(val, len, "%s", value);
+
+ if (ret < 0 || ret >= len) {
+ return (EOVERFLOW);
+ }
+
+ return (0);
+}
+
+static int
+simnet_getprop_wifi(const simnet_dev_t *sdev, const char *name,
+ const mac_prop_id_t num, const uint_t len, void *val)
+{
+ const simnet_wifidev_t *wdev = sdev->sd_wifidev;
+ int err = 0;
+
+ switch (num) {
case MAC_PROP_WL_ESSID:
- (void) memcpy(wldp_buf, &wdev->swd_essid,
- sizeof (wl_essid_t));
+ (void) memcpy(val, &wdev->swd_essid, sizeof (wl_essid_t));
break;
case MAC_PROP_WL_BSSID:
- (void) memcpy(wldp_buf, &wdev->swd_bssid,
- sizeof (wl_bssid_t));
+ (void) memcpy(val, &wdev->swd_bssid, sizeof (wl_bssid_t));
break;
case MAC_PROP_WL_PHY_CONFIG:
case MAC_PROP_WL_AUTH_MODE:
case MAC_PROP_WL_ENCRYPTION:
break;
case MAC_PROP_WL_LINKSTATUS:
- (void) memcpy(wldp_buf, &wdev->swd_linkstatus,
+ (void) memcpy(val, &wdev->swd_linkstatus,
sizeof (wdev->swd_linkstatus));
break;
case MAC_PROP_WL_ESS_LIST: {
wl_ess_conf_t *w_ess_conf;
- ((wl_ess_list_t *)wldp_buf)->wl_ess_list_num =
- wdev->swd_esslist_num;
+ ((wl_ess_list_t *)val)->wl_ess_list_num = wdev->swd_esslist_num;
/* LINTED E_BAD_PTR_CAST_ALIGN */
- w_ess_conf = (wl_ess_conf_t *)((char *)wldp_buf +
+ w_ess_conf = (wl_ess_conf_t *)((char *)val +
offsetof(wl_ess_list_t, wl_ess_list_ess));
- for (i = 0; i < wdev->swd_esslist_num; i++) {
+ for (uint_t i = 0; i < wdev->swd_esslist_num; i++) {
(void) memcpy(w_ess_conf, wdev->swd_esslist[i],
sizeof (wl_ess_conf_t));
w_ess_conf++;
@@ -1295,18 +1477,17 @@ simnet_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
break;
}
case MAC_PROP_WL_RSSI:
- *(wl_rssi_t *)wldp_buf = wdev->swd_rssi;
+ *(wl_rssi_t *)val = wdev->swd_rssi;
break;
case MAC_PROP_WL_RADIO:
- *(wl_radio_t *)wldp_buf = B_TRUE;
+ *(wl_radio_t *)val = B_TRUE;
break;
case MAC_PROP_WL_POWER_MODE:
break;
case MAC_PROP_WL_DESIRED_RATES:
break;
case MAC_PROP_PRIVATE:
- err = simnet_get_priv_prop(sdev, pr_name, wldp_length,
- wldp_buf);
+ err = simnet_get_priv_prop_wifi(sdev, name, len, val);
break;
default:
err = ENOTSUP;
@@ -1316,14 +1497,54 @@ simnet_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
return (err);
}
+static int
+simnet_getprop_ether(const simnet_dev_t *sdev, const char *name,
+ const mac_prop_id_t num, const uint_t len, void *val)
+{
+ int err = 0;
+
+ switch (num) {
+ case MAC_PROP_PRIVATE:
+ err = simnet_get_priv_prop_ether(sdev, name, len, val);
+ break;
+ default:
+ err = ENOTSUP;
+ break;
+ }
+
+ return (err);
+}
+
+static int
+simnet_m_getprop(void *arg, const char *name, const mac_prop_id_t num,
+ const uint_t len, void *val)
+{
+ const simnet_dev_t *sdev = arg;
+ int err = 0;
+
+ switch (sdev->sd_type) {
+ case DL_ETHER:
+ err = simnet_getprop_ether(sdev, name, num, len, val);
+ break;
+ case DL_WIFI:
+ err = simnet_getprop_wifi(sdev, name, num, len, val);
+ break;
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ return (err);
+}
+
static void
-simnet_priv_propinfo(const char *pr_name, mac_prop_info_handle_t prh)
+simnet_priv_propinfo_wifi(const char *name, mac_prop_info_handle_t prh)
{
char valstr[MAXNAMELEN];
bzero(valstr, sizeof (valstr));
- if (strcmp(pr_name, "_wl_esslist") == 0) {
+ if (strcmp(name, "_wl_esslist") == 0) {
(void) snprintf(valstr, sizeof (valstr), "%d", 0);
}
@@ -1332,15 +1553,10 @@ simnet_priv_propinfo(const char *pr_name, mac_prop_info_handle_t prh)
}
static void
-simnet_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
+simnet_propinfo_wifi(const char *name, const mac_prop_id_t num,
mac_prop_info_handle_t prh)
{
- simnet_dev_t *sdev = arg;
-
- if (sdev->sd_type == DL_ETHER)
- return;
-
- switch (wldp_pr_num) {
+ switch (num) {
case MAC_PROP_WL_BSSTYPE:
case MAC_PROP_WL_ESS_LIST:
case MAC_PROP_WL_SUPPORTED_RATES:
@@ -1348,7 +1564,53 @@ simnet_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
break;
case MAC_PROP_PRIVATE:
- simnet_priv_propinfo(pr_name, prh);
+ simnet_priv_propinfo_wifi(name, prh);
+ break;
+ }
+}
+
+static void
+simnet_priv_propinfo_ether(const char *name, mac_prop_info_handle_t prh)
+{
+ if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0 ||
+ strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
+ strcmp(name, SD_PROP_LSO) == 0) {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
+ }
+
+ if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
+ mac_prop_info_set_default_str(prh, "none");
+ }
+
+ if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
+ strcmp(name, SD_PROP_LSO) == 0) {
+ mac_prop_info_set_default_str(prh, "off");
+ }
+}
+
+static void
+simnet_propinfo_ether(const char *name, const mac_prop_id_t num,
+ mac_prop_info_handle_t prh)
+{
+ switch (num) {
+ case MAC_PROP_PRIVATE:
+ simnet_priv_propinfo_ether(name, prh);
+ break;
+ }
+}
+
+static void
+simnet_m_propinfo(void *arg, const char *name, const mac_prop_id_t num,
+ const mac_prop_info_handle_t prh)
+{
+ simnet_dev_t *sdev = arg;
+
+ switch (sdev->sd_type) {
+ case DL_ETHER:
+ simnet_propinfo_ether(name, num, prh);
+ break;
+ case DL_WIFI:
+ simnet_propinfo_wifi(name, num, prh);
break;
}
}
diff --git a/usr/src/uts/common/io/simnet/simnet_impl.h b/usr/src/uts/common/io/simnet/simnet_impl.h
index 74dcba5113..659cb10d9b 100644
--- a/usr/src/uts/common/io/simnet/simnet_impl.h
+++ b/usr/src/uts/common/io/simnet/simnet_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_SIMNET_IMPL_H
@@ -84,13 +85,23 @@ typedef struct simnet_dev {
uint_t sd_mac_len;
uchar_t sd_mac_addr[MAXMACADDRLEN];
simnet_stats_t sd_stats;
+
+ /* Capabilities */
+ uint_t sd_tx_cksum;
+ boolean_t sd_lso;
} simnet_dev_t;
+/* Simnet dladm private properties. */
+#define SD_PROP_TX_ULP_CKSUM "_tx_ulp_cksum"
+#define SD_PROP_TX_IP_CKSUM "_tx_ipv4_cksum"
+#define SD_PROP_LSO "_lso"
+
/* Simnet device flags */
#define SDF_SHUTDOWN 0x00000001 /* Device shutdown, no new ops */
#define SDF_STARTED 0x00000002 /* Device started, allow ops */
#define SIMNET_MAX_MTU 9000 /* Max MTU supported by simnet driver */
+#define SD_LSO_MAXLEN 65535 /* Max LSO supported by simnet driver */
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/os/ip_cksum.c b/usr/src/uts/common/os/ip_cksum.c
index 1fa1c9425b..0a237e86ec 100644
--- a/usr/src/uts/common/os/ip_cksum.c
+++ b/usr/src/uts/common/os/ip_cksum.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -34,6 +35,7 @@
#include <sys/vtrace.h>
#include <inet/sctp_crc32.h>
#include <inet/ip.h>
+#include <inet/ip6.h>
#include <sys/multidata.h>
#include <sys/multidata_impl.h>
@@ -556,3 +558,109 @@ ip_csum_hdr(ipha_t *ipha)
sum = 0;
return ((uint16_t)sum);
}
+
+/*
+ * This function takes an mblk and IPv6 header as input and returns
+ * three pieces of information.
+ *
+ * 'hdr_length_ptr': The IPv6 header length including extension headers.
+ *
+ * 'nethdrpp': A pointer to the "next hedader" value, aka the
+ * transport header. This argument may be set to NULL if
+ * only the length is desired.
+ *
+ * return: Whether or not the header was malformed.
+ *
+ * This function assumes the IPv6 header along with all extensions are
+ * contained solely in this mblk: i.e., there is no b_cont walking.
+ */
+boolean_t
+ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr,
+ uint8_t **nexthdrpp)
+{
+ uint16_t length;
+ uint_t ehdrlen;
+ uint8_t *nexthdrp;
+ uint8_t *whereptr;
+ uint8_t *endptr;
+ ip6_dest_t *desthdr;
+ ip6_rthdr_t *rthdr;
+ ip6_frag_t *fraghdr;
+
+ ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
+ length = IPV6_HDR_LEN;
+ whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
+ endptr = mp->b_wptr;
+
+ nexthdrp = &ip6h->ip6_nxt;
+ while (whereptr < endptr) {
+ /* Is there enough left for len + nexthdr? */
+ if (whereptr + MIN_EHDR_LEN > endptr)
+ break;
+
+ switch (*nexthdrp) {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ /* Assumes the headers are identical for hbh and dst */
+ desthdr = (ip6_dest_t *)whereptr;
+ ehdrlen = 8 * (desthdr->ip6d_len + 1);
+ if ((uchar_t *)desthdr + ehdrlen > endptr)
+ return (B_FALSE);
+ nexthdrp = &desthdr->ip6d_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ rthdr = (ip6_rthdr_t *)whereptr;
+ ehdrlen = 8 * (rthdr->ip6r_len + 1);
+ if ((uchar_t *)rthdr + ehdrlen > endptr)
+ return (B_FALSE);
+ nexthdrp = &rthdr->ip6r_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+ fraghdr = (ip6_frag_t *)whereptr;
+ ehdrlen = sizeof (ip6_frag_t);
+ if ((uchar_t *)&fraghdr[1] > endptr)
+ return (B_FALSE);
+ nexthdrp = &fraghdr->ip6f_nxt;
+ break;
+ case IPPROTO_NONE:
+ /* No next header means we're finished */
+ default:
+ *hdr_length_ptr = length;
+
+ if (nexthdrpp != NULL)
+ *nexthdrpp = nexthdrp;
+
+ return (B_TRUE);
+ }
+ length += ehdrlen;
+ whereptr += ehdrlen;
+ *hdr_length_ptr = length;
+
+ if (nexthdrpp != NULL)
+ *nexthdrpp = nexthdrp;
+ }
+ switch (*nexthdrp) {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_FRAGMENT:
+ /*
+ * If any know extension headers are still to be processed,
+ * the packet's malformed (or at least all the IP header(s) are
+ * not in the same mblk - and that should never happen.
+ */
+ return (B_FALSE);
+
+ default:
+ /*
+ * If we get here, we know that all of the IP headers were in
+ * the same mblk, even if the ULP header is in the next mblk.
+ */
+ *hdr_length_ptr = length;
+
+ if (nexthdrpp != NULL)
+ *nexthdrpp = nexthdrp;
+
+ return (B_TRUE);
+ }
+}
diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h
index ce09304699..26c535fb42 100644
--- a/usr/src/uts/common/sys/mac_impl.h
+++ b/usr/src/uts/common/sys/mac_impl.h
@@ -35,6 +35,7 @@
#include <net/if.h>
#include <sys/mac_flow_impl.h>
#include <netinet/ip6.h>
+#include <sys/pattr.h>
#ifdef __cplusplus
extern "C" {
@@ -289,54 +290,6 @@ struct mac_group_s {
#define GROUP_INTR_ENABLE_FUNC(g) (g)->mrg_info.mgi_intr.mi_enable
#define GROUP_INTR_DISABLE_FUNC(g) (g)->mrg_info.mgi_intr.mi_disable
-#define MAC_RING_TX(mhp, rh, mp, rest) { \
- mac_ring_handle_t mrh = rh; \
- mac_impl_t *mimpl = (mac_impl_t *)mhp; \
- /* \
- * Send packets through a selected tx ring, or through the \
- * default handler if there is no selected ring. \
- */ \
- if (mrh == NULL) \
- mrh = mimpl->mi_default_tx_ring; \
- if (mrh == NULL) { \
- rest = mimpl->mi_tx(mimpl->mi_driver, mp); \
- } else { \
- rest = mac_hwring_tx(mrh, mp); \
- } \
-}
-
-/*
- * This is the final stop before reaching the underlying driver
- * or aggregation, so this is where the bridging hook is implemented.
- * Packets that are bridged will return through mac_bridge_tx(), with
- * rh nulled out if the bridge chooses to send output on a different
- * link due to forwarding.
- */
-#define MAC_TX(mip, rh, mp, src_mcip) { \
- mac_ring_handle_t rhandle = (rh); \
- /* \
- * If there is a bound Hybrid I/O share, send packets through \
- * the default tx ring. (When there's a bound Hybrid I/O share, \
- * the tx rings of this client are mapped in the guest domain \
- * and not accessible from here.) \
- */ \
- _NOTE(CONSTANTCONDITION) \
- if ((src_mcip)->mci_state_flags & MCIS_SHARE_BOUND) \
- rhandle = (mip)->mi_default_tx_ring; \
- if (mip->mi_promisc_list != NULL) \
- mac_promisc_dispatch(mip, mp, src_mcip); \
- /* \
- * Grab the proper transmit pointer and handle. Special \
- * optimization: we can test mi_bridge_link itself atomically, \
- * and if that indicates no bridge send packets through tx ring.\
- */ \
- if (mip->mi_bridge_link == NULL) { \
- MAC_RING_TX(mip, rhandle, mp, mp); \
- } else { \
- mp = mac_bridge_tx(mip, rhandle, mp); \
- } \
-}
-
/* mci_tx_flag */
#define MCI_TX_QUIESCE 0x1
@@ -485,6 +438,9 @@ struct mac_impl_s {
mac_led_mode_t mi_led_modes;
mac_capab_led_t mi_led;
+ /* Cache of the Tx DB_CKSUMFLAGS that this MAC supports. */
+ uint16_t mi_tx_cksum_flags; /* WO */
+
/*
* MAC address and VLAN lists. SL protected.
*/
@@ -743,6 +699,9 @@ extern void mac_packet_print(mac_handle_t, mblk_t *);
extern void mac_rx_deliver(void *, mac_resource_handle_t, mblk_t *,
mac_header_info_t *);
extern void mac_tx_notify(mac_impl_t *);
+extern mblk_t *mac_ring_tx(mac_handle_t, mac_ring_handle_t, mblk_t *);
+extern mblk_t *mac_provider_tx(mac_impl_t *, mac_ring_handle_t, mblk_t *,
+ mac_client_impl_t *);
extern void mac_callback_add(mac_cb_info_t *, mac_cb_t **, mac_cb_t *);
extern boolean_t mac_callback_remove(mac_cb_info_t *, mac_cb_t **, mac_cb_t *);