1 // SPDX-License-Identifier: GPL-2.0 2 /* OpenVPN data channel offload 3 * 4 * Copyright (C) 2020-2025 OpenVPN, Inc. 5 * 6 * Author: Antonio Quartulli <antonio@openvpn.net> 7 * James Yonan <james@openvpn.net> 8 */ 9 10 #include <linux/atomic.h> 11 #include <linux/jiffies.h> 12 #include <linux/net.h> 13 #include <linux/netdevice.h> 14 #include <linux/types.h> 15 16 #include "ovpnpriv.h" 17 #include "main.h" 18 #include "pktid.h" 19 20 void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid) 21 { 22 atomic_set(&pid->seq_num, 1); 23 } 24 25 void ovpn_pktid_recv_init(struct ovpn_pktid_recv *pr) 26 { 27 memset(pr, 0, sizeof(*pr)); 28 spin_lock_init(&pr->lock); 29 } 30 31 /* Packet replay detection. 32 * Allows ID backtrack of up to REPLAY_WINDOW_SIZE - 1. 33 */ 34 int ovpn_pktid_recv(struct ovpn_pktid_recv *pr, u32 pkt_id, u32 pkt_time) 35 { 36 const unsigned long now = jiffies; 37 int ret; 38 39 /* ID must not be zero */ 40 if (unlikely(pkt_id == 0)) 41 return -EINVAL; 42 43 spin_lock_bh(&pr->lock); 44 45 /* expire backtracks at or below pr->id after PKTID_RECV_EXPIRE time */ 46 if (unlikely(time_after_eq(now, pr->expire))) 47 pr->id_floor = pr->id; 48 49 /* time changed? */ 50 if (unlikely(pkt_time != pr->time)) { 51 if (pkt_time > pr->time) { 52 /* time moved forward, accept */ 53 pr->base = 0; 54 pr->extent = 0; 55 pr->id = 0; 56 pr->time = pkt_time; 57 pr->id_floor = 0; 58 } else { 59 /* time moved backward, reject */ 60 ret = -ETIME; 61 goto out; 62 } 63 } 64 65 if (likely(pkt_id == pr->id + 1)) { 66 /* well-formed ID sequence (incremented by 1) */ 67 pr->base = REPLAY_INDEX(pr->base, -1); 68 pr->history[pr->base / 8] |= (1 << (pr->base % 8)); 69 if (pr->extent < REPLAY_WINDOW_SIZE) 70 ++pr->extent; 71 pr->id = pkt_id; 72 } else if (pkt_id > pr->id) { 73 /* ID jumped forward by more than one */ 74 const unsigned int delta = pkt_id - pr->id; 75 76 if (delta < REPLAY_WINDOW_SIZE) { 77 unsigned int i; 78 79 pr->base = REPLAY_INDEX(pr->base, -delta); 80 pr->history[pr->base / 8] |= (1 << (pr->base % 8)); 81 pr->extent += delta; 82 if (pr->extent > REPLAY_WINDOW_SIZE) 83 pr->extent = REPLAY_WINDOW_SIZE; 84 for (i = 1; i < delta; ++i) { 85 unsigned int newb = REPLAY_INDEX(pr->base, i); 86 87 pr->history[newb / 8] &= ~BIT(newb % 8); 88 } 89 } else { 90 pr->base = 0; 91 pr->extent = REPLAY_WINDOW_SIZE; 92 memset(pr->history, 0, sizeof(pr->history)); 93 pr->history[0] = 1; 94 } 95 pr->id = pkt_id; 96 } else { 97 /* ID backtrack */ 98 const unsigned int delta = pr->id - pkt_id; 99 100 if (delta > pr->max_backtrack) 101 pr->max_backtrack = delta; 102 if (delta < pr->extent) { 103 if (pkt_id > pr->id_floor) { 104 const unsigned int ri = REPLAY_INDEX(pr->base, 105 delta); 106 u8 *p = &pr->history[ri / 8]; 107 const u8 mask = (1 << (ri % 8)); 108 109 if (*p & mask) { 110 ret = -EINVAL; 111 goto out; 112 } 113 *p |= mask; 114 } else { 115 ret = -EINVAL; 116 goto out; 117 } 118 } else { 119 ret = -EINVAL; 120 goto out; 121 } 122 } 123 124 pr->expire = now + PKTID_RECV_EXPIRE; 125 ret = 0; 126 out: 127 spin_unlock_bh(&pr->lock); 128 return ret; 129 } 130