Skip to content

Commit 8534731

Browse files
ordexPaolo Abeni
authored andcommitted
ovpn: implement packet processing
This change implements encryption/decryption and encapsulation/decapsulation of OpenVPN packets. Support for generic crypto state is added along with a wrapper for the AEAD crypto kernel API. 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 ab66abb commit 8534731

File tree

15 files changed

+1105
-17
lines changed

15 files changed

+1105
-17
lines changed

drivers/net/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ config OVPN
121121
depends on IPV6 || !IPV6
122122
select DST_CACHE
123123
select NET_UDP_TUNNEL
124+
select CRYPTO
125+
select CRYPTO_AES
126+
select CRYPTO_GCM
127+
select CRYPTO_CHACHA20POLY1305
124128
help
125129
This module enhances the performance of the OpenVPN userspace software
126130
by offloading the data channel processing to kernelspace.

drivers/net/ovpn/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
obj-$(CONFIG_OVPN) := ovpn.o
1010
ovpn-y += bind.o
11+
ovpn-y += crypto.o
12+
ovpn-y += crypto_aead.o
1113
ovpn-y += main.o
1214
ovpn-y += io.o
1315
ovpn-y += netlink.o
1416
ovpn-y += netlink-gen.o
1517
ovpn-y += peer.o
18+
ovpn-y += pktid.o
1619
ovpn-y += socket.o
1720
ovpn-y += udp.o

drivers/net/ovpn/bind.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,8 @@ struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss)
4848
*/
4949
void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new)
5050
{
51-
struct ovpn_bind *old;
51+
lockdep_assert_held(&peer->lock);
5252

53-
spin_lock_bh(&peer->lock);
54-
old = rcu_replace_pointer(peer->bind, new, true);
55-
spin_unlock_bh(&peer->lock);
56-
57-
kfree_rcu(old, rcu);
53+
kfree_rcu(rcu_replace_pointer(peer->bind, new,
54+
lockdep_is_held(&peer->lock)), rcu);
5855
}

drivers/net/ovpn/crypto.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* OpenVPN data channel offload
3+
*
4+
* Copyright (C) 2020-2025 OpenVPN, Inc.
5+
*
6+
* Author: James Yonan <[email protected]>
7+
* Antonio Quartulli <[email protected]>
8+
*/
9+
10+
#include <linux/types.h>
11+
#include <linux/net.h>
12+
#include <linux/netdevice.h>
13+
#include <uapi/linux/ovpn.h>
14+
15+
#include "ovpnpriv.h"
16+
#include "main.h"
17+
#include "pktid.h"
18+
#include "crypto_aead.h"
19+
#include "crypto.h"
20+
21+
static void ovpn_ks_destroy_rcu(struct rcu_head *head)
22+
{
23+
struct ovpn_crypto_key_slot *ks;
24+
25+
ks = container_of(head, struct ovpn_crypto_key_slot, rcu);
26+
ovpn_aead_crypto_key_slot_destroy(ks);
27+
}
28+
29+
void ovpn_crypto_key_slot_release(struct kref *kref)
30+
{
31+
struct ovpn_crypto_key_slot *ks;
32+
33+
ks = container_of(kref, struct ovpn_crypto_key_slot, refcount);
34+
call_rcu(&ks->rcu, ovpn_ks_destroy_rcu);
35+
}
36+
37+
/* can only be invoked when all peer references have been dropped (i.e. RCU
38+
* release routine)
39+
*/
40+
void ovpn_crypto_state_release(struct ovpn_crypto_state *cs)
41+
{
42+
struct ovpn_crypto_key_slot *ks;
43+
44+
ks = rcu_access_pointer(cs->slots[0]);
45+
if (ks) {
46+
RCU_INIT_POINTER(cs->slots[0], NULL);
47+
ovpn_crypto_key_slot_put(ks);
48+
}
49+
50+
ks = rcu_access_pointer(cs->slots[1]);
51+
if (ks) {
52+
RCU_INIT_POINTER(cs->slots[1], NULL);
53+
ovpn_crypto_key_slot_put(ks);
54+
}
55+
}
56+
57+
/* Reset the ovpn_crypto_state object in a way that is atomic
58+
* to RCU readers.
59+
*/
60+
int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
61+
const struct ovpn_peer_key_reset *pkr)
62+
{
63+
struct ovpn_crypto_key_slot *old = NULL, *new;
64+
u8 idx;
65+
66+
if (pkr->slot != OVPN_KEY_SLOT_PRIMARY &&
67+
pkr->slot != OVPN_KEY_SLOT_SECONDARY)
68+
return -EINVAL;
69+
70+
new = ovpn_aead_crypto_key_slot_new(&pkr->key);
71+
if (IS_ERR(new))
72+
return PTR_ERR(new);
73+
74+
spin_lock_bh(&cs->lock);
75+
idx = cs->primary_idx;
76+
switch (pkr->slot) {
77+
case OVPN_KEY_SLOT_PRIMARY:
78+
old = rcu_replace_pointer(cs->slots[idx], new,
79+
lockdep_is_held(&cs->lock));
80+
break;
81+
case OVPN_KEY_SLOT_SECONDARY:
82+
old = rcu_replace_pointer(cs->slots[!idx], new,
83+
lockdep_is_held(&cs->lock));
84+
break;
85+
}
86+
spin_unlock_bh(&cs->lock);
87+
88+
if (old)
89+
ovpn_crypto_key_slot_put(old);
90+
91+
return 0;
92+
}
93+
94+
void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
95+
enum ovpn_key_slot slot)
96+
{
97+
struct ovpn_crypto_key_slot *ks = NULL;
98+
u8 idx;
99+
100+
if (slot != OVPN_KEY_SLOT_PRIMARY &&
101+
slot != OVPN_KEY_SLOT_SECONDARY) {
102+
pr_warn("Invalid slot to release: %u\n", slot);
103+
return;
104+
}
105+
106+
spin_lock_bh(&cs->lock);
107+
idx = cs->primary_idx;
108+
switch (slot) {
109+
case OVPN_KEY_SLOT_PRIMARY:
110+
ks = rcu_replace_pointer(cs->slots[idx], NULL,
111+
lockdep_is_held(&cs->lock));
112+
break;
113+
case OVPN_KEY_SLOT_SECONDARY:
114+
ks = rcu_replace_pointer(cs->slots[!idx], NULL,
115+
lockdep_is_held(&cs->lock));
116+
break;
117+
}
118+
spin_unlock_bh(&cs->lock);
119+
120+
if (!ks) {
121+
pr_debug("Key slot already released: %u\n", slot);
122+
return;
123+
}
124+
125+
pr_debug("deleting key slot %u, key_id=%u\n", slot, ks->key_id);
126+
ovpn_crypto_key_slot_put(ks);
127+
}
128+
129+
void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs)
130+
{
131+
const struct ovpn_crypto_key_slot *old_primary, *old_secondary;
132+
u8 idx;
133+
134+
spin_lock_bh(&cs->lock);
135+
idx = cs->primary_idx;
136+
old_primary = rcu_dereference_protected(cs->slots[idx],
137+
lockdep_is_held(&cs->lock));
138+
old_secondary = rcu_dereference_protected(cs->slots[!idx],
139+
lockdep_is_held(&cs->lock));
140+
/* perform real swap by switching the index of the primary key */
141+
WRITE_ONCE(cs->primary_idx, !cs->primary_idx);
142+
143+
pr_debug("key swapped: (old primary) %d <-> (new primary) %d\n",
144+
old_primary ? old_primary->key_id : -1,
145+
old_secondary ? old_secondary->key_id : -1);
146+
147+
spin_unlock_bh(&cs->lock);
148+
}

drivers/net/ovpn/crypto.h

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/* OpenVPN data channel offload
3+
*
4+
* Copyright (C) 2020-2025 OpenVPN, Inc.
5+
*
6+
* Author: James Yonan <[email protected]>
7+
* Antonio Quartulli <[email protected]>
8+
*/
9+
10+
#ifndef _NET_OVPN_OVPNCRYPTO_H_
11+
#define _NET_OVPN_OVPNCRYPTO_H_
12+
13+
#include "pktid.h"
14+
#include "proto.h"
15+
16+
/* info needed for both encrypt and decrypt directions */
17+
struct ovpn_key_direction {
18+
const u8 *cipher_key;
19+
size_t cipher_key_size;
20+
const u8 *nonce_tail; /* only needed for GCM modes */
21+
size_t nonce_tail_size; /* only needed for GCM modes */
22+
};
23+
24+
/* all info for a particular symmetric key (primary or secondary) */
25+
struct ovpn_key_config {
26+
enum ovpn_cipher_alg cipher_alg;
27+
u8 key_id;
28+
struct ovpn_key_direction encrypt;
29+
struct ovpn_key_direction decrypt;
30+
};
31+
32+
/* used to pass settings from netlink to the crypto engine */
33+
struct ovpn_peer_key_reset {
34+
enum ovpn_key_slot slot;
35+
struct ovpn_key_config key;
36+
};
37+
38+
struct ovpn_crypto_key_slot {
39+
u8 key_id;
40+
41+
struct crypto_aead *encrypt;
42+
struct crypto_aead *decrypt;
43+
u8 nonce_tail_xmit[OVPN_NONCE_TAIL_SIZE];
44+
u8 nonce_tail_recv[OVPN_NONCE_TAIL_SIZE];
45+
46+
struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp;
47+
struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp;
48+
struct kref refcount;
49+
struct rcu_head rcu;
50+
};
51+
52+
struct ovpn_crypto_state {
53+
struct ovpn_crypto_key_slot __rcu *slots[2];
54+
u8 primary_idx;
55+
56+
/* protects primary and secondary slots */
57+
spinlock_t lock;
58+
};
59+
60+
static inline bool ovpn_crypto_key_slot_hold(struct ovpn_crypto_key_slot *ks)
61+
{
62+
return kref_get_unless_zero(&ks->refcount);
63+
}
64+
65+
static inline void ovpn_crypto_state_init(struct ovpn_crypto_state *cs)
66+
{
67+
RCU_INIT_POINTER(cs->slots[0], NULL);
68+
RCU_INIT_POINTER(cs->slots[1], NULL);
69+
cs->primary_idx = 0;
70+
spin_lock_init(&cs->lock);
71+
}
72+
73+
static inline struct ovpn_crypto_key_slot *
74+
ovpn_crypto_key_id_to_slot(const struct ovpn_crypto_state *cs, u8 key_id)
75+
{
76+
struct ovpn_crypto_key_slot *ks;
77+
u8 idx;
78+
79+
if (unlikely(!cs))
80+
return NULL;
81+
82+
rcu_read_lock();
83+
idx = READ_ONCE(cs->primary_idx);
84+
ks = rcu_dereference(cs->slots[idx]);
85+
if (ks && ks->key_id == key_id) {
86+
if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
87+
ks = NULL;
88+
goto out;
89+
}
90+
91+
ks = rcu_dereference(cs->slots[!idx]);
92+
if (ks && ks->key_id == key_id) {
93+
if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
94+
ks = NULL;
95+
goto out;
96+
}
97+
98+
/* when both key slots are occupied but no matching key ID is found, ks
99+
* has to be reset to NULL to avoid carrying a stale pointer
100+
*/
101+
ks = NULL;
102+
out:
103+
rcu_read_unlock();
104+
105+
return ks;
106+
}
107+
108+
static inline struct ovpn_crypto_key_slot *
109+
ovpn_crypto_key_slot_primary(const struct ovpn_crypto_state *cs)
110+
{
111+
struct ovpn_crypto_key_slot *ks;
112+
113+
rcu_read_lock();
114+
ks = rcu_dereference(cs->slots[cs->primary_idx]);
115+
if (unlikely(ks && !ovpn_crypto_key_slot_hold(ks)))
116+
ks = NULL;
117+
rcu_read_unlock();
118+
119+
return ks;
120+
}
121+
122+
void ovpn_crypto_key_slot_release(struct kref *kref);
123+
124+
static inline void ovpn_crypto_key_slot_put(struct ovpn_crypto_key_slot *ks)
125+
{
126+
kref_put(&ks->refcount, ovpn_crypto_key_slot_release);
127+
}
128+
129+
int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
130+
const struct ovpn_peer_key_reset *pkr);
131+
132+
void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
133+
enum ovpn_key_slot slot);
134+
135+
void ovpn_crypto_state_release(struct ovpn_crypto_state *cs);
136+
137+
void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs);
138+
139+
#endif /* _NET_OVPN_OVPNCRYPTO_H_ */

0 commit comments

Comments
 (0)