1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# +--------------------+                     +----------------------+
5# | H1                 |                     |                   H2 |
6# |                    |                     |                      |
7# |          $h1.200 + |                     | + $h2.200            |
8# |     192.0.2.1/28 | |                     | | 192.0.2.18/28      |
9# | 2001:db8:1::1/64 | |                     | | 2001:db8:2::1/64   |
10# |                  | |                     | |                    |
11# |              $h1 + |                     | + $h2                |
12# |                  | |                     | |                    |
13# +------------------|-+                     +-|--------------------+
14#                    |                         |
15# +------------------|-------------------------|--------------------+
16# | SW               |                         |                    |
17# |                  |                         |                    |
18# |             $rp1 +                         + $rp2               |
19# |                  |                         |                    |
20# |         $rp1.200 +                         + $rp2.200           |
21# |     192.0.2.2/28                             192.0.2.17/28      |
22# | 2001:db8:1::2/64                             2001:db8:2::2/64   |
23# |                                                                 |
24# +-----------------------------------------------------------------+
25
26ALL_TESTS="
27	ping_ipv4
28	ping_ipv6
29	test_stats_rx_ipv4
30	test_stats_tx_ipv4
31	test_stats_rx_ipv6
32	test_stats_tx_ipv6
33	respin_enablement
34	test_stats_rx_ipv4
35	test_stats_tx_ipv4
36	test_stats_rx_ipv6
37	test_stats_tx_ipv6
38	reapply_config
39	ping_ipv4
40	ping_ipv6
41	test_stats_rx_ipv4
42	test_stats_tx_ipv4
43	test_stats_rx_ipv6
44	test_stats_tx_ipv6
45	test_stats_report_rx
46	test_stats_report_tx
47	test_destroy_enabled
48	test_double_enable
49"
50NUM_NETIFS=4
51source lib.sh
52
53h1_create()
54{
55	simple_if_init $h1
56	vlan_create $h1 200 v$h1 192.0.2.1/28 2001:db8:1::1/64
57	ip route add 192.0.2.16/28 vrf v$h1 nexthop via 192.0.2.2
58	ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2
59}
60
61h1_destroy()
62{
63	ip -6 route del 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2
64	ip route del 192.0.2.16/28 vrf v$h1 nexthop via 192.0.2.2
65	vlan_destroy $h1 200
66	simple_if_fini $h1
67}
68
69h2_create()
70{
71	simple_if_init $h2
72	vlan_create $h2 200 v$h2 192.0.2.18/28 2001:db8:2::1/64
73	ip route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.17
74	ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::2
75}
76
77h2_destroy()
78{
79	ip -6 route del 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::2
80	ip route del 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.17
81	vlan_destroy $h2 200
82	simple_if_fini $h2
83}
84
85router_rp1_200_create()
86{
87	ip link add name $rp1.200 up \
88		link $rp1 addrgenmode eui64 type vlan id 200
89	ip address add dev $rp1.200 192.0.2.2/28
90	ip address add dev $rp1.200 2001:db8:1::2/64
91	ip stats set dev $rp1.200 l3_stats on
92}
93
94router_rp1_200_destroy()
95{
96	ip stats set dev $rp1.200 l3_stats off
97	ip address del dev $rp1.200 2001:db8:1::2/64
98	ip address del dev $rp1.200 192.0.2.2/28
99	ip link del dev $rp1.200
100}
101
102router_create()
103{
104	ip link set dev $rp1 up
105	router_rp1_200_create
106
107	ip link set dev $rp2 up
108	vlan_create $rp2 200 "" 192.0.2.17/28 2001:db8:2::2/64
109}
110
111router_destroy()
112{
113	vlan_destroy $rp2 200
114	ip link set dev $rp2 down
115
116	router_rp1_200_destroy
117	ip link set dev $rp1 down
118}
119
120setup_prepare()
121{
122	h1=${NETIFS[p1]}
123	rp1=${NETIFS[p2]}
124
125	rp2=${NETIFS[p3]}
126	h2=${NETIFS[p4]}
127
128	rp1mac=$(mac_get $rp1)
129	rp2mac=$(mac_get $rp2)
130
131	vrf_prepare
132
133	h1_create
134	h2_create
135
136	router_create
137
138	forwarding_enable
139}
140
141cleanup()
142{
143	pre_cleanup
144
145	forwarding_restore
146
147	router_destroy
148
149	h2_destroy
150	h1_destroy
151
152	vrf_cleanup
153}
154
155ping_ipv4()
156{
157	ping_test $h1.200 192.0.2.18 " IPv4"
158}
159
160ping_ipv6()
161{
162	ping_test $h1.200 2001:db8:2::1 " IPv6"
163}
164
165send_packets_rx_ipv4()
166{
167	# Send 21 packets instead of 20, because the first one might trap and go
168	# through the SW datapath, which might not bump the HW counter.
169	$MZ $h1.200 -c 21 -d 20msec -p 100 \
170	    -a own -b $rp1mac -A 192.0.2.1 -B 192.0.2.18 \
171	    -q -t udp sp=54321,dp=12345
172}
173
174send_packets_rx_ipv6()
175{
176	$MZ $h1.200 -6 -c 21 -d 20msec -p 100 \
177	    -a own -b $rp1mac -A 2001:db8:1::1 -B 2001:db8:2::1 \
178	    -q -t udp sp=54321,dp=12345
179}
180
181send_packets_tx_ipv4()
182{
183	$MZ $h2.200 -c 21 -d 20msec -p 100 \
184	    -a own -b $rp2mac -A 192.0.2.18 -B 192.0.2.1 \
185	    -q -t udp sp=54321,dp=12345
186}
187
188send_packets_tx_ipv6()
189{
190	$MZ $h2.200 -6 -c 21 -d 20msec -p 100 \
191	    -a own -b $rp2mac -A 2001:db8:2::1 -B 2001:db8:1::1 \
192	    -q -t udp sp=54321,dp=12345
193}
194
195___test_stats()
196{
197	local dir=$1; shift
198	local prot=$1; shift
199
200	local a
201	local b
202
203	a=$(hw_stats_get l3_stats $rp1.200 ${dir} packets)
204	send_packets_${dir}_${prot}
205	"$@"
206	b=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= $a + 20" \
207		       hw_stats_get l3_stats $rp1.200 ${dir} packets)
208	check_err $? "Traffic not reflected in the counter: $a -> $b"
209}
210
211__test_stats()
212{
213	local dir=$1; shift
214	local prot=$1; shift
215
216	RET=0
217	___test_stats "$dir" "$prot"
218	log_test "Test $dir packets: $prot"
219}
220
221test_stats_rx_ipv4()
222{
223	__test_stats rx ipv4
224}
225
226test_stats_tx_ipv4()
227{
228	__test_stats tx ipv4
229}
230
231test_stats_rx_ipv6()
232{
233	__test_stats rx ipv6
234}
235
236test_stats_tx_ipv6()
237{
238	__test_stats tx ipv6
239}
240
241# Make sure everything works well even after stats have been disabled and
242# reenabled on the same device without touching the L3 configuration.
243respin_enablement()
244{
245	log_info "Turning stats off and on again"
246	ip stats set dev $rp1.200 l3_stats off
247	ip stats set dev $rp1.200 l3_stats on
248}
249
250# For the initial run, l3_stats is enabled on a completely set up netdevice. Now
251# do it the other way around: enabling the L3 stats on an L2 netdevice, and only
252# then apply the L3 configuration.
253reapply_config()
254{
255	log_info "Reapplying configuration"
256
257	router_rp1_200_destroy
258
259	ip link add name $rp1.200 link $rp1 addrgenmode none type vlan id 200
260	ip stats set dev $rp1.200 l3_stats on
261	ip link set dev $rp1.200 up addrgenmode eui64
262	ip address add dev $rp1.200 192.0.2.2/28
263	ip address add dev $rp1.200 2001:db8:1::2/64
264}
265
266__test_stats_report()
267{
268	local dir=$1; shift
269	local prot=$1; shift
270
271	local a
272	local b
273
274	RET=0
275
276	a=$(hw_stats_get l3_stats $rp1.200 ${dir} packets)
277	send_packets_${dir}_${prot}
278	ip address flush dev $rp1.200
279	b=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= $a + 20" \
280		       hw_stats_get l3_stats $rp1.200 ${dir} packets)
281	check_err $? "Traffic not reflected in the counter: $a -> $b"
282	log_test "Test ${dir} packets: stats pushed on loss of L3"
283
284	ip stats set dev $rp1.200 l3_stats off
285	ip link del dev $rp1.200
286	router_rp1_200_create
287}
288
289test_stats_report_rx()
290{
291	__test_stats_report rx ipv4
292}
293
294test_stats_report_tx()
295{
296	__test_stats_report tx ipv4
297}
298
299test_destroy_enabled()
300{
301	RET=0
302
303	ip link del dev $rp1.200
304	router_rp1_200_create
305
306	log_test "Destroy l3_stats-enabled netdev"
307}
308
309test_double_enable()
310{
311	RET=0
312	___test_stats rx ipv4 \
313		ip stats set dev $rp1.200 l3_stats on
314	log_test "Test stat retention across a spurious enablement"
315}
316
317trap cleanup EXIT
318
319setup_prepare
320setup_wait
321
322used=$(ip -j stats show dev $rp1.200 group offload subgroup hw_stats_info |
323	   jq '.[].info.l3_stats.used')
324kind=$(ip -j -d link show dev $rp1 |
325	   jq -r '.[].linkinfo.info_kind')
326if [[ $used != true ]]; then
327	if [[ $kind == veth ]]; then
328		log_test_skip "l3_stats not offloaded on veth interface"
329		EXIT_STATUS=$ksft_skip
330	else
331		RET=1 log_test "l3_stats not offloaded"
332	fi
333else
334	tests_run
335fi
336
337exit $EXIT_STATUS
338