This bugfix batch includes the following changes:

* dropped bogus call to setup_udp_tunnel_sock() during
   cleanup, substituted by proper state unwind
 * fixed race condition between peer removal (by kernel
   space) and socket closing (by user space)
 * fixed sleep in atomic context along TCP RX error path
 * fixes for ovpn kselftests
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEmavcbPjUEuTeX8D8C3DlOqA41YcFAmg+17IACgkQC3DlOqA4
 1YfGnQf7BCqweJAqPZs45DrMO/zu0mygbvzxI5x1VnBSQvtUfEEwgY3Mk9Y/8OxW
 +QxD9yYl8sf30ZzGTFFYtBq0XwvcP0Nlo4WFD7+2ufvFgojIAwCsF25of+hODGLZ
 5KF83VYDAMvUmK7Rq4gGyi/HC7+rWNhShfoXBh6nfQjNa75NWwIwp8wEsTMovq2Y
 DiH6VzCjkUgnRjJYG5g/D6n0pHnbUkrAPi3AT+aB8PX9sikrTZhBWX4mmY8n6Xlo
 KT83ByzJqc5svnbsQEGRuhXbyuKipBhcVmOohmulBB/uhXUZHDcem9L0wp1hR2SB
 y/lZQxt9bNdEXfh0J7r5QxI2vkZgZQ==
 =Y6+P
 -----END PGP SIGNATURE-----

Merge tag 'ovpn-net-20250603' of https://github.com/OpenVPN/ovpn-net-next

Antonio Quartulli says:

====================
In this batch you can find the following bug fixes:

Patch 1: when releasing a UDP socket we were wrongly invoking
setup_udp_tunnel_sock() with an empty config. This was not
properly shutting down the UDP encap state.
With this patch we simply undo what was done during setup.

Patch 2: ovpn was holding a reference to a 'struct socket'
without increasing its reference counter. This was intended
and worked as expected until we hit a race condition where
user space tries to close the socket while kernel space is
also releasing it. In this case the (struct socket *)->sk
member would disappear under our feet leading to a null-ptr-deref.
This patch fixes this issue by having struct ovpn_socket hold
a reference directly to the sk member while also increasing
its reference counter.

Patch 3: in case of errors along the TCP RX path (softirq)
we want to immediately delete the peer, but this operation may
sleep. With this patch we move the peer deletion to a scheduled
worker.

Patch 4 and 5 are instead fixing minor issues in the ovpn
kselftests.

* tag 'ovpn-net-20250603' of https://github.com/OpenVPN/ovpn-net-next:
  selftest/net/ovpn: fix missing file
  selftest/net/ovpn: fix TCP socket creation
  ovpn: avoid sleep in atomic context in TCP RX error path
  ovpn: ensure sk is still valid during cleanup
  ovpn: properly deconfigure UDP-tunnel
====================

Link: https://patch.msgid.link/20250603111110.4575-1-antonio@openvpn.net/
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-06-05 12:37:10 +02:00
commit ec6a328b2e
11 changed files with 128 additions and 108 deletions

View File

@ -134,7 +134,7 @@ void ovpn_decrypt_post(void *data, int ret)
rcu_read_lock();
sock = rcu_dereference(peer->sock);
if (sock && sock->sock->sk->sk_protocol == IPPROTO_UDP)
if (sock && sock->sk->sk_protocol == IPPROTO_UDP)
/* check if this peer changed local or remote endpoint */
ovpn_peer_endpoints_update(peer, skb);
rcu_read_unlock();
@ -270,12 +270,12 @@ void ovpn_encrypt_post(void *data, int ret)
if (unlikely(!sock))
goto err_unlock;
switch (sock->sock->sk->sk_protocol) {
switch (sock->sk->sk_protocol) {
case IPPROTO_UDP:
ovpn_udp_send_skb(peer, sock->sock, skb);
ovpn_udp_send_skb(peer, sock->sk, skb);
break;
case IPPROTO_TCP:
ovpn_tcp_send_skb(peer, sock->sock, skb);
ovpn_tcp_send_skb(peer, sock->sk, skb);
break;
default:
/* no transport configured yet */

View File

@ -501,7 +501,7 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
/* when using a TCP socket the remote IP is not expected */
rcu_read_lock();
sock = rcu_dereference(peer->sock);
if (sock && sock->sock->sk->sk_protocol == IPPROTO_TCP &&
if (sock && sock->sk->sk_protocol == IPPROTO_TCP &&
(attrs[OVPN_A_PEER_REMOTE_IPV4] ||
attrs[OVPN_A_PEER_REMOTE_IPV6])) {
rcu_read_unlock();
@ -559,14 +559,14 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
goto err_unlock;
}
if (!net_eq(genl_info_net(info), sock_net(sock->sock->sk))) {
if (!net_eq(genl_info_net(info), sock_net(sock->sk))) {
id = peernet2id_alloc(genl_info_net(info),
sock_net(sock->sock->sk),
sock_net(sock->sk),
GFP_ATOMIC);
if (nla_put_s32(skb, OVPN_A_PEER_SOCKET_NETNSID, id))
goto err_unlock;
}
local_port = inet_sk(sock->sock->sk)->inet_sport;
local_port = inet_sk(sock->sk)->inet_sport;
rcu_read_unlock();
if (nla_put_u32(skb, OVPN_A_PEER_ID, peer->id))
@ -1153,8 +1153,8 @@ int ovpn_nl_peer_del_notify(struct ovpn_peer *peer)
ret = -EINVAL;
goto err_unlock;
}
genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sock->sk),
msg, 0, OVPN_NLGRP_PEERS, GFP_ATOMIC);
genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg, 0,
OVPN_NLGRP_PEERS, GFP_ATOMIC);
rcu_read_unlock();
return 0;
@ -1218,8 +1218,8 @@ int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id)
ret = -EINVAL;
goto err_unlock;
}
genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sock->sk),
msg, 0, OVPN_NLGRP_PEERS, GFP_ATOMIC);
genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg, 0,
OVPN_NLGRP_PEERS, GFP_ATOMIC);
rcu_read_unlock();
return 0;

View File

@ -1145,7 +1145,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
if (sk) {
ovpn_sock = rcu_access_pointer(peer->sock);
if (!ovpn_sock || ovpn_sock->sock->sk != sk) {
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
ovpn_peer_put(peer);
return;
@ -1175,7 +1175,7 @@ static void ovpn_peers_release_mp(struct ovpn_priv *ovpn, struct sock *sk,
if (sk) {
rcu_read_lock();
ovpn_sock = rcu_dereference(peer->sock);
remove = ovpn_sock && ovpn_sock->sock->sk == sk;
remove = ovpn_sock && ovpn_sock->sk == sk;
rcu_read_unlock();
}

View File

@ -24,9 +24,9 @@ static void ovpn_socket_release_kref(struct kref *kref)
struct ovpn_socket *sock = container_of(kref, struct ovpn_socket,
refcount);
if (sock->sock->sk->sk_protocol == IPPROTO_UDP)
if (sock->sk->sk_protocol == IPPROTO_UDP)
ovpn_udp_socket_detach(sock);
else if (sock->sock->sk->sk_protocol == IPPROTO_TCP)
else if (sock->sk->sk_protocol == IPPROTO_TCP)
ovpn_tcp_socket_detach(sock);
}
@ -75,14 +75,6 @@ void ovpn_socket_release(struct ovpn_peer *peer)
if (!sock)
return;
/* sanity check: we should not end up here if the socket
* was already closed
*/
if (!sock->sock->sk) {
DEBUG_NET_WARN_ON_ONCE(1);
return;
}
/* Drop the reference while holding the sock lock to avoid
* concurrent ovpn_socket_new call to mess up with a partially
* detached socket.
@ -90,22 +82,24 @@ void ovpn_socket_release(struct ovpn_peer *peer)
* Holding the lock ensures that a socket with refcnt 0 is fully
* detached before it can be picked by a concurrent reader.
*/
lock_sock(sock->sock->sk);
lock_sock(sock->sk);
released = ovpn_socket_put(peer, sock);
release_sock(sock->sock->sk);
release_sock(sock->sk);
/* align all readers with sk_user_data being NULL */
synchronize_rcu();
/* following cleanup should happen with lock released */
if (released) {
if (sock->sock->sk->sk_protocol == IPPROTO_UDP) {
if (sock->sk->sk_protocol == IPPROTO_UDP) {
netdev_put(sock->ovpn->dev, &sock->dev_tracker);
} else if (sock->sock->sk->sk_protocol == IPPROTO_TCP) {
} else if (sock->sk->sk_protocol == IPPROTO_TCP) {
/* wait for TCP jobs to terminate */
ovpn_tcp_socket_wait_finish(sock);
ovpn_peer_put(sock->peer);
}
/* drop reference acquired in ovpn_socket_new() */
sock_put(sock->sk);
/* we can call plain kfree() because we already waited one RCU
* period due to synchronize_rcu()
*/
@ -118,12 +112,14 @@ static bool ovpn_socket_hold(struct ovpn_socket *sock)
return kref_get_unless_zero(&sock->refcount);
}
static int ovpn_socket_attach(struct ovpn_socket *sock, struct ovpn_peer *peer)
static int ovpn_socket_attach(struct ovpn_socket *ovpn_sock,
struct socket *sock,
struct ovpn_peer *peer)
{
if (sock->sock->sk->sk_protocol == IPPROTO_UDP)
return ovpn_udp_socket_attach(sock, peer->ovpn);
else if (sock->sock->sk->sk_protocol == IPPROTO_TCP)
return ovpn_tcp_socket_attach(sock, peer);
if (sock->sk->sk_protocol == IPPROTO_UDP)
return ovpn_udp_socket_attach(ovpn_sock, sock, peer->ovpn);
else if (sock->sk->sk_protocol == IPPROTO_TCP)
return ovpn_tcp_socket_attach(ovpn_sock, peer);
return -EOPNOTSUPP;
}
@ -138,14 +134,15 @@ static int ovpn_socket_attach(struct ovpn_socket *sock, struct ovpn_peer *peer)
struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
{
struct ovpn_socket *ovpn_sock;
struct sock *sk = sock->sk;
int ret;
lock_sock(sock->sk);
lock_sock(sk);
/* a TCP socket can only be owned by a single peer, therefore there
* can't be any other user
*/
if (sock->sk->sk_protocol == IPPROTO_TCP && sock->sk->sk_user_data) {
if (sk->sk_protocol == IPPROTO_TCP && sk->sk_user_data) {
ovpn_sock = ERR_PTR(-EBUSY);
goto sock_release;
}
@ -153,8 +150,8 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
/* a UDP socket can be shared across multiple peers, but we must make
* sure it is not owned by something else
*/
if (sock->sk->sk_protocol == IPPROTO_UDP) {
u8 type = READ_ONCE(udp_sk(sock->sk)->encap_type);
if (sk->sk_protocol == IPPROTO_UDP) {
u8 type = READ_ONCE(udp_sk(sk)->encap_type);
/* socket owned by other encapsulation module */
if (type && type != UDP_ENCAP_OVPNINUDP) {
@ -163,7 +160,7 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
}
rcu_read_lock();
ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
ovpn_sock = rcu_dereference_sk_user_data(sk);
if (ovpn_sock) {
/* socket owned by another ovpn instance, we can't use it */
if (ovpn_sock->ovpn != peer->ovpn) {
@ -200,11 +197,22 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
goto sock_release;
}
ovpn_sock->sock = sock;
ovpn_sock->sk = sk;
kref_init(&ovpn_sock->refcount);
ret = ovpn_socket_attach(ovpn_sock, peer);
/* the newly created ovpn_socket is holding reference to sk,
* therefore we increase its refcounter.
*
* This ovpn_socket instance is referenced by all peers
* using the same socket.
*
* ovpn_socket_release() will take care of dropping the reference.
*/
sock_hold(sk);
ret = ovpn_socket_attach(ovpn_sock, sock, peer);
if (ret < 0) {
sock_put(sk);
kfree(ovpn_sock);
ovpn_sock = ERR_PTR(ret);
goto sock_release;
@ -213,11 +221,11 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
/* TCP sockets are per-peer, therefore they are linked to their unique
* peer
*/
if (sock->sk->sk_protocol == IPPROTO_TCP) {
if (sk->sk_protocol == IPPROTO_TCP) {
INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work);
ovpn_sock->peer = peer;
ovpn_peer_hold(peer);
} else if (sock->sk->sk_protocol == IPPROTO_UDP) {
} else if (sk->sk_protocol == IPPROTO_UDP) {
/* in UDP we only link the ovpn instance since the socket is
* shared among multiple peers
*/
@ -226,8 +234,8 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
GFP_KERNEL);
}
rcu_assign_sk_user_data(sock->sk, ovpn_sock);
rcu_assign_sk_user_data(sk, ovpn_sock);
sock_release:
release_sock(sock->sk);
release_sock(sk);
return ovpn_sock;
}

View File

@ -22,7 +22,7 @@ struct ovpn_peer;
* @ovpn: ovpn instance owning this socket (UDP only)
* @dev_tracker: reference tracker for associated dev (UDP only)
* @peer: unique peer transmitting over this socket (TCP only)
* @sock: the low level sock object
* @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)
@ -36,7 +36,7 @@ struct ovpn_socket {
struct ovpn_peer *peer;
};
struct socket *sock;
struct sock *sk;
struct kref refcount;
struct work_struct work;
struct work_struct tcp_tx_work;

View File

@ -124,14 +124,18 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
* this peer, therefore ovpn_peer_hold() is not expected to fail
*/
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err;
goto err_nopeer;
ovpn_recv(peer, skb);
return;
err:
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
schedule_work(&peer->tcp.defer_del_work);
dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
kfree_skb(skb);
ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
}
static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
@ -186,18 +190,18 @@ static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
void ovpn_tcp_socket_detach(struct ovpn_socket *ovpn_sock)
{
struct ovpn_peer *peer = ovpn_sock->peer;
struct socket *sock = ovpn_sock->sock;
struct sock *sk = ovpn_sock->sk;
strp_stop(&peer->tcp.strp);
skb_queue_purge(&peer->tcp.user_queue);
/* restore CBs that were saved in ovpn_sock_set_tcp_cb() */
sock->sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready;
sock->sk->sk_write_space = peer->tcp.sk_cb.sk_write_space;
sock->sk->sk_prot = peer->tcp.sk_cb.prot;
sock->sk->sk_socket->ops = peer->tcp.sk_cb.ops;
sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready;
sk->sk_write_space = peer->tcp.sk_cb.sk_write_space;
sk->sk_prot = peer->tcp.sk_cb.prot;
sk->sk_socket->ops = peer->tcp.sk_cb.ops;
rcu_assign_sk_user_data(sock->sk, NULL);
rcu_assign_sk_user_data(sk, NULL);
}
void ovpn_tcp_socket_wait_finish(struct ovpn_socket *sock)
@ -283,10 +287,10 @@ void ovpn_tcp_tx_work(struct work_struct *work)
sock = container_of(work, struct ovpn_socket, tcp_tx_work);
lock_sock(sock->sock->sk);
lock_sock(sock->sk);
if (sock->peer)
ovpn_tcp_send_sock(sock->peer, sock->sock->sk);
release_sock(sock->sock->sk);
ovpn_tcp_send_sock(sock->peer, sock->sk);
release_sock(sock->sk);
}
static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk,
@ -307,15 +311,15 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk,
ovpn_tcp_send_sock(peer, sk);
}
void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock,
void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk,
struct sk_buff *skb)
{
u16 len = skb->len;
*(__be16 *)__skb_push(skb, sizeof(u16)) = htons(len);
spin_lock_nested(&sock->sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING);
if (sock_owned_by_user(sock->sk)) {
spin_lock_nested(&sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING);
if (sock_owned_by_user(sk)) {
if (skb_queue_len(&peer->tcp.out_queue) >=
READ_ONCE(net_hotdata.max_backlog)) {
dev_dstats_tx_dropped(peer->ovpn->dev);
@ -324,10 +328,10 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock,
}
__skb_queue_tail(&peer->tcp.out_queue, skb);
} else {
ovpn_tcp_send_sock_skb(peer, sock->sk, skb);
ovpn_tcp_send_sock_skb(peer, sk, skb);
}
unlock:
spin_unlock(&sock->sk->sk_lock.slock);
spin_unlock(&sk->sk_lock.slock);
}
static void ovpn_tcp_release(struct sock *sk)
@ -474,7 +478,6 @@ static void ovpn_tcp_peer_del_work(struct work_struct *work)
int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
struct ovpn_peer *peer)
{
struct socket *sock = ovpn_sock->sock;
struct strp_callbacks cb = {
.rcv_msg = ovpn_tcp_rcv,
.parse_msg = ovpn_tcp_parse,
@ -482,20 +485,20 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
int ret;
/* make sure no pre-existing encapsulation handler exists */
if (sock->sk->sk_user_data)
if (ovpn_sock->sk->sk_user_data)
return -EBUSY;
/* only a fully connected socket is expected. Connection should be
* handled in userspace
*/
if (sock->sk->sk_state != TCP_ESTABLISHED) {
if (ovpn_sock->sk->sk_state != TCP_ESTABLISHED) {
net_err_ratelimited("%s: provided TCP socket is not in ESTABLISHED state: %d\n",
netdev_name(peer->ovpn->dev),
sock->sk->sk_state);
ovpn_sock->sk->sk_state);
return -EINVAL;
}
ret = strp_init(&peer->tcp.strp, sock->sk, &cb);
ret = strp_init(&peer->tcp.strp, ovpn_sock->sk, &cb);
if (ret < 0) {
DEBUG_NET_WARN_ON_ONCE(1);
return ret;
@ -503,31 +506,31 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
INIT_WORK(&peer->tcp.defer_del_work, ovpn_tcp_peer_del_work);
__sk_dst_reset(sock->sk);
__sk_dst_reset(ovpn_sock->sk);
skb_queue_head_init(&peer->tcp.user_queue);
skb_queue_head_init(&peer->tcp.out_queue);
/* save current CBs so that they can be restored upon socket release */
peer->tcp.sk_cb.sk_data_ready = sock->sk->sk_data_ready;
peer->tcp.sk_cb.sk_write_space = sock->sk->sk_write_space;
peer->tcp.sk_cb.prot = sock->sk->sk_prot;
peer->tcp.sk_cb.ops = sock->sk->sk_socket->ops;
peer->tcp.sk_cb.sk_data_ready = ovpn_sock->sk->sk_data_ready;
peer->tcp.sk_cb.sk_write_space = ovpn_sock->sk->sk_write_space;
peer->tcp.sk_cb.prot = ovpn_sock->sk->sk_prot;
peer->tcp.sk_cb.ops = ovpn_sock->sk->sk_socket->ops;
/* assign our static CBs and prot/ops */
sock->sk->sk_data_ready = ovpn_tcp_data_ready;
sock->sk->sk_write_space = ovpn_tcp_write_space;
ovpn_sock->sk->sk_data_ready = ovpn_tcp_data_ready;
ovpn_sock->sk->sk_write_space = ovpn_tcp_write_space;
if (sock->sk->sk_family == AF_INET) {
sock->sk->sk_prot = &ovpn_tcp_prot;
sock->sk->sk_socket->ops = &ovpn_tcp_ops;
if (ovpn_sock->sk->sk_family == AF_INET) {
ovpn_sock->sk->sk_prot = &ovpn_tcp_prot;
ovpn_sock->sk->sk_socket->ops = &ovpn_tcp_ops;
} else {
sock->sk->sk_prot = &ovpn_tcp6_prot;
sock->sk->sk_socket->ops = &ovpn_tcp6_ops;
ovpn_sock->sk->sk_prot = &ovpn_tcp6_prot;
ovpn_sock->sk->sk_socket->ops = &ovpn_tcp6_ops;
}
/* avoid using task_frag */
sock->sk->sk_allocation = GFP_ATOMIC;
sock->sk->sk_use_task_frag = false;
ovpn_sock->sk->sk_allocation = GFP_ATOMIC;
ovpn_sock->sk->sk_use_task_frag = false;
/* enqueue the RX worker */
strp_check_rcv(&peer->tcp.strp);

View File

@ -30,7 +30,8 @@ void ovpn_tcp_socket_wait_finish(struct ovpn_socket *sock);
* Required by the OpenVPN protocol in order to extract packets from
* the TCP stream on the receiver side.
*/
void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock, struct sk_buff *skb);
void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk,
struct sk_buff *skb);
void ovpn_tcp_tx_work(struct work_struct *work);
#endif /* _NET_OVPN_TCP_H_ */

View File

@ -43,7 +43,7 @@ static struct ovpn_socket *ovpn_socket_from_udp_sock(struct sock *sk)
return NULL;
/* make sure that sk matches our stored transport socket */
if (unlikely(!ovpn_sock->sock || sk != ovpn_sock->sock->sk))
if (unlikely(!ovpn_sock->sk || sk != ovpn_sock->sk))
return NULL;
return ovpn_sock;
@ -335,32 +335,22 @@ static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
/**
* ovpn_udp_send_skb - prepare skb and send it over via UDP
* @peer: the destination peer
* @sock: the RCU protected peer socket
* @sk: peer socket
* @skb: the packet to send
*/
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct socket *sock,
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk,
struct sk_buff *skb)
{
int ret = -1;
int ret;
skb->dev = peer->ovpn->dev;
/* no checksum performed at this layer */
skb->ip_summed = CHECKSUM_NONE;
/* get socket info */
if (unlikely(!sock)) {
net_warn_ratelimited("%s: no sock for remote peer %u\n",
netdev_name(peer->ovpn->dev), peer->id);
goto out;
}
/* crypto layer -> transport (UDP) */
ret = ovpn_udp_output(peer, &peer->dst_cache, sock->sk, skb);
out:
if (unlikely(ret < 0)) {
ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb);
if (unlikely(ret < 0))
kfree_skb(skb);
return;
}
}
static void ovpn_udp_encap_destroy(struct sock *sk)
@ -383,6 +373,7 @@ static void ovpn_udp_encap_destroy(struct sock *sk)
/**
* ovpn_udp_socket_attach - set udp-tunnel CBs on socket and link it to ovpn
* @ovpn_sock: socket to configure
* @sock: the socket container to be passed to setup_udp_tunnel_sock()
* @ovpn: the openvp instance to link
*
* After invoking this function, the sock will be controlled by ovpn so that
@ -390,7 +381,7 @@ static void ovpn_udp_encap_destroy(struct sock *sk)
*
* Return: 0 on success or a negative error code otherwise
*/
int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock,
struct ovpn_priv *ovpn)
{
struct udp_tunnel_sock_cfg cfg = {
@ -398,17 +389,16 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
.encap_rcv = ovpn_udp_encap_recv,
.encap_destroy = ovpn_udp_encap_destroy,
};
struct socket *sock = ovpn_sock->sock;
struct ovpn_socket *old_data;
int ret;
/* make sure no pre-existing encapsulation handler exists */
rcu_read_lock();
old_data = rcu_dereference_sk_user_data(sock->sk);
old_data = rcu_dereference_sk_user_data(ovpn_sock->sk);
if (!old_data) {
/* socket is currently unused - we can take it */
rcu_read_unlock();
setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg);
setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock, &cfg);
return 0;
}
@ -421,7 +411,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
* Unlikely TCP, a single UDP socket can be used to talk to many remote
* hosts and therefore openvpn instantiates one only for all its peers
*/
if ((READ_ONCE(udp_sk(sock->sk)->encap_type) == UDP_ENCAP_OVPNINUDP) &&
if ((READ_ONCE(udp_sk(ovpn_sock->sk)->encap_type) == UDP_ENCAP_OVPNINUDP) &&
old_data->ovpn == ovpn) {
netdev_dbg(ovpn->dev,
"provided socket already owned by this interface\n");
@ -442,8 +432,16 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
*/
void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock)
{
struct udp_tunnel_sock_cfg cfg = { };
struct sock *sk = ovpn_sock->sk;
setup_udp_tunnel_sock(sock_net(ovpn_sock->sock->sk), ovpn_sock->sock,
&cfg);
/* Re-enable multicast loopback */
inet_set_bit(MC_LOOP, sk);
/* Disable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */
inet_dec_convert_csum(sk);
WRITE_ONCE(udp_sk(sk)->encap_type, 0);
WRITE_ONCE(udp_sk(sk)->encap_rcv, NULL);
WRITE_ONCE(udp_sk(sk)->encap_destroy, NULL);
rcu_assign_sk_user_data(sk, NULL);
}

View File

@ -15,11 +15,11 @@ struct ovpn_peer;
struct ovpn_priv;
struct socket;
int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock,
struct ovpn_priv *ovpn);
void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock);
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct socket *sock,
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk,
struct sk_buff *skb);
#endif /* _NET_OVPN_UDP_H_ */

View File

@ -2166,6 +2166,7 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
ovpn->peers_file = argv[4];
ovpn->sa_family = AF_INET;
if (argc > 5 && !strcmp(argv[5], "ipv6"))
ovpn->sa_family = AF_INET6;
break;

View File

@ -0,0 +1,9 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2025 OpenVPN, Inc.
#
# Author: Antonio Quartulli <antonio@openvpn.net>
MTU="1500"
source test.sh