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