99a2dd955f
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>
310 lines
6.4 KiB
C
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_ */
|