xref: /linux/tools/testing/selftests/net/ovpn/test.sh (revision f2c53ea949c5048f96b3dbb5a5ee7131ce4ff2de)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# Copyright (C) 2020-2025 OpenVPN, Inc.
4#
5#  Author:	Antonio Quartulli <antonio@openvpn.net>
6
7#set -x
8set -eE
9
10source ./common.sh
11
12ovpn_test_finished=0
13
14ovpn_test_exit() {
15	ovpn_cleanup
16	modprobe -r ovpn || true
17
18	if [ "${ovpn_test_finished}" -eq 0 ]; then
19		ktap_print_totals
20	fi
21}
22
23ovpn_prepare_network() {
24	local p
25	local peer_ns
26
27	for p in $(seq 0 ${OVPN_NUM_PEERS}); do
28		ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}"
29	done
30
31	for p in $(seq 0 ${OVPN_NUM_PEERS}); do
32		ovpn_cmd_ok "start notification listener peer${p}" \
33			ovpn_setup_listener "${p}"
34		# starting all YNL listeners back-to-back can intermittently
35		# stall their startup so serialize launches a bit
36		sleep 0.5
37	done
38
39	for p in $(seq 0 ${OVPN_NUM_PEERS}); do
40		ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \
41			"${p}" 5.5.5.$((p + 1))/24 "${MTU}"
42	done
43
44	for p in $(seq 0 ${OVPN_NUM_PEERS}); do
45		ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}"
46	done
47
48	for p in $(seq 1 ${OVPN_NUM_PEERS}); do
49		peer_ns="ovpn_peer${p}"
50		ovpn_cmd_ok "set peer0 timeout for peer ${p}" \
51			ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \
52				${p} 60 120
53		ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \
54			ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \
55				tun${p} $((p + OVPN_ID_OFFSET)) 60 120
56	done
57}
58
59ovpn_new_test_peer() {
60	local peer_id="$1"
61
62	shift
63	ip netns exec ovpn_peer0 "${OVPN_CLI}" new_peer tun0 \
64		"${peer_id}" none 65000 10.10.1.2 1 "$@"
65}
66
67ovpn_set_peer_vpn_addr() {
68	ip netns exec ovpn_peer0 "${OVPN_CLI}" set_peer tun0 \
69		"$1" 60 120 "$2"
70}
71
72ovpn_run_vpn_addr_validation() {
73	local addr
74	local peer1_addr4
75	local test_peer_id=$((OVPN_NUM_PEERS + 1))
76	local test_peer_addr6="2001:db8::2"
77	# Do not include 0.0.0.0 or :: here. They are invalid on creation, but
78	# clear one address family on update and are valid if the other remains.
79	local -a invalid_addrs=(
80		"127.0.0.1"
81		"224.0.0.1"
82		"255.255.255.255"
83		"::1"
84		"::192.0.2.1"
85		"::ffff:192.0.2.1"
86		"ff02::1"
87	)
88
89	peer1_addr4=$(ovpn_peer_vpn_addr 1)
90
91	ovpn_cmd_fail "reject peer without VPN address" \
92		ovpn_new_test_peer "${test_peer_id}"
93
94	for addr in "0.0.0.0" "::" "${invalid_addrs[@]}"; do
95		ovpn_cmd_fail "reject new peer VPN address ${addr}" \
96			ovpn_new_test_peer "${test_peer_id}" "${addr}"
97	done
98
99	ovpn_cmd_fail "reject duplicate IPv4 address on peer creation" \
100		ovpn_new_test_peer "${test_peer_id}" "${peer1_addr4}"
101	ovpn_cmd_fail "reject clearing the last peer VPN address" \
102		ovpn_set_peer_vpn_addr 1 0.0.0.0
103
104	for addr in "${invalid_addrs[@]}"; do
105		ovpn_cmd_fail "reject updated peer VPN address ${addr}" \
106			ovpn_set_peer_vpn_addr 1 "${addr}"
107	done
108
109	ovpn_cmd_fail "reject duplicate IPv4 address on peer update" \
110		ovpn_set_peer_vpn_addr 2 "${peer1_addr4}"
111
112	ovpn_cmd_ok "add peer IPv6 address" \
113		ovpn_set_peer_vpn_addr 1 "${test_peer_addr6}"
114	ovpn_cmd_fail "reject duplicate IPv6 address on peer creation" \
115		ovpn_new_test_peer "${test_peer_id}" "${test_peer_addr6}"
116	ovpn_cmd_fail "reject duplicate IPv6 address on peer update" \
117		ovpn_set_peer_vpn_addr 2 "${test_peer_addr6}"
118
119	ovpn_cmd_ok "clear peer IPv4 address" \
120		ovpn_set_peer_vpn_addr 1 0.0.0.0
121	ovpn_cmd_fail "reject clearing the remaining peer IPv6 address" \
122		ovpn_set_peer_vpn_addr 1 ::
123	ovpn_cmd_ok "restore peer IPv4 address" \
124		ovpn_set_peer_vpn_addr 1 "${peer1_addr4}"
125	ovpn_cmd_ok "clear peer IPv6 address" \
126		ovpn_set_peer_vpn_addr 1 ::
127}
128
129ovpn_run_basic_traffic() {
130	local p
131	local header1
132	local header2
133	local peer_ns
134	local raddr
135	local tcpdump_pid1
136	local tcpdump_pid2
137	local tcpdump_timeout="1.5s"
138
139	for p in $(seq 1 ${OVPN_NUM_PEERS}); do
140		# The first part of the data packet header consists of:
141		# - TCP only: 2 bytes for the packet length
142		# - 5 bits for opcode ("9" for DATA_V2)
143		# - 3 bits for key-id ("0" at this point)
144		# - 12 bytes for peer-id:
145		#     - with asymmetric ID: "${p}" one way and "${p} + 9" the
146		#	other way
147		#     - with symmetric ID: "${p}" both ways
148		header1=$(printf "0x4800000%x" ${p})
149		header2=$(printf "0x4800000%x" $((p + OVPN_ID_OFFSET)))
150		raddr=""
151		if [ "${OVPN_PROTO}" == "UDP" ]; then
152			raddr=$(awk "NR == ${p} {print \$3}" \
153				"${OVPN_UDP_PEERS_FILE}")
154		fi
155		peer_ns="ovpn_peer${p}"
156
157		timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
158			tcpdump --immediate-mode -p -ni veth${p} -c 1 \
159			"$(ovpn_build_capture_filter "${header1}" "${raddr}")" \
160			>/dev/null 2>&1 &
161		tcpdump_pid1=$!
162		timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
163			tcpdump --immediate-mode -p -ni veth${p} -c 1 \
164			"$(ovpn_build_capture_filter "${header2}" "${raddr}")" \
165			>/dev/null 2>&1 &
166		tcpdump_pid2=$!
167
168		sleep 0.3
169		ovpn_cmd_ok "send baseline traffic to peer ${p}" \
170			ip netns exec ovpn_peer0 \
171			ping -qfc 100 -w 3 5.5.5.$((p + 1))
172		ovpn_cmd_ok "send large-payload traffic to peer ${p}" \
173			ip netns exec ovpn_peer0 \
174			ping -qfc 100 -s 3000 -w 3 5.5.5.$((p + 1))
175
176		wait "${tcpdump_pid1}" || return 1
177		wait "${tcpdump_pid2}" || return 1
178	done
179}
180
181ovpn_run_lan_traffic() {
182	ovpn_cmd_ok "ping LAN behind peer1" \
183		ip netns exec ovpn_peer0 ping -qfc 100 -w 3 "${OVPN_LAN_IP}"
184}
185
186ovpn_run_float_mode() {
187	local p
188	local peer_ns
189
190	for p in $(seq 1 ${OVPN_NUM_PEERS}); do
191		peer_ns="ovpn_peer${p}"
192		ovpn_cmd_ok "float: remove old transport address on peer${p}" \
193			ip -n "${peer_ns}" addr del 10.10.${p}.2/24 dev veth${p}
194		ovpn_cmd_ok "float: add new transport address on peer${p}" \
195			ip -n "${peer_ns}" addr add 10.10.${p}.3/24 dev veth${p}
196	done
197	for p in $(seq 1 ${OVPN_NUM_PEERS}); do
198		peer_ns="ovpn_peer${p}"
199		ovpn_cmd_ok "ping tunnel after float peer ${p}" \
200			ip netns exec "${peer_ns}" ping -qfc 100 -w 3 5.5.5.1
201	done
202}
203
204ovpn_run_iperf() {
205	local iperf_pid
206
207	ovpn_run_bg iperf_pid ip netns exec ovpn_peer0 iperf3 -1 -s
208	sleep 1
209
210	ovpn_cmd_ok "run iperf throughput flow" \
211		ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1
212	wait "${iperf_pid}" || return 1
213}
214
215ovpn_run_key_rollover() {
216	local p
217	local peer_ns
218
219	ovpn_log "Adding secondary key and then swap:"
220
221	for p in $(seq 1 ${OVPN_NUM_PEERS}); do
222		peer_ns="ovpn_peer${p}"
223		ovpn_cmd_ok "add secondary key on peer0 for peer ${p}" \
224			ip netns exec ovpn_peer0 ${OVPN_CLI} new_key tun0 \
225				${p} 2 1 ${OVPN_ALG} 0 data64.key
226		ovpn_cmd_ok "add secondary key on peer${p} for peer ${p}" \
227			ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${p} \
228				$((p + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 \
229				data64.key
230		ovpn_cmd_ok "swap keys on peer${p}" \
231			ip netns exec "${peer_ns}" ${OVPN_CLI} swap_keys \
232				tun${p} $((p + OVPN_ID_OFFSET))
233	done
234}
235
236ovpn_run_queries() {
237	ovpn_log "Querying all peers:"
238
239	ovpn_cmd_ok "query all peers from peer0" \
240		ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0
241	ovpn_cmd_ok "query all peers from peer1" \
242		ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1
243
244	ovpn_log "Querying peer 1:"
245
246	ovpn_cmd_ok "query peer 1 from peer0" \
247		ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 1
248}
249
250ovpn_query_peer_missing() {
251	ovpn_log "Querying non-existent peer 20:"
252
253	ovpn_cmd_fail "query missing peer 20 on peer0" \
254		ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 20
255}
256
257ovpn_run_peer_cleanup() {
258	local p
259	local peer_ns
260
261	ovpn_log "Deleting peer 1:"
262
263	ovpn_cmd_ok "delete peer1 on peer0" \
264		ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 1
265	ovpn_cmd_ok "delete peer1 on peer1" \
266		ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 \
267			$((1 + OVPN_ID_OFFSET))
268
269	ovpn_log "Querying keys:"
270
271	for p in $(seq 2 ${OVPN_NUM_PEERS}); do
272		peer_ns="ovpn_peer${p}"
273		ovpn_cmd_ok "query peer${p} key 1" \
274			ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \
275				$((p + OVPN_ID_OFFSET)) 1
276		ovpn_cmd_ok "query peer${p} key 2" \
277			ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \
278				$((p + OVPN_ID_OFFSET)) 2
279	done
280}
281
282ovpn_run_traffic_delete_peer() {
283	local ping_pid
284
285	ovpn_log "Deleting peer while sending traffic:"
286
287	ovpn_run_bg ping_pid ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1
288	sleep 2
289	ovpn_cmd_ok "delete peer0 peer 2" \
290		ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 2
291
292	if [ "${OVPN_PROTO}" == "TCP" ]; then
293		# In TCP mode this command is expected to fail for both peers.
294		ovpn_cmd_mayfail "delete peer2 peer 2 (TCP non-fatal)" \
295			ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 \
296				$((2 + OVPN_ID_OFFSET))
297	else
298		ovpn_cmd_ok "delete peer2 peer 2" ip netns exec ovpn_peer2 \
299			${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET))
300	fi
301
302	wait "${ping_pid}" || true
303}
304
305ovpn_run_key_cleanup() {
306	local p
307	local peer_ns
308
309	ovpn_log "Deleting keys:"
310
311	for p in $(seq 3 ${OVPN_NUM_PEERS}); do
312		peer_ns="ovpn_peer${p}"
313		ovpn_cmd_ok "delete key 1 for peer${p}" \
314			ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \
315				$((p + OVPN_ID_OFFSET)) 1
316		ovpn_cmd_ok "delete key 2 for peer${p}" \
317			ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \
318				$((p + OVPN_ID_OFFSET)) 2
319	done
320}
321
322ovpn_run_timeouts() {
323	local p
324	local peer_ns
325
326	ovpn_log "Setting timeout to 3s MP:"
327
328	for p in $(seq 3 ${OVPN_NUM_PEERS}); do
329		# Non-fatal: this may fail in some protocol modes.
330		ovpn_cmd_mayfail "set peer0 timeout for peer ${p} (non-fatal)" \
331			ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \
332				${p} 3 3
333		peer_ns="ovpn_peer${p}"
334		ovpn_cmd_ok "disable timeout on peer${p} while peer0 adjusts \
335			state" ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \
336			tun${p} $((p + OVPN_ID_OFFSET)) 0 0
337	done
338	# wait for peers to timeout
339	sleep 5
340
341	ovpn_log "Setting timeout to 3s P2P:"
342
343	for p in $(seq 3 ${OVPN_NUM_PEERS}); do
344		peer_ns="ovpn_peer${p}"
345		ovpn_cmd_ok "set peer${p} P2P timeout" \
346			ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \
347				tun${p} $((p + OVPN_ID_OFFSET)) 3 3
348	done
349	sleep 5
350}
351
352ovpn_run_notifications() {
353	local p
354
355	for p in $(seq 0 ${OVPN_NUM_PEERS}); do
356		ovpn_cmd_ok "validate listener output for peer ${p}" \
357			ovpn_compare_ntfs "${p}"
358	done
359}
360
361trap ovpn_test_exit EXIT
362trap ovpn_stage_err ERR
363
364ktap_print_header
365if [ "${OVPN_FLOAT}" == "1" ]; then
366	ktap_set_plan 14
367else
368	ktap_set_plan 13
369fi
370
371ovpn_cleanup
372modprobe -q ovpn || true
373
374ovpn_run_stage "setup network topology" ovpn_prepare_network
375ovpn_run_stage "validate peer VPN addresses" ovpn_run_vpn_addr_validation
376ovpn_run_stage "run baseline data traffic" ovpn_run_basic_traffic
377ovpn_run_stage "run LAN traffic behind peer1" ovpn_run_lan_traffic
378[ "${OVPN_FLOAT}" == "1" ] && ovpn_run_stage "run floating peer checks" \
379	ovpn_run_float_mode
380ovpn_run_stage "run iperf throughput" ovpn_run_iperf
381ovpn_run_stage "run key rollout" ovpn_run_key_rollover
382ovpn_run_stage "query peers" ovpn_run_queries
383ovpn_run_stage "query missing peer fails" ovpn_query_peer_missing
384ovpn_run_stage "peer lifecycle and key queries" ovpn_run_peer_cleanup
385ovpn_run_stage "delete peer while traffic" ovpn_run_traffic_delete_peer
386ovpn_run_stage "delete stale keys" ovpn_run_key_cleanup
387ovpn_run_stage "check timeout behavior" ovpn_run_timeouts
388ovpn_run_stage "validate notification output" ovpn_run_notifications
389
390ovpn_test_finished=1
391ktap_finished
392