Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
7OVPN_COMMON_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
8source "$OVPN_COMMON_DIR/../../kselftest/ktap_helpers.sh"
9
10OVPN_UDP_PEERS_FILE=${OVPN_UDP_PEERS_FILE:-udp_peers.txt}
11OVPN_TCP_PEERS_FILE=${OVPN_TCP_PEERS_FILE:-tcp_peers.txt}
12OVPN_CLI=${OVPN_CLI:-${OVPN_COMMON_DIR}/ovpn-cli}
13OVPN_YNL=${OVPN_YNL:-${OVPN_COMMON_DIR}/../../../../net/ynl/pyynl/cli.py}
14OVPN_ALG=${OVPN_ALG:-aes}
15OVPN_PROTO=${OVPN_PROTO:-UDP}
16OVPN_FLOAT=${OVPN_FLOAT:-0}
17OVPN_SYMMETRIC_ID=${OVPN_SYMMETRIC_ID:-0}
18OVPN_VERBOSE=${OVPN_VERBOSE:-0}
19
20export OVPN_ID_OFFSET=$(( 9 * (OVPN_SYMMETRIC_ID == 0) ))
21
22OVPN_JQ_FILTER='map(if type == "array" then .[] else . end) |
23 map(select(.msg.peer | has("remote-ipv6") | not)) |
24 map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]'
25OVPN_LAN_IP="11.11.11.11"
26
27declare -A OVPN_TMP_JSONS=()
28declare -A OVPN_LISTENER_PIDS=()
29OVPN_CURRENT_STAGE=""
30
31ovpn_is_verbose() {
32 [[ "${OVPN_VERBOSE}" == "1" ]]
33}
34
35ovpn_log() {
36 ovpn_is_verbose || return 0
37 printf '%s\n' "$*"
38}
39
40ovpn_print_cmd_output() {
41 local output_file="$1"
42 local line
43
44 [[ -s "${output_file}" ]] || return 0
45
46 while IFS= read -r line; do
47 ovpn_log "${line}"
48 done < "${output_file}"
49}
50
51ovpn_cmd_run() {
52 local mode="$1"
53 local label="$2"
54 local output_file
55 local rc
56 local ret=0
57
58 shift 2
59
60 output_file=$(mktemp)
61 if "$@" >"${output_file}" 2>&1; then
62 rc=0
63 else
64 rc=$?
65 fi
66
67 case "${mode}" in
68 ok)
69 if [[ "${rc}" -ne 0 ]]; then
70 cat "${output_file}"
71 printf '%s\n' \
72 "${label}: command failed with rc=${rc}: $*"
73 ret="${rc}"
74 fi
75 ;;
76 mayfail)
77 ;;
78 fail)
79 [[ "${rc}" -eq 0 ]] && ret=1
80 ;;
81 esac
82
83 if ovpn_is_verbose && [[ "${rc}" -eq 0 || "${mode}" != "ok" ]]; then
84 ovpn_print_cmd_output "${output_file}"
85 fi
86
87 rm -f "${output_file}"
88 return "${ret}"
89}
90
91ovpn_cmd_ok() {
92 ovpn_cmd_run ok "$@"
93}
94
95ovpn_cmd_mayfail() {
96 ovpn_cmd_run mayfail "$@"
97}
98
99ovpn_cmd_fail() {
100 ovpn_cmd_run fail "$@"
101}
102
103ovpn_run_bg() {
104 local pid_var="$1"
105
106 shift
107 if ovpn_is_verbose; then
108 "$@" &
109 else
110 "$@" >/dev/null 2>&1 &
111 fi
112
113 printf -v "${pid_var}" '%s' "$!"
114}
115
116ovpn_run_stage() {
117 local label="$1"
118
119 shift
120 OVPN_CURRENT_STAGE="${label}"
121 "$@"
122 OVPN_CURRENT_STAGE=""
123 ktap_test_pass "${label}"
124}
125
126ovpn_stage_err() {
127 # ERR trap is global under set -eE: only report failures that happen
128 # while ovpn_run_stage() is actively executing a stage body.
129 if [[ -n "${OVPN_CURRENT_STAGE}" ]]; then
130 ktap_test_fail "${OVPN_CURRENT_STAGE}"
131 OVPN_CURRENT_STAGE=""
132 fi
133}
134
135ovpn_create_ns() {
136 ip netns add "ovpn_peer${1}"
137}
138
139ovpn_setup_ns() {
140 local peer="ovpn_peer${1}"
141 local server_ns="ovpn_peer0"
142 local peer_ns
143 MODE="P2P"
144
145 if [ ${1} -eq 0 ]; then
146 MODE="MP"
147 for p in $(seq 1 ${OVPN_NUM_PEERS}); do
148 peer_ns="ovpn_peer${p}"
149 ip link add veth${p} netns "${server_ns}" type veth \
150 peer name veth${p} netns "${peer_ns}"
151
152 ip -n "${server_ns}" addr add 10.10.${p}.1/24 dev \
153 veth${p}
154 ip -n "${server_ns}" addr add fd00:0:0:${p}::1/64 dev \
155 veth${p}
156 ip -n "${server_ns}" link set veth${p} up
157
158 ip -n "${peer_ns}" addr add 10.10.${p}.2/24 dev veth${p}
159 ip -n "${peer_ns}" addr add fd00:0:0:${p}::2/64 dev \
160 veth${p}
161 ip -n "${peer_ns}" link set veth${p} up
162 done
163 fi
164
165 ip netns exec "${peer}" ${OVPN_CLI} new_iface tun${1} $MODE
166 ip -n "${peer}" addr add ${2} dev tun${1}
167 # add a secondary IP to peer 1, to test a LAN behind a client
168 if [ ${1} -eq 1 -a -n "${OVPN_LAN_IP}" ]; then
169 ip -n "${peer}" addr add ${OVPN_LAN_IP} dev tun${1}
170 ip -n "${server_ns}" route add ${OVPN_LAN_IP} via \
171 $(echo ${2} |sed -e s'!/.*!!') dev tun0
172 fi
173 if [ -n "${3}" ]; then
174 ip -n "${peer}" link set mtu ${3} dev tun${1}
175 fi
176 ip -n "${peer}" link set tun${1} up
177}
178
179ovpn_build_capture_filter() {
180 # match the first four bytes of the openvpn data payload
181 if [ "${OVPN_PROTO}" == "UDP" ]; then
182 # For UDP, libpcap transport indexing only works for IPv4, so
183 # use an explicit IPv4 or IPv6 expression based on the peer
184 # address. The IPv6 branch assumes there are no extension
185 # headers in the outer packet.
186 if [[ "${2}" == *:* ]]; then
187 printf "ip6 and ip6[6] = 17 and ip6[48:4] = %s" "${1}"
188 else
189 printf "ip and udp[8:4] = %s" "${1}"
190 fi
191 else
192 # openvpn over TCP prepends a 2-byte packet length ahead of the
193 # DATA_V2 opcode, so skip it before matching the payload header
194 printf "ip and tcp[(((tcp[12] & 0xf0) >> 2) + 2):4] = %s" "${1}"
195 fi
196}
197
198ovpn_setup_listener() {
199 local peer="$1"
200 local file
201 local peer_ns="ovpn_peer${peer}"
202
203 file=$(mktemp)
204 PYTHONUNBUFFERED=1 ip netns exec "${peer_ns}" "${OVPN_YNL}" --family \
205 ovpn --subscribe peers --output-json > "${file}" \
206 2>/dev/null &
207 OVPN_LISTENER_PIDS["${peer}"]=$!
208 OVPN_TMP_JSONS["${peer}"]="${file}"
209}
210
211ovpn_add_peer() {
212 labels=("ASYMM" "SYMM")
213 local peer_ns
214 local server_ns="ovpn_peer0"
215 M_ID=${labels[OVPN_SYMMETRIC_ID]}
216
217 if [ "${OVPN_PROTO}" == "UDP" ]; then
218 if [ ${1} -eq 0 ]; then
219 ip netns exec "${server_ns}" ${OVPN_CLI} \
220 new_multi_peer tun0 1 ${M_ID} \
221 ${OVPN_UDP_PEERS_FILE}
222
223 for p in $(seq 1 ${OVPN_NUM_PEERS}); do
224 ip netns exec "${server_ns}" ${OVPN_CLI} \
225 new_key tun0 ${p} 1 0 ${OVPN_ALG} 0 \
226 data64.key
227 done
228 else
229 peer_ns="ovpn_peer${1}"
230 if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then
231 PEER_ID=${1}
232 TX_ID="none"
233 else
234 PEER_ID=$(awk "NR == ${1} {print \$2}" \
235 ${OVPN_UDP_PEERS_FILE})
236 TX_ID=${1}
237 fi
238 RADDR=$(awk "NR == ${1} {print \$3}" \
239 ${OVPN_UDP_PEERS_FILE})
240 RPORT=$(awk "NR == ${1} {print \$4}" \
241 ${OVPN_UDP_PEERS_FILE})
242 LPORT=$(awk "NR == ${1} {print \$6}" \
243 ${OVPN_UDP_PEERS_FILE})
244 ip netns exec "${peer_ns}" ${OVPN_CLI} new_peer \
245 tun${1} ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} \
246 ${RPORT}
247 ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${1} \
248 ${PEER_ID} 1 0 ${OVPN_ALG} 1 data64.key
249 fi
250 else
251 if [ ${1} -eq 0 ]; then
252 (ip netns exec "${server_ns}" ${OVPN_CLI} listen tun0 \
253 1 ${M_ID} ${OVPN_TCP_PEERS_FILE} && {
254 for p in $(seq 1 ${OVPN_NUM_PEERS}); do
255 ip netns exec "${server_ns}" \
256 ${OVPN_CLI} new_key tun0 ${p} \
257 1 0 ${OVPN_ALG} 0 data64.key
258 done
259 }) &
260 sleep 5
261 else
262 peer_ns="ovpn_peer${1}"
263 if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then
264 PEER_ID=${1}
265 TX_ID="none"
266 else
267 PEER_ID=$(awk "NR == ${1} {print \$2}" \
268 ${OVPN_TCP_PEERS_FILE})
269 TX_ID=${1}
270 fi
271 ip netns exec "${peer_ns}" ${OVPN_CLI} connect tun${1} \
272 ${PEER_ID} ${TX_ID} 10.10.${1}.1 1 data64.key
273 fi
274 fi
275}
276
277ovpn_compare_ntfs() {
278 local diff_rc=0
279 local diff_file
280
281 if [ ${#OVPN_TMP_JSONS[@]} -gt 0 ]; then
282 suffix=""
283 [ "${OVPN_SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm"
284 [ "$OVPN_FLOAT" == 1 ] && suffix="${suffix}-float"
285 expected="json/peer${1}${suffix}.json"
286 received="${OVPN_TMP_JSONS[$1]}"
287 diff_file=$(mktemp)
288
289 ovpn_stop_listener "${1}" 1
290 printf "Checking notifications for peer ${1}... "
291 if diff <(jq -s "${OVPN_JQ_FILTER}" ${expected}) \
292 <(jq -s "${OVPN_JQ_FILTER}" ${received}) \
293 >"${diff_file}" 2>&1; then
294 echo "OK"
295 else
296 diff_rc=$?
297 echo "failed"
298 cat "${diff_file}"
299 fi
300
301 rm -f "${diff_file}" || true
302 rm -f "${received}" || true
303 unset "OVPN_TMP_JSONS[$1]"
304 fi
305
306 return "${diff_rc}"
307}
308
309ovpn_stop_listener() {
310 local peer="$1"
311 local keep_json="${2:-0}"
312 local pid="${OVPN_LISTENER_PIDS[$peer]:-}"
313 local json="${OVPN_TMP_JSONS[$peer]:-}"
314
315 if [[ -n "${pid}" ]]; then
316 kill -TERM "${pid}" 2>/dev/null || true
317 wait "${pid}" 2>/dev/null || true
318 unset "OVPN_LISTENER_PIDS[$peer]"
319 fi
320
321 if [[ -n "${json}" && "${keep_json}" -eq 0 ]]; then
322 rm -f "${json}" || true
323 unset "OVPN_TMP_JSONS[$peer]"
324 fi
325}
326
327ovpn_cleanup_peer_ns() {
328 local peer="$1"
329 local peer_id="${peer#ovpn_peer}"
330
331 ip -n "${peer}" link set tun${peer_id} down 2>/dev/null || true
332 ip netns exec "${peer}" ${OVPN_CLI} del_iface tun${peer_id} \
333 1>/dev/null 2>&1 || true
334 ip netns del "${peer}" 2>/dev/null || true
335}
336
337ovpn_cleanup() {
338 local peer
339
340 # some ovpn-cli processes sleep in background so they need manual poking
341 killall "$(basename "${OVPN_CLI}")" 2>/dev/null || true
342
343 for peer in "${!OVPN_LISTENER_PIDS[@]}"; do
344 ovpn_stop_listener "${peer}" 2>/dev/null
345 done
346
347 for p in $(seq 1 10); do
348 ip -n ovpn_peer0 link del veth${p} 2>/dev/null || true
349 done
350
351 # remove from ovpn's netns pool
352 while IFS= read -r peer; do
353 [[ -n "${peer}" ]] || continue
354 ovpn_cleanup_peer_ns "${peer}" 2>/dev/null
355 done < <(ip netns list 2>/dev/null | awk '/^ovpn_/ {print $1}')
356}
357
358if [ "${OVPN_PROTO}" == "UDP" ]; then
359 OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_UDP_PEERS_FILE} | \
360 awk '{print $1}')}
361else
362 OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_TCP_PEERS_FILE} | \
363 awk '{print $1}')}
364fi