mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Included changes:
* add Ralf Lici as ovpn reviewer * wait for the server-side key setup before generating TCP traffic in selftests * enable TCP_NODELAY on TCP sockets in selftests * drop redundant peer NULL checks in the crypto post functions * remove the unused work field from struct ovpn_socket * fix the documented return value of ovpn_bind_from_sockaddr() -----BEGIN PGP SIGNATURE----- iJEEABYIADkWIQQr0db7q+Rc7Zog28Fc8QQzwdnOtwUCaszhlxsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQXPEEM8HZzrfcaAD9GC730T7nKpn21y7Kv9m6 3nnfPU4j8eEI/525tizfvcgBAIT7qDgXP86BFJHQKXHpgjyNzBqK9QsU+8kwSeUd gCkB =xbNA -----END PGP SIGNATURE----- Merge tag 'ovpn-net-next-20261012' of https://github.com/OpenVPN/ovpn-net-next Antonio Quartulli says: ==================== Included changes: * add Ralf Lici as ovpn reviewer * wait for the server-side key setup before generating TCP traffic in selftests * enable TCP_NODELAY on TCP sockets in selftests * drop redundant peer NULL checks in the crypto post functions * remove the unused work field from struct ovpn_socket * fix the documented return value of ovpn_bind_from_sockaddr() * tag 'ovpn-net-next-20261012' of https://github.com/OpenVPN/ovpn-net-next: MAINTAINERS: ovpn: add Ralf Lici as reviewer selftests: ovpn: wait for the TCP server key setup before traffic selftests: ovpn: enable TCP_NODELAY on TCP sockets ovpn: remove redundant peer NULL checks in crypto post functions ovpn: remove unused work field from struct ovpn_socket ovpn: Fix the return value in ovpn_bind_from_sockaddr() kernel-doc ==================== Link: https://patch.msgid.link/ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
17ec1b9b3f
|
|
@ -20537,6 +20537,7 @@ F: drivers/irqchip/irq-or1k-*
|
|||
|
||||
OPENVPN DATA CHANNEL OFFLOAD
|
||||
M: Antonio Quartulli <antonio@openvpn.net>
|
||||
R: Ralf Lici <ralf@mandelbit.com>
|
||||
R: Sabrina Dubroca <sd@queasysnail.net>
|
||||
L: openvpn-devel@lists.sourceforge.net (subscribers-only)
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
* ovpn_bind_from_sockaddr - retrieve binding matching sockaddr
|
||||
* @ss: the sockaddr to match
|
||||
*
|
||||
* Return: the bind matching the passed sockaddr if found, NULL otherwise
|
||||
* Return: the new bind for the passed sockaddr, an ERR_PTR() on failure
|
||||
*/
|
||||
struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -206,8 +206,7 @@ void ovpn_decrypt_post(void *data, int ret)
|
|||
drop_nocount:
|
||||
if (likely(ks))
|
||||
ovpn_crypto_key_slot_put(ks);
|
||||
if (likely(peer))
|
||||
ovpn_peer_put(peer);
|
||||
ovpn_peer_put(peer);
|
||||
}
|
||||
|
||||
/* RX path entry point: decrypt packet and forward it to the device */
|
||||
|
|
@ -305,8 +304,7 @@ void ovpn_encrypt_post(void *data, int ret)
|
|||
kfree_skb(skb);
|
||||
if (likely(ks))
|
||||
ovpn_crypto_key_slot_put(ks);
|
||||
if (likely(peer))
|
||||
ovpn_peer_put(peer);
|
||||
ovpn_peer_put(peer);
|
||||
}
|
||||
|
||||
static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ struct ovpn_peer;
|
|||
* @peer: unique peer transmitting over this socket (TCP only)
|
||||
* @sk: the low level sock object
|
||||
* @refcount: amount of contexts currently referencing this object
|
||||
* @work: member used to schedule release routine (it may block)
|
||||
* @tcp_tx_work: work for deferring outgoing packet processing (TCP only)
|
||||
*/
|
||||
struct ovpn_socket {
|
||||
|
|
@ -38,7 +37,6 @@ struct ovpn_socket {
|
|||
|
||||
struct sock *sk;
|
||||
struct kref refcount;
|
||||
struct work_struct work;
|
||||
struct work_struct tcp_tx_work;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ TEST_PROGS := \
|
|||
test-symmetric-id-float.sh \
|
||||
test-symmetric-id-tcp.sh \
|
||||
test-symmetric-id.sh \
|
||||
test-tcp-nagle.sh \
|
||||
test-tcp.sh \
|
||||
test.sh \
|
||||
# end of TEST_PROGS
|
||||
|
|
|
|||
|
|
@ -191,20 +191,14 @@ ovpn_setup_ns() {
|
|||
|
||||
ovpn_build_capture_filter() {
|
||||
# match the first four bytes of the openvpn data payload
|
||||
if [ "${OVPN_PROTO}" == "UDP" ]; then
|
||||
# For UDP, libpcap transport indexing only works for IPv4, so
|
||||
# use an explicit IPv4 or IPv6 expression based on the peer
|
||||
# address. The IPv6 branch assumes there are no extension
|
||||
# headers in the outer packet.
|
||||
if [[ "${2}" == *:* ]]; then
|
||||
printf "ip6 and ip6[6] = 17 and ip6[48:4] = %s" "${1}"
|
||||
else
|
||||
printf "ip and udp[8:4] = %s" "${1}"
|
||||
fi
|
||||
# For UDP, libpcap transport indexing only works for IPv4, so
|
||||
# use an explicit IPv4 or IPv6 expression based on the peer
|
||||
# address. The IPv6 branch assumes there are no extension
|
||||
# headers in the outer packet.
|
||||
if [[ "${2}" == *:* ]]; then
|
||||
printf "ip6 and ip6[6] = 17 and ip6[48:4] = %s" "${1}"
|
||||
else
|
||||
# openvpn over TCP prepends a 2-byte packet length ahead of the
|
||||
# DATA_V2 opcode, so skip it before matching the payload header
|
||||
printf "ip and tcp[(((tcp[12] & 0xf0) >> 2) + 2):4] = %s" "${1}"
|
||||
printf "ip and udp[8:4] = %s" "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -262,14 +256,19 @@ ovpn_add_peer() {
|
|||
fi
|
||||
else
|
||||
if [ ${1} -eq 0 ]; then
|
||||
OVPN_TCP_KEYS_LOG=$(mktemp)
|
||||
(ip netns exec "${server_ns}" ${OVPN_CLI} listen tun0 \
|
||||
1 ${M_ID} ${OVPN_TCP_PEERS_FILE} && {
|
||||
rc=0
|
||||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
ip netns exec "${server_ns}" \
|
||||
${OVPN_CLI} new_key tun0 ${p} \
|
||||
1 0 ${OVPN_ALG} 0 data64.key
|
||||
1 0 ${OVPN_ALG} 0 data64.key \
|
||||
|| rc=1
|
||||
done
|
||||
}) &
|
||||
exit ${rc}
|
||||
}) >"${OVPN_TCP_KEYS_LOG}" 2>&1 &
|
||||
OVPN_TCP_KEYS_PID=$!
|
||||
sleep 5
|
||||
else
|
||||
peer_ns="ovpn_peer${1}"
|
||||
|
|
@ -287,6 +286,58 @@ ovpn_add_peer() {
|
|||
fi
|
||||
}
|
||||
|
||||
# In TCP mode the server accepts the peers and installs their data keys from a
|
||||
# background subshell. "listen" returns once a connection has been accepted for
|
||||
# every entry of the peers file, so the key installation runs after the last
|
||||
# ovpn_add_peer() call. Wait for that subshell before generating any traffic,
|
||||
# otherwise the first packets may race the key installation and get dropped for
|
||||
# lack of a key.
|
||||
#
|
||||
# The wait is bounded: both the accept loop and the subsequent receive in
|
||||
# ovpn-cli are unbounded, so a peer that never connects (for instance when
|
||||
# OVPN_NUM_PEERS is smaller than the number of entries in the peers file) would
|
||||
# otherwise hang the whole test instead of failing.
|
||||
OVPN_TCP_KEYS_TIMEOUT=${OVPN_TCP_KEYS_TIMEOUT:-30}
|
||||
|
||||
ovpn_wait_tcp_keys() {
|
||||
local deadline rc
|
||||
|
||||
[ -n "${OVPN_TCP_KEYS_PID:-}" ] || return 0
|
||||
|
||||
deadline=$((SECONDS + OVPN_TCP_KEYS_TIMEOUT))
|
||||
while kill -0 "${OVPN_TCP_KEYS_PID}" 2>/dev/null; do
|
||||
if [ "${SECONDS}" -ge "${deadline}" ]; then
|
||||
printf '%s\n' \
|
||||
"server-side key setup still running after ${OVPN_TCP_KEYS_TIMEOUT}s"
|
||||
kill "${OVPN_TCP_KEYS_PID}" 2>/dev/null
|
||||
wait "${OVPN_TCP_KEYS_PID}" 2>/dev/null
|
||||
ovpn_dump_tcp_keys_log
|
||||
return 1
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
wait "${OVPN_TCP_KEYS_PID}"
|
||||
rc=$?
|
||||
|
||||
# ovpn_cmd_run() has already removed the temporary file this subshell
|
||||
# inherited, so without this its output would be lost and a failed
|
||||
# new_key would only show up later as a ping failure.
|
||||
if [ "${rc}" -ne 0 ]; then
|
||||
ovpn_dump_tcp_keys_log
|
||||
fi
|
||||
rm -f "${OVPN_TCP_KEYS_LOG}"
|
||||
|
||||
return "${rc}"
|
||||
}
|
||||
|
||||
ovpn_dump_tcp_keys_log() {
|
||||
[ -s "${OVPN_TCP_KEYS_LOG:-}" ] || return 0
|
||||
|
||||
printf '%s\n' "--- server-side key setup output ---"
|
||||
cat "${OVPN_TCP_KEYS_LOG}"
|
||||
}
|
||||
|
||||
ovpn_compare_ntfs() {
|
||||
local diff_rc=0
|
||||
local diff_file
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <linux/ovpn.h>
|
||||
|
|
@ -470,6 +472,33 @@ static int ovpn_parse_key_direction(const char *dir, struct ovpn_ctx *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* openvpn 2.7.6 and later always set TCP_NODELAY, so mirror that by
|
||||
* default. Peers running 2.7.0 to 2.7.5 still leave Nagle enabled, and
|
||||
* mixed-version deployments will keep doing so for a while, so let the
|
||||
* selftests exercise that configuration too by setting OVPN_TCP_NODELAY=0.
|
||||
*/
|
||||
static bool ovpn_tcp_nodelay_wanted(void)
|
||||
{
|
||||
const char *env = getenv("OVPN_TCP_NODELAY");
|
||||
|
||||
return !env || strcmp(env, "0");
|
||||
}
|
||||
|
||||
static int ovpn_tcp_nodelay(int socket)
|
||||
{
|
||||
int opt = 1;
|
||||
int ret;
|
||||
|
||||
if (!ovpn_tcp_nodelay_wanted())
|
||||
return 0;
|
||||
|
||||
ret = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
||||
if (ret < 0)
|
||||
perror("setsockopt for TCP_NODELAY");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ovpn_socket(struct ovpn_ctx *ctx, sa_family_t family, int proto)
|
||||
{
|
||||
struct sockaddr_storage local_sock = { 0 };
|
||||
|
|
@ -606,6 +635,12 @@ static int ovpn_accept(struct ovpn_ctx *ctx)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (ovpn_tcp_nodelay(ret) < 0) {
|
||||
close(ret);
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
err:
|
||||
close(ctx->socket);
|
||||
|
|
@ -623,6 +658,10 @@ static int ovpn_connect(struct ovpn_ctx *ovpn)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ret = ovpn_tcp_nodelay(s);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
switch (ovpn->remote.in4.sin_family) {
|
||||
case AF_INET:
|
||||
socklen = sizeof(struct sockaddr_in);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ ovpn_prepare_network() {
|
|||
ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}"
|
||||
done
|
||||
|
||||
ovpn_cmd_ok "wait for server-side key setup" ovpn_wait_tcp_keys
|
||||
|
||||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
peer_ns="ovpn_peer${p}"
|
||||
ovpn_cmd_ok "set peer0 timeout for peer ${p}" \
|
||||
|
|
|
|||
12
tools/testing/selftests/net/ovpn/test-tcp-nagle.sh
Executable file
12
tools/testing/selftests/net/ovpn/test-tcp-nagle.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2025 OpenVPN, Inc.
|
||||
#
|
||||
# Author: Antonio Quartulli <antonio@openvpn.net>
|
||||
|
||||
# Peers running openvpn 2.7.0 to 2.7.5 leave Nagle enabled on their TCP
|
||||
# sockets. Keep exercising that configuration so it does not silently break.
|
||||
OVPN_PROTO="TCP"
|
||||
export OVPN_TCP_NODELAY=0
|
||||
|
||||
source test.sh
|
||||
|
|
@ -45,6 +45,8 @@ ovpn_prepare_network() {
|
|||
ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}"
|
||||
done
|
||||
|
||||
ovpn_cmd_ok "wait for server-side key setup" ovpn_wait_tcp_keys
|
||||
|
||||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
peer_ns="ovpn_peer${p}"
|
||||
ovpn_cmd_ok "set peer0 timeout for peer ${p}" \
|
||||
|
|
@ -137,35 +139,33 @@ ovpn_run_basic_traffic() {
|
|||
local tcpdump_timeout="1.5s"
|
||||
|
||||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
# The first part of the data packet header consists of:
|
||||
# - TCP only: 2 bytes for the packet length
|
||||
# - 5 bits for opcode ("9" for DATA_V2)
|
||||
# - 3 bits for key-id ("0" at this point)
|
||||
# - 12 bytes for peer-id:
|
||||
# - with asymmetric ID: "${p}" one way and "${p} + 9" the
|
||||
# other way
|
||||
# - with symmetric ID: "${p}" both ways
|
||||
header1=$(printf "0x4800000%x" ${p})
|
||||
header2=$(printf "0x4800000%x" $((p + OVPN_ID_OFFSET)))
|
||||
raddr=""
|
||||
if [ "${OVPN_PROTO}" == "UDP" ]; then
|
||||
# The first part of the data packet header consists of:
|
||||
# - 5 bits for opcode ("9" for DATA_V2)
|
||||
# - 3 bits for key-id ("0" at this point)
|
||||
# - 3 bytes for peer-id:
|
||||
# - with asymmetric ID: "${p}" one way and "${p} + 9" the
|
||||
# other way
|
||||
# - with symmetric ID: "${p}" both ways
|
||||
header1=$(printf "0x4800000%x" ${p})
|
||||
header2=$(printf "0x4800000%x" $((p + OVPN_ID_OFFSET)))
|
||||
raddr=$(awk "NR == ${p} {print \$3}" \
|
||||
"${OVPN_UDP_PEERS_FILE}")
|
||||
peer_ns="ovpn_peer${p}"
|
||||
|
||||
timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
|
||||
tcpdump --immediate-mode -p -ni veth${p} -c 1 \
|
||||
"$(ovpn_build_capture_filter "${header1}" "${raddr}")" \
|
||||
>/dev/null 2>&1 &
|
||||
tcpdump_pid1=$!
|
||||
timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
|
||||
tcpdump --immediate-mode -p -ni veth${p} -c 1 \
|
||||
"$(ovpn_build_capture_filter "${header2}" "${raddr}")" \
|
||||
>/dev/null 2>&1 &
|
||||
tcpdump_pid2=$!
|
||||
|
||||
sleep 0.3
|
||||
fi
|
||||
peer_ns="ovpn_peer${p}"
|
||||
|
||||
timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
|
||||
tcpdump --immediate-mode -p -ni veth${p} -c 1 \
|
||||
"$(ovpn_build_capture_filter "${header1}" "${raddr}")" \
|
||||
>/dev/null 2>&1 &
|
||||
tcpdump_pid1=$!
|
||||
timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \
|
||||
tcpdump --immediate-mode -p -ni veth${p} -c 1 \
|
||||
"$(ovpn_build_capture_filter "${header2}" "${raddr}")" \
|
||||
>/dev/null 2>&1 &
|
||||
tcpdump_pid2=$!
|
||||
|
||||
sleep 0.3
|
||||
ovpn_cmd_ok "send baseline traffic to peer ${p}" \
|
||||
ip netns exec ovpn_peer0 \
|
||||
ping -qfc 100 -w 3 5.5.5.$((p + 1))
|
||||
|
|
@ -173,8 +173,10 @@ ovpn_run_basic_traffic() {
|
|||
ip netns exec ovpn_peer0 \
|
||||
ping -qfc 100 -s 3000 -w 3 5.5.5.$((p + 1))
|
||||
|
||||
wait "${tcpdump_pid1}" || return 1
|
||||
wait "${tcpdump_pid2}" || return 1
|
||||
if [ "${OVPN_PROTO}" == "UDP" ]; then
|
||||
wait "${tcpdump_pid1}" || return 1
|
||||
wait "${tcpdump_pid2}" || return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user