| #!/bin/bash | |
| # SPDX-License-Identifier: GPL-2.0 | |
| # | |
| # This test is for checking devlink-trap functionality. It makes use of | |
| # netdevsim which implements the required callbacks. | |
| lib_dir=$(dirname $0)/../../../net/forwarding | |
| ALL_TESTS=" | |
| init_test | |
| trap_action_test | |
| trap_metadata_test | |
| bad_trap_test | |
| bad_trap_action_test | |
| trap_stats_test | |
| trap_group_action_test | |
| bad_trap_group_test | |
| trap_group_stats_test | |
| trap_policer_test | |
| trap_policer_bind_test | |
| port_del_test | |
| dev_del_test | |
| " | |
| NETDEVSIM_PATH=/sys/bus/netdevsim/ | |
| DEV_ADDR=1337 | |
| DEV=netdevsim${DEV_ADDR} | |
| DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/ | |
| SLEEP_TIME=1 | |
| NETDEV="" | |
| NUM_NETIFS=0 | |
| source $lib_dir/lib.sh | |
| DEVLINK_DEV= | |
| source $lib_dir/devlink_lib.sh | |
| DEVLINK_DEV=netdevsim/${DEV} | |
| require_command udevadm | |
| modprobe netdevsim &> /dev/null | |
| if [ ! -d "$NETDEVSIM_PATH" ]; then | |
| echo "SKIP: No netdevsim support" | |
| exit 1 | |
| fi | |
| if [ -d "${NETDEVSIM_PATH}/devices/netdevsim${DEV_ADDR}" ]; then | |
| echo "SKIP: Device netdevsim${DEV_ADDR} already exists" | |
| exit 1 | |
| fi | |
| check_netdev_down() | |
| { | |
| state=$(cat /sys/class/net/${NETDEV}/flags) | |
| if [ $((state & 1)) -ne 0 ]; then | |
| echo "WARNING: unexpected interface UP, disable NetworkManager?" | |
| ip link set dev $NETDEV down | |
| fi | |
| } | |
| init_test() | |
| { | |
| RET=0 | |
| test $(devlink_traps_num_get) -ne 0 | |
| check_err $? "No traps were registered" | |
| log_test "Initialization" | |
| } | |
| trap_action_test() | |
| { | |
| local orig_action | |
| local trap_name | |
| local action | |
| RET=0 | |
| for trap_name in $(devlink_traps_get); do | |
| # The action of non-drop traps cannot be changed. | |
| if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then | |
| devlink_trap_action_set $trap_name "trap" | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != "trap" ]; then | |
| check_err 1 "Trap $trap_name did not change action to trap" | |
| fi | |
| devlink_trap_action_set $trap_name "drop" | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != "drop" ]; then | |
| check_err 1 "Trap $trap_name did not change action to drop" | |
| fi | |
| else | |
| orig_action=$(devlink_trap_action_get $trap_name) | |
| devlink_trap_action_set $trap_name "trap" | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != $orig_action ]; then | |
| check_err 1 "Trap $trap_name changed action when should not" | |
| fi | |
| devlink_trap_action_set $trap_name "drop" | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != $orig_action ]; then | |
| check_err 1 "Trap $trap_name changed action when should not" | |
| fi | |
| fi | |
| done | |
| log_test "Trap action" | |
| } | |
| trap_metadata_test() | |
| { | |
| local trap_name | |
| RET=0 | |
| for trap_name in $(devlink_traps_get); do | |
| devlink_trap_metadata_test $trap_name "input_port" | |
| check_err $? "Input port not reported as metadata of trap $trap_name" | |
| if [ $trap_name == "ingress_flow_action_drop" ] || | |
| [ $trap_name == "egress_flow_action_drop" ]; then | |
| devlink_trap_metadata_test $trap_name "flow_action_cookie" | |
| check_err $? "Flow action cookie not reported as metadata of trap $trap_name" | |
| fi | |
| done | |
| log_test "Trap metadata" | |
| } | |
| bad_trap_test() | |
| { | |
| RET=0 | |
| devlink_trap_action_set "made_up_trap" "drop" | |
| check_fail $? "Did not get an error for non-existing trap" | |
| log_test "Non-existing trap" | |
| } | |
| bad_trap_action_test() | |
| { | |
| local traps_arr | |
| local trap_name | |
| RET=0 | |
| # Pick first trap. | |
| traps_arr=($(devlink_traps_get)) | |
| trap_name=${traps_arr[0]} | |
| devlink_trap_action_set $trap_name "made_up_action" | |
| check_fail $? "Did not get an error for non-existing trap action" | |
| log_test "Non-existing trap action" | |
| } | |
| trap_stats_test() | |
| { | |
| local trap_name | |
| RET=0 | |
| check_netdev_down | |
| for trap_name in $(devlink_traps_get); do | |
| devlink_trap_stats_idle_test $trap_name | |
| check_err $? "Stats of trap $trap_name not idle when netdev down" | |
| ip link set dev $NETDEV up | |
| if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then | |
| devlink_trap_action_set $trap_name "trap" | |
| devlink_trap_stats_idle_test $trap_name | |
| check_fail $? "Stats of trap $trap_name idle when action is trap" | |
| devlink_trap_action_set $trap_name "drop" | |
| devlink_trap_stats_idle_test $trap_name | |
| check_err $? "Stats of trap $trap_name not idle when action is drop" | |
| echo "y"> $DEBUGFS_DIR/fail_trap_drop_counter_get | |
| devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null | |
| check_fail $? "Managed to read trap (hard dropped) statistics when should not" | |
| echo "n"> $DEBUGFS_DIR/fail_trap_drop_counter_get | |
| devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null | |
| check_err $? "Did not manage to read trap (hard dropped) statistics when should" | |
| devlink_trap_drop_stats_idle_test $trap_name | |
| check_fail $? "Drop stats of trap $trap_name idle when should not" | |
| else | |
| devlink_trap_stats_idle_test $trap_name | |
| check_fail $? "Stats of non-drop trap $trap_name idle when should not" | |
| fi | |
| ip link set dev $NETDEV down | |
| done | |
| log_test "Trap statistics" | |
| } | |
| trap_group_action_test() | |
| { | |
| local curr_group group_name | |
| local trap_name | |
| local trap_type | |
| local action | |
| RET=0 | |
| for group_name in $(devlink_trap_groups_get); do | |
| devlink_trap_group_action_set $group_name "trap" | |
| for trap_name in $(devlink_traps_get); do | |
| curr_group=$(devlink_trap_group_get $trap_name) | |
| if [ $curr_group != $group_name ]; then | |
| continue | |
| fi | |
| trap_type=$(devlink_trap_type_get $trap_name) | |
| if [ $trap_type != "drop" ]; then | |
| continue | |
| fi | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != "trap" ]; then | |
| check_err 1 "Trap $trap_name did not change action to trap" | |
| fi | |
| done | |
| devlink_trap_group_action_set $group_name "drop" | |
| for trap_name in $(devlink_traps_get); do | |
| curr_group=$(devlink_trap_group_get $trap_name) | |
| if [ $curr_group != $group_name ]; then | |
| continue | |
| fi | |
| trap_type=$(devlink_trap_type_get $trap_name) | |
| if [ $trap_type != "drop" ]; then | |
| continue | |
| fi | |
| action=$(devlink_trap_action_get $trap_name) | |
| if [ $action != "drop" ]; then | |
| check_err 1 "Trap $trap_name did not change action to drop" | |
| fi | |
| done | |
| done | |
| log_test "Trap group action" | |
| } | |
| bad_trap_group_test() | |
| { | |
| RET=0 | |
| devlink_trap_group_action_set "made_up_trap_group" "drop" | |
| check_fail $? "Did not get an error for non-existing trap group" | |
| log_test "Non-existing trap group" | |
| } | |
| trap_group_stats_test() | |
| { | |
| local group_name | |
| RET=0 | |
| check_netdev_down | |
| for group_name in $(devlink_trap_groups_get); do | |
| devlink_trap_group_stats_idle_test $group_name | |
| check_err $? "Stats of trap group $group_name not idle when netdev down" | |
| ip link set dev $NETDEV up | |
| devlink_trap_group_action_set $group_name "trap" | |
| devlink_trap_group_stats_idle_test $group_name | |
| check_fail $? "Stats of trap group $group_name idle when action is trap" | |
| devlink_trap_group_action_set $group_name "drop" | |
| ip link set dev $NETDEV down | |
| done | |
| log_test "Trap group statistics" | |
| } | |
| trap_policer_test() | |
| { | |
| local packets_t0 | |
| local packets_t1 | |
| RET=0 | |
| if [ $(devlink_trap_policers_num_get) -eq 0 ]; then | |
| check_err 1 "Failed to dump policers" | |
| fi | |
| devlink trap policer set $DEVLINK_DEV policer 1337 &> /dev/null | |
| check_fail $? "Did not get an error for setting a non-existing policer" | |
| devlink trap policer show $DEVLINK_DEV policer 1337 &> /dev/null | |
| check_fail $? "Did not get an error for getting a non-existing policer" | |
| devlink trap policer set $DEVLINK_DEV policer 1 rate 2000 burst 16 | |
| check_err $? "Failed to set valid parameters for a valid policer" | |
| if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then | |
| check_err 1 "Policer rate was not changed" | |
| fi | |
| if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then | |
| check_err 1 "Policer burst size was not changed" | |
| fi | |
| devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null | |
| check_fail $? "Policer rate was changed to rate lower than limit" | |
| devlink trap policer set $DEVLINK_DEV policer 1 rate 9000 &> /dev/null | |
| check_fail $? "Policer rate was changed to rate higher than limit" | |
| devlink trap policer set $DEVLINK_DEV policer 1 burst 2 &> /dev/null | |
| check_fail $? "Policer burst size was changed to burst size lower than limit" | |
| devlink trap policer set $DEVLINK_DEV policer 1 rate 65537 &> /dev/null | |
| check_fail $? "Policer burst size was changed to burst size higher than limit" | |
| echo "y" > $DEBUGFS_DIR/fail_trap_policer_set | |
| devlink trap policer set $DEVLINK_DEV policer 1 rate 3000 &> /dev/null | |
| check_fail $? "Managed to set policer rate when should not" | |
| echo "n" > $DEBUGFS_DIR/fail_trap_policer_set | |
| if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then | |
| check_err 1 "Policer rate was changed to an invalid value" | |
| fi | |
| if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then | |
| check_err 1 "Policer burst size was changed to an invalid value" | |
| fi | |
| packets_t0=$(devlink_trap_policer_rx_dropped_get 1) | |
| sleep .5 | |
| packets_t1=$(devlink_trap_policer_rx_dropped_get 1) | |
| if [ ! $packets_t1 -gt $packets_t0 ]; then | |
| check_err 1 "Policer drop counter was not incremented" | |
| fi | |
| echo "y"> $DEBUGFS_DIR/fail_trap_policer_counter_get | |
| devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null | |
| check_fail $? "Managed to read policer drop counter when should not" | |
| echo "n"> $DEBUGFS_DIR/fail_trap_policer_counter_get | |
| devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null | |
| check_err $? "Did not manage to read policer drop counter when should" | |
| log_test "Trap policer" | |
| } | |
| trap_group_check_policer() | |
| { | |
| local group_name=$1; shift | |
| devlink -j -p trap group show $DEVLINK_DEV group $group_name \ | |
| | jq -e '.[][][]["policer"]' &> /dev/null | |
| } | |
| trap_policer_bind_test() | |
| { | |
| RET=0 | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 1 | |
| check_err $? "Failed to bind a valid policer" | |
| if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then | |
| check_err 1 "Bound policer was not changed" | |
| fi | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 1337 \ | |
| &> /dev/null | |
| check_fail $? "Did not get an error for binding a non-existing policer" | |
| if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then | |
| check_err 1 "Bound policer was changed when should not" | |
| fi | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 0 | |
| check_err $? "Failed to unbind a policer when using ID 0" | |
| trap_group_check_policer "l2_drops" | |
| check_fail $? "Trap group has a policer after unbinding with ID 0" | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 1 | |
| check_err $? "Failed to bind a valid policer" | |
| devlink trap group set $DEVLINK_DEV group l2_drops nopolicer | |
| check_err $? "Failed to unbind a policer when using 'nopolicer' keyword" | |
| trap_group_check_policer "l2_drops" | |
| check_fail $? "Trap group has a policer after unbinding with 'nopolicer' keyword" | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 1 | |
| check_err $? "Failed to bind a valid policer" | |
| echo "y"> $DEBUGFS_DIR/fail_trap_group_set | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 2 \ | |
| &> /dev/null | |
| check_fail $? "Managed to bind a policer when should not" | |
| echo "n"> $DEBUGFS_DIR/fail_trap_group_set | |
| devlink trap group set $DEVLINK_DEV group l2_drops policer 2 | |
| check_err $? "Did not manage to bind a policer when should" | |
| devlink trap group set $DEVLINK_DEV group l2_drops action drop \ | |
| policer 1337 &> /dev/null | |
| check_fail $? "Did not get an error for partially modified trap group" | |
| log_test "Trap policer binding" | |
| } | |
| port_del_test() | |
| { | |
| local group_name | |
| local i | |
| # The test never fails. It is meant to exercise different code paths | |
| # and make sure we properly dismantle a port while packets are | |
| # in-flight. | |
| RET=0 | |
| devlink_traps_enable_all | |
| for i in $(seq 1 10); do | |
| ip link set dev $NETDEV up | |
| sleep $SLEEP_TIME | |
| netdevsim_port_destroy | |
| netdevsim_port_create | |
| udevadm settle | |
| done | |
| devlink_traps_disable_all | |
| log_test "Port delete" | |
| } | |
| dev_del_test() | |
| { | |
| local group_name | |
| local i | |
| # The test never fails. It is meant to exercise different code paths | |
| # and make sure we properly unregister traps while packets are | |
| # in-flight. | |
| RET=0 | |
| devlink_traps_enable_all | |
| for i in $(seq 1 10); do | |
| ip link set dev $NETDEV up | |
| sleep $SLEEP_TIME | |
| cleanup | |
| setup_prepare | |
| done | |
| devlink_traps_disable_all | |
| log_test "Device delete" | |
| } | |
| netdevsim_dev_create() | |
| { | |
| echo "$DEV_ADDR 0" > ${NETDEVSIM_PATH}/new_device | |
| } | |
| netdevsim_dev_destroy() | |
| { | |
| echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device | |
| } | |
| netdevsim_port_create() | |
| { | |
| echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/new_port | |
| } | |
| netdevsim_port_destroy() | |
| { | |
| echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/del_port | |
| } | |
| setup_prepare() | |
| { | |
| local netdev | |
| netdevsim_dev_create | |
| if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}" ]; then | |
| echo "Failed to create netdevsim device" | |
| exit 1 | |
| fi | |
| netdevsim_port_create | |
| if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}/net/" ]; then | |
| echo "Failed to create netdevsim port" | |
| exit 1 | |
| fi | |
| # Wait for udev to rename newly created netdev. | |
| udevadm settle | |
| NETDEV=$(ls ${NETDEVSIM_PATH}/devices/${DEV}/net/) | |
| } | |
| cleanup() | |
| { | |
| pre_cleanup | |
| netdevsim_port_destroy | |
| netdevsim_dev_destroy | |
| } | |
| trap cleanup EXIT | |
| setup_prepare | |
| tests_run | |
| exit $EXIT_STATUS |