Skip to content

Commit f6226ae

Browse files
ordexPaolo Abeni
authored andcommitted
ovpn: introduce the ovpn_socket object
This specific structure is used in the ovpn kernel module to wrap and carry around a standard kernel socket. ovpn takes ownership of passed sockets and therefore an ovpn specific objects is attached to them for status tracking purposes. Initially only UDP support is introduced. TCP will come in a later patch. Cc: [email protected] Signed-off-by: Antonio Quartulli <[email protected]> Link: https://patch.msgid.link/[email protected] Reviewed-by: Sabrina Dubroca <[email protected]> Tested-by: Oleksandr Natalenko <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 80747ca commit f6226ae

File tree

9 files changed

+362
-7
lines changed

9 files changed

+362
-7
lines changed

drivers/net/ovpn/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ ovpn-y += io.o
1313
ovpn-y += netlink.o
1414
ovpn-y += netlink-gen.o
1515
ovpn-y += peer.o
16+
ovpn-y += socket.o
17+
ovpn-y += udp.o

drivers/net/ovpn/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ static void ovpn_dellink(struct net_device *dev, struct list_head *head)
116116
struct ovpn_priv *ovpn = netdev_priv(dev);
117117

118118
if (ovpn->mode == OVPN_MODE_P2P)
119-
ovpn_peer_release_p2p(ovpn, OVPN_DEL_PEER_REASON_TEARDOWN);
119+
ovpn_peer_release_p2p(ovpn, NULL,
120+
OVPN_DEL_PEER_REASON_TEARDOWN);
120121

121122
unregister_netdevice_queue(dev, head);
122123
}

drivers/net/ovpn/peer.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616
#include "main.h"
1717
#include "netlink.h"
1818
#include "peer.h"
19+
#include "socket.h"
1920

2021
static void unlock_ovpn(struct ovpn_priv *ovpn,
21-
struct llist_head *release_list)
22+
struct llist_head *release_list)
2223
__releases(&ovpn->lock)
2324
{
2425
struct ovpn_peer *peer;
2526

2627
spin_unlock_bh(&ovpn->lock);
2728

28-
llist_for_each_entry(peer, release_list->first, release_entry)
29+
llist_for_each_entry(peer, release_list->first, release_entry) {
30+
ovpn_socket_release(peer);
2931
ovpn_peer_put(peer);
32+
}
3033
}
3134

3235
/**
@@ -394,18 +397,33 @@ int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
394397
/**
395398
* ovpn_peer_release_p2p - release peer upon P2P device teardown
396399
* @ovpn: the instance being torn down
400+
* @sk: if not NULL, release peer only if it's using this specific socket
397401
* @reason: the reason for releasing the peer
398402
*/
399-
void ovpn_peer_release_p2p(struct ovpn_priv *ovpn,
403+
void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
400404
enum ovpn_del_peer_reason reason)
401405
{
406+
struct ovpn_socket *ovpn_sock;
402407
LLIST_HEAD(release_list);
403408
struct ovpn_peer *peer;
404409

405410
spin_lock_bh(&ovpn->lock);
406411
peer = rcu_dereference_protected(ovpn->peer,
407412
lockdep_is_held(&ovpn->lock));
408-
if (peer)
409-
ovpn_peer_remove(peer, reason, &release_list);
413+
if (!peer) {
414+
spin_unlock_bh(&ovpn->lock);
415+
return;
416+
}
417+
418+
if (sk) {
419+
ovpn_sock = rcu_access_pointer(peer->sock);
420+
if (!ovpn_sock || ovpn_sock->sock->sk != sk) {
421+
spin_unlock_bh(&ovpn->lock);
422+
ovpn_peer_put(peer);
423+
return;
424+
}
425+
}
426+
427+
ovpn_peer_remove(peer, reason, &release_list);
410428
unlock_ovpn(ovpn, &release_list);
411429
}

drivers/net/ovpn/peer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include <net/dst_cache.h>
1414

15+
#include "socket.h"
16+
1517
/**
1618
* struct ovpn_peer - the main remote peer object
1719
* @ovpn: main openvpn instance this peer belongs to
@@ -20,6 +22,7 @@
2022
* @vpn_addrs: IP addresses assigned over the tunnel
2123
* @vpn_addrs.ipv4: IPv4 assigned to peer on the tunnel
2224
* @vpn_addrs.ipv6: IPv6 assigned to peer on the tunnel
25+
* @sock: the socket being used to talk to this peer
2326
* @dst_cache: cache for dst_entry used to send to peer
2427
* @bind: remote peer binding
2528
* @delete_reason: why peer was deleted (i.e. timeout, transport error, ..)
@@ -36,6 +39,7 @@ struct ovpn_peer {
3639
struct in_addr ipv4;
3740
struct in6_addr ipv6;
3841
} vpn_addrs;
42+
struct ovpn_socket __rcu *sock;
3943
struct dst_cache dst_cache;
4044
struct ovpn_bind __rcu *bind;
4145
enum ovpn_del_peer_reason delete_reason;
@@ -70,7 +74,7 @@ static inline void ovpn_peer_put(struct ovpn_peer *peer)
7074
struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id);
7175
int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer);
7276
int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason);
73-
void ovpn_peer_release_p2p(struct ovpn_priv *ovpn,
77+
void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
7478
enum ovpn_del_peer_reason reason);
7579

7680
struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_priv *ovpn,

drivers/net/ovpn/socket.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* OpenVPN data channel offload
3+
*
4+
* Copyright (C) 2020-2025 OpenVPN, Inc.
5+
*
6+
* Author: James Yonan <[email protected]>
7+
* Antonio Quartulli <[email protected]>
8+
*/
9+
10+
#include <linux/net.h>
11+
#include <linux/netdevice.h>
12+
#include <linux/udp.h>
13+
14+
#include "ovpnpriv.h"
15+
#include "main.h"
16+
#include "io.h"
17+
#include "peer.h"
18+
#include "socket.h"
19+
#include "udp.h"
20+
21+
static void ovpn_socket_release_kref(struct kref *kref)
22+
{
23+
struct ovpn_socket *sock = container_of(kref, struct ovpn_socket,
24+
refcount);
25+
26+
if (sock->sock->sk->sk_protocol == IPPROTO_UDP)
27+
ovpn_udp_socket_detach(sock);
28+
29+
kfree_rcu(sock, rcu);
30+
}
31+
32+
/**
33+
* ovpn_socket_put - decrease reference counter
34+
* @peer: peer whose socket reference counter should be decreased
35+
* @sock: the RCU protected peer socket
36+
*
37+
* This function is only used internally. Users willing to release
38+
* references to the ovpn_socket should use ovpn_socket_release()
39+
*/
40+
static void ovpn_socket_put(struct ovpn_peer *peer, struct ovpn_socket *sock)
41+
{
42+
kref_put(&sock->refcount, ovpn_socket_release_kref);
43+
}
44+
45+
/**
46+
* ovpn_socket_release - release resources owned by socket user
47+
* @peer: peer whose socket should be released
48+
*
49+
* This function should be invoked when the user is shutting
50+
* down and wants to drop its link to the socket.
51+
*
52+
* In case of UDP, the detach routine will drop a reference to the
53+
* ovpn netdev, pointed by the ovpn_socket.
54+
*
55+
* In case of TCP, releasing the socket will cause dropping
56+
* the refcounter for the peer it is linked to, thus allowing the peer
57+
* disappear as well.
58+
*
59+
* This function is expected to be invoked exactly once per peer
60+
*
61+
* NOTE: this function may sleep
62+
*/
63+
void ovpn_socket_release(struct ovpn_peer *peer)
64+
{
65+
struct ovpn_socket *sock;
66+
67+
might_sleep();
68+
69+
sock = rcu_replace_pointer(peer->sock, NULL, true);
70+
/* release may be invoked after socket was detached */
71+
if (!sock)
72+
return;
73+
74+
/* sanity check: we should not end up here if the socket
75+
* was already closed
76+
*/
77+
if (!sock->sock->sk) {
78+
DEBUG_NET_WARN_ON_ONCE(1);
79+
return;
80+
}
81+
82+
/* Drop the reference while holding the sock lock to avoid
83+
* concurrent ovpn_socket_new call to mess up with a partially
84+
* detached socket.
85+
*
86+
* Holding the lock ensures that a socket with refcnt 0 is fully
87+
* detached before it can be picked by a concurrent reader.
88+
*/
89+
lock_sock(sock->sock->sk);
90+
ovpn_socket_put(peer, sock);
91+
release_sock(sock->sock->sk);
92+
93+
/* align all readers with sk_user_data being NULL */
94+
synchronize_rcu();
95+
}
96+
97+
static bool ovpn_socket_hold(struct ovpn_socket *sock)
98+
{
99+
return kref_get_unless_zero(&sock->refcount);
100+
}
101+
102+
static int ovpn_socket_attach(struct ovpn_socket *sock, struct ovpn_peer *peer)
103+
{
104+
if (sock->sock->sk->sk_protocol == IPPROTO_UDP)
105+
return ovpn_udp_socket_attach(sock, peer->ovpn);
106+
107+
return -EOPNOTSUPP;
108+
}
109+
110+
/**
111+
* ovpn_socket_new - create a new socket and initialize it
112+
* @sock: the kernel socket to embed
113+
* @peer: the peer reachable via this socket
114+
*
115+
* Return: an openvpn socket on success or a negative error code otherwise
116+
*/
117+
struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
118+
{
119+
struct ovpn_socket *ovpn_sock;
120+
int ret;
121+
122+
lock_sock(sock->sk);
123+
124+
/* a TCP socket can only be owned by a single peer, therefore there
125+
* can't be any other user
126+
*/
127+
if (sock->sk->sk_protocol == IPPROTO_TCP && sock->sk->sk_user_data) {
128+
ovpn_sock = ERR_PTR(-EBUSY);
129+
goto sock_release;
130+
}
131+
132+
/* a UDP socket can be shared across multiple peers, but we must make
133+
* sure it is not owned by something else
134+
*/
135+
if (sock->sk->sk_protocol == IPPROTO_UDP) {
136+
u8 type = READ_ONCE(udp_sk(sock->sk)->encap_type);
137+
138+
/* socket owned by other encapsulation module */
139+
if (type && type != UDP_ENCAP_OVPNINUDP) {
140+
ovpn_sock = ERR_PTR(-EBUSY);
141+
goto sock_release;
142+
}
143+
144+
rcu_read_lock();
145+
ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
146+
if (ovpn_sock) {
147+
/* socket owned by another ovpn instance, we can't use it */
148+
if (ovpn_sock->ovpn != peer->ovpn) {
149+
ovpn_sock = ERR_PTR(-EBUSY);
150+
rcu_read_unlock();
151+
goto sock_release;
152+
}
153+
154+
/* this socket is already owned by this instance,
155+
* therefore we can increase the refcounter and
156+
* use it as expected
157+
*/
158+
if (WARN_ON(!ovpn_socket_hold(ovpn_sock))) {
159+
/* this should never happen because setting
160+
* the refcnt to 0 and detaching the socket
161+
* is expected to be atomic
162+
*/
163+
ovpn_sock = ERR_PTR(-EAGAIN);
164+
rcu_read_unlock();
165+
goto sock_release;
166+
}
167+
168+
rcu_read_unlock();
169+
goto sock_release;
170+
}
171+
rcu_read_unlock();
172+
}
173+
174+
/* socket is not owned: attach to this ovpn instance */
175+
176+
ovpn_sock = kzalloc(sizeof(*ovpn_sock), GFP_KERNEL);
177+
if (!ovpn_sock) {
178+
ovpn_sock = ERR_PTR(-ENOMEM);
179+
goto sock_release;
180+
}
181+
182+
ovpn_sock->ovpn = peer->ovpn;
183+
ovpn_sock->sock = sock;
184+
kref_init(&ovpn_sock->refcount);
185+
186+
ret = ovpn_socket_attach(ovpn_sock, peer);
187+
if (ret < 0) {
188+
kfree(ovpn_sock);
189+
ovpn_sock = ERR_PTR(ret);
190+
goto sock_release;
191+
}
192+
193+
rcu_assign_sk_user_data(sock->sk, ovpn_sock);
194+
sock_release:
195+
release_sock(sock->sk);
196+
return ovpn_sock;
197+
}

drivers/net/ovpn/socket.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/* OpenVPN data channel offload
3+
*
4+
* Copyright (C) 2020-2025 OpenVPN, Inc.
5+
*
6+
* Author: James Yonan <[email protected]>
7+
* Antonio Quartulli <[email protected]>
8+
*/
9+
10+
#ifndef _NET_OVPN_SOCK_H_
11+
#define _NET_OVPN_SOCK_H_
12+
13+
#include <linux/net.h>
14+
#include <linux/kref.h>
15+
#include <net/sock.h>
16+
17+
struct ovpn_priv;
18+
struct ovpn_peer;
19+
20+
/**
21+
* struct ovpn_socket - a kernel socket referenced in the ovpn code
22+
* @ovpn: ovpn instance owning this socket (UDP only)
23+
* @sock: the low level sock object
24+
* @refcount: amount of contexts currently referencing this object
25+
* @rcu: member used to schedule RCU destructor callback
26+
*/
27+
struct ovpn_socket {
28+
struct ovpn_priv *ovpn;
29+
struct socket *sock;
30+
struct kref refcount;
31+
struct rcu_head rcu;
32+
};
33+
34+
struct ovpn_socket *ovpn_socket_new(struct socket *sock,
35+
struct ovpn_peer *peer);
36+
void ovpn_socket_release(struct ovpn_peer *peer);
37+
38+
#endif /* _NET_OVPN_SOCK_H_ */

0 commit comments

Comments
 (0)