Skip to content

Commit 08857b5

Browse files
ordexPaolo Abeni
authored andcommitted
ovpn: implement basic TX path (UDP)
Packets sent over the ovpn interface are processed and transmitted to the connected peer, if any. Implementation is UDP only. TCP will be added by a later patch. Note: no crypto/encapsulation exists yet. Packets are just captured and sent. Signed-off-by: Antonio Quartulli <[email protected]> Link: https://patch.msgid.link/[email protected] Reviewed-by: Sabrina Dubroca <[email protected]> Tested-by: Oleksandr Natalenko <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent f6226ae commit 08857b5

File tree

7 files changed

+464
-2
lines changed

7 files changed

+464
-2
lines changed

drivers/net/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ config OVPN
120120
depends on NET && INET
121121
depends on IPV6 || !IPV6
122122
select DST_CACHE
123+
select NET_UDP_TUNNEL
123124
help
124125
This module enhances the performance of the OpenVPN userspace software
125126
by offloading the data channel processing to kernelspace.

drivers/net/ovpn/io.c

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,149 @@
99

1010
#include <linux/netdevice.h>
1111
#include <linux/skbuff.h>
12+
#include <net/gso.h>
1213

1314
#include "io.h"
15+
#include "ovpnpriv.h"
16+
#include "peer.h"
17+
#include "udp.h"
18+
#include "skb.h"
19+
#include "socket.h"
20+
21+
static void ovpn_encrypt_post(struct sk_buff *skb, int ret)
22+
{
23+
struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer;
24+
struct ovpn_socket *sock;
25+
26+
if (unlikely(ret < 0))
27+
goto err;
28+
29+
skb_mark_not_on_list(skb);
30+
31+
rcu_read_lock();
32+
sock = rcu_dereference(peer->sock);
33+
if (unlikely(!sock))
34+
goto err_unlock;
35+
36+
switch (sock->sock->sk->sk_protocol) {
37+
case IPPROTO_UDP:
38+
ovpn_udp_send_skb(peer, sock->sock, skb);
39+
break;
40+
default:
41+
/* no transport configured yet */
42+
goto err_unlock;
43+
}
44+
/* skb passed down the stack - don't free it */
45+
skb = NULL;
46+
err_unlock:
47+
rcu_read_unlock();
48+
err:
49+
if (unlikely(skb))
50+
dev_dstats_tx_dropped(peer->ovpn->dev);
51+
ovpn_peer_put(peer);
52+
kfree_skb(skb);
53+
}
54+
55+
static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
56+
{
57+
ovpn_skb_cb(skb)->peer = peer;
58+
59+
/* take a reference to the peer because the crypto code may run async.
60+
* ovpn_encrypt_post() will release it upon completion
61+
*/
62+
if (unlikely(!ovpn_peer_hold(peer))) {
63+
DEBUG_NET_WARN_ON_ONCE(1);
64+
return false;
65+
}
66+
67+
ovpn_encrypt_post(skb, 0);
68+
return true;
69+
}
70+
71+
/* send skb to connected peer, if any */
72+
static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb,
73+
struct ovpn_peer *peer)
74+
{
75+
struct sk_buff *curr, *next;
76+
77+
/* this might be a GSO-segmented skb list: process each skb
78+
* independently
79+
*/
80+
skb_list_walk_safe(skb, curr, next) {
81+
if (unlikely(!ovpn_encrypt_one(peer, curr))) {
82+
dev_dstats_tx_dropped(ovpn->dev);
83+
kfree_skb(curr);
84+
}
85+
}
86+
87+
ovpn_peer_put(peer);
88+
}
1489

1590
/* Send user data to the network
1691
*/
1792
netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
1893
{
94+
struct ovpn_priv *ovpn = netdev_priv(dev);
95+
struct sk_buff *segments, *curr, *next;
96+
struct sk_buff_head skb_list;
97+
struct ovpn_peer *peer;
98+
__be16 proto;
99+
int ret;
100+
101+
/* reset netfilter state */
102+
nf_reset_ct(skb);
103+
104+
/* verify IP header size in network packet */
105+
proto = ovpn_ip_check_protocol(skb);
106+
if (unlikely(!proto || skb->protocol != proto))
107+
goto drop;
108+
109+
if (skb_is_gso(skb)) {
110+
segments = skb_gso_segment(skb, 0);
111+
if (IS_ERR(segments)) {
112+
ret = PTR_ERR(segments);
113+
net_err_ratelimited("%s: cannot segment payload packet: %d\n",
114+
netdev_name(dev), ret);
115+
goto drop;
116+
}
117+
118+
consume_skb(skb);
119+
skb = segments;
120+
}
121+
122+
/* from this moment on, "skb" might be a list */
123+
124+
__skb_queue_head_init(&skb_list);
125+
skb_list_walk_safe(skb, curr, next) {
126+
skb_mark_not_on_list(curr);
127+
128+
curr = skb_share_check(curr, GFP_ATOMIC);
129+
if (unlikely(!curr)) {
130+
net_err_ratelimited("%s: skb_share_check failed for payload packet\n",
131+
netdev_name(dev));
132+
dev_dstats_tx_dropped(ovpn->dev);
133+
continue;
134+
}
135+
136+
__skb_queue_tail(&skb_list, curr);
137+
}
138+
skb_list.prev->next = NULL;
139+
140+
/* retrieve peer serving the destination IP of this packet */
141+
peer = ovpn_peer_get_by_dst(ovpn, skb);
142+
if (unlikely(!peer)) {
143+
net_dbg_ratelimited("%s: no peer to send data to\n",
144+
netdev_name(ovpn->dev));
145+
goto drop;
146+
}
147+
148+
ovpn_send(ovpn, skb_list.next, peer);
149+
150+
return NETDEV_TX_OK;
151+
152+
drop:
153+
dev_dstats_tx_dropped(ovpn->dev);
19154
skb_tx_error(skb);
20-
kfree_skb(skb);
155+
kfree_skb_list(skb);
21156
return NET_XMIT_DROP;
22157
}

drivers/net/ovpn/peer.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,38 @@ static void ovpn_peer_remove(struct ovpn_peer *peer,
294294
llist_add(&peer->release_entry, release_list);
295295
}
296296

297+
/**
298+
* ovpn_peer_get_by_dst - Lookup peer to send skb to
299+
* @ovpn: the private data representing the current VPN session
300+
* @skb: the skb to extract the destination address from
301+
*
302+
* This function takes a tunnel packet and looks up the peer to send it to
303+
* after encapsulation. The skb is expected to be the in-tunnel packet, without
304+
* any OpenVPN related header.
305+
*
306+
* Assume that the IP header is accessible in the skb data.
307+
*
308+
* Return: the peer if found or NULL otherwise.
309+
*/
310+
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
311+
struct sk_buff *skb)
312+
{
313+
struct ovpn_peer *peer = NULL;
314+
315+
/* in P2P mode, no matter the destination, packets are always sent to
316+
* the single peer listening on the other side
317+
*/
318+
if (ovpn->mode == OVPN_MODE_P2P) {
319+
rcu_read_lock();
320+
peer = rcu_dereference(ovpn->peer);
321+
if (unlikely(peer && !ovpn_peer_hold(peer)))
322+
peer = NULL;
323+
rcu_read_unlock();
324+
}
325+
326+
return peer;
327+
}
328+
297329
/**
298330
* ovpn_peer_add_p2p - add peer to related tables in a P2P instance
299331
* @ovpn: the instance to add the peer to

drivers/net/ovpn/peer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,7 @@ void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
8080
struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_priv *ovpn,
8181
struct sk_buff *skb);
8282
struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id);
83+
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
84+
struct sk_buff *skb);
8385

8486
#endif /* _NET_OVPN_OVPNPEER_H_ */

drivers/net/ovpn/skb.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/* OpenVPN data channel offload
3+
*
4+
* Copyright (C) 2020-2025 OpenVPN, Inc.
5+
*
6+
* Author: Antonio Quartulli <[email protected]>
7+
* James Yonan <[email protected]>
8+
*/
9+
10+
#ifndef _NET_OVPN_SKB_H_
11+
#define _NET_OVPN_SKB_H_
12+
13+
#include <linux/in.h>
14+
#include <linux/in6.h>
15+
#include <linux/ip.h>
16+
#include <linux/ipv6.h>
17+
#include <linux/skbuff.h>
18+
#include <linux/socket.h>
19+
#include <linux/types.h>
20+
21+
struct ovpn_cb {
22+
struct ovpn_peer *peer;
23+
};
24+
25+
static inline struct ovpn_cb *ovpn_skb_cb(struct sk_buff *skb)
26+
{
27+
BUILD_BUG_ON(sizeof(struct ovpn_cb) > sizeof(skb->cb));
28+
return (struct ovpn_cb *)skb->cb;
29+
}
30+
31+
/* Return IP protocol version from skb header.
32+
* Return 0 if protocol is not IPv4/IPv6 or cannot be read.
33+
*/
34+
static inline __be16 ovpn_ip_check_protocol(struct sk_buff *skb)
35+
{
36+
__be16 proto = 0;
37+
38+
/* skb could be non-linear,
39+
* make sure IP header is in non-fragmented part
40+
*/
41+
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
42+
return 0;
43+
44+
if (ip_hdr(skb)->version == 4) {
45+
proto = htons(ETH_P_IP);
46+
} else if (ip_hdr(skb)->version == 6) {
47+
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
48+
return 0;
49+
proto = htons(ETH_P_IPV6);
50+
}
51+
52+
return proto;
53+
}
54+
55+
#endif /* _NET_OVPN_SKB_H_ */

0 commit comments

Comments
 (0)