diff options
author | Ryan Zezeski <rpz@joyent.com> | 2019-01-18 15:43:50 -0700 |
---|---|---|
committer | Ryan Zezeski <rpz@joyent.com> | 2019-06-20 06:05:27 -0600 |
commit | 87738edeea3a17bfc0f19c6e1c3a597f3970e943 (patch) | |
tree | 8fa16e1e88ba990e21a5e03bdd7743459a6498dd | |
parent | 285d665c1bfb19b7a0d31074cbb554aae649ca56 (diff) | |
download | illumos-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>
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 *); |