xref: /freebsd/tests/sys/net/routing/test_routing.sh (revision bc301fee4cb2c3e9ce220dc3e0cbb9d7d5a83d6f)
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