| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # |
| # Test devlink-trap L3 exceptions functionality over mlxsw. |
| # Check all exception traps to make sure they are triggered under the right |
| # conditions. |
| |
| # +---------------------------------+ |
| # | H1 (vrf) | |
| # | + $h1 | |
| # | | 192.0.2.1/24 | |
| # | | 2001:db8:1::1/64 | |
| # | | | |
| # | | default via 192.0.2.2 | |
| # | | default via 2001:db8:1::2 | |
| # +----|----------------------------+ |
| # | |
| # +----|----------------------------------------------------------------------+ |
| # | SW | | |
| # | + $rp1 | |
| # | 192.0.2.2/24 | |
| # | 2001:db8:1::2/64 | |
| # | | |
| # | 2001:db8:2::2/64 | |
| # | 198.51.100.2/24 | |
| # | + $rp2 | |
| # | | | |
| # +----|----------------------------------------------------------------------+ |
| # | |
| # +----|----------------------------+ |
| # | | default via 198.51.100.2 | |
| # | | default via 2001:db8:2::2 | |
| # | | | |
| # | | 2001:db8:2::1/64 | |
| # | | 198.51.100.1/24 | |
| # | + $h2 | |
| # | H2 (vrf) | |
| # +---------------------------------+ |
| |
| lib_dir=$(dirname $0)/../../../net/forwarding |
| |
| ALL_TESTS=" |
| mtu_value_is_too_small_test |
| ttl_value_is_too_small_test |
| mc_reverse_path_forwarding_test |
| reject_route_test |
| unresolved_neigh_test |
| ipv4_lpm_miss_test |
| ipv6_lpm_miss_test |
| " |
| |
| NUM_NETIFS=4 |
| source $lib_dir/lib.sh |
| source $lib_dir/tc_common.sh |
| source $lib_dir/devlink_lib.sh |
| |
| require_command $MCD |
| require_command $MC_CLI |
| table_name=selftests |
| |
| h1_create() |
| { |
| simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64 |
| |
| ip -4 route add default vrf v$h1 nexthop via 192.0.2.2 |
| ip -6 route add default vrf v$h1 nexthop via 2001:db8:1::2 |
| |
| tc qdisc add dev $h1 clsact |
| } |
| |
| h1_destroy() |
| { |
| tc qdisc del dev $h1 clsact |
| |
| ip -6 route del default vrf v$h1 nexthop via 2001:db8:1::2 |
| ip -4 route del default vrf v$h1 nexthop via 192.0.2.2 |
| |
| simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64 |
| } |
| |
| h2_create() |
| { |
| simple_if_init $h2 198.51.100.1/24 2001:db8:2::1/64 |
| |
| ip -4 route add default vrf v$h2 nexthop via 198.51.100.2 |
| ip -6 route add default vrf v$h2 nexthop via 2001:db8:2::2 |
| } |
| |
| h2_destroy() |
| { |
| ip -6 route del default vrf v$h2 nexthop via 2001:db8:2::2 |
| ip -4 route del default vrf v$h2 nexthop via 198.51.100.2 |
| |
| simple_if_fini $h2 198.51.100.1/24 2001:db8:2::1/64 |
| } |
| |
| router_create() |
| { |
| ip link set dev $rp1 up |
| ip link set dev $rp2 up |
| |
| tc qdisc add dev $rp2 clsact |
| |
| __addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64 |
| __addr_add_del $rp2 add 198.51.100.2/24 2001:db8:2::2/64 |
| } |
| |
| router_destroy() |
| { |
| __addr_add_del $rp2 del 198.51.100.2/24 2001:db8:2::2/64 |
| __addr_add_del $rp1 del 192.0.2.2/24 2001:db8:1::2/64 |
| |
| tc qdisc del dev $rp2 clsact |
| |
| ip link set dev $rp2 down |
| ip link set dev $rp1 down |
| } |
| |
| setup_prepare() |
| { |
| h1=${NETIFS[p1]} |
| rp1=${NETIFS[p2]} |
| |
| rp2=${NETIFS[p3]} |
| h2=${NETIFS[p4]} |
| |
| rp1mac=$(mac_get $rp1) |
| |
| start_mcd |
| |
| vrf_prepare |
| forwarding_enable |
| |
| h1_create |
| h2_create |
| |
| router_create |
| } |
| |
| cleanup() |
| { |
| pre_cleanup |
| |
| router_destroy |
| |
| h2_destroy |
| h1_destroy |
| |
| forwarding_restore |
| vrf_cleanup |
| |
| kill_mcd |
| } |
| |
| ping_check() |
| { |
| ping_do $h1 198.51.100.1 |
| check_err $? "Packets that should not be trapped were trapped" |
| } |
| |
| trap_action_check() |
| { |
| local trap_name=$1; shift |
| local expected_action=$1; shift |
| |
| action=$(devlink_trap_action_get $trap_name) |
| if [ "$action" != $expected_action ]; then |
| check_err 1 "Trap $trap_name has wrong action: $action" |
| fi |
| } |
| |
| mtu_value_is_too_small_test() |
| { |
| local trap_name="mtu_value_is_too_small" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| # type - Destination Unreachable |
| # code - Fragmentation Needed and Don't Fragment was Set |
| tc filter add dev $h1 ingress protocol ip pref 1 handle 101 \ |
| flower skip_hw ip_proto icmp type 3 code 4 action pass |
| |
| mtu_set $rp2 1300 |
| |
| # Generate IP packets bigger than router's MTU with don't fragment |
| # flag on. |
| $MZ $h1 -t udp "sp=54321,dp=12345,df" -p 1400 -c 0 -d 1msec -b $rp1mac \ |
| -B 198.51.100.1 -q & |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| tc_check_packets_hitting "dev $h1 ingress" 101 |
| check_err $? "Packets were not received to h1" |
| |
| log_test "MTU value is too small" |
| |
| mtu_restore $rp2 |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| tc filter del dev $h1 ingress protocol ip pref 1 handle 101 flower |
| } |
| |
| __ttl_value_is_too_small_test() |
| { |
| local ttl_val=$1; shift |
| local trap_name="ttl_value_is_too_small" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| # type - Time Exceeded |
| # code - Time to Live exceeded in Transit |
| tc filter add dev $h1 ingress protocol ip pref 1 handle 101 \ |
| flower skip_hw ip_proto icmp type 11 code 0 action pass |
| |
| # Generate IP packets with small TTL |
| $MZ $h1 -t udp "ttl=$ttl_val,sp=54321,dp=12345" -c 0 -d 1msec \ |
| -b $rp1mac -B 198.51.100.1 -q & |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| tc_check_packets_hitting "dev $h1 ingress" 101 |
| check_err $? "Packets were not received to h1" |
| |
| log_test "TTL value is too small: TTL=$ttl_val" |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| tc filter del dev $h1 ingress protocol ip pref 1 handle 101 flower |
| } |
| |
| ttl_value_is_too_small_test() |
| { |
| __ttl_value_is_too_small_test 0 |
| __ttl_value_is_too_small_test 1 |
| } |
| |
| start_mcd() |
| { |
| SMCROUTEDIR="$(mktemp -d)" |
| for ((i = 1; i <= $NUM_NETIFS; ++i)); do |
| echo "phyint ${NETIFS[p$i]} enable" >> \ |
| $SMCROUTEDIR/$table_name.conf |
| done |
| |
| $MCD -N -I $table_name -f $SMCROUTEDIR/$table_name.conf \ |
| -P $SMCROUTEDIR/$table_name.pid |
| } |
| |
| kill_mcd() |
| { |
| pkill $MCD |
| rm -rf $SMCROUTEDIR |
| } |
| |
| __mc_reverse_path_forwarding_test() |
| { |
| local desc=$1; shift |
| local src_ip=$1; shift |
| local dst_ip=$1; shift |
| local dst_mac=$1; shift |
| local proto=$1; shift |
| local flags=${1:-""}; shift |
| local trap_name="mc_reverse_path_forwarding" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \ |
| flower dst_ip $dst_ip ip_proto udp action drop |
| |
| $MC_CLI -I $table_name add $rp1 $src_ip $dst_ip $rp2 |
| |
| # Generate packets to multicast address. |
| $MZ $h2 $flags -t udp "sp=54321,dp=12345" -c 0 -p 128 \ |
| -a 00:11:22:33:44:55 -b $dst_mac \ |
| -A $src_ip -B $dst_ip -q & |
| |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| tc_check_packets "dev $rp2 egress" 101 0 |
| check_err $? "Packets were not dropped" |
| |
| log_test "Multicast reverse path forwarding: $desc" |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| tc filter del dev $rp2 egress protocol $proto pref 1 handle 101 flower |
| } |
| |
| mc_reverse_path_forwarding_test() |
| { |
| __mc_reverse_path_forwarding_test "IPv4" "192.0.2.1" "225.1.2.3" \ |
| "01:00:5e:01:02:03" "ip" |
| __mc_reverse_path_forwarding_test "IPv6" "2001:db8:1::1" "ff0e::3" \ |
| "33:33:00:00:00:03" "ipv6" "-6" |
| } |
| |
| __reject_route_test() |
| { |
| local desc=$1; shift |
| local dst_ip=$1; shift |
| local proto=$1; shift |
| local ip_proto=$1; shift |
| local type=$1; shift |
| local code=$1; shift |
| local unreachable=$1; shift |
| local flags=${1:-""}; shift |
| local trap_name="reject_route" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| tc filter add dev $h1 ingress protocol $proto pref 1 handle 101 flower \ |
| skip_hw ip_proto $ip_proto type $type code $code action pass |
| |
| ip route add unreachable $unreachable |
| |
| # Generate pacekts to h2. The destination IP is unreachable. |
| $MZ $flags $h1 -t udp "sp=54321,dp=12345" -c 0 -d 1msec -b $rp1mac \ |
| -B $dst_ip -q & |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| tc_check_packets_hitting "dev $h1 ingress" 101 |
| check_err $? "ICMP packet was not received to h1" |
| |
| log_test "Reject route: $desc" |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| ip route del unreachable $unreachable |
| tc filter del dev $h1 ingress protocol $proto pref 1 handle 101 flower |
| } |
| |
| reject_route_test() |
| { |
| # type - Destination Unreachable |
| # code - Host Unreachable |
| __reject_route_test "IPv4" 198.51.100.1 "ip" "icmp" 3 1 \ |
| "198.51.100.0/26" |
| # type - Destination Unreachable |
| # code - No Route |
| __reject_route_test "IPv6" 2001:db8:2::1 "ipv6" "icmpv6" 1 0 \ |
| "2001:db8:2::0/66" "-6" |
| } |
| |
| __host_miss_test() |
| { |
| local desc=$1; shift |
| local dip=$1; shift |
| local trap_name="unresolved_neigh" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| ip neigh flush dev $rp2 |
| |
| t0_packets=$(devlink_trap_rx_packets_get $trap_name) |
| |
| # Generate packets to h2 (will incur a unresolved neighbor). |
| # The ping should pass and devlink counters should be increased. |
| ping_do $h1 $dip |
| check_err $? "ping failed: $desc" |
| |
| t1_packets=$(devlink_trap_rx_packets_get $trap_name) |
| |
| if [[ $t0_packets -eq $t1_packets ]]; then |
| check_err 1 "Trap counter did not increase" |
| fi |
| |
| log_test "Unresolved neigh: host miss: $desc" |
| } |
| |
| __invalid_nexthop_test() |
| { |
| local desc=$1; shift |
| local dip=$1; shift |
| local extra_add=$1; shift |
| local subnet=$1; shift |
| local via_add=$1; shift |
| local trap_name="unresolved_neigh" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| ip address add $extra_add/$subnet dev $h2 |
| |
| # Check that correct route does not trigger unresolved_neigh |
| ip $flags route add $dip via $extra_add dev $rp2 |
| |
| # Generate packets in order to discover all neighbours. |
| # Without it, counters of unresolved_neigh will be increased |
| # during neighbours discovery and the check below will fail |
| # for a wrong reason |
| ping_do $h1 $dip |
| |
| t0_packets=$(devlink_trap_rx_packets_get $trap_name) |
| ping_do $h1 $dip |
| t1_packets=$(devlink_trap_rx_packets_get $trap_name) |
| |
| if [[ $t0_packets -ne $t1_packets ]]; then |
| check_err 1 "Trap counter increased when it should not" |
| fi |
| |
| ip $flags route del $dip via $extra_add dev $rp2 |
| |
| # Check that route to nexthop that does not exist trigger |
| # unresolved_neigh |
| ip $flags route add $dip via $via_add dev $h2 |
| |
| t0_packets=$(devlink_trap_rx_packets_get $trap_name) |
| ping_do $h1 $dip |
| t1_packets=$(devlink_trap_rx_packets_get $trap_name) |
| |
| if [[ $t0_packets -eq $t1_packets ]]; then |
| check_err 1 "Trap counter did not increase" |
| fi |
| |
| ip $flags route del $dip via $via_add dev $h2 |
| ip address del $extra_add/$subnet dev $h2 |
| log_test "Unresolved neigh: nexthop does not exist: $desc" |
| } |
| |
| __invalid_nexthop_bucket_test() |
| { |
| local desc=$1; shift |
| local dip=$1; shift |
| local via_add=$1; shift |
| local trap_name="unresolved_neigh" |
| |
| RET=0 |
| |
| # Check that route to nexthop that does not exist triggers |
| # unresolved_neigh |
| ip nexthop add id 1 via $via_add dev $rp2 |
| ip nexthop add id 10 group 1 type resilient buckets 32 |
| ip route add $dip nhid 10 |
| |
| t0_packets=$(devlink_trap_rx_packets_get $trap_name) |
| ping_do $h1 $dip |
| t1_packets=$(devlink_trap_rx_packets_get $trap_name) |
| |
| if [[ $t0_packets -eq $t1_packets ]]; then |
| check_err 1 "Trap counter did not increase" |
| fi |
| |
| ip route del $dip nhid 10 |
| ip nexthop del id 10 |
| ip nexthop del id 1 |
| log_test "Unresolved neigh: nexthop bucket does not exist: $desc" |
| } |
| |
| unresolved_neigh_test() |
| { |
| __host_miss_test "IPv4" 198.51.100.1 |
| __host_miss_test "IPv6" 2001:db8:2::1 |
| __invalid_nexthop_test "IPv4" 198.51.100.1 198.51.100.3 24 198.51.100.4 |
| __invalid_nexthop_test "IPv6" 2001:db8:2::1 2001:db8:2::3 64 \ |
| 2001:db8:2::4 |
| __invalid_nexthop_bucket_test "IPv4" 198.51.100.1 198.51.100.4 |
| __invalid_nexthop_bucket_test "IPv6" 2001:db8:2::1 2001:db8:2::4 |
| } |
| |
| vrf_without_routes_create() |
| { |
| # VRF creating makes the links to be down and then up again. |
| # By default, IPv6 address is not saved after link becomes down. |
| # Save IPv6 address using sysctl configuration. |
| sysctl_set net.ipv6.conf.$rp1.keep_addr_on_down 1 |
| sysctl_set net.ipv6.conf.$rp2.keep_addr_on_down 1 |
| |
| ip link add dev vrf1 type vrf table 101 |
| ip link set dev $rp1 master vrf1 |
| ip link set dev $rp2 master vrf1 |
| ip link set dev vrf1 up |
| |
| # Wait for rp1 and rp2 to be up |
| setup_wait |
| } |
| |
| vrf_without_routes_destroy() |
| { |
| ip link set dev $rp1 nomaster |
| ip link set dev $rp2 nomaster |
| ip link del dev vrf1 |
| |
| sysctl_restore net.ipv6.conf.$rp2.keep_addr_on_down |
| sysctl_restore net.ipv6.conf.$rp1.keep_addr_on_down |
| |
| # Wait for interfaces to be up |
| setup_wait |
| } |
| |
| ipv4_lpm_miss_test() |
| { |
| local trap_name="ipv4_lpm_miss" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| # Create a VRF without a default route |
| vrf_without_routes_create |
| |
| # Generate packets through a VRF without a matching route. |
| $MZ $h1 -t udp "sp=54321,dp=12345" -c 0 -d 1msec -b $rp1mac \ |
| -B 203.0.113.1 -q & |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| log_test "LPM miss: IPv4" |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| vrf_without_routes_destroy |
| } |
| |
| ipv6_lpm_miss_test() |
| { |
| local trap_name="ipv6_lpm_miss" |
| local expected_action="trap" |
| local mz_pid |
| |
| RET=0 |
| |
| ping_check $trap_name |
| trap_action_check $trap_name $expected_action |
| |
| # Create a VRF without a default route |
| vrf_without_routes_create |
| |
| # Generate packets through a VRF without a matching route. |
| $MZ -6 $h1 -t udp "sp=54321,dp=12345" -c 0 -d 1msec -b $rp1mac \ |
| -B 2001:db8::1 -q & |
| mz_pid=$! |
| |
| devlink_trap_exception_test $trap_name |
| |
| log_test "LPM miss: IPv6" |
| |
| kill $mz_pid && wait $mz_pid &> /dev/null |
| vrf_without_routes_destroy |
| } |
| |
| trap cleanup EXIT |
| |
| setup_prepare |
| setup_wait |
| |
| tests_run |
| |
| exit $EXIT_STATUS |