drivers/net/ovpn/socket.c

Source file repositories/reference/linux-study-clean/drivers/net/ovpn/socket.c

File Facts

System
Linux kernel
Corpus path
drivers/net/ovpn/socket.c
Extension
.c
Size
6607 bytes
Lines
245
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (sock->sk->sk_protocol == IPPROTO_UDP) {
			netdev_put(sock->ovpn->dev, &sock->dev_tracker);
		} 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()
		 */
		kfree(sock);
	}
}

static bool ovpn_socket_hold(struct ovpn_socket *sock)
{
	return kref_get_unless_zero(&sock->refcount);
}

static int ovpn_socket_attach(struct ovpn_socket *ovpn_sock,
			      struct socket *sock,
			      struct ovpn_peer *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;
}

/**
 * ovpn_socket_new - create a new socket and initialize it
 * @sock: the kernel socket to embed
 * @peer: the peer reachable via this socket
 *
 * Return: an openvpn socket on success or a negative error code otherwise
 */
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(sk);

	/* a TCP socket can only be owned by a single peer, therefore there
	 * can't be any other user
	 */
	if (sk->sk_protocol == IPPROTO_TCP && sk->sk_user_data) {
		ovpn_sock = ERR_PTR(-EBUSY);
		goto sock_release;
	}

	/* a UDP socket can be shared across multiple peers, but we must make
	 * sure it is not owned by something else
	 */
	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) {
			ovpn_sock = ERR_PTR(-EBUSY);
			goto sock_release;
		}

		rcu_read_lock();
		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) {
				ovpn_sock = ERR_PTR(-EBUSY);
				rcu_read_unlock();
				goto sock_release;
			}

			/* this socket is already owned by this instance,
			 * therefore we can increase the refcounter and
			 * use it as expected
			 */
			if (WARN_ON(!ovpn_socket_hold(ovpn_sock))) {
				/* this should never happen because setting
				 * the refcnt to 0 and detaching the socket
				 * is expected to be atomic
				 */
				ovpn_sock = ERR_PTR(-EAGAIN);
				rcu_read_unlock();
				goto sock_release;

Annotation

Implementation Notes