1*bc301feeSPouria Mousavizadeh Tehrani# 2*bc301feeSPouria Mousavizadeh Tehrani# Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> 3*bc301feeSPouria Mousavizadeh Tehrani# 4*bc301feeSPouria Mousavizadeh Tehrani# SPDX-License-Identifier: BSD-2-Clause 5*bc301feeSPouria Mousavizadeh Tehrani# 6*bc301feeSPouria Mousavizadeh Tehrani 7*bc301feeSPouria Mousavizadeh Tehrani. $(atf_get_srcdir)/../../common/vnet.subr 8*bc301feeSPouria Mousavizadeh Tehrani 9*bc301feeSPouria Mousavizadeh Tehranijq_rtentry() 10*bc301feeSPouria Mousavizadeh Tehrani{ 11*bc301feeSPouria Mousavizadeh Tehrani local route="$1" 12*bc301feeSPouria Mousavizadeh Tehrani 13*bc301feeSPouria Mousavizadeh Tehrani jq -r '.statistics."route-information"."route-table"."rt-family".[]."rt-entry".[] | 14*bc301feeSPouria Mousavizadeh Tehrani select(.destination == "'${route}'")' 15*bc301feeSPouria Mousavizadeh Tehrani} 16*bc301feeSPouria Mousavizadeh Tehrani 17*bc301feeSPouria Mousavizadeh Tehranijq_nhop_filter() 18*bc301feeSPouria Mousavizadeh Tehrani{ 19*bc301feeSPouria Mousavizadeh Tehrani local nhop="$1" 20*bc301feeSPouria Mousavizadeh Tehrani local weight="$2" 21*bc301feeSPouria Mousavizadeh Tehrani local metric="$3" 22*bc301feeSPouria Mousavizadeh Tehrani 23*bc301feeSPouria Mousavizadeh Tehrani jq -r 'select(.gateway == "'${nhop}'") | 24*bc301feeSPouria Mousavizadeh Tehrani select(.weight == '${weight}') | 25*bc301feeSPouria Mousavizadeh Tehrani select(.metric == '${metric}') | 26*bc301feeSPouria Mousavizadeh Tehrani .gateway' 27*bc301feeSPouria Mousavizadeh Tehrani} 28*bc301feeSPouria Mousavizadeh Tehrani 29*bc301feeSPouria Mousavizadeh Tehrani 30*bc301feeSPouria Mousavizadeh Tehraniatf_test_case "add_lowest_metric" "cleanup" 31*bc301feeSPouria Mousavizadeh Tehraniadd_lowest_metric_head() 32*bc301feeSPouria Mousavizadeh Tehrani{ 33*bc301feeSPouria Mousavizadeh Tehrani atf_set descr 'Create 4 routes to same dst and verify the lowest metric wins' 34*bc301feeSPouria Mousavizadeh Tehrani atf_set require.user root 35*bc301feeSPouria Mousavizadeh Tehrani atf_set require.progs jq 36*bc301feeSPouria Mousavizadeh Tehrani} 37*bc301feeSPouria Mousavizadeh Tehrani 38*bc301feeSPouria Mousavizadeh Tehraniadd_lowest_metric_body() 39*bc301feeSPouria Mousavizadeh Tehrani{ 40*bc301feeSPouria Mousavizadeh Tehrani local epair laddr route nhop1 nhop2 nhop3 41*bc301feeSPouria Mousavizadeh Tehrani 42*bc301feeSPouria Mousavizadeh Tehrani laddr="3fff::1" 43*bc301feeSPouria Mousavizadeh Tehrani route="3fff:a::" 44*bc301feeSPouria Mousavizadeh Tehrani nhop1="3fff::1" 45*bc301feeSPouria Mousavizadeh Tehrani nhop2="3fff::2" 46*bc301feeSPouria Mousavizadeh Tehrani nhop3="3fff::3" 47*bc301feeSPouria Mousavizadeh Tehrani 48*bc301feeSPouria Mousavizadeh Tehrani vnet_init 49*bc301feeSPouria Mousavizadeh Tehrani epair=$(vnet_mkepair) 50*bc301feeSPouria Mousavizadeh Tehrani 51*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 52*bc301feeSPouria Mousavizadeh Tehrani ifconfig ${epair}a inet6 ${laddr} up 53*bc301feeSPouria Mousavizadeh Tehrani 54*bc301feeSPouria Mousavizadeh Tehrani # Create an ECMP route with metric 2 55*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 56*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop2} -weight 10 -metric 2 57*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 58*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop3} -weight 10 -metric 2 59*bc301feeSPouria Mousavizadeh Tehrani 60*bc301feeSPouria Mousavizadeh Tehrani # Validate routes 61*bc301feeSPouria Mousavizadeh Tehrani atf_check -o save:netstat \ 62*bc301feeSPouria Mousavizadeh Tehrani netstat -rn6 --libxo json 63*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 10 2) 64*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 65*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop3} 10 2) 66*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop3" 67*bc301feeSPouria Mousavizadeh Tehrani 68*bc301feeSPouria Mousavizadeh Tehrani # Create a route with metric 3 69*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 70*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} -metric 3 71*bc301feeSPouria Mousavizadeh Tehrani # Verify that nhop1 is not the best route 72*bc301feeSPouria Mousavizadeh Tehrani atf_check -o not-match:".*gateway: ${nhop1}.*" \ 73*bc301feeSPouria Mousavizadeh Tehrani route -n6 get -net ${route}/64 74*bc301feeSPouria Mousavizadeh Tehrani 75*bc301feeSPouria Mousavizadeh Tehrani # Create a route to the same nhop with same metric 3 and verify it fails 76*bc301feeSPouria Mousavizadeh Tehrani atf_check -s exit:1 -o ignore -e match:".*exists.*" \ 77*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} -metric 3 78*bc301feeSPouria Mousavizadeh Tehrani 79*bc301feeSPouria Mousavizadeh Tehrani # Create a route to an existing nhop with lower metric 80*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 81*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} -metric 1 82*bc301feeSPouria Mousavizadeh Tehrani # Verify that nhop1 is now the best route 83*bc301feeSPouria Mousavizadeh Tehrani atf_check -o match:".*gateway: ${nhop1}.*" \ 84*bc301feeSPouria Mousavizadeh Tehrani route -n6 get -net ${route}/64 85*bc301feeSPouria Mousavizadeh Tehrani} 86*bc301feeSPouria Mousavizadeh Tehrani 87*bc301feeSPouria Mousavizadeh Tehraniadd_lowest_metric_cleanup() 88*bc301feeSPouria Mousavizadeh Tehrani{ 89*bc301feeSPouria Mousavizadeh Tehrani vnet_cleanup 90*bc301feeSPouria Mousavizadeh Tehrani} 91*bc301feeSPouria Mousavizadeh Tehrani 92*bc301feeSPouria Mousavizadeh Tehraniatf_test_case "add_default_metric" "cleanup" 93*bc301feeSPouria Mousavizadeh Tehraniadd_default_metric_head() 94*bc301feeSPouria Mousavizadeh Tehrani{ 95*bc301feeSPouria Mousavizadeh Tehrani atf_set descr 'Create a route and verify the default metric is set' 96*bc301feeSPouria Mousavizadeh Tehrani atf_set require.user root 97*bc301feeSPouria Mousavizadeh Tehrani atf_set require.progs jq 98*bc301feeSPouria Mousavizadeh Tehrani} 99*bc301feeSPouria Mousavizadeh Tehrani 100*bc301feeSPouria Mousavizadeh Tehraniadd_default_metric_body() 101*bc301feeSPouria Mousavizadeh Tehrani{ 102*bc301feeSPouria Mousavizadeh Tehrani local epair laddr route nhop1 103*bc301feeSPouria Mousavizadeh Tehrani 104*bc301feeSPouria Mousavizadeh Tehrani laddr="3fff::1" 105*bc301feeSPouria Mousavizadeh Tehrani route="3fff:a::" 106*bc301feeSPouria Mousavizadeh Tehrani nhop1="3fff::1" 107*bc301feeSPouria Mousavizadeh Tehrani 108*bc301feeSPouria Mousavizadeh Tehrani vnet_init 109*bc301feeSPouria Mousavizadeh Tehrani epair=$(vnet_mkepair) 110*bc301feeSPouria Mousavizadeh Tehrani 111*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 112*bc301feeSPouria Mousavizadeh Tehrani ifconfig ${epair}a inet6 ${laddr} up 113*bc301feeSPouria Mousavizadeh Tehrani 114*bc301feeSPouria Mousavizadeh Tehrani # Create a route without specifying its metric 115*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 116*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} 117*bc301feeSPouria Mousavizadeh Tehrani 118*bc301feeSPouria Mousavizadeh Tehrani # Verify the route has the default metric of 1 119*bc301feeSPouria Mousavizadeh Tehrani atf_check -o save:netstat \ 120*bc301feeSPouria Mousavizadeh Tehrani netstat -rn6 --libxo json 121*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop1} 1 1) 122*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop1" 123*bc301feeSPouria Mousavizadeh Tehrani} 124*bc301feeSPouria Mousavizadeh Tehrani 125*bc301feeSPouria Mousavizadeh Tehraniadd_default_metric_cleanup() 126*bc301feeSPouria Mousavizadeh Tehrani{ 127*bc301feeSPouria Mousavizadeh Tehrani vnet_cleanup 128*bc301feeSPouria Mousavizadeh Tehrani} 129*bc301feeSPouria Mousavizadeh Tehrani 130*bc301feeSPouria Mousavizadeh Tehraniatf_test_case "delete_route_with_metric" "cleanup" 131*bc301feeSPouria Mousavizadeh Tehranidelete_route_with_metric_head() 132*bc301feeSPouria Mousavizadeh Tehrani{ 133*bc301feeSPouria Mousavizadeh Tehrani atf_set descr 'Create multiple routes to same dst and delete routes with specific metric' 134*bc301feeSPouria Mousavizadeh Tehrani atf_set require.user root 135*bc301feeSPouria Mousavizadeh Tehrani atf_set require.progs jq 136*bc301feeSPouria Mousavizadeh Tehrani} 137*bc301feeSPouria Mousavizadeh Tehrani 138*bc301feeSPouria Mousavizadeh Tehranidelete_route_with_metric_body() 139*bc301feeSPouria Mousavizadeh Tehrani{ 140*bc301feeSPouria Mousavizadeh Tehrani local epair laddr route nhop1 nhop2 141*bc301feeSPouria Mousavizadeh Tehrani 142*bc301feeSPouria Mousavizadeh Tehrani laddr="3fff::1" 143*bc301feeSPouria Mousavizadeh Tehrani route="3fff:a::" 144*bc301feeSPouria Mousavizadeh Tehrani nhop1="3fff::1" 145*bc301feeSPouria Mousavizadeh Tehrani nhop2="3fff::2" 146*bc301feeSPouria Mousavizadeh Tehrani 147*bc301feeSPouria Mousavizadeh Tehrani vnet_init 148*bc301feeSPouria Mousavizadeh Tehrani epair=$(vnet_mkepair) 149*bc301feeSPouria Mousavizadeh Tehrani 150*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 151*bc301feeSPouria Mousavizadeh Tehrani ifconfig ${epair}a inet6 ${laddr} up 152*bc301feeSPouria Mousavizadeh Tehrani 153*bc301feeSPouria Mousavizadeh Tehrani # Create two groups of ECMP routes with metric 2 and 3, and 154*bc301feeSPouria Mousavizadeh Tehrani # another route with metric 4. 155*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 156*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} -metric 3 157*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 158*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop1} -weight 10 -metric 2 159*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 160*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop2} -weight 10 -metric 2 161*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 162*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop2} -metric 3 163*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 164*bc301feeSPouria Mousavizadeh Tehrani route -6 add -net ${route}/64 -gateway ${nhop2} -metric 4 165*bc301feeSPouria Mousavizadeh Tehrani 166*bc301feeSPouria Mousavizadeh Tehrani # Validate we have 5 routes 167*bc301feeSPouria Mousavizadeh Tehrani atf_check -o save:netstat \ 168*bc301feeSPouria Mousavizadeh Tehrani netstat -rn6 --libxo json 169*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop1} 1 3) 170*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop1" 171*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop1} 10 2) 172*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop1" 173*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 10 2) 174*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 175*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 1 3) 176*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 177*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 1 4) 178*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 179*bc301feeSPouria Mousavizadeh Tehrani 180*bc301feeSPouria Mousavizadeh Tehrani # Delete one of the nexthops of them best ECMP route 181*bc301feeSPouria Mousavizadeh Tehrani # Test that deleting a route by specifying gateway + metric works. 182*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 183*bc301feeSPouria Mousavizadeh Tehrani route -n6 delete -net ${route}/64 -gateway ${nhop2} -metric 2 184*bc301feeSPouria Mousavizadeh Tehrani 185*bc301feeSPouria Mousavizadeh Tehrani # Verify that nhop1 is the best route now 186*bc301feeSPouria Mousavizadeh Tehrani atf_check -o match:".*gateway: ${nhop1}.*" \ 187*bc301feeSPouria Mousavizadeh Tehrani route -n6 get -net ${route}/64 188*bc301feeSPouria Mousavizadeh Tehrani 189*bc301feeSPouria Mousavizadeh Tehrani # But other route with nhops2 should exists. 190*bc301feeSPouria Mousavizadeh Tehrani atf_check -o save:netstat \ 191*bc301feeSPouria Mousavizadeh Tehrani netstat -rn6 --libxo json 192*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 1 3) 193*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 194*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 1 4) 195*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 196*bc301feeSPouria Mousavizadeh Tehrani 197*bc301feeSPouria Mousavizadeh Tehrani # Delete routes with nhop1 as nexthop without specifying metric. 198*bc301feeSPouria Mousavizadeh Tehrani # Test that deleting a route by gateway removes all routes with 199*bc301feeSPouria Mousavizadeh Tehrani # that gateway, regardless of metric value. 200*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 201*bc301feeSPouria Mousavizadeh Tehrani route -n6 delete -net ${route}/64 -gateway ${nhop1} 202*bc301feeSPouria Mousavizadeh Tehrani 203*bc301feeSPouria Mousavizadeh Tehrani # Verify that nhop2 is the best route now 204*bc301feeSPouria Mousavizadeh Tehrani atf_check -o match:".*gateway: ${nhop2}.*" \ 205*bc301feeSPouria Mousavizadeh Tehrani route -n6 get -net ${route}/64 206*bc301feeSPouria Mousavizadeh Tehrani 207*bc301feeSPouria Mousavizadeh Tehrani # Delete routes with metric 3 without specifying their gateway. 208*bc301feeSPouria Mousavizadeh Tehrani # Test that deleting a route by metric removes all routes with 209*bc301feeSPouria Mousavizadeh Tehrani # that metric, regardless of gateway value. 210*bc301feeSPouria Mousavizadeh Tehrani atf_check -o ignore \ 211*bc301feeSPouria Mousavizadeh Tehrani route -n6 delete -net ${route}/64 -metric 3 212*bc301feeSPouria Mousavizadeh Tehrani 213*bc301feeSPouria Mousavizadeh Tehrani # Verify that nhop2 is still the best route with metric of 4 214*bc301feeSPouria Mousavizadeh Tehrani atf_check -o match:".*gateway: ${nhop2}.*" \ 215*bc301feeSPouria Mousavizadeh Tehrani route -n6 get -net ${route}/64 216*bc301feeSPouria Mousavizadeh Tehrani output=$(cat netstat | jq_rtentry ${route}/64 | jq_nhop_filter ${nhop2} 1 4) 217*bc301feeSPouria Mousavizadeh Tehrani atf_check_equal "$output" "$nhop2" 218*bc301feeSPouria Mousavizadeh Tehrani} 219*bc301feeSPouria Mousavizadeh Tehrani 220*bc301feeSPouria Mousavizadeh Tehranidelete_route_with_metric_cleanup() 221*bc301feeSPouria Mousavizadeh Tehrani{ 222*bc301feeSPouria Mousavizadeh Tehrani vnet_cleanup 223*bc301feeSPouria Mousavizadeh Tehrani} 224*bc301feeSPouria Mousavizadeh Tehrani 225*bc301feeSPouria Mousavizadeh Tehrani 226*bc301feeSPouria Mousavizadeh Tehraniatf_init_test_cases() 227*bc301feeSPouria Mousavizadeh Tehrani{ 228*bc301feeSPouria Mousavizadeh Tehrani atf_add_test_case "add_lowest_metric" 229*bc301feeSPouria Mousavizadeh Tehrani atf_add_test_case "add_default_metric" 230*bc301feeSPouria Mousavizadeh Tehrani atf_add_test_case "delete_route_with_metric" 231*bc301feeSPouria Mousavizadeh Tehrani} 232