numam-dpdk/lib/ipsec/ipsec_sqn.h
Bruce Richardson 99a2dd955f lib: remove librte_ prefix from directory names
There is no reason for the DPDK libraries to all have 'librte_' prefix on
the directory names. This prefix makes the directory names longer and also
makes it awkward to add features referring to individual libraries in the
build - should the lib names be specified with or without the prefix.
Therefore, we can just remove the library prefix and use the library's
unique name as the directory name, i.e. 'eal' rather than 'librte_eal'

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
2021-04-21 14:04:09 +02:00

310 lines
6.4 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2018 Intel Corporation
*/
#ifndef _IPSEC_SQN_H_
#define _IPSEC_SQN_H_
#define WINDOW_BUCKET_BITS 6 /* uint64_t */
#define WINDOW_BUCKET_SIZE (1 << WINDOW_BUCKET_BITS)
#define WINDOW_BIT_LOC_MASK (WINDOW_BUCKET_SIZE - 1)
/* minimum number of bucket, power of 2*/
#define WINDOW_BUCKET_MIN 2
#define WINDOW_BUCKET_MAX (INT16_MAX + 1)
#define IS_ESN(sa) ((sa)->sqn_mask == UINT64_MAX)
#define SQN_ATOMIC(sa) ((sa)->type & RTE_IPSEC_SATP_SQN_ATOM)
/*
* gets SQN.hi32 bits, SQN supposed to be in network byte order.
*/
static inline rte_be32_t
sqn_hi32(rte_be64_t sqn)
{
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
return (sqn >> 32);
#else
return sqn;
#endif
}
/*
* gets SQN.low32 bits, SQN supposed to be in network byte order.
*/
static inline rte_be32_t
sqn_low32(rte_be64_t sqn)
{
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
return sqn;
#else
return (sqn >> 32);
#endif
}
/*
* gets SQN.low16 bits, SQN supposed to be in network byte order.
*/
static inline rte_be16_t
sqn_low16(rte_be64_t sqn)
{
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
return sqn;
#else
return (sqn >> 48);
#endif
}
/*
* According to RFC4303 A2.1, determine the high-order bit of sequence number.
* use 32bit arithmetic inside, return uint64_t.
*/
static inline uint64_t
reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
{
uint32_t th, tl, bl;
tl = t;
th = t >> 32;
bl = tl - w + 1;
/* case A: window is within one sequence number subspace */
if (tl >= (w - 1))
th += (sqn < bl);
/* case B: window spans two sequence number subspaces */
else if (th != 0)
th -= (sqn >= bl);
/* return constructed sequence with proper high-order bits */
return (uint64_t)th << 32 | sqn;
}
/**
* Perform the replay checking.
*
* struct rte_ipsec_sa contains the window and window related parameters,
* such as the window size, bitmask, and the last acknowledged sequence number.
*
* Based on RFC 6479.
* Blocks are 64 bits unsigned integers
*/
static inline int32_t
esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
uint64_t sqn)
{
uint32_t bit, bucket;
/* replay not enabled */
if (sa->replay.win_sz == 0)
return 0;
/* seq is larger than lastseq */
if (sqn > rsn->sqn)
return 0;
/* seq is outside window */
if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
return -EINVAL;
/* seq is inside the window */
bit = sqn & WINDOW_BIT_LOC_MASK;
bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
/* already seen packet */
if (rsn->window[bucket] & ((uint64_t)1 << bit))
return -EINVAL;
return 0;
}
/**
* For outbound SA perform the sequence number update.
*/
static inline uint64_t
esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
{
uint64_t n, s, sqn;
n = *num;
if (SQN_ATOMIC(sa))
sqn = __atomic_add_fetch(&sa->sqn.outb, n, __ATOMIC_RELAXED);
else {
sqn = sa->sqn.outb + n;
sa->sqn.outb = sqn;
}
/* overflow */
if (sqn > sa->sqn_mask) {
s = sqn - sa->sqn_mask;
*num = (s < n) ? n - s : 0;
}
return sqn - n;
}
/**
* For inbound SA perform the sequence number and replay window update.
*/
static inline int32_t
esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
uint64_t sqn)
{
uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
/* handle ESN */
if (IS_ESN(sa))
sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
/* seq is outside window*/
if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
return -EINVAL;
/* update the bit */
bucket = (sqn >> WINDOW_BUCKET_BITS);
/* check if the seq is within the range */
if (sqn > rsn->sqn) {
last_bucket = rsn->sqn >> WINDOW_BUCKET_BITS;
diff = bucket - last_bucket;
/* seq is way after the range of WINDOW_SIZE */
if (diff > sa->replay.nb_bucket)
diff = sa->replay.nb_bucket;
for (i = 0; i != diff; i++) {
new_bucket = (i + last_bucket + 1) &
sa->replay.bucket_index_mask;
rsn->window[new_bucket] = 0;
}
rsn->sqn = sqn;
}
bucket &= sa->replay.bucket_index_mask;
bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
/* already seen packet */
if (rsn->window[bucket] & bit)
return -EINVAL;
rsn->window[bucket] |= bit;
return 0;
}
/**
* To achieve ability to do multiple readers single writer for
* SA replay window information and sequence number (RSN)
* basic RCU schema is used:
* SA have 2 copies of RSN (one for readers, another for writers).
* Each RSN contains a rwlock that has to be grabbed (for read/write)
* to avoid races between readers and writer.
* Writer is responsible to make a copy or reader RSN, update it
* and mark newly updated RSN as readers one.
* That approach is intended to minimize contention and cache sharing
* between writer and readers.
*/
/**
* Copy replay window and SQN.
*/
static inline void
rsn_copy(const struct rte_ipsec_sa *sa, uint32_t dst, uint32_t src)
{
uint32_t i, n;
struct replay_sqn *d;
const struct replay_sqn *s;
d = sa->sqn.inb.rsn[dst];
s = sa->sqn.inb.rsn[src];
n = sa->replay.nb_bucket;
d->sqn = s->sqn;
for (i = 0; i != n; i++)
d->window[i] = s->window[i];
}
/**
* Get RSN for read-only access.
*/
static inline struct replay_sqn *
rsn_acquire(struct rte_ipsec_sa *sa)
{
uint32_t n;
struct replay_sqn *rsn;
n = sa->sqn.inb.rdidx;
rsn = sa->sqn.inb.rsn[n];
if (!SQN_ATOMIC(sa))
return rsn;
/* check there are no writers */
while (rte_rwlock_read_trylock(&rsn->rwl) < 0) {
rte_pause();
n = sa->sqn.inb.rdidx;
rsn = sa->sqn.inb.rsn[n];
rte_compiler_barrier();
}
return rsn;
}
/**
* Release read-only access for RSN.
*/
static inline void
rsn_release(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
{
if (SQN_ATOMIC(sa))
rte_rwlock_read_unlock(&rsn->rwl);
}
/**
* Start RSN update.
*/
static inline struct replay_sqn *
rsn_update_start(struct rte_ipsec_sa *sa)
{
uint32_t k, n;
struct replay_sqn *rsn;
n = sa->sqn.inb.wridx;
/* no active writers */
RTE_ASSERT(n == sa->sqn.inb.rdidx);
if (!SQN_ATOMIC(sa))
return sa->sqn.inb.rsn[n];
k = REPLAY_SQN_NEXT(n);
sa->sqn.inb.wridx = k;
rsn = sa->sqn.inb.rsn[k];
rte_rwlock_write_lock(&rsn->rwl);
rsn_copy(sa, k, n);
return rsn;
}
/**
* Finish RSN update.
*/
static inline void
rsn_update_finish(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
{
uint32_t n;
if (!SQN_ATOMIC(sa))
return;
n = sa->sqn.inb.wridx;
RTE_ASSERT(n != sa->sqn.inb.rdidx);
RTE_ASSERT(rsn == sa->sqn.inb.rsn[n]);
rte_rwlock_write_unlock(&rsn->rwl);
sa->sqn.inb.rdidx = n;
}
#endif /* _IPSEC_SQN_H_ */