xref: /linux/tools/testing/selftests/net/ovpn/common.sh (revision 367f4b163a8cff20d0cb06eb265a1bf1c6652bd9)
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
7UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt}
8TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt}
9OVPN_CLI=${OVPN_CLI:-./ovpn-cli}
10YNL_CLI=${YNL_CLI:-../../../../net/ynl/pyynl/cli.py}
11ALG=${ALG:-aes}
12PROTO=${PROTO:-UDP}
13FLOAT=${FLOAT:-0}
14SYMMETRIC_ID=${SYMMETRIC_ID:-0}
15
16export ID_OFFSET=$(( 9 * (SYMMETRIC_ID == 0) ))
17
18JQ_FILTER='map(select(.msg.peer | has("remote-ipv6") | not)) |
19	map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]'
20LAN_IP="11.11.11.11"
21
22declare -A tmp_jsons=()
23declare -A listener_pids=()
24
25create_ns() {
26	ip netns add peer${1}
27}
28
29setup_ns() {
30	MODE="P2P"
31
32	if [ ${1} -eq 0 ]; then
33		MODE="MP"
34		for p in $(seq 1 ${NUM_PEERS}); do
35			ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p}
36
37			ip -n peer0 addr add 10.10.${p}.1/24 dev veth${p}
38			ip -n peer0 addr add fd00:0:0:${p}::1/64 dev veth${p}
39			ip -n peer0 link set veth${p} up
40
41			ip -n peer${p} addr add 10.10.${p}.2/24 dev veth${p}
42			ip -n peer${p} addr add fd00:0:0:${p}::2/64 dev veth${p}
43			ip -n peer${p} link set veth${p} up
44		done
45	fi
46
47	ip netns exec peer${1} ${OVPN_CLI} new_iface tun${1} $MODE
48	ip -n peer${1} addr add ${2} dev tun${1}
49	# add a secondary IP to peer 1, to test a LAN behind a client
50	if [ ${1} -eq 1 -a -n "${LAN_IP}" ]; then
51		ip -n peer${1} addr add ${LAN_IP} dev tun${1}
52		ip -n peer0 route add ${LAN_IP} via $(echo ${2} |sed -e s'!/.*!!') dev tun0
53	fi
54	if [ -n "${3}" ]; then
55		ip -n peer${1} link set mtu ${3} dev tun${1}
56	fi
57	ip -n peer${1} link set tun${1} up
58}
59
60build_capture_filter() {
61	# match the first four bytes of the openvpn data payload
62	if [ "${PROTO}" == "UDP" ]; then
63		# For UDP, libpcap transport indexing only works for IPv4, so
64		# use an explicit IPv4 or IPv6 expression based on the peer
65		# address. The IPv6 branch assumes there are no extension
66		# headers in the outer packet.
67		if [[ "${2}" == *:* ]]; then
68			printf "ip6 and ip6[6] = 17 and ip6[48:4] = %s" "${1}"
69		else
70			printf "ip and udp[8:4] = %s" "${1}"
71		fi
72	else
73		# openvpn over TCP prepends a 2-byte packet length ahead of the
74		# DATA_V2 opcode, so skip it before matching the payload header
75		printf "ip and tcp[(((tcp[12] & 0xf0) >> 2) + 2):4] = %s" "${1}"
76	fi
77}
78
79setup_listener() {
80	file=$(mktemp)
81	PYTHONUNBUFFERED=1 ip netns exec peer${p} ${YNL_CLI} --family ovpn \
82		--subscribe peers --output-json --duration 40 > ${file} &
83	listener_pids[$1]=$!
84	tmp_jsons[$1]="${file}"
85}
86
87add_peer() {
88	labels=("ASYMM" "SYMM")
89	M_ID=${labels[SYMMETRIC_ID]}
90
91	if [ "${PROTO}" == "UDP" ]; then
92		if [ ${1} -eq 0 ]; then
93			ip netns exec peer0 ${OVPN_CLI} new_multi_peer tun0 1 \
94				${M_ID} ${UDP_PEERS_FILE}
95
96			for p in $(seq 1 ${NUM_PEERS}); do
97				ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 ${ALG} 0 \
98					data64.key
99			done
100		else
101			if [ "${SYMMETRIC_ID}" -eq 1 ]; then
102				PEER_ID=${1}
103				TX_ID="none"
104			else
105				PEER_ID=$(awk "NR == ${1} {print \$2}" \
106					${UDP_PEERS_FILE})
107				TX_ID=${1}
108			fi
109			RADDR=$(awk "NR == ${1} {print \$3}" ${UDP_PEERS_FILE})
110			RPORT=$(awk "NR == ${1} {print \$4}" ${UDP_PEERS_FILE})
111			LPORT=$(awk "NR == ${1} {print \$6}" ${UDP_PEERS_FILE})
112			ip netns exec peer${1} ${OVPN_CLI} new_peer tun${1} \
113				${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} ${RPORT}
114			ip netns exec peer${1} ${OVPN_CLI} new_key tun${1} \
115				${PEER_ID} 1 0 ${ALG} 1 data64.key
116		fi
117	else
118		if [ ${1} -eq 0 ]; then
119			(ip netns exec peer0 ${OVPN_CLI} listen tun0 1 ${M_ID} \
120				${TCP_PEERS_FILE} && {
121				for p in $(seq 1 ${NUM_PEERS}); do
122					ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 \
123						${ALG} 0 data64.key
124				done
125			}) &
126			sleep 5
127		else
128			if [ "${SYMMETRIC_ID}" -eq 1 ]; then
129				PEER_ID=${1}
130				TX_ID="none"
131			else
132				PEER_ID=$(awk "NR == ${1} {print \$2}" \
133					${TCP_PEERS_FILE})
134				TX_ID=${1}
135			fi
136			ip netns exec peer${1} ${OVPN_CLI} connect tun${1} \
137				${PEER_ID} ${TX_ID} 10.10.${1}.1 1 data64.key
138		fi
139	fi
140}
141
142compare_ntfs() {
143	if [ ${#tmp_jsons[@]} -gt 0 ]; then
144		suffix=""
145		[ "${SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm"
146		[ "$FLOAT" == 1 ] && suffix="${suffix}-float"
147		expected="json/peer${1}${suffix}.json"
148		received="${tmp_jsons[$1]}"
149
150		kill -TERM ${listener_pids[$1]} || true
151		wait ${listener_pids[$1]} || true
152		printf "Checking notifications for peer ${1}... "
153		if diff <(jq -s "${JQ_FILTER}" ${expected}) \
154			<(jq -s "${JQ_FILTER}" ${received}); then
155			echo "OK"
156		fi
157
158		rm -f ${received} || true
159	fi
160}
161
162cleanup() {
163	# some ovpn-cli processes sleep in background so they need manual poking
164	killall $(basename ${OVPN_CLI}) 2>/dev/null || true
165
166	# netns peer0 is deleted without erasing ifaces first
167	for p in $(seq 1 10); do
168		ip -n peer${p} link set tun${p} down 2>/dev/null || true
169		ip netns exec peer${p} ${OVPN_CLI} del_iface tun${p} 2>/dev/null || true
170	done
171	for p in $(seq 1 10); do
172		ip -n peer0 link del veth${p} 2>/dev/null || true
173	done
174	for p in $(seq 0 10); do
175		ip netns del peer${p} 2>/dev/null || true
176	done
177}
178
179if [ "${PROTO}" == "UDP" ]; then
180	NUM_PEERS=${NUM_PEERS:-$(wc -l ${UDP_PEERS_FILE} | awk '{print $1}')}
181else
182	NUM_PEERS=${NUM_PEERS:-$(wc -l ${TCP_PEERS_FILE} | awk '{print $1}')}
183fi
184