| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # |
| # Test unicast FIB offload indication. |
| |
| lib_dir=$(dirname $0)/../../../net/forwarding |
| |
| ALL_TESTS=" |
| ipv6_route_add |
| ipv6_route_replace |
| ipv6_route_nexthop_group_share |
| ipv6_route_rate |
| " |
| NUM_NETIFS=4 |
| source $lib_dir/lib.sh |
| source $lib_dir/devlink_lib.sh |
| |
| tor1_create() |
| { |
| simple_if_init $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128 |
| } |
| |
| tor1_destroy() |
| { |
| simple_if_fini $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128 |
| } |
| |
| tor2_create() |
| { |
| simple_if_init $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128 |
| } |
| |
| tor2_destroy() |
| { |
| simple_if_fini $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128 |
| } |
| |
| spine_create() |
| { |
| ip link set dev $spine_p1 up |
| ip link set dev $spine_p2 up |
| |
| __addr_add_del $spine_p1 add 2001:db8:1::1/64 |
| __addr_add_del $spine_p2 add 2001:db8:2::1/64 |
| } |
| |
| spine_destroy() |
| { |
| __addr_add_del $spine_p2 del 2001:db8:2::1/64 |
| __addr_add_del $spine_p1 del 2001:db8:1::1/64 |
| |
| ip link set dev $spine_p2 down |
| ip link set dev $spine_p1 down |
| } |
| |
| ipv6_offload_check() |
| { |
| local pfx="$1"; shift |
| local expected_num=$1; shift |
| local num |
| |
| # Try to avoid races with route offload |
| sleep .1 |
| |
| num=$(ip -6 route show match ${pfx} | grep "offload" | wc -l) |
| |
| if [ $num -eq $expected_num ]; then |
| return 0 |
| fi |
| |
| return 1 |
| } |
| |
| ipv6_route_add_prefix() |
| { |
| RET=0 |
| |
| # Add a prefix route and check that it is offloaded. |
| ip -6 route add 2001:db8:3::/64 dev $spine_p1 metric 100 |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1 |
| check_err $? "prefix route not offloaded" |
| |
| # Append an identical prefix route with an higher metric and check that |
| # offload indication did not change. |
| ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 200 |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1 |
| check_err $? "lowest metric not offloaded after append" |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0 |
| check_err $? "highest metric offloaded when should not" |
| |
| # Prepend an identical prefix route with lower metric and check that |
| # it is offloaded and the others are not. |
| ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 10 |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 10" 1 |
| check_err $? "lowest metric not offloaded after prepend" |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 0 |
| check_err $? "mid metric offloaded when should not" |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0 |
| check_err $? "highest metric offloaded when should not" |
| |
| # Delete the routes and add the same route with a different nexthop |
| # device. Check that it is offloaded. |
| ip -6 route flush 2001:db8:3::/64 dev $spine_p1 |
| ip -6 route add 2001:db8:3::/64 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 dev $spine_p2" 1 |
| |
| log_test "IPv6 prefix route add" |
| |
| ip -6 route flush 2001:db8:3::/64 |
| } |
| |
| ipv6_route_add_mpath() |
| { |
| RET=0 |
| |
| # Add a multipath route and check that it is offloaded. |
| ip -6 route add 2001:db8:3::/64 metric 100 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded when should" |
| |
| # Append another nexthop and check that it is offloaded as well. |
| ip -6 route append 2001:db8:3::/64 metric 100 \ |
| nexthop via 2001:db8:1::3 dev $spine_p1 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 3 |
| check_err $? "appended nexthop not offloaded when should" |
| |
| # Mimic route replace by removing the route and adding it back with |
| # only two nexthops. |
| ip -6 route del 2001:db8:3::/64 |
| ip -6 route add 2001:db8:3::/64 metric 100 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded after delete & add" |
| |
| # Append a nexthop with an higher metric and check that the offload |
| # indication did not change. |
| ip -6 route append 2001:db8:3::/64 metric 200 \ |
| nexthop via 2001:db8:1::3 dev $spine_p1 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "lowest metric not offloaded after append" |
| ipv6_offload_check "2001:db8:3::/64 metric 200" 0 |
| check_err $? "highest metric offloaded when should not" |
| |
| # Prepend a nexthop with a lower metric and check that it is offloaded |
| # and the others are not. |
| ip -6 route append 2001:db8:3::/64 metric 10 \ |
| nexthop via 2001:db8:1::3 dev $spine_p1 |
| ipv6_offload_check "2001:db8:3::/64 metric 10" 1 |
| check_err $? "lowest metric not offloaded after prepend" |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 0 |
| check_err $? "mid metric offloaded when should not" |
| ipv6_offload_check "2001:db8:3::/64 metric 200" 0 |
| check_err $? "highest metric offloaded when should not" |
| |
| log_test "IPv6 multipath route add" |
| |
| ip -6 route flush 2001:db8:3::/64 |
| } |
| |
| ipv6_route_add() |
| { |
| ipv6_route_add_prefix |
| ipv6_route_add_mpath |
| } |
| |
| ipv6_route_replace() |
| { |
| RET=0 |
| |
| # Replace prefix route with prefix route. |
| ip -6 route add 2001:db8:3::/64 metric 100 dev $spine_p1 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 1 |
| check_err $? "prefix route not offloaded when should" |
| ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 1 |
| check_err $? "prefix route not offloaded after replace" |
| |
| # Replace prefix route with multipath route. |
| ip -6 route replace 2001:db8:3::/64 metric 100 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded after replace" |
| |
| # Replace multipath route with prefix route. A prefix route cannot |
| # replace a multipath route, so it is appended. |
| ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p1 |
| ipv6_offload_check "2001:db8:3::/64 metric 100 dev $spine_p1" 0 |
| check_err $? "prefix route offloaded after 'replacing' multipath route" |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded after being 'replaced' by prefix route" |
| |
| # Replace multipath route with multipath route. |
| ip -6 route replace 2001:db8:3::/64 metric 100 \ |
| nexthop via 2001:db8:1::3 dev $spine_p1 \ |
| nexthop via 2001:db8:2::3 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded after replacing multipath route" |
| |
| # Replace a non-existing multipath route with a multipath route and |
| # check that it is appended and not offloaded. |
| ip -6 route replace 2001:db8:3::/64 metric 200 \ |
| nexthop via 2001:db8:1::3 dev $spine_p1 \ |
| nexthop via 2001:db8:2::3 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64 metric 100" 2 |
| check_err $? "multipath route not offloaded after non-existing route was 'replaced'" |
| ipv6_offload_check "2001:db8:3::/64 metric 200" 0 |
| check_err $? "multipath route offloaded after 'replacing' non-existing route" |
| |
| log_test "IPv6 route replace" |
| |
| ip -6 route flush 2001:db8:3::/64 |
| } |
| |
| ipv6_route_nexthop_group_share() |
| { |
| RET=0 |
| |
| # The driver consolidates identical nexthop groups in order to reduce |
| # the resource usage in its adjacency table. Check that the deletion |
| # of one multipath route using the group does not affect the other. |
| ip -6 route add 2001:db8:3::/64 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ip -6 route add 2001:db8:4::/64 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ipv6_offload_check "2001:db8:3::/64" 2 |
| check_err $? "multipath route not offloaded when should" |
| ipv6_offload_check "2001:db8:4::/64" 2 |
| check_err $? "multipath route not offloaded when should" |
| ip -6 route del 2001:db8:3::/64 |
| ipv6_offload_check "2001:db8:4::/64" 2 |
| check_err $? "multipath route not offloaded after deletion of route sharing the nexthop group" |
| |
| # Check that after unsharing a nexthop group the routes are still |
| # marked as offloaded. |
| ip -6 route add 2001:db8:3::/64 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 \ |
| nexthop via 2001:db8:2::2 dev $spine_p2 |
| ip -6 route del 2001:db8:4::/64 \ |
| nexthop via 2001:db8:1::2 dev $spine_p1 |
| ipv6_offload_check "2001:db8:4::/64" 1 |
| check_err $? "singlepath route not offloaded after unsharing the nexthop group" |
| ipv6_offload_check "2001:db8:3::/64" 2 |
| check_err $? "multipath route not offloaded after unsharing the nexthop group" |
| |
| log_test "IPv6 nexthop group sharing" |
| |
| ip -6 route flush 2001:db8:3::/64 |
| ip -6 route flush 2001:db8:4::/64 |
| } |
| |
| ipv6_route_rate() |
| { |
| local batch_dir=$(mktemp -d) |
| local num_rts=$((40 * 1024)) |
| local num_nhs=16 |
| local total |
| local start |
| local diff |
| local end |
| local nhs |
| local i |
| |
| RET=0 |
| |
| # Prepare 40K /64 multipath routes with 16 nexthops each and check how |
| # long it takes to add them. A limit of 60 seconds is set. It is much |
| # higher than insertion should take and meant to flag a serious |
| # regression. |
| total=$((nums_nhs * num_rts)) |
| |
| for i in $(seq 1 $num_nhs); do |
| ip -6 address add 2001:db8:1::10:$i/128 dev $tor1_p1 |
| nexthops+=" nexthop via 2001:db8:1::10:$i dev $spine_p1" |
| done |
| |
| for i in $(seq 1 $num_rts); do |
| echo "route add 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \ |
| >> $batch_dir/add.batch |
| echo "route del 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \ |
| >> $batch_dir/del.batch |
| done |
| |
| start=$(date +%s.%N) |
| |
| ip -batch $batch_dir/add.batch |
| count=$(ip -6 route show | grep offload | wc -l) |
| while [ $count -lt $total ]; do |
| sleep .01 |
| count=$(ip -6 route show | grep offload | wc -l) |
| done |
| |
| end=$(date +%s.%N) |
| |
| diff=$(echo "$end - $start" | bc -l) |
| test "$(echo "$diff > 60" | bc -l)" -eq 0 |
| check_err $? "route insertion took too long" |
| log_info "inserted $num_rts routes in $diff seconds" |
| |
| log_test "IPv6 routes insertion rate" |
| |
| ip -batch $batch_dir/del.batch |
| for i in $(seq 1 $num_nhs); do |
| ip -6 address del 2001:db8:1::10:$i/128 dev $tor1_p1 |
| done |
| rm -rf $batch_dir |
| } |
| |
| setup_prepare() |
| { |
| spine_p1=${NETIFS[p1]} |
| tor1_p1=${NETIFS[p2]} |
| |
| spine_p2=${NETIFS[p3]} |
| tor2_p1=${NETIFS[p4]} |
| |
| vrf_prepare |
| forwarding_enable |
| |
| tor1_create |
| tor2_create |
| spine_create |
| } |
| |
| cleanup() |
| { |
| pre_cleanup |
| |
| spine_destroy |
| tor2_destroy |
| tor1_destroy |
| |
| forwarding_restore |
| vrf_cleanup |
| } |
| |
| trap cleanup EXIT |
| |
| setup_prepare |
| setup_wait |
| |
| tests_run |
| |
| exit $EXIT_STATUS |