diff mbox series

[net-next,v2,9/9] selftests: net: Add bridge neighbor suppression test

Message ID 20230419153500.2655036-10-idosch@nvidia.com (mailing list archive)
State Accepted
Commit 7648ac72dcd7e22ac1fa5e573e536592773831dc
Delegated to: Netdev Maintainers
Headers show
Series bridge: Add per-{Port, VLAN} neighbor suppression | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 18 this patch: 18
netdev/cc_maintainers warning 2 maintainers not CCed: shuah@kernel.org linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 18 this patch: 18
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success net selftest script(s) already in Makefile
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 18 this patch: 18
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 159 exceeds 80 columns WARNING: line length of 161 exceeds 80 columns WARNING: line length of 176 exceeds 80 columns WARNING: line length of 177 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 92 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Ido Schimmel April 19, 2023, 3:35 p.m. UTC
Add test cases for bridge neighbor suppression, testing both per-port
and per-{Port, VLAN} neighbor suppression with both ARP and NS packets.

Example truncated output:

 # ./test_bridge_neigh_suppress.sh
 [...]
 Tests passed: 148
 Tests failed:   0

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
---
 tools/testing/selftests/net/Makefile          |   1 +
 .../net/test_bridge_neigh_suppress.sh         | 862 ++++++++++++++++++
 2 files changed, 863 insertions(+)
 create mode 100755 tools/testing/selftests/net/test_bridge_neigh_suppress.sh
diff mbox series

Patch

diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 1de34ec99290..c12df57d5539 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -83,6 +83,7 @@  TEST_GEN_FILES += nat6to4.o
 TEST_GEN_FILES += ip_local_port_range
 TEST_GEN_FILES += bind_wildcard
 TEST_PROGS += test_vxlan_mdb.sh
+TEST_PROGS += test_bridge_neigh_suppress.sh
 
 TEST_FILES := settings
 
diff --git a/tools/testing/selftests/net/test_bridge_neigh_suppress.sh b/tools/testing/selftests/net/test_bridge_neigh_suppress.sh
new file mode 100755
index 000000000000..d80f2cd87614
--- /dev/null
+++ b/tools/testing/selftests/net/test_bridge_neigh_suppress.sh
@@ -0,0 +1,862 @@ 
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# This test is for checking bridge neighbor suppression functionality. The
+# topology consists of two bridges (VTEPs) connected using VXLAN. A single
+# host is connected to each bridge over multiple VLANs. The test checks that
+# ARP/NS messages from the first host are suppressed on the VXLAN port when
+# should.
+#
+# +-----------------------+              +------------------------+
+# | h1                    |              | h2                     |
+# |                       |              |                        |
+# | + eth0.10             |              | + eth0.10              |
+# | | 192.0.2.1/28        |              | | 192.0.2.2/28         |
+# | | 2001:db8:1::1/64    |              | | 2001:db8:1::2/64     |
+# | |                     |              | |                      |
+# | |  + eth0.20          |              | |  + eth0.20           |
+# | \  | 192.0.2.17/28    |              | \  | 192.0.2.18/28     |
+# |  \ | 2001:db8:2::1/64 |              |  \ | 2001:db8:2::2/64  |
+# |   \|                  |              |   \|                   |
+# |    + eth0             |              |    + eth0              |
+# +----|------------------+              +----|-------------------+
+#      |                                      |
+#      |                                      |
+# +----|-------------------------------+ +----|-------------------------------+
+# |    + swp1                   + vx0  | |    + swp1                   + vx0  |
+# |    |                        |      | |    |                        |      |
+# |    |           br0          |      | |    |                        |      |
+# |    +------------+-----------+      | |    +------------+-----------+      |
+# |                 |                  | |                 |                  |
+# |                 |                  | |                 |                  |
+# |             +---+---+              | |             +---+---+              |
+# |             |       |              | |             |       |              |
+# |             |       |              | |             |       |              |
+# |             +       +              | |             +       +              |
+# |          br0.10  br0.20            | |          br0.10  br0.20            |
+# |                                    | |                                    |
+# |                 192.0.2.33         | |                 192.0.2.34         |
+# |                 + lo               | |                 + lo               |
+# |                                    | |                                    |
+# |                                    | |                                    |
+# |                   192.0.2.49/28    | |    192.0.2.50/28                   |
+# |                           veth0 +-------+ veth0                           |
+# |                                    | |                                    |
+# | sw1                                | | sw2                                |
+# +------------------------------------+ +------------------------------------+
+
+ret=0
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+# All tests in this script. Can be overridden with -t option.
+TESTS="
+	neigh_suppress_arp
+	neigh_suppress_ns
+	neigh_vlan_suppress_arp
+	neigh_vlan_suppress_ns
+"
+VERBOSE=0
+PAUSE_ON_FAIL=no
+PAUSE=no
+
+################################################################################
+# Utilities
+
+log_test()
+{
+	local rc=$1
+	local expected=$2
+	local msg="$3"
+
+	if [ ${rc} -eq ${expected} ]; then
+		printf "TEST: %-60s  [ OK ]\n" "${msg}"
+		nsuccess=$((nsuccess+1))
+	else
+		ret=1
+		nfail=$((nfail+1))
+		printf "TEST: %-60s  [FAIL]\n" "${msg}"
+		if [ "$VERBOSE" = "1" ]; then
+			echo "    rc=$rc, expected $expected"
+		fi
+
+		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+		echo
+			echo "hit enter to continue, 'q' to quit"
+			read a
+			[ "$a" = "q" ] && exit 1
+		fi
+	fi
+
+	if [ "${PAUSE}" = "yes" ]; then
+		echo
+		echo "hit enter to continue, 'q' to quit"
+		read a
+		[ "$a" = "q" ] && exit 1
+	fi
+
+	[ "$VERBOSE" = "1" ] && echo
+}
+
+run_cmd()
+{
+	local cmd="$1"
+	local out
+	local stderr="2>/dev/null"
+
+	if [ "$VERBOSE" = "1" ]; then
+		printf "COMMAND: $cmd\n"
+		stderr=
+	fi
+
+	out=$(eval $cmd $stderr)
+	rc=$?
+	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+		echo "    $out"
+	fi
+
+	return $rc
+}
+
+tc_check_packets()
+{
+	local ns=$1; shift
+	local id=$1; shift
+	local handle=$1; shift
+	local count=$1; shift
+	local pkts
+
+	sleep 0.1
+	pkts=$(tc -n $ns -j -s filter show $id \
+		| jq ".[] | select(.options.handle == $handle) | \
+		.options.actions[0].stats.packets")
+	[[ $pkts == $count ]]
+}
+
+################################################################################
+# Setup
+
+setup_topo_ns()
+{
+	local ns=$1; shift
+
+	ip netns add $ns
+	ip -n $ns link set dev lo up
+
+	ip netns exec $ns sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+	ip netns exec $ns sysctl -qw net.ipv6.conf.default.ignore_routes_with_linkdown=1
+	ip netns exec $ns sysctl -qw net.ipv6.conf.all.accept_dad=0
+	ip netns exec $ns sysctl -qw net.ipv6.conf.default.accept_dad=0
+}
+
+setup_topo()
+{
+	local ns
+
+	for ns in h1 h2 sw1 sw2; do
+		setup_topo_ns $ns
+	done
+
+	ip link add name veth0 type veth peer name veth1
+	ip link set dev veth0 netns h1 name eth0
+	ip link set dev veth1 netns sw1 name swp1
+
+	ip link add name veth0 type veth peer name veth1
+	ip link set dev veth0 netns sw1 name veth0
+	ip link set dev veth1 netns sw2 name veth0
+
+	ip link add name veth0 type veth peer name veth1
+	ip link set dev veth0 netns h2 name eth0
+	ip link set dev veth1 netns sw2 name swp1
+}
+
+setup_host_common()
+{
+	local ns=$1; shift
+	local v4addr1=$1; shift
+	local v4addr2=$1; shift
+	local v6addr1=$1; shift
+	local v6addr2=$1; shift
+
+	ip -n $ns link set dev eth0 up
+	ip -n $ns link add link eth0 name eth0.10 up type vlan id 10
+	ip -n $ns link add link eth0 name eth0.20 up type vlan id 20
+
+	ip -n $ns address add $v4addr1 dev eth0.10
+	ip -n $ns address add $v4addr2 dev eth0.20
+	ip -n $ns address add $v6addr1 dev eth0.10
+	ip -n $ns address add $v6addr2 dev eth0.20
+}
+
+setup_h1()
+{
+	local ns=h1
+	local v4addr1=192.0.2.1/28
+	local v4addr2=192.0.2.17/28
+	local v6addr1=2001:db8:1::1/64
+	local v6addr2=2001:db8:2::1/64
+
+	setup_host_common $ns $v4addr1 $v4addr2 $v6addr1 $v6addr2
+}
+
+setup_h2()
+{
+	local ns=h2
+	local v4addr1=192.0.2.2/28
+	local v4addr2=192.0.2.18/28
+	local v6addr1=2001:db8:1::2/64
+	local v6addr2=2001:db8:2::2/64
+
+	setup_host_common $ns $v4addr1 $v4addr2 $v6addr1 $v6addr2
+}
+
+setup_sw_common()
+{
+	local ns=$1; shift
+	local local_addr=$1; shift
+	local remote_addr=$1; shift
+	local veth_addr=$1; shift
+	local gw_addr=$1; shift
+
+	ip -n $ns address add $local_addr/32 dev lo
+
+	ip -n $ns link set dev veth0 up
+	ip -n $ns address add $veth_addr/28 dev veth0
+	ip -n $ns route add default via $gw_addr
+
+	ip -n $ns link add name br0 up type bridge vlan_filtering 1 \
+		vlan_default_pvid 0 mcast_snooping 0
+
+	ip -n $ns link add link br0 name br0.10 up type vlan id 10
+	bridge -n $ns vlan add vid 10 dev br0 self
+
+	ip -n $ns link add link br0 name br0.20 up type vlan id 20
+	bridge -n $ns vlan add vid 20 dev br0 self
+
+	ip -n $ns link set dev swp1 up master br0
+	bridge -n $ns vlan add vid 10 dev swp1
+	bridge -n $ns vlan add vid 20 dev swp1
+
+	ip -n $ns link add name vx0 up master br0 type vxlan \
+		local $local_addr dstport 4789 nolearning external
+	bridge -n $ns fdb add 00:00:00:00:00:00 dev vx0 self static \
+		dst $remote_addr src_vni 10010
+	bridge -n $ns fdb add 00:00:00:00:00:00 dev vx0 self static \
+		dst $remote_addr src_vni 10020
+	bridge -n $ns link set dev vx0 vlan_tunnel on learning off
+
+	bridge -n $ns vlan add vid 10 dev vx0
+	bridge -n $ns vlan add vid 10 dev vx0 tunnel_info id 10010
+
+	bridge -n $ns vlan add vid 20 dev vx0
+	bridge -n $ns vlan add vid 20 dev vx0 tunnel_info id 10020
+}
+
+setup_sw1()
+{
+	local ns=sw1
+	local local_addr=192.0.2.33
+	local remote_addr=192.0.2.34
+	local veth_addr=192.0.2.49
+	local gw_addr=192.0.2.50
+
+	setup_sw_common $ns $local_addr $remote_addr $veth_addr $gw_addr
+}
+
+setup_sw2()
+{
+	local ns=sw2
+	local local_addr=192.0.2.34
+	local remote_addr=192.0.2.33
+	local veth_addr=192.0.2.50
+	local gw_addr=192.0.2.49
+
+	setup_sw_common $ns $local_addr $remote_addr $veth_addr $gw_addr
+}
+
+setup()
+{
+	set -e
+
+	setup_topo
+	setup_h1
+	setup_h2
+	setup_sw1
+	setup_sw2
+
+	sleep 5
+
+	set +e
+}
+
+cleanup()
+{
+	local ns
+
+	for ns in h1 h2 sw1 sw2; do
+		ip netns del $ns &> /dev/null
+	done
+}
+
+################################################################################
+# Tests
+
+neigh_suppress_arp_common()
+{
+	local vid=$1; shift
+	local sip=$1; shift
+	local tip=$1; shift
+	local h2_mac
+
+	echo
+	echo "Per-port ARP suppression - VLAN $vid"
+	echo "----------------------------------"
+
+	run_cmd "tc -n sw1 qdisc replace dev vx0 clsact"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 101 proto 0x0806 flower indev swp1 arp_tip $tip arp_sip $sip arp_op request action pass"
+
+	# Initial state - check that ARP requests are not suppressed and that
+	# ARP replies are received.
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "ARP suppression"
+
+	# Enable neighbor suppression and check that nothing changes compared
+	# to the initial state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "ARP suppression"
+
+	# Install an FDB entry for the remote host and check that nothing
+	# changes compared to the initial state.
+	h2_mac=$(ip -n h2 -j -p link show eth0.$vid | jq -r '.[]["address"]')
+	run_cmd "bridge -n sw1 fdb replace $h2_mac dev vx0 master static vlan $vid"
+	log_test $? 0 "FDB entry installation"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "ARP suppression"
+
+	# Install a neighbor on the matching SVI interface and check that ARP
+	# requests are suppressed.
+	run_cmd "ip -n sw1 neigh replace $tip lladdr $h2_mac nud permanent dev br0.$vid"
+	log_test $? 0 "Neighbor entry installation"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "ARP suppression"
+
+	# Take the second host down and check that ARP requests are suppressed
+	# and that ARP replies are received.
+	run_cmd "ip -n h2 link set dev eth0.$vid down"
+	log_test $? 0 "H2 down"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "ARP suppression"
+
+	run_cmd "ip -n h2 link set dev eth0.$vid up"
+	log_test $? 0 "H2 up"
+
+	# Disable neighbor suppression and check that ARP requests are no
+	# longer suppressed.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 0 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 4
+	log_test $? 0 "ARP suppression"
+
+	# Take the second host down and check that ARP requests are not
+	# suppressed and that ARP replies are not received.
+	run_cmd "ip -n h2 link set dev eth0.$vid down"
+	log_test $? 0 "H2 down"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip -I eth0.$vid $tip"
+	log_test $? 1 "arping"
+	tc_check_packets sw1 "dev vx0 egress" 101 5
+	log_test $? 0 "ARP suppression"
+}
+
+neigh_suppress_arp()
+{
+	local vid=10
+	local sip=192.0.2.1
+	local tip=192.0.2.2
+
+	neigh_suppress_arp_common $vid $sip $tip
+
+	vid=20
+	sip=192.0.2.17
+	tip=192.0.2.18
+	neigh_suppress_arp_common $vid $sip $tip
+}
+
+neigh_suppress_ns_common()
+{
+	local vid=$1; shift
+	local saddr=$1; shift
+	local daddr=$1; shift
+	local maddr=$1; shift
+	local h2_mac
+
+	echo
+	echo "Per-port NS suppression - VLAN $vid"
+	echo "---------------------------------"
+
+	run_cmd "tc -n sw1 qdisc replace dev vx0 clsact"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 101 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $maddr src_ip $saddr type 135 code 0 action pass"
+
+	# Initial state - check that NS messages are not suppressed and that ND
+	# messages are received.
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "NS suppression"
+
+	# Enable neighbor suppression and check that nothing changes compared
+	# to the initial state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "NS suppression"
+
+	# Install an FDB entry for the remote host and check that nothing
+	# changes compared to the initial state.
+	h2_mac=$(ip -n h2 -j -p link show eth0.$vid | jq -r '.[]["address"]')
+	run_cmd "bridge -n sw1 fdb replace $h2_mac dev vx0 master static vlan $vid"
+	log_test $? 0 "FDB entry installation"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "NS suppression"
+
+	# Install a neighbor on the matching SVI interface and check that NS
+	# messages are suppressed.
+	run_cmd "ip -n sw1 neigh replace $daddr lladdr $h2_mac nud permanent dev br0.$vid"
+	log_test $? 0 "Neighbor entry installation"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "NS suppression"
+
+	# Take the second host down and check that NS messages are suppressed
+	# and that ND messages are received.
+	run_cmd "ip -n h2 link set dev eth0.$vid down"
+	log_test $? 0 "H2 down"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 3
+	log_test $? 0 "NS suppression"
+
+	run_cmd "ip -n h2 link set dev eth0.$vid up"
+	log_test $? 0 "H2 up"
+
+	# Disable neighbor suppression and check that NS messages are no longer
+	# suppressed.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 0 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 4
+	log_test $? 0 "NS suppression"
+
+	# Take the second host down and check that NS messages are not
+	# suppressed and that ND messages are not received.
+	run_cmd "ip -n h2 link set dev eth0.$vid down"
+	log_test $? 0 "H2 down"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr -w 5000 $daddr eth0.$vid"
+	log_test $? 2 "ndisc6"
+	tc_check_packets sw1 "dev vx0 egress" 101 5
+	log_test $? 0 "NS suppression"
+}
+
+neigh_suppress_ns()
+{
+	local vid=10
+	local saddr=2001:db8:1::1
+	local daddr=2001:db8:1::2
+	local maddr=ff02::1:ff00:2
+
+	neigh_suppress_ns_common $vid $saddr $daddr $maddr
+
+	vid=20
+	saddr=2001:db8:2::1
+	daddr=2001:db8:2::2
+	maddr=ff02::1:ff00:2
+
+	neigh_suppress_ns_common $vid $saddr $daddr $maddr
+}
+
+neigh_vlan_suppress_arp()
+{
+	local vid1=10
+	local vid2=20
+	local sip1=192.0.2.1
+	local sip2=192.0.2.17
+	local tip1=192.0.2.2
+	local tip2=192.0.2.18
+	local h2_mac1
+	local h2_mac2
+
+	echo
+	echo "Per-{Port, VLAN} ARP suppression"
+	echo "--------------------------------"
+
+	run_cmd "tc -n sw1 qdisc replace dev vx0 clsact"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 101 proto 0x0806 flower indev swp1 arp_tip $tip1 arp_sip $sip1 arp_op request action pass"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 102 proto 0x0806 flower indev swp1 arp_tip $tip2 arp_sip $sip2 arp_op request action pass"
+
+	h2_mac1=$(ip -n h2 -j -p link show eth0.$vid1 | jq -r '.[]["address"]')
+	h2_mac2=$(ip -n h2 -j -p link show eth0.$vid2 | jq -r '.[]["address"]')
+	run_cmd "bridge -n sw1 fdb replace $h2_mac1 dev vx0 master static vlan $vid1"
+	run_cmd "bridge -n sw1 fdb replace $h2_mac2 dev vx0 master static vlan $vid2"
+	run_cmd "ip -n sw1 neigh replace $tip1 lladdr $h2_mac1 nud permanent dev br0.$vid1"
+	run_cmd "ip -n sw1 neigh replace $tip2 lladdr $h2_mac2 nud permanent dev br0.$vid2"
+
+	# Enable per-{Port, VLAN} neighbor suppression and check that ARP
+	# requests are not suppressed and that ARP replies are received.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_vlan_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress on\""
+	log_test $? 0 "\"neigh_vlan_suppress\" is on"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 1
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+
+	# Enable neighbor suppression on VLAN 10 and check that only on this
+	# VLAN ARP requests are suppressed.
+	run_cmd "bridge -n sw1 vlan set vid $vid1 dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid1)"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid2 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off (VLAN $vid2)"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 2
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+
+	# Enable neighbor suppression on the port and check that it has no
+	# effect compared to previous state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 3
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+
+	# Disable neighbor suppression on the port and check that it has no
+	# effect compared to previous state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 4
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+
+	# Disable neighbor suppression on VLAN 10 and check that ARP requests
+	# are no longer suppressed on this VLAN.
+	run_cmd "bridge -n sw1 vlan set vid $vid1 dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off (VLAN $vid1)"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 5
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+
+	# Disable per-{Port, VLAN} neighbor suppression, enable neighbor
+	# suppression on the port and check that on both VLANs ARP requests are
+	# suppressed.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_vlan_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress off\""
+	log_test $? 0 "\"neigh_vlan_suppress\" is off"
+
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip1 -I eth0.$vid1 $tip1"
+	log_test $? 0 "arping (VLAN $vid1)"
+	run_cmd "ip netns exec h1 arping -q -b -c 1 -w 5 -s $sip2 -I eth0.$vid2 $tip2"
+	log_test $? 0 "arping (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "ARP suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 5
+	log_test $? 0 "ARP suppression (VLAN $vid2)"
+}
+
+neigh_vlan_suppress_ns()
+{
+	local vid1=10
+	local vid2=20
+	local saddr1=2001:db8:1::1
+	local saddr2=2001:db8:2::1
+	local daddr1=2001:db8:1::2
+	local daddr2=2001:db8:2::2
+	local maddr=ff02::1:ff00:2
+	local h2_mac1
+	local h2_mac2
+
+	echo
+	echo "Per-{Port, VLAN} NS suppression"
+	echo "-------------------------------"
+
+	run_cmd "tc -n sw1 qdisc replace dev vx0 clsact"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 101 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $maddr src_ip $saddr1 type 135 code 0 action pass"
+	run_cmd "tc -n sw1 filter replace dev vx0 egress pref 1 handle 102 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $maddr src_ip $saddr2 type 135 code 0 action pass"
+
+	h2_mac1=$(ip -n h2 -j -p link show eth0.$vid1 | jq -r '.[]["address"]')
+	h2_mac2=$(ip -n h2 -j -p link show eth0.$vid2 | jq -r '.[]["address"]')
+	run_cmd "bridge -n sw1 fdb replace $h2_mac1 dev vx0 master static vlan $vid1"
+	run_cmd "bridge -n sw1 fdb replace $h2_mac2 dev vx0 master static vlan $vid2"
+	run_cmd "ip -n sw1 neigh replace $daddr1 lladdr $h2_mac1 nud permanent dev br0.$vid1"
+	run_cmd "ip -n sw1 neigh replace $daddr2 lladdr $h2_mac2 nud permanent dev br0.$vid2"
+
+	# Enable per-{Port, VLAN} neighbor suppression and check that NS
+	# messages are not suppressed and that ND messages are received.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_vlan_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress on\""
+	log_test $? 0 "\"neigh_vlan_suppress\" is on"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 1
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+
+	# Enable neighbor suppression on VLAN 10 and check that only on this
+	# VLAN NS messages are suppressed.
+	run_cmd "bridge -n sw1 vlan set vid $vid1 dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid1)"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid2 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off (VLAN $vid2)"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 2
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+
+	# Enable neighbor suppression on the port and check that it has no
+	# effect compared to previous state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 3
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+
+	# Disable neighbor suppression on the port and check that it has no
+	# effect compared to previous state.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 1
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 4
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+
+	# Disable neighbor suppression on VLAN 10 and check that NS messages
+	# are no longer suppressed on this VLAN.
+	run_cmd "bridge -n sw1 vlan set vid $vid1 dev vx0 neigh_suppress off"
+	run_cmd "bridge -n sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress off\""
+	log_test $? 0 "\"neigh_suppress\" is off (VLAN $vid1)"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 5
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+
+	# Disable per-{Port, VLAN} neighbor suppression, enable neighbor
+	# suppression on the port and check that on both VLANs NS messages are
+	# suppressed.
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_vlan_suppress off"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress off\""
+	log_test $? 0 "\"neigh_vlan_suppress\" is off"
+
+	run_cmd "bridge -n sw1 link set dev vx0 neigh_suppress on"
+	run_cmd "bridge -n sw1 -d link show dev vx0 | grep \"neigh_suppress on\""
+	log_test $? 0 "\"neigh_suppress\" is on"
+
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr1 -w 5000 $daddr1 eth0.$vid1"
+	log_test $? 0 "ndisc6 (VLAN $vid1)"
+	run_cmd "ip netns exec h1 ndisc6 -q -r 1 -s $saddr2 -w 5000 $daddr2 eth0.$vid2"
+	log_test $? 0 "ndisc6 (VLAN $vid2)"
+
+	tc_check_packets sw1 "dev vx0 egress" 101 2
+	log_test $? 0 "NS suppression (VLAN $vid1)"
+	tc_check_packets sw1 "dev vx0 egress" 102 5
+	log_test $? 0 "NS suppression (VLAN $vid2)"
+}
+
+################################################################################
+# Usage
+
+usage()
+{
+	cat <<EOF
+usage: ${0##*/} OPTS
+
+        -t <test>   Test(s) to run (default: all)
+                    (options: $TESTS)
+        -p          Pause on fail
+        -P          Pause after each test before cleanup
+        -v          Verbose mode (show commands and output)
+EOF
+}
+
+################################################################################
+# Main
+
+trap cleanup EXIT
+
+while getopts ":t:pPvh" opt; do
+	case $opt in
+		t) TESTS=$OPTARG;;
+		p) PAUSE_ON_FAIL=yes;;
+		P) PAUSE=yes;;
+		v) VERBOSE=$(($VERBOSE + 1));;
+		h) usage; exit 0;;
+		*) usage; exit 1;;
+	esac
+done
+
+# Make sure we don't pause twice.
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
+if [ "$(id -u)" -ne 0 ];then
+	echo "SKIP: Need root privileges"
+	exit $ksft_skip;
+fi
+
+if [ ! -x "$(command -v ip)" ]; then
+	echo "SKIP: Could not run test without ip tool"
+	exit $ksft_skip
+fi
+
+if [ ! -x "$(command -v bridge)" ]; then
+	echo "SKIP: Could not run test without bridge tool"
+	exit $ksft_skip
+fi
+
+if [ ! -x "$(command -v tc)" ]; then
+	echo "SKIP: Could not run test without tc tool"
+	exit $ksft_skip
+fi
+
+if [ ! -x "$(command -v arping)" ]; then
+	echo "SKIP: Could not run test without arping tool"
+	exit $ksft_skip
+fi
+
+if [ ! -x "$(command -v ndisc6)" ]; then
+	echo "SKIP: Could not run test without ndisc6 tool"
+	exit $ksft_skip
+fi
+
+if [ ! -x "$(command -v jq)" ]; then
+	echo "SKIP: Could not run test without jq tool"
+	exit $ksft_skip
+fi
+
+bridge link help 2>&1 | grep -q "neigh_vlan_suppress"
+if [ $? -ne 0 ]; then
+   echo "SKIP: iproute2 bridge too old, missing per-VLAN neighbor suppression support"
+   exit $ksft_skip
+fi
+
+# Start clean.
+cleanup
+
+for t in $TESTS
+do
+	setup; $t; cleanup;
+done
+
+if [ "$TESTS" != "none" ]; then
+	printf "\nTests passed: %3d\n" ${nsuccess}
+	printf "Tests failed: %3d\n"   ${nfail}
+fi
+
+exit $ret