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