mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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 <antonio@openvpn.net> Link: https://patch.msgid.link/20250415-b4-ovpn-v26-9-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>
129 lines
2.9 KiB
C
129 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* OpenVPN data channel offload
|
|
*
|
|
* Copyright (C) 2020-2025 OpenVPN, Inc.
|
|
*
|
|
* Author: Antonio Quartulli <antonio@openvpn.net>
|
|
* James Yonan <james@openvpn.net>
|
|
*/
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/net.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "ovpnpriv.h"
|
|
#include "main.h"
|
|
#include "pktid.h"
|
|
|
|
void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid)
|
|
{
|
|
atomic_set(&pid->seq_num, 1);
|
|
}
|
|
|
|
void ovpn_pktid_recv_init(struct ovpn_pktid_recv *pr)
|
|
{
|
|
memset(pr, 0, sizeof(*pr));
|
|
spin_lock_init(&pr->lock);
|
|
}
|
|
|
|
/* Packet replay detection.
|
|
* Allows ID backtrack of up to REPLAY_WINDOW_SIZE - 1.
|
|
*/
|
|
int ovpn_pktid_recv(struct ovpn_pktid_recv *pr, u32 pkt_id, u32 pkt_time)
|
|
{
|
|
const unsigned long now = jiffies;
|
|
int ret;
|
|
|
|
/* ID must not be zero */
|
|
if (unlikely(pkt_id == 0))
|
|
return -EINVAL;
|
|
|
|
spin_lock_bh(&pr->lock);
|
|
|
|
/* expire backtracks at or below pr->id after PKTID_RECV_EXPIRE time */
|
|
if (unlikely(time_after_eq(now, pr->expire)))
|
|
pr->id_floor = pr->id;
|
|
|
|
/* time changed? */
|
|
if (unlikely(pkt_time != pr->time)) {
|
|
if (pkt_time > pr->time) {
|
|
/* time moved forward, accept */
|
|
pr->base = 0;
|
|
pr->extent = 0;
|
|
pr->id = 0;
|
|
pr->time = pkt_time;
|
|
pr->id_floor = 0;
|
|
} else {
|
|
/* time moved backward, reject */
|
|
ret = -ETIME;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (likely(pkt_id == pr->id + 1)) {
|
|
/* well-formed ID sequence (incremented by 1) */
|
|
pr->base = REPLAY_INDEX(pr->base, -1);
|
|
pr->history[pr->base / 8] |= (1 << (pr->base % 8));
|
|
if (pr->extent < REPLAY_WINDOW_SIZE)
|
|
++pr->extent;
|
|
pr->id = pkt_id;
|
|
} else if (pkt_id > pr->id) {
|
|
/* ID jumped forward by more than one */
|
|
const unsigned int delta = pkt_id - pr->id;
|
|
|
|
if (delta < REPLAY_WINDOW_SIZE) {
|
|
unsigned int i;
|
|
|
|
pr->base = REPLAY_INDEX(pr->base, -delta);
|
|
pr->history[pr->base / 8] |= (1 << (pr->base % 8));
|
|
pr->extent += delta;
|
|
if (pr->extent > REPLAY_WINDOW_SIZE)
|
|
pr->extent = REPLAY_WINDOW_SIZE;
|
|
for (i = 1; i < delta; ++i) {
|
|
unsigned int newb = REPLAY_INDEX(pr->base, i);
|
|
|
|
pr->history[newb / 8] &= ~BIT(newb % 8);
|
|
}
|
|
} else {
|
|
pr->base = 0;
|
|
pr->extent = REPLAY_WINDOW_SIZE;
|
|
memset(pr->history, 0, sizeof(pr->history));
|
|
pr->history[0] = 1;
|
|
}
|
|
pr->id = pkt_id;
|
|
} else {
|
|
/* ID backtrack */
|
|
const unsigned int delta = pr->id - pkt_id;
|
|
|
|
if (delta > pr->max_backtrack)
|
|
pr->max_backtrack = delta;
|
|
if (delta < pr->extent) {
|
|
if (pkt_id > pr->id_floor) {
|
|
const unsigned int ri = REPLAY_INDEX(pr->base,
|
|
delta);
|
|
u8 *p = &pr->history[ri / 8];
|
|
const u8 mask = (1 << (ri % 8));
|
|
|
|
if (*p & mask) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
*p |= mask;
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
pr->expire = now + PKTID_RECV_EXPIRE;
|
|
ret = 0;
|
|
out:
|
|
spin_unlock_bh(&pr->lock);
|
|
return ret;
|
|
}
|