File Index
Files and directories
Sign in
FILE

socket.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ovpn/socket.c C
  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 <james@openvpn.net>
  7 *		Antonio Quartulli <antonio@openvpn.net>
  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 "tcp.h"
 20#include "udp.h"
 21
 22static void ovpn_socket_release_kref(struct kref *kref)
 23{
 24	struct ovpn_socket *sock = container_of(kref, struct ovpn_socket,
 25						refcount);
 26
 27	if (sock->sk->sk_protocol == IPPROTO_UDP)
 28		ovpn_udp_socket_detach(sock);
 29	else if (sock->sk->sk_protocol == IPPROTO_TCP)
 30		ovpn_tcp_socket_detach(sock);
 31}
 32
 33/**
 34 * ovpn_socket_put - decrease reference counter
 35 * @peer: peer whose socket reference counter should be decreased
 36 * @sock: the RCU protected peer socket
 37 *
 38 * This function is only used internally. Users willing to release
 39 * references to the ovpn_socket should use ovpn_socket_release()
 40 *
 41 * Return: true if the socket was released, false otherwise
 42 */
 43static bool ovpn_socket_put(struct ovpn_peer *peer, struct ovpn_socket *sock)
 44{
 45	return kref_put(&sock->refcount, ovpn_socket_release_kref);
 46}
 47
 48/**
 49 * ovpn_socket_release - release resources owned by socket user
 50 * @peer: peer whose socket should be released
 51 *
 52 * This function should be invoked when the peer is being removed
 53 * and wants to drop its link to the socket.
 54 *
 55 * In case of UDP, the detach routine will drop a reference to the
 56 * ovpn netdev, pointed by the ovpn_socket.
 57 *
 58 * In case of TCP, releasing the socket will cause dropping
 59 * the refcounter for the peer it is linked to, thus allowing the peer
 60 * disappear as well.
 61 *
 62 * This function is expected to be invoked exactly once per peer
 63 *
 64 * NOTE: this function may sleep
 65 */
 66void ovpn_socket_release(struct ovpn_peer *peer)
 67{
 68	struct ovpn_socket *sock;
 69	bool released;
 70
 71	might_sleep();
 72
 73	sock = rcu_replace_pointer(peer->sock, NULL, true);
 74	/* release may be invoked after socket was detached */
 75	if (!sock)
 76		return;
 77
 78	/* Drop the reference while holding the sock lock to avoid
 79	 * concurrent ovpn_socket_new call to mess up with a partially
 80	 * detached socket.
 81	 *
 82	 * Holding the lock ensures that a socket with refcnt 0 is fully
 83	 * detached before it can be picked by a concurrent reader.
 84	 */
 85	lock_sock(sock->sk);
 86	released = ovpn_socket_put(peer, sock);
 87	release_sock(sock->sk);
 88
 89	/* align all readers with sk_user_data being NULL */
 90	synchronize_rcu();
 91
 92	/* following cleanup should happen with lock released */
 93	if (released) {
 94		if (sock->sk->sk_protocol == IPPROTO_UDP) {
 95			netdev_put(sock->ovpn->dev, &sock->dev_tracker);
 96		} else if (sock->sk->sk_protocol == IPPROTO_TCP) {
 97			/* wait for TCP jobs to terminate */
 98			ovpn_tcp_socket_wait_finish(sock);
 99			ovpn_peer_put(sock->peer);
100		}
101		/* drop reference acquired in ovpn_socket_new() */
102		sock_put(sock->sk);
103		/* we can call plain kfree() because we already waited one RCU
104		 * period due to synchronize_rcu()
105		 */
106		kfree(sock);
107	}
108}
109
110static bool ovpn_socket_hold(struct ovpn_socket *sock)
111{
112	return kref_get_unless_zero(&sock->refcount);
113}
114
115static int ovpn_socket_attach(struct ovpn_socket *ovpn_sock,
116			      struct socket *sock,
117			      struct ovpn_peer *peer)
118{
119	if (sock->sk->sk_protocol == IPPROTO_UDP)
120		return ovpn_udp_socket_attach(ovpn_sock, sock, peer->ovpn);
121	else if (sock->sk->sk_protocol == IPPROTO_TCP)
122		return ovpn_tcp_socket_attach(ovpn_sock, peer);
123
124	return -EOPNOTSUPP;
125}
126
127/**
128 * ovpn_socket_new - create a new socket and initialize it
129 * @sock: the kernel socket to embed
130 * @peer: the peer reachable via this socket
131 *
132 * Return: an openvpn socket on success or a negative error code otherwise
133 */
134struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
135{
136	struct ovpn_socket *ovpn_sock;
137	struct sock *sk = sock->sk;
138	int ret;
139
140	lock_sock(sk);
141
142	/* a TCP socket can only be owned by a single peer, therefore there
143	 * can't be any other user
144	 */
145	if (sk->sk_protocol == IPPROTO_TCP && sk->sk_user_data) {
146		ovpn_sock = ERR_PTR(-EBUSY);
147		goto sock_release;
148	}
149
150	/* a UDP socket can be shared across multiple peers, but we must make
151	 * sure it is not owned by something else
152	 */
153	if (sk->sk_protocol == IPPROTO_UDP) {
154		u8 type = READ_ONCE(udp_sk(sk)->encap_type);
155
156		/* socket owned by other encapsulation module */
157		if (type && type != UDP_ENCAP_OVPNINUDP) {
158			ovpn_sock = ERR_PTR(-EBUSY);
159			goto sock_release;
160		}
161
162		rcu_read_lock();
163		ovpn_sock = rcu_dereference_sk_user_data(sk);
164		if (ovpn_sock) {
165			/* socket owned by another ovpn instance, we can't use it */
166			if (ovpn_sock->ovpn != peer->ovpn) {
167				ovpn_sock = ERR_PTR(-EBUSY);
168				rcu_read_unlock();
169				goto sock_release;
170			}
171
172			/* this socket is already owned by this instance,
173			 * therefore we can increase the refcounter and
174			 * use it as expected
175			 */
176			if (WARN_ON(!ovpn_socket_hold(ovpn_sock))) {
177				/* this should never happen because setting
178				 * the refcnt to 0 and detaching the socket
179				 * is expected to be atomic
180				 */
181				ovpn_sock = ERR_PTR(-EAGAIN);
182				rcu_read_unlock();
183				goto sock_release;
184			}
185
186			rcu_read_unlock();
187			goto sock_release;
188		}
189		rcu_read_unlock();
190	}
191
192	/* socket is not owned: attach to this ovpn instance */
193
194	ovpn_sock = kzalloc(sizeof(*ovpn_sock), GFP_KERNEL);
195	if (!ovpn_sock) {
196		ovpn_sock = ERR_PTR(-ENOMEM);
197		goto sock_release;
198	}
199
200	ovpn_sock->sk = sk;
201	kref_init(&ovpn_sock->refcount);
202
203	/* TCP sockets are per-peer, therefore they are linked to their unique
204	 * peer
205	 */
206	if (sk->sk_protocol == IPPROTO_TCP) {
207		INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work);
208		ovpn_sock->peer = peer;
209		ovpn_peer_hold(peer);
210	} else if (sk->sk_protocol == IPPROTO_UDP) {
211		/* in UDP we only link the ovpn instance since the socket is
212		 * shared among multiple peers
213		 */
214		ovpn_sock->ovpn = peer->ovpn;
215		netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker,
216			    GFP_KERNEL);
217	}
218
219	/* the newly created ovpn_socket is holding reference to sk,
220	 * therefore we increase its refcounter.
221	 *
222	 * This ovpn_socket instance is referenced by all peers
223	 * using the same socket.
224	 *
225	 * ovpn_socket_release() will take care of dropping the reference.
226	 */
227	sock_hold(sk);
228
229	ret = ovpn_socket_attach(ovpn_sock, sock, peer);
230	if (ret < 0) {
231		if (sk->sk_protocol == IPPROTO_TCP)
232			ovpn_peer_put(peer);
233		else if (sk->sk_protocol == IPPROTO_UDP)
234			netdev_put(peer->ovpn->dev, &ovpn_sock->dev_tracker);
235
236		sock_put(sk);
237		kfree(ovpn_sock);
238		ovpn_sock = ERR_PTR(ret);
239	}
240
241sock_release:
242	release_sock(sk);
243	return ovpn_sock;
244}
~/ Read-only file preview.