root/drivers/net/ovpn/pktid.h
/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel offload
 *
 *  Copyright (C) 2020-2025 OpenVPN, Inc.
 *
 *  Author:     Antonio Quartulli <antonio@openvpn.net>
 *              James Yonan <james@openvpn.net>
 */

#ifndef _NET_OVPN_OVPNPKTID_H_
#define _NET_OVPN_OVPNPKTID_H_

#include "proto.h"

/* If no packets received for this length of time, set a backtrack floor
 * at highest received packet ID thus far.
 */
#define PKTID_RECV_EXPIRE (30 * HZ)

/* Packet-ID state for transmitter */
struct ovpn_pktid_xmit {
        atomic_t seq_num;
};

/* replay window sizing in bytes = 2^REPLAY_WINDOW_ORDER */
#define REPLAY_WINDOW_ORDER 8

#define REPLAY_WINDOW_BYTES BIT(REPLAY_WINDOW_ORDER)
#define REPLAY_WINDOW_SIZE  (REPLAY_WINDOW_BYTES * 8)
#define REPLAY_INDEX(base, i) (((base) + (i)) & (REPLAY_WINDOW_SIZE - 1))

/* Packet-ID state for receiver.
 * Other than lock member, can be zeroed to initialize.
 */
struct ovpn_pktid_recv {
        /* "sliding window" bitmask of recent packet IDs received */
        DECLARE_BITMAP(history, REPLAY_WINDOW_SIZE);
        /* bit position of deque base in history */
        unsigned int base;
        /* extent (in bits) of deque in history */
        unsigned int extent;
        /* expiration of history in jiffies */
        unsigned long expire;
        /* highest sequence number received */
        u32 id;
        /* highest time stamp received */
        u32 time;
        /* we will only accept backtrack IDs > id_floor */
        u32 id_floor;
        unsigned int max_backtrack;
        /* protects entire pktd ID state */
        spinlock_t lock;
};

/* Get the next packet ID for xmit */
static inline int ovpn_pktid_xmit_next(struct ovpn_pktid_xmit *pid, u32 *pktid)
{
        const u32 seq_num = atomic_fetch_add_unless(&pid->seq_num, 1, 0);
        /* when the 32bit space is over, we return an error because the packet
         * ID is used to create the cipher IV and we do not want to reuse the
         * same value more than once
         */
        if (unlikely(!seq_num))
                return -ERANGE;

        *pktid = seq_num;

        return 0;
}

/* Write 12-byte AEAD IV to dest */
static inline void ovpn_pktid_aead_write(const u32 pktid,
                                         const u8 nt[],
                                         unsigned char *dest)
{
        *(__force __be32 *)(dest) = htonl(pktid);
        BUILD_BUG_ON(4 + OVPN_NONCE_TAIL_SIZE != OVPN_NONCE_SIZE);
        memcpy(dest + 4, nt, OVPN_NONCE_TAIL_SIZE);
}

void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid);
void ovpn_pktid_recv_init(struct ovpn_pktid_recv *pr);

int ovpn_pktid_recv(struct ovpn_pktid_recv *pr, u32 pkt_id, u32 pkt_time);

#endif /* _NET_OVPN_OVPNPKTID_H_ */