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

ovpn: implement basic RX path (UDP)



Packets received over the socket are forwarded to the user device.

Implementation is UDP only. TCP will be added by a later patch.

Note: no decryption/decapsulation exists yet, packets are forwarded as
they arrive without much processing.

Signed-off-by: default avatarAntonio Quartulli <antonio@openvpn.net>
Link: https://patch.msgid.link/20250415-b4-ovpn-v26-8-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 08857b5e
Loading
Loading
Loading
Loading
+63 −1
Original line number Diff line number Diff line
@@ -9,15 +9,77 @@

#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/gro_cells.h>
#include <net/gso.h>

#include "io.h"
#include "ovpnpriv.h"
#include "peer.h"
#include "io.h"
#include "netlink.h"
#include "proto.h"
#include "udp.h"
#include "skb.h"
#include "socket.h"

/* Called after decrypt to write the IP packet to the device.
 * This method is expected to manage/free the skb.
 */
static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
{
	unsigned int pkt_len;
	int ret;

	/* we can't guarantee the packet wasn't corrupted before entering the
	 * VPN, therefore we give other layers a chance to check that
	 */
	skb->ip_summed = CHECKSUM_NONE;

	/* skb hash for transport packet no longer valid after decapsulation */
	skb_clear_hash(skb);

	/* post-decrypt scrub -- prepare to inject encapsulated packet onto the
	 * interface, based on __skb_tunnel_rx() in dst.h
	 */
	skb->dev = peer->ovpn->dev;
	skb_set_queue_mapping(skb, 0);
	skb_scrub_packet(skb, true);

	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb_reset_inner_headers(skb);

	/* cause packet to be "received" by the interface */
	pkt_len = skb->len;
	ret = gro_cells_receive(&peer->ovpn->gro_cells, skb);
	if (likely(ret == NET_RX_SUCCESS))
		/* update RX stats with the size of decrypted packet */
		dev_dstats_rx_add(peer->ovpn->dev, pkt_len);
}

static void ovpn_decrypt_post(struct sk_buff *skb, int ret)
{
	struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer;

	if (unlikely(ret < 0))
		goto drop;

	ovpn_netdev_write(peer, skb);
	/* skb is passed to upper layer - don't free it */
	skb = NULL;
drop:
	if (unlikely(skb))
		dev_dstats_rx_dropped(peer->ovpn->dev);
	ovpn_peer_put(peer);
	kfree_skb(skb);
}

/* RX path entry point: decrypt packet and forward it to the device */
void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb)
{
	ovpn_skb_cb(skb)->peer = peer;
	ovpn_decrypt_post(skb, 0);
}

static void ovpn_encrypt_post(struct sk_buff *skb, int ret)
{
	struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer;
+2 −0
Original line number Diff line number Diff line
@@ -21,4 +21,6 @@

netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev);

void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb);

#endif /* _NET_OVPN_OVPN_H_ */
+18 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <net/gro_cells.h>
#include <net/ip.h>
#include <net/rtnetlink.h>
#include <uapi/linux/if_arp.h>
@@ -21,8 +22,25 @@
#include "io.h"
#include "peer.h"
#include "proto.h"
#include "udp.h"

static int ovpn_net_init(struct net_device *dev)
{
	struct ovpn_priv *ovpn = netdev_priv(dev);

	return gro_cells_init(&ovpn->gro_cells, dev);
}

static void ovpn_net_uninit(struct net_device *dev)
{
	struct ovpn_priv *ovpn = netdev_priv(dev);

	gro_cells_destroy(&ovpn->gro_cells);
}

static const struct net_device_ops ovpn_netdev_ops = {
	.ndo_init		= ovpn_net_init,
	.ndo_uninit		= ovpn_net_uninit,
	.ndo_start_xmit		= ovpn_net_xmit,
};

+3 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#ifndef _NET_OVPN_OVPNSTRUCT_H_
#define _NET_OVPN_OVPNSTRUCT_H_

#include <net/gro_cells.h>
#include <uapi/linux/if_link.h>
#include <uapi/linux/ovpn.h>

@@ -19,12 +20,14 @@
 * @mode: device operation mode (i.e. p2p, mp, ..)
 * @lock: protect this object
 * @peer: in P2P mode, this is the only remote peer
 * @gro_cells: pointer to the Generic Receive Offload cell
 */
struct ovpn_priv {
	struct net_device *dev;
	enum ovpn_mode mode;
	spinlock_t lock; /* protect writing to the ovpn_priv object */
	struct ovpn_peer __rcu *peer;
	struct gro_cells gro_cells;
};

#endif /* _NET_OVPN_OVPNSTRUCT_H_ */
+49 −1
Original line number Diff line number Diff line
@@ -10,6 +10,11 @@
#ifndef _NET_OVPN_PROTO_H_
#define _NET_OVPN_PROTO_H_

#include "main.h"

#include <linux/bitfield.h>
#include <linux/skbuff.h>

/* When the OpenVPN protocol is ran in AEAD mode, use
 * the OpenVPN packet ID as the AEAD nonce:
 *
@@ -34,5 +39,48 @@
#define OVPN_NONCE_WIRE_SIZE (OVPN_NONCE_SIZE - OVPN_NONCE_TAIL_SIZE)

#define OVPN_OPCODE_SIZE		4 /* DATA_V2 opcode size */
#define OVPN_OPCODE_KEYID_MASK		0x07000000
#define OVPN_OPCODE_PKTTYPE_MASK	0xF8000000
#define OVPN_OPCODE_PEERID_MASK		0x00FFFFFF

/* packet opcodes of interest to us */
#define OVPN_DATA_V1			6 /* data channel v1 packet */
#define OVPN_DATA_V2			9 /* data channel v2 packet */

#define OVPN_PEER_ID_UNDEF		0x00FFFFFF

/**
 * ovpn_opcode_from_skb - extract OP code from skb at specified offset
 * @skb: the packet to extract the OP code from
 * @offset: the offset in the data buffer where the OP code is located
 *
 * Note: this function assumes that the skb head was pulled enough
 * to access the first 4 bytes.
 *
 * Return: the OP code
 */
static inline u8 ovpn_opcode_from_skb(const struct sk_buff *skb, u16 offset)
{
	u32 opcode = be32_to_cpu(*(__be32 *)(skb->data + offset));

	return FIELD_GET(OVPN_OPCODE_PKTTYPE_MASK, opcode);
}

/**
 * ovpn_peer_id_from_skb - extract peer ID from skb at specified offset
 * @skb: the packet to extract the OP code from
 * @offset: the offset in the data buffer where the OP code is located
 *
 * Note: this function assumes that the skb head was pulled enough
 * to access the first 4 bytes.
 *
 * Return: the peer ID
 */
static inline u32 ovpn_peer_id_from_skb(const struct sk_buff *skb, u16 offset)
{
	u32 opcode = be32_to_cpu(*(__be32 *)(skb->data + offset));

	return FIELD_GET(OVPN_OPCODE_PEERID_MASK, opcode);
}

#endif /* _NET_OVPN_PROTO_H_ */
#endif /* _NET_OVPN_OVPNPROTO_H_ */
Loading