Commit 80747cae authored by Antonio Quartulli's avatar Antonio Quartulli Committed by Paolo Abeni
Browse files

ovpn: introduce the ovpn_peer object



An ovpn_peer object holds the whole status of a remote peer
(regardless whether it is a server or a client).

This includes status for crypto, tx/rx buffers, napi, etc.

Only support for one peer is introduced (P2P mode).
Multi peer support is introduced with a later patch.

Along with the ovpn_peer, also the ovpn_bind object is introcued
as the two are strictly related.
An ovpn_bind object wraps a sockaddr representing the local
coordinates being used to talk to a specific peer.

Signed-off-by: default avatarAntonio Quartulli <antonio@openvpn.net>
Link: https://patch.msgid.link/20250415-b4-ovpn-v26-5-577f6097b964@openvpn.net


Reviewed-by: default avatarSabrina Dubroca <sd@queasysnail.net>
Tested-by: default avatarOleksandr Natalenko <oleksandr@natalenko.name>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 8327a3ba
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ config OVPN
	tristate "OpenVPN data channel offload"
	depends on NET && INET
	depends on IPV6 || !IPV6
	select DST_CACHE
	help
	  This module enhances the performance of the OpenVPN userspace software
	  by offloading the data channel processing to kernelspace.
+2 −0
Original line number Diff line number Diff line
@@ -7,7 +7,9 @@
# Author:	Antonio Quartulli <antonio@openvpn.net>

obj-$(CONFIG_OVPN) := ovpn.o
ovpn-y += bind.o
ovpn-y += main.o
ovpn-y += io.o
ovpn-y += netlink.o
ovpn-y += netlink-gen.o
ovpn-y += peer.o
+58 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel offload
 *
 *  Copyright (C) 2012-2025 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include <linux/netdevice.h>
#include <linux/socket.h>

#include "ovpnpriv.h"
#include "bind.h"
#include "peer.h"

/**
 * ovpn_bind_from_sockaddr - retrieve binding matching sockaddr
 * @ss: the sockaddr to match
 *
 * Return: the bind matching the passed sockaddr if found, NULL otherwise
 */
struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss)
{
	struct ovpn_bind *bind;
	size_t sa_len;

	if (ss->ss_family == AF_INET)
		sa_len = sizeof(struct sockaddr_in);
	else if (ss->ss_family == AF_INET6)
		sa_len = sizeof(struct sockaddr_in6);
	else
		return ERR_PTR(-EAFNOSUPPORT);

	bind = kzalloc(sizeof(*bind), GFP_ATOMIC);
	if (unlikely(!bind))
		return ERR_PTR(-ENOMEM);

	memcpy(&bind->remote, ss, sa_len);

	return bind;
}

/**
 * ovpn_bind_reset - assign new binding to peer
 * @peer: the peer whose binding has to be replaced
 * @new: the new bind to assign
 */
void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new)
{
	struct ovpn_bind *old;

	spin_lock_bh(&peer->lock);
	old = rcu_replace_pointer(peer->bind, new, true);
	spin_unlock_bh(&peer->lock);

	kfree_rcu(old, rcu);
}
+101 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel offload
 *
 *  Copyright (C) 2012-2025 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_OVPNBIND_H_
#define _NET_OVPN_OVPNBIND_H_

#include <net/ip.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>

struct ovpn_peer;

/**
 * union ovpn_sockaddr - basic transport layer address
 * @in4: IPv4 address
 * @in6: IPv6 address
 */
union ovpn_sockaddr {
	struct sockaddr_in in4;
	struct sockaddr_in6 in6;
};

/**
 * struct ovpn_bind - remote peer binding
 * @remote: the remote peer sockaddress
 * @local: local endpoint used to talk to the peer
 * @local.ipv4: local IPv4 used to talk to the peer
 * @local.ipv6: local IPv6 used to talk to the peer
 * @rcu: used to schedule RCU cleanup job
 */
struct ovpn_bind {
	union ovpn_sockaddr remote;  /* remote sockaddr */

	union {
		struct in_addr ipv4;
		struct in6_addr ipv6;
	} local;

	struct rcu_head rcu;
};

/**
 * ovpn_bind_skb_src_match - match packet source with binding
 * @bind: the binding to match
 * @skb: the packet to match
 *
 * Return: true if the packet source matches the remote peer sockaddr
 * in the binding
 */
static inline bool ovpn_bind_skb_src_match(const struct ovpn_bind *bind,
					   const struct sk_buff *skb)
{
	const union ovpn_sockaddr *remote;

	if (unlikely(!bind))
		return false;

	remote = &bind->remote;

	switch (skb->protocol) {
	case htons(ETH_P_IP):
		if (unlikely(remote->in4.sin_family != AF_INET))
			return false;

		if (unlikely(remote->in4.sin_addr.s_addr != ip_hdr(skb)->saddr))
			return false;

		if (unlikely(remote->in4.sin_port != udp_hdr(skb)->source))
			return false;
		break;
	case htons(ETH_P_IPV6):
		if (unlikely(remote->in6.sin6_family != AF_INET6))
			return false;

		if (unlikely(!ipv6_addr_equal(&remote->in6.sin6_addr,
					      &ipv6_hdr(skb)->saddr)))
			return false;

		if (unlikely(remote->in6.sin6_port != udp_hdr(skb)->source))
			return false;
		break;
	default:
		return false;
	}

	return true;
}

struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *sa);
void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *bind);

#endif /* _NET_OVPN_OVPNBIND_H_ */
+13 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "main.h"
#include "netlink.h"
#include "io.h"
#include "peer.h"
#include "proto.h"

static const struct net_device_ops ovpn_netdev_ops = {
@@ -92,6 +93,7 @@ static int ovpn_newlink(struct net_device *dev,

	ovpn->dev = dev;
	ovpn->mode = mode;
	spin_lock_init(&ovpn->lock);

	/* Set carrier explicitly after registration, this way state is
	 * clearly defined.
@@ -109,6 +111,16 @@ static int ovpn_newlink(struct net_device *dev,
	return register_netdevice(dev);
}

static void ovpn_dellink(struct net_device *dev, struct list_head *head)
{
	struct ovpn_priv *ovpn = netdev_priv(dev);

	if (ovpn->mode == OVPN_MODE_P2P)
		ovpn_peer_release_p2p(ovpn, OVPN_DEL_PEER_REASON_TEARDOWN);

	unregister_netdevice_queue(dev, head);
}

static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
	struct ovpn_priv *ovpn = netdev_priv(dev);
@@ -127,7 +139,7 @@ static struct rtnl_link_ops ovpn_link_ops = {
	.policy = ovpn_policy,
	.maxtype = IFLA_OVPN_MAX,
	.newlink = ovpn_newlink,
	.dellink = unregister_netdevice_queue,
	.dellink = ovpn_dellink,
	.fill_info = ovpn_fill_info,
};

Loading