|
- #!/bin/sh -
- #
- # Copyright 2021 John-Mark Gurney.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions
- # are met:
- # 1. Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- #
- # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- # SUCH DAMAGE.
- #
-
- #
- # ls testinterfaces.sh | entr sh -c 'fsync testinterfaces.sh && sh testinterfaces.sh run ue0 ue1'
- # ifconfig ue0 -vnet testjail; ifconfig ue1 -vnet checkjail
- #
-
- # Tests to add:
- # Multicast filter programming
- # bad checksum filtering
- # TSO/LRO functionality
- # polling?
- # TOE?
- # WOL
- # vlan hw tso
- # vlan hwfilter
- # hardware timestamp (may not be testable)
- #
-
- testjail=testjail
- checkjail=checkjail
-
- # Function to setup jails as needed for tests.
- setup()
- {
- if ! jail -c persist=1 path=/ name=testjail vnet=new vnet.interface="$testiface"; then
- echo failed to start test jail
- exit 1
- fi
-
- if ! jail -c persist=1 path=/ name=checkjail vnet=new vnet.interface="$checkiface"; then
- echo failed to start check jail
- exit 1
- fi
-
- run test ifconfig lo0 up
- run check ifconfig lo0 up
- }
-
- # Verify that jails are present
- checkjails()
- {
- if ! jls -j "$testjail" >/dev/null 2>/dev/null; then
- echo "test jail not present."
- return 1
- fi
- if ! jls -j "$checkjail" >/dev/null 2>/dev/null; then
- echo "check jail not present."
- return 1
- fi
- }
-
- # Destroy jails
- cleanup()
- {
- jail -r "$testjail" 2>/dev/null
- jail -r "$checkjail" 2>/dev/null
- }
-
- # Subroutin for down'ing the interface, and removing any addresses from it.
- # There are issues where if the addresses are not cleared, the same address
- # won't always work.
- cleaniface()
- {
- jailname="$1"
- ifacename="$2"
-
- run "$jailname" ifconfig $ifacename down
-
- run "$jailname" ifconfig $ifacename | grep inet | while read a b c; do
- if [ x"$a" = x"inet" -o x"$a" = x"inet6" ]; then
- #if [ x"$a" = x"inet" ]; then
- run "$jailname" ifconfig $ifacename "$a" "$b" remove
- fi
- done
- run "$jailname" ifconfig $ifacename
- }
-
- # Setup the temp directory, switch to it, and more
- starttest()
- {
- tmpdir=$(mktemp -d -t testiface)
- if [ $? != 0 ]; then
- echo "failed to make temp directory"
- exit 2
- fi
-
- oldpwd=$(pwd)
- cd "$tmpdir"
-
- run test tcpdump -n -p -i $(getiface test) -w "$tmpdir"/test.tcpdump 2> /dev/null &
- testdumppid="$!"
- # XXX - -p should be used on both interfaces
- run check tcpdump -n -i $(getiface check) -w "$tmpdir"/check.tcpdump 2> /dev/null &
- checkdumppid="$!"
-
- echo "$testdumppid $checkdumppid"
- }
-
- endtest()
- {
- echo "$testdumppid" "$checkdumppid"
- if ! kill "$testdumppid" "$checkdumppid"; then
- echo "unable to kill off tcpdumps"
- fi
-
- if ! wait "$testdumppid" "$checkdumppid"; then
- echo "tcpdumps failed to exit"
- fi
-
- cd "$oldpwd"
-
- # only attempt to clean up when successful
- if [ "$1" -eq 0 ]; then
- echo rm -rf "$tmpdir"
- else
- echo "test failed, data in $tmpdir"
- fi
- }
-
- # Run a command in a jail. The first arg is either check or test.
- # Remainder of the args are passed for execution.
- run()
- {
- if [ x"$1" = x"check" ]; then
- jid="$checkjail"
- elif [ x"$1" = x"test" ]; then
- jid="$testjail"
- else
- echo Invalid: "$1" >&2
- exit 1
- fi
-
- shift
- jexec "$jid" "$@"
- }
-
- # Return the interface that is in the specified jail.
- # The first arg is either check or test.
- getiface()
- {
- if [ x"$1" = x"check" ]; then
- echo "$checkiface"
- elif [ x"$1" = x"test" ]; then
- echo "$testiface"
- else
- echo Invalid: "$1" >&2
- exit 1
- fi
- }
-
- # Run ifconfig on the base interface in a jail.
- # The first arg is either check or test.
- # Remainder of the args are passed to ifconfig for execution.
- ifjail()
- {
- j="$1"
- shift
-
- iface=$(getiface "$j")
-
- run "$j" ifconfig "$iface" "$@"
- }
-
- # Convert an ifconfig option to the options returned by ifconfig
- iftoopt()
- {
- case "$1" in
- -txcsum6|txcsum6)
- echo TXCSUM_IPV6
- ;;
- -rxcsum6|rxcsum6)
- echo RXCSUM_IPV6
- ;;
- -txcsum|txcsum)
- echo TXCSUM
- ;;
- -rxcsum|rxcsum)
- echo RXCSUM
- ;;
- -vlanhwtag|vlanhwtag)
- echo VLAN_HWTAGGING
- ;;
- *)
- echo "Unknown option: $1" >&2
- exit 5
- ;;
- esac
- }
-
- # Get the list of capabilities or options for the interface in the jail.
- # The first arg is either check or test.
- # If the second arg is specified, it must be one of cap or opt,
- # specifying which of the capabilities or options to be returned
- # respectively. If this arg is not specified, it defaults to
- # capabilities.
- getcaps()
- {
- if [ x"$2" = x"" -o x"$2" = x"cap" ]; then
- ifoptcap=capabilities
- elif [ x"$2" = x"opt" ]; then
- ifoptcap=options
- else
- echo "Invalid getcaps arg: $2" >&2
- exit 1
- fi
- run "$1" ifconfig -m $(getiface "$1") |
- awk '$1 ~ /^'"$ifoptcap"'=/ { split($0, a, "<"); split(a[2], b, ">"); split(b[1], caps, ","); for (i in caps) print caps[i] }' |
- sort
- }
-
- # Verify that the interface in a jail is capable of the spefified feature.
- # The first arg is either check or test.
- # The second arg is the capability as printed in the options line.
- # This is checked against the capabilities line printed by -m.
- # Return 0 if present, return 1 if not present
- hascap()
- {
- getcaps "$1" | grep "^$(iftoopt "$2")$" >/dev/null
- }
-
- # Verify that the interface in a jail has a specific capability enabled.
- # The first arg is either check or test.
- # The second arg is the capability as printed in the options line.
- # This is checked against the options line.
- # Return 0 if present, return 1 if not present
- verifycap()
- {
- if [ x"${2#-}" = x"${2}" ]; then
- # without leading hyphen, present == success
- presentretval="0"
- absentretval="1"
- else
- # with leading hyphen, not present == success
- presentretval="1"
- absentretval="0"
- fi
- if getcaps "$1" opt | grep "^$(iftoopt "$2")$" >/dev/null; then
- return $presentretval
- else
- return $absentretval
- fi
- }
-
- # Make sure that the carrier on both interface is up before returning.
- # There is currently no timeout.
- waitcarrier()
- {
- local i
-
- for i in test check; do
- while :; do
- # make sure carrier is present and any IPv6 addresses
- # are no longer tentative
- if ifjail "$i" | grep 'status: active' >/dev/null &&
- ( ! ifjail "$i" | grep 'tentative' >/dev/null); then
- break
- fi
- sleep .5
- done
- done
- }
-
- ##################################
- ######### START OF TESTS #########
- ##################################
- #
- # Run tests verify that vlan hardware tagging works (or at least doesn't
- # break anything. There isn't an easy way to verify that packets in the
- # kernel get/do not get the mbuf tag. (dtrace MAY be an option)
-
- hwvlantest()
- {
- local i err
-
- err=0
-
- for i in "-vlanhwtag" "vlanhwtag"; do
- if ! hascap test "$i"; then
- echo "skipping: $i"
- continue
- fi
- echo "testing: $i"
-
- ifjail test "$i"
- verifycap test "$i" || return 1
- ifjail check -vlanhwtag
- verifycap check -vlanhwtag || return 1
-
- cleaniface test $testiface
- cleaniface check $checkiface
-
- sleep .5
-
- ifjail test up
- ifjail check up
-
- # make sure they don't exist
- run test ifconfig $testiface.42 destroy 2>/dev/null
- run check ifconfig $checkiface.42 destroy 2>/dev/null
-
- run test ifconfig $testiface.42 create 172.30.5.5/24
- run check ifconfig $checkiface.42 create 172.30.5.4/24
-
- waitcarrier
-
- #run check tcpdump -XXX -p -q -c 2 -n -i "$checkiface" ip &
- #run test tcpdump -XXX -p -q -c 2 -n -i "$testiface" ip &
- #run test ifconfig -a
- if ! run test ping -o -c 4 -t 5 -i .5 172.30.5.4 >/dev/null; then
- echo FAILED on "$i"!!!!
- #run test ifconfig -a
- err=1
- fi
-
- #run test ifconfig $testiface.42 destroy
- #run check ifconfig $checkiface.42 destroy
-
- if [ x"$err" != x"0" ]; then
- return 1
- fi
- done
- }
-
- # Internal function for testing checksum.
- # The first argument is the flag for the interface under test.
- # If it ends with a 6, then IPv6 will be used for testing.
-
- csuminternal()
- {
- local i
-
- i="$1"
-
- #if ! hascap test "$i"; then
- # echo "skipping $i, test interface not capable"
- # continue
- #fi
-
- echo "testing: $i"
-
- if [ x"$i" = x"${i%6}" ]; then
- # IPv4
- testnet="172.29.5.5/24"
- checknet="172.29.5.4/24"
- checkaddr="172.29.5.4"
- pingcmd="ping"
- ncopt=""
- else
- # IPv6
- testnet="inet6 fc00:0b5d:041c:7e37::7e37/64"
- checknet="inet6 fc00:0b5d:041c:7e37::c43c/64"
- checkaddr="fc00:0b5d:041c:7e37::c43c"
- pingcmd="ping6"
- ncopt="-6"
- fi
-
- cleaniface test $testiface
- cleaniface check $checkiface
-
- ifjail test "$i"
- verifycap test "$i" || return 1
- ifjail check -txcsum -rxcsum -txcsum6 -rxcsum6
-
- verifycap check "-txcsum" || return 1
- verifycap check "-rxcsum" || return 1
-
- sleep .5
-
- ifjail test up
- ifjail check up
-
- run test ifconfig $testiface $testnet
- run check ifconfig $checkiface $checknet
-
- waitcarrier
-
- sleep .5
-
- #run check tcpdump -XXX -p -q -n -i "$checkiface" not ip6 &
- #run test tcpdump -XXX -p -q -n -i "$testiface" not ip6 &
- #run test ifconfig -a
- #run check ifconfig -a
-
- # make sure connectivity works
- if ! run test "$pingcmd" -o -c 4 -t 5 -i .5 "$checkaddr" >/dev/null; then
- ifjail test
- ifjail check
- echo ping FAILED on "$i"!!!!
- return 1
- fi
-
- mkfifo /tmp/tififo.$$
- run check sh -c 'cat /tmp/tififo.'$$' | nc '"$ncopt"' -l 3333 | cat > /tmp/tififo.'$$ 2>/dev/null &
- sleep .5
- value="foobar $$ TCP $i"
- res=$( (echo "$value"; sleep .5) | run test nc -w 2 -N "$checkaddr" 3333)
- # We cannot use kill %% to kill the nc -l process as it only
- # kills jexec, and NOT the jail process
- # The following is heavy, but should be the only one running
- run check killall nc >/dev/null 2>/dev/null
- rm /tmp/tififo.$$
- if [ x"$res" != x"$value" ]; then
- echo TCP FAILED on "$i"!!!!
- return 1
- fi
-
- mkfifo /tmp/tififo.$$
- run check sh -c 'cat /tmp/tififo.'$$' | nc '"$ncopt"' -u -l 3333 | cat > /tmp/tififo.'$$ 2>/dev/null &
- sleep .5
- value="foobar $$ UDP $i"
- res=$( (echo "$value"; sleep .5) | run test nc -u -w 2 -N "$checkaddr" 3333)
- # We cannot use kill %% to kill the nc -l process as it only
- # kills jexec, and NOT the jail process
- # The following is heavy, but should be the only one running
- run check killall nc >/dev/null 2>/dev/null
- rm /tmp/tififo.$$
- if [ x"$res" != x"$value" ]; then
- echo UDP FAILED on "$i"!!!!
- return 1
- fi
-
- # make sure that checksum validation works by sending invalid packets
- # this includes using dtrace to verify the results from the driver
- }
-
- # Iterate through the various hardware checksum off loading flags.
-
- csumtest()
- {
- local i
-
- #for i in "-rxcsum6" "rxcsum6" "-txcsum6" "txcsum6" "-rxcsum" "rxcsum" "-txcsum" "txcsum"; do
- for i in "-rxcsum" "rxcsum" "-txcsum" "txcsum"; do
- if ! csuminternal "$i"; then
- return 1
- fi
- done
- }
-
- # Verify that packets can be sent and received w/ a larger MTU.
- # This test is a bit tricky in that it sets both interfaces to the
- # larger MTU. Better option would be to set both sides to the larger
- # MTU, but add a route w/ the -mtu specified for the return (or transmit)
- # side so that we aren't testing both sides at the same time.
-
- mtutest()
- {
- local i err
-
- oldtestmtu=$(ifjail test | awk '{ print $6; exit 0 }')
- oldcheckmtu=$(ifjail check | awk '{ print $6; exit 0 }')
- err=0
- for i in 1500 1504 2025 4074 7000 8000 9000 9216; do
- echo "testing mtu: $i"
-
- if ! ifjail test mtu "$i" 2>/dev/null; then
- echo "Failed to set mtu $i on test interface, skipping..."
- continue
- fi
-
- if ! ifjail check mtu "$i" 2>/dev/null; then
- echo "Failed to set mtu $i on check interface."
- echo "Please use different check interface."
- return 1
- fi
-
- cleaniface test $testiface
- cleaniface check $checkiface
-
- #run test arp -an
- #run check arp -an
- #run test ifconfig $testiface
- #run check ifconfig $checkiface
-
- sleep .5
-
- ifjail test up
- ifjail check up
-
- waitcarrier
-
- run test ifconfig $testiface 172.29.5.5/24
- run check ifconfig $checkiface 172.29.5.4/24
-
- # Subtract off ethernet header, ICMP header and ethernet CRC
- if ! run test ping -D -s $(($i - 8 - 20)) -o -c 4 -t 5 -i .5 172.29.5.4 >/dev/null; then
- echo failed for MTU size $i
- err=1
- break
- fi
- done
-
- # restore MTU
- ifjail test mtu $oldtestmtu
- ifjail check mtu $oldcheckmtu
-
- if [ x"$err" != x"0" ]; then
- return 1
- fi
- }
-
- runtest()
- {
- test="$1"
-
- starttest
-
- eval "$test"test
- res="$?"
-
- endtest "$res"
-
- if [ "$res" -ne 0 ]; then
- echo "$test"test failed.
- exit 1
- fi
- }
-
- usage()
- {
- echo "Usage: $0 {init,run,deinit,$(echo "$alltests" | sed -e 's/ /,/g')} <ifaceundertest> <checkinterface>"
- echo ""
- echo "The ifaceundertest is the name of the interface which featres are being"
- echo "tested."
- echo ""
- echo "The checinterface is the name of the interface used to validate that the"
- echo "test interface operatates correctly. Features of this interface will"
- echo "be turned off to prevent any of it's hardware offloading capabilities"
- echo "interfering with the test."
- }
-
- cmd="$1"
- testiface="$2"
- checkiface="$3"
-
- if [ -z "$cmd" ]; then
- echo "command must be specified!"
- echo ""
- usage
- exit 1
- fi
-
- if [ -z "$testiface" -o -z "$checkiface" ]; then
- echo "both interfaces must be specified!"
- echo ""
- usage
- exit 1
- fi
-
- alltests="mtu csum hwvlan"
- case "$cmd" in
- init)
- setup
- ;;
-
- mtu|csum|hwvlan)
- if ! checkjails; then
- exit 1
- fi
-
- echo starting $cmd, test $testiface, check $checkiface
-
- runtest "$cmd"
-
- echo "done"
- ;;
- run)
- if ! checkjails; then
- exit 1
- fi
-
- echo starting, test $testiface, check $checkiface
-
- for i in $alltests; do
- runtest "$i"
- done
-
- echo "done"
- ;;
-
- deinit)
- cleanup
- ;;
-
- *)
- echo "invalid cmd: $cmd"
- echo
- usage
- exit 1
- ;;
- esac
|