mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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: Antonio Quartulli <antonio@openvpn.net> Link: https://patch.msgid.link/20250415-b4-ovpn-v26-8-577f6097b964@openvpn.net Reviewed-by: Sabrina Dubroca <sd@queasysnail.net> Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
08857b5ec5
commit
ab66abbc76
8 changed files with 290 additions and 5 deletions
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
||||
#endif /* _NET_OVPN_PROTO_H_ */
|
||||
/* 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_OVPNPROTO_H_ */
|
||||
|
|
|
@ -23,8 +23,10 @@ static void ovpn_socket_release_kref(struct kref *kref)
|
|||
struct ovpn_socket *sock = container_of(kref, struct ovpn_socket,
|
||||
refcount);
|
||||
|
||||
if (sock->sock->sk->sk_protocol == IPPROTO_UDP)
|
||||
if (sock->sock->sk->sk_protocol == IPPROTO_UDP) {
|
||||
ovpn_udp_socket_detach(sock);
|
||||
netdev_put(sock->ovpn->dev, &sock->dev_tracker);
|
||||
}
|
||||
|
||||
kfree_rcu(sock, rcu);
|
||||
}
|
||||
|
@ -179,7 +181,6 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
|
|||
goto sock_release;
|
||||
}
|
||||
|
||||
ovpn_sock->ovpn = peer->ovpn;
|
||||
ovpn_sock->sock = sock;
|
||||
kref_init(&ovpn_sock->refcount);
|
||||
|
||||
|
@ -190,6 +191,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
|
|||
goto sock_release;
|
||||
}
|
||||
|
||||
if (sock->sk->sk_protocol == IPPROTO_UDP) {
|
||||
/* in UDP we only link the ovpn instance since the socket is
|
||||
* shared among multiple peers
|
||||
*/
|
||||
ovpn_sock->ovpn = peer->ovpn;
|
||||
netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
rcu_assign_sk_user_data(sock->sk, ovpn_sock);
|
||||
sock_release:
|
||||
release_sock(sock->sk);
|
||||
|
|
|
@ -20,12 +20,19 @@ struct ovpn_peer;
|
|||
/**
|
||||
* struct ovpn_socket - a kernel socket referenced in the ovpn code
|
||||
* @ovpn: ovpn instance owning this socket (UDP only)
|
||||
* @dev_tracker: reference tracker for associated dev (UDP only)
|
||||
* @sock: the low level sock object
|
||||
* @refcount: amount of contexts currently referencing this object
|
||||
* @rcu: member used to schedule RCU destructor callback
|
||||
*/
|
||||
struct ovpn_socket {
|
||||
struct ovpn_priv *ovpn;
|
||||
union {
|
||||
struct {
|
||||
struct ovpn_priv *ovpn;
|
||||
netdevice_tracker dev_tracker;
|
||||
};
|
||||
};
|
||||
|
||||
struct socket *sock;
|
||||
struct kref refcount;
|
||||
struct rcu_head rcu;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <net/dst_cache.h>
|
||||
#include <net/route.h>
|
||||
#include <net/ipv6_stubs.h>
|
||||
#include <net/transp_v6.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/udp_tunnel.h>
|
||||
|
||||
|
@ -23,9 +24,114 @@
|
|||
#include "bind.h"
|
||||
#include "io.h"
|
||||
#include "peer.h"
|
||||
#include "proto.h"
|
||||
#include "socket.h"
|
||||
#include "udp.h"
|
||||
|
||||
/* Retrieve the corresponding ovpn object from a UDP socket
|
||||
* rcu_read_lock must be held on entry
|
||||
*/
|
||||
static struct ovpn_socket *ovpn_socket_from_udp_sock(struct sock *sk)
|
||||
{
|
||||
struct ovpn_socket *ovpn_sock;
|
||||
|
||||
if (unlikely(READ_ONCE(udp_sk(sk)->encap_type) != UDP_ENCAP_OVPNINUDP))
|
||||
return NULL;
|
||||
|
||||
ovpn_sock = rcu_dereference_sk_user_data(sk);
|
||||
if (unlikely(!ovpn_sock))
|
||||
return NULL;
|
||||
|
||||
/* make sure that sk matches our stored transport socket */
|
||||
if (unlikely(!ovpn_sock->sock || sk != ovpn_sock->sock->sk))
|
||||
return NULL;
|
||||
|
||||
return ovpn_sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_udp_encap_recv - Start processing a received UDP packet.
|
||||
* @sk: socket over which the packet was received
|
||||
* @skb: the received packet
|
||||
*
|
||||
* If the first byte of the payload is:
|
||||
* - DATA_V2 the packet is accepted for further processing,
|
||||
* - DATA_V1 the packet is dropped as not supported,
|
||||
* - anything else the packet is forwarded to the UDP stack for
|
||||
* delivery to user space.
|
||||
*
|
||||
* Return:
|
||||
* 0 if skb was consumed or dropped
|
||||
* >0 if skb should be passed up to userspace as UDP (packet not consumed)
|
||||
* <0 if skb should be resubmitted as proto -N (packet not consumed)
|
||||
*/
|
||||
static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct ovpn_socket *ovpn_sock;
|
||||
struct ovpn_priv *ovpn;
|
||||
struct ovpn_peer *peer;
|
||||
u32 peer_id;
|
||||
u8 opcode;
|
||||
|
||||
ovpn_sock = ovpn_socket_from_udp_sock(sk);
|
||||
if (unlikely(!ovpn_sock)) {
|
||||
net_err_ratelimited("ovpn: %s invoked on non ovpn socket\n",
|
||||
__func__);
|
||||
goto drop_noovpn;
|
||||
}
|
||||
|
||||
ovpn = ovpn_sock->ovpn;
|
||||
if (unlikely(!ovpn)) {
|
||||
net_err_ratelimited("ovpn: cannot obtain ovpn object from UDP socket\n");
|
||||
goto drop_noovpn;
|
||||
}
|
||||
|
||||
/* Make sure the first 4 bytes of the skb data buffer after the UDP
|
||||
* header are accessible.
|
||||
* They are required to fetch the OP code, the key ID and the peer ID.
|
||||
*/
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(struct udphdr) +
|
||||
OVPN_OPCODE_SIZE))) {
|
||||
net_dbg_ratelimited("%s: packet too small from UDP socket\n",
|
||||
netdev_name(ovpn->dev));
|
||||
goto drop;
|
||||
}
|
||||
|
||||
opcode = ovpn_opcode_from_skb(skb, sizeof(struct udphdr));
|
||||
if (unlikely(opcode != OVPN_DATA_V2)) {
|
||||
/* DATA_V1 is not supported */
|
||||
if (opcode == OVPN_DATA_V1)
|
||||
goto drop;
|
||||
|
||||
/* unknown or control packet: let it bubble up to userspace */
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_id = ovpn_peer_id_from_skb(skb, sizeof(struct udphdr));
|
||||
/* some OpenVPN server implementations send data packets with the
|
||||
* peer-id set to UNDEF. In this case we skip the peer lookup by peer-id
|
||||
* and we try with the transport address
|
||||
*/
|
||||
if (peer_id == OVPN_PEER_ID_UNDEF)
|
||||
peer = ovpn_peer_get_by_transp_addr(ovpn, skb);
|
||||
else
|
||||
peer = ovpn_peer_get_by_id(ovpn, peer_id);
|
||||
|
||||
if (unlikely(!peer))
|
||||
goto drop;
|
||||
|
||||
/* pop off outer UDP header */
|
||||
__skb_pull(skb, sizeof(struct udphdr));
|
||||
ovpn_recv(peer, skb);
|
||||
return 0;
|
||||
|
||||
drop:
|
||||
dev_dstats_rx_dropped(ovpn->dev);
|
||||
drop_noovpn:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_udp4_output - send IPv4 packet over udp socket
|
||||
* @peer: the destination peer
|
||||
|
@ -247,6 +353,25 @@ out:
|
|||
}
|
||||
}
|
||||
|
||||
static void ovpn_udp_encap_destroy(struct sock *sk)
|
||||
{
|
||||
struct ovpn_socket *sock;
|
||||
struct ovpn_priv *ovpn;
|
||||
|
||||
rcu_read_lock();
|
||||
sock = rcu_dereference_sk_user_data(sk);
|
||||
if (!sock || !sock->ovpn) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
ovpn = sock->ovpn;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (ovpn->mode == OVPN_MODE_P2P)
|
||||
ovpn_peer_release_p2p(ovpn, sk,
|
||||
OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_udp_socket_attach - set udp-tunnel CBs on socket and link it to ovpn
|
||||
* @ovpn_sock: socket to configure
|
||||
|
@ -260,6 +385,11 @@ out:
|
|||
int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
|
||||
struct ovpn_priv *ovpn)
|
||||
{
|
||||
struct udp_tunnel_sock_cfg cfg = {
|
||||
.encap_type = UDP_ENCAP_OVPNINUDP,
|
||||
.encap_rcv = ovpn_udp_encap_recv,
|
||||
.encap_destroy = ovpn_udp_encap_destroy,
|
||||
};
|
||||
struct socket *sock = ovpn_sock->sock;
|
||||
struct ovpn_socket *old_data;
|
||||
int ret;
|
||||
|
@ -270,6 +400,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
|
|||
if (!old_data) {
|
||||
/* socket is currently unused - we can take it */
|
||||
rcu_read_unlock();
|
||||
setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -303,4 +434,8 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock,
|
|||
*/
|
||||
void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock)
|
||||
{
|
||||
struct udp_tunnel_sock_cfg cfg = { };
|
||||
|
||||
setup_udp_tunnel_sock(sock_net(ovpn_sock->sock->sk), ovpn_sock->sock,
|
||||
&cfg);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue