#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # +-----------------------+ +-----------------------+ +-----------------------+ # | H1 (vrf) | | H2 (vrf) | | H3 (vrf) | # | + $h1 | | + $h2 | | + $h3 | # | | 192.0.2.1/28 | | | 192.0.2.2/28 | | | 192.0.2.18/28 | # | | 2001:db8:1::1/64 | | | 2001:db8:1::2/64 | | | 2001:db8:2::2/64 | # | | | | | | | | | # +----|------------------+ +----|------------------+ +----|------------------+ # | | | # +----|-------------------------|-------------------------|------------------+ # | +--|-------------------------|------------------+ | | # | | + $swp1 + $swp2 | + $swp3 | # | | | 192.0.2.17/28 | # | | BR1 (802.1q) | 2001:db8:2::1/64 | # | | 192.0.2.3/28 | | # | | 2001:db8:1::3/64 | | # | +-----------------------------------------------+ SW | # +---------------------------------------------------------------------------+ # #shellcheck disable=SC2317 # SC doesn't see our uses of functions. #shellcheck disable=SC2034 # ... and global variables ALL_TESTS=" test_d_no_sharing test_d_sharing test_q_no_sharing test_q_sharing test_addr_set " NUM_NETIFS=6 source lib.sh pMAC=00:11:22:33:44:55 bMAC=00:11:22:33:44:66 mMAC=00:11:22:33:44:77 xMAC=00:11:22:33:44:88 host_create() { local h=$1; shift local ipv4=$1; shift local ipv6=$1; shift adf_simple_if_init "$h" "$ipv4" "$ipv6" adf_ip_route_add vrf "v$h" 192.0.2.16/28 nexthop via 192.0.2.3 adf_ip_route_add vrf "v$h" 2001:db8:2::/64 nexthop via 2001:db8:1::3 } h3_create() { adf_simple_if_init "$h3" 192.0.2.18/28 2001:db8:2::2/64 adf_ip_route_add vrf "v$h3" 192.0.2.0/28 nexthop via 192.0.2.17 adf_ip_route_add vrf "v$h3" 2001:db8:1::/64 nexthop via 2001:db8:2::1 tc qdisc add dev "$h3" clsact defer tc qdisc del dev "$h3" clsact tc filter add dev "$h3" ingress proto ip pref 104 \ flower skip_hw ip_proto udp dst_port 4096 \ action pass defer tc filter del dev "$h3" ingress proto ip pref 104 tc qdisc add dev "$h2" clsact defer tc qdisc del dev "$h2" clsact tc filter add dev "$h2" ingress proto ip pref 104 \ flower skip_hw ip_proto udp dst_port 4096 \ action pass defer tc filter del dev "$h2" ingress proto ip pref 104 } switch_create() { adf_ip_link_set_up "$swp1" adf_ip_link_set_up "$swp2" adf_ip_addr_add "$swp3" 192.0.2.17/28 adf_ip_addr_add "$swp3" 2001:db8:2::1/64 adf_ip_link_set_up "$swp3" } setup_prepare() { h1=${NETIFS[p1]} swp1=${NETIFS[p2]} swp2=${NETIFS[p3]} h2=${NETIFS[p4]} swp3=${NETIFS[p5]} h3=${NETIFS[p6]} adf_vrf_prepare adf_forwarding_enable host_create "$h1" 192.0.2.1/28 2001:db8:1::1/64 host_create "$h2" 192.0.2.2/28 2001:db8:1::2/64 h3_create switch_create } adf_bridge_configure() { local dev adf_ip_addr_add br 192.0.2.3/28 adf_ip_addr_add br 2001:db8:1::3/64 adf_bridge_vlan_add dev br vid 1 pvid untagged self adf_bridge_vlan_add dev br vid 2 self adf_bridge_vlan_add dev br vid 3 self for dev in "$swp1" "$swp2"; do adf_ip_link_set_master "$dev" br adf_bridge_vlan_add dev "$dev" vid 1 pvid untagged adf_bridge_vlan_add dev "$dev" vid 2 adf_bridge_vlan_add dev "$dev" vid 3 done } adf_bridge_create() { local mac adf_ip_link_add br up type bridge vlan_default_pvid 0 "$@" mac=$(mac_get br) adf_bridge_configure adf_ip_link_set_addr br "$mac" } check_fdb_local_vlan_0_support() { if adf_ip_link_add XXbr up type bridge vlan_filtering 1 \ fdb_local_vlan_0 1 &>/dev/null; then return 0 fi log_test_skip "FDB sharing" \ "iproute 2 or the kernel do not support fdb_local_vlan_0" } check_mac_presence() { local should_fail=$1; shift local dev=$1; shift local vlan=$1; shift local mac mac=$(mac_get "$dev") if ((vlan == 0)); then vlan=null fi bridge -j fdb show dev "$dev" | jq -e --arg mac "$mac" --argjson vlan "$vlan" \ '.[] | select(.mac == $mac) | select(.vlan == $vlan)' > /dev/null check_err_fail "$should_fail" $? "FDB dev $dev vid $vlan addr $mac exists" } do_sharing_test() { local should_fail=$1; shift local what=$1; shift local dev RET=0 for dev in "$swp1" "$swp2" br; do check_mac_presence 0 "$dev" 0 check_mac_presence "$should_fail" "$dev" 1 check_mac_presence "$should_fail" "$dev" 2 check_mac_presence "$should_fail" "$dev" 3 done log_test "$what" } do_end_to_end_test() { local mac=$1; shift local what=$1; shift local probe_dev=${1-$h3}; shift local expect=${1-10}; shift local t0 local t1 local dd RET=0 # In mausezahn, use $dev MAC as the destination MAC. In the MAC sharing # context, that will cause an FDB miss on VLAN 1 and prompt a second # lookup in VLAN 0. t0=$(tc_rule_stats_get "$probe_dev" 104 ingress) $MZ "$h1" -c 10 -p 64 -a own -b "$mac" \ -A 192.0.2.1 -B 192.0.2.18 -t udp "dp=4096,sp=2048" -q sleep 1 t1=$(tc_rule_stats_get "$probe_dev" 104 ingress) dd=$((t1 - t0)) ((dd == expect)) check_err $? "Expected $expect packets on $probe_dev got $dd" log_test "$what" } do_tests() { local should_fail=$1; shift local what=$1; shift local swp1_mac local br_mac swp1_mac=$(mac_get "$swp1") br_mac=$(mac_get br) do_sharing_test "$should_fail" "$what" do_end_to_end_test "$swp1_mac" "$what: end to end, $swp1 MAC" do_end_to_end_test "$br_mac" "$what: end to end, br MAC" } bridge_standard() { local vlan_filtering=$1; shift if ((vlan_filtering)); then echo 802.1q else echo 802.1d fi } nonexistent_fdb_test() { local vlan_filtering=$1; shift local standard standard=$(bridge_standard "$vlan_filtering") # We expect flooding, so $h2 should get the traffic. do_end_to_end_test "$xMAC" "$standard: Nonexistent FDB" "$h2" } misleading_fdb_test() { local vlan_filtering=$1; shift local standard standard=$(bridge_standard "$vlan_filtering") defer_scope_push # Add an FDB entry on VLAN 0. The lookup on VLAN-aware bridge # shouldn't pick this up even with fdb_local_vlan_0 enabled, so # the traffic should be flooded. This all holds on # vlan_filtering bridge, on non-vlan_filtering one the FDB entry # is expected to be found as usual, no flooding takes place. # # Adding only on VLAN 0 is a bit tricky, because bridge is # trying to be nice and interprets the request as if the FDB # should be added on each VLAN. bridge fdb add "$mMAC" dev "$swp1" master bridge fdb del "$mMAC" dev "$swp1" vlan 1 master bridge fdb del "$mMAC" dev "$swp1" vlan 2 master bridge fdb del "$mMAC" dev "$swp1" vlan 3 master local expect=$((vlan_filtering ? 10 : 0)) do_end_to_end_test "$mMAC" \ "$standard: Lookup of non-local MAC on VLAN 0" \ "$h2" "$expect" defer_scope_pop } change_mac() { local dev=$1; shift local mac=$1; shift local cur_mac cur_mac=$(mac_get "$dev") log_info "Change $dev MAC $cur_mac -> $mac" adf_ip_link_set_addr "$dev" "$mac" defer log_info "Change $dev MAC back" } do_test_no_sharing() { local vlan_filtering=$1; shift local standard standard=$(bridge_standard "$vlan_filtering") adf_bridge_create vlan_filtering "$vlan_filtering" setup_wait do_tests 0 "$standard, no FDB sharing" change_mac "$swp1" "$pMAC" change_mac br "$bMAC" do_tests 0 "$standard, no FDB sharing after MAC change" in_defer_scope check_fdb_local_vlan_0_support || return log_info "Set fdb_local_vlan_0=1" ip link set dev br type bridge fdb_local_vlan_0 1 do_tests 1 "$standard, fdb sharing after toggle" } do_test_sharing() { local vlan_filtering=$1; shift local standard standard=$(bridge_standard "$vlan_filtering") in_defer_scope check_fdb_local_vlan_0_support || return adf_bridge_create vlan_filtering "$vlan_filtering" fdb_local_vlan_0 1 setup_wait do_tests 1 "$standard, FDB sharing" nonexistent_fdb_test "$vlan_filtering" misleading_fdb_test "$vlan_filtering" change_mac "$swp1" "$pMAC" change_mac br "$bMAC" do_tests 1 "$standard, FDB sharing after MAC change" log_info "Set fdb_local_vlan_0=0" ip link set dev br type bridge fdb_local_vlan_0 0 do_tests 0 "$standard, No FDB sharing after toggle" } test_d_no_sharing() { do_test_no_sharing 0 } test_d_sharing() { do_test_sharing 0 } test_q_no_sharing() { do_test_no_sharing 1 } test_q_sharing() { do_test_sharing 1 } adf_addr_set_bridge_create() { adf_ip_link_add br up type bridge vlan_filtering 0 adf_ip_link_set_addr br "$(mac_get br)" adf_bridge_configure } test_addr_set() { adf_addr_set_bridge_create setup_wait do_end_to_end_test "$(mac_get br)" "NET_ADDR_SET: end to end, br MAC" } trap cleanup EXIT setup_prepare tests_run