// SPDX-License-Identifier: GPL-2.0 /* OpenVPN data channel offload * * Copyright (C) 2020-2025 OpenVPN, Inc. * * Author: Antonio Quartulli * James Yonan */ #include #include #include #include #include #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; }