xref: /dpdk/lib/ipsec/ipsec_sqn.h (revision 27779857f474be4eb82367804963c19322b2de44)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2018 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #ifndef _IPSEC_SQN_H_
699a2dd95SBruce Richardson #define _IPSEC_SQN_H_
799a2dd95SBruce Richardson 
899a2dd95SBruce Richardson #define WINDOW_BUCKET_BITS		6 /* uint64_t */
999a2dd95SBruce Richardson #define WINDOW_BUCKET_SIZE		(1 << WINDOW_BUCKET_BITS)
1099a2dd95SBruce Richardson #define WINDOW_BIT_LOC_MASK		(WINDOW_BUCKET_SIZE - 1)
1199a2dd95SBruce Richardson 
1299a2dd95SBruce Richardson /* minimum number of bucket, power of 2*/
1399a2dd95SBruce Richardson #define WINDOW_BUCKET_MIN		2
1499a2dd95SBruce Richardson #define WINDOW_BUCKET_MAX		(INT16_MAX + 1)
1599a2dd95SBruce Richardson 
1699a2dd95SBruce Richardson #define IS_ESN(sa)	((sa)->sqn_mask == UINT64_MAX)
1799a2dd95SBruce Richardson 
1899a2dd95SBruce Richardson #define	SQN_ATOMIC(sa)	((sa)->type & RTE_IPSEC_SATP_SQN_ATOM)
1999a2dd95SBruce Richardson 
2099a2dd95SBruce Richardson /*
2199a2dd95SBruce Richardson  * gets SQN.hi32 bits, SQN supposed to be in network byte order.
2299a2dd95SBruce Richardson  */
2399a2dd95SBruce Richardson static inline rte_be32_t
sqn_hi32(rte_be64_t sqn)2499a2dd95SBruce Richardson sqn_hi32(rte_be64_t sqn)
2599a2dd95SBruce Richardson {
2699a2dd95SBruce Richardson #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
2799a2dd95SBruce Richardson 	return (sqn >> 32);
2899a2dd95SBruce Richardson #else
2999a2dd95SBruce Richardson 	return sqn;
3099a2dd95SBruce Richardson #endif
3199a2dd95SBruce Richardson }
3299a2dd95SBruce Richardson 
3399a2dd95SBruce Richardson /*
3499a2dd95SBruce Richardson  * gets SQN.low32 bits, SQN supposed to be in network byte order.
3599a2dd95SBruce Richardson  */
3699a2dd95SBruce Richardson static inline rte_be32_t
sqn_low32(rte_be64_t sqn)3799a2dd95SBruce Richardson sqn_low32(rte_be64_t sqn)
3899a2dd95SBruce Richardson {
3999a2dd95SBruce Richardson #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
4099a2dd95SBruce Richardson 	return sqn;
4199a2dd95SBruce Richardson #else
4299a2dd95SBruce Richardson 	return (sqn >> 32);
4399a2dd95SBruce Richardson #endif
4499a2dd95SBruce Richardson }
4599a2dd95SBruce Richardson 
4699a2dd95SBruce Richardson /*
4799a2dd95SBruce Richardson  * gets SQN.low16 bits, SQN supposed to be in network byte order.
4899a2dd95SBruce Richardson  */
4999a2dd95SBruce Richardson static inline rte_be16_t
sqn_low16(rte_be64_t sqn)5099a2dd95SBruce Richardson sqn_low16(rte_be64_t sqn)
5199a2dd95SBruce Richardson {
5299a2dd95SBruce Richardson #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
5399a2dd95SBruce Richardson 	return sqn;
5499a2dd95SBruce Richardson #else
5599a2dd95SBruce Richardson 	return (sqn >> 48);
5699a2dd95SBruce Richardson #endif
5799a2dd95SBruce Richardson }
5899a2dd95SBruce Richardson 
5999a2dd95SBruce Richardson /*
6099a2dd95SBruce Richardson  * According to RFC4303 A2.1, determine the high-order bit of sequence number.
6199a2dd95SBruce Richardson  * use 32bit arithmetic inside, return uint64_t.
6299a2dd95SBruce Richardson  */
6399a2dd95SBruce Richardson static inline uint64_t
reconstruct_esn(uint64_t t,uint32_t sqn,uint32_t w)6499a2dd95SBruce Richardson reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
6599a2dd95SBruce Richardson {
6699a2dd95SBruce Richardson 	uint32_t th, tl, bl;
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson 	tl = t;
6999a2dd95SBruce Richardson 	th = t >> 32;
7099a2dd95SBruce Richardson 	bl = tl - w + 1;
7199a2dd95SBruce Richardson 
7299a2dd95SBruce Richardson 	/* case A: window is within one sequence number subspace */
7399a2dd95SBruce Richardson 	if (tl >= (w - 1))
7499a2dd95SBruce Richardson 		th += (sqn < bl);
7599a2dd95SBruce Richardson 	/* case B: window spans two sequence number subspaces */
7699a2dd95SBruce Richardson 	else if (th != 0)
7799a2dd95SBruce Richardson 		th -= (sqn >= bl);
7899a2dd95SBruce Richardson 
7999a2dd95SBruce Richardson 	/* return constructed sequence with proper high-order bits */
8099a2dd95SBruce Richardson 	return (uint64_t)th << 32 | sqn;
8199a2dd95SBruce Richardson }
8299a2dd95SBruce Richardson 
8399a2dd95SBruce Richardson /**
8499a2dd95SBruce Richardson  * Perform the replay checking.
8599a2dd95SBruce Richardson  *
8699a2dd95SBruce Richardson  * struct rte_ipsec_sa contains the window and window related parameters,
8799a2dd95SBruce Richardson  * such as the window size, bitmask, and the last acknowledged sequence number.
8899a2dd95SBruce Richardson  *
8999a2dd95SBruce Richardson  * Based on RFC 6479.
9099a2dd95SBruce Richardson  * Blocks are 64 bits unsigned integers
9199a2dd95SBruce Richardson  */
9299a2dd95SBruce Richardson static inline int32_t
esn_inb_check_sqn(const struct replay_sqn * rsn,const struct rte_ipsec_sa * sa,uint64_t sqn)9399a2dd95SBruce Richardson esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
9499a2dd95SBruce Richardson 	uint64_t sqn)
9599a2dd95SBruce Richardson {
9699a2dd95SBruce Richardson 	uint32_t bit, bucket;
9799a2dd95SBruce Richardson 
9899a2dd95SBruce Richardson 	/* replay not enabled */
9999a2dd95SBruce Richardson 	if (sa->replay.win_sz == 0)
10099a2dd95SBruce Richardson 		return 0;
10199a2dd95SBruce Richardson 
10299a2dd95SBruce Richardson 	/* seq is larger than lastseq */
10399a2dd95SBruce Richardson 	if (sqn > rsn->sqn)
10499a2dd95SBruce Richardson 		return 0;
10599a2dd95SBruce Richardson 
10699a2dd95SBruce Richardson 	/* seq is outside window */
10799a2dd95SBruce Richardson 	if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
10899a2dd95SBruce Richardson 		return -EINVAL;
10999a2dd95SBruce Richardson 
11099a2dd95SBruce Richardson 	/* seq is inside the window */
11199a2dd95SBruce Richardson 	bit = sqn & WINDOW_BIT_LOC_MASK;
11299a2dd95SBruce Richardson 	bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
11399a2dd95SBruce Richardson 
11499a2dd95SBruce Richardson 	/* already seen packet */
11599a2dd95SBruce Richardson 	if (rsn->window[bucket] & ((uint64_t)1 << bit))
11699a2dd95SBruce Richardson 		return -EINVAL;
11799a2dd95SBruce Richardson 
11899a2dd95SBruce Richardson 	return 0;
11999a2dd95SBruce Richardson }
12099a2dd95SBruce Richardson 
12199a2dd95SBruce Richardson /**
12299a2dd95SBruce Richardson  * For outbound SA perform the sequence number update.
12399a2dd95SBruce Richardson  */
12499a2dd95SBruce Richardson static inline uint64_t
esn_outb_update_sqn(struct rte_ipsec_sa * sa,uint32_t * num)12599a2dd95SBruce Richardson esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
12699a2dd95SBruce Richardson {
12799a2dd95SBruce Richardson 	uint64_t n, s, sqn;
12899a2dd95SBruce Richardson 
12999a2dd95SBruce Richardson 	n = *num;
13099a2dd95SBruce Richardson 	if (SQN_ATOMIC(sa))
131*27779857STyler Retzlaff 		sqn = rte_atomic_fetch_add_explicit(&sa->sqn.outb, n, rte_memory_order_relaxed) + n;
13299a2dd95SBruce Richardson 	else {
13399a2dd95SBruce Richardson 		sqn = sa->sqn.outb + n;
13499a2dd95SBruce Richardson 		sa->sqn.outb = sqn;
13599a2dd95SBruce Richardson 	}
13699a2dd95SBruce Richardson 
13799a2dd95SBruce Richardson 	/* overflow */
13899a2dd95SBruce Richardson 	if (sqn > sa->sqn_mask) {
13999a2dd95SBruce Richardson 		s = sqn - sa->sqn_mask;
14099a2dd95SBruce Richardson 		*num = (s < n) ?  n - s : 0;
14199a2dd95SBruce Richardson 	}
14299a2dd95SBruce Richardson 
14399a2dd95SBruce Richardson 	return sqn - n;
14499a2dd95SBruce Richardson }
14599a2dd95SBruce Richardson 
14699a2dd95SBruce Richardson /**
14799a2dd95SBruce Richardson  * For inbound SA perform the sequence number and replay window update.
14899a2dd95SBruce Richardson  */
14999a2dd95SBruce Richardson static inline int32_t
esn_inb_update_sqn(struct replay_sqn * rsn,const struct rte_ipsec_sa * sa,uint64_t sqn)15099a2dd95SBruce Richardson esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
15199a2dd95SBruce Richardson 	uint64_t sqn)
15299a2dd95SBruce Richardson {
15399a2dd95SBruce Richardson 	uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
15499a2dd95SBruce Richardson 
15599a2dd95SBruce Richardson 	/* handle ESN */
15699a2dd95SBruce Richardson 	if (IS_ESN(sa))
15799a2dd95SBruce Richardson 		sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
15899a2dd95SBruce Richardson 
15999a2dd95SBruce Richardson 	/* seq is outside window*/
16099a2dd95SBruce Richardson 	if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
16199a2dd95SBruce Richardson 		return -EINVAL;
16299a2dd95SBruce Richardson 
16399a2dd95SBruce Richardson 	/* update the bit */
16499a2dd95SBruce Richardson 	bucket = (sqn >> WINDOW_BUCKET_BITS);
16599a2dd95SBruce Richardson 
16699a2dd95SBruce Richardson 	/* check if the seq is within the range */
16799a2dd95SBruce Richardson 	if (sqn > rsn->sqn) {
16899a2dd95SBruce Richardson 		last_bucket = rsn->sqn >> WINDOW_BUCKET_BITS;
16999a2dd95SBruce Richardson 		diff = bucket - last_bucket;
17099a2dd95SBruce Richardson 		/* seq is way after the range of WINDOW_SIZE */
17199a2dd95SBruce Richardson 		if (diff > sa->replay.nb_bucket)
17299a2dd95SBruce Richardson 			diff = sa->replay.nb_bucket;
17399a2dd95SBruce Richardson 
17499a2dd95SBruce Richardson 		for (i = 0; i != diff; i++) {
17599a2dd95SBruce Richardson 			new_bucket = (i + last_bucket + 1) &
17699a2dd95SBruce Richardson 				sa->replay.bucket_index_mask;
17799a2dd95SBruce Richardson 			rsn->window[new_bucket] = 0;
17899a2dd95SBruce Richardson 		}
17999a2dd95SBruce Richardson 		rsn->sqn = sqn;
18099a2dd95SBruce Richardson 	}
18199a2dd95SBruce Richardson 
18299a2dd95SBruce Richardson 	bucket &= sa->replay.bucket_index_mask;
18399a2dd95SBruce Richardson 	bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
18499a2dd95SBruce Richardson 
18599a2dd95SBruce Richardson 	/* already seen packet */
18699a2dd95SBruce Richardson 	if (rsn->window[bucket] & bit)
18799a2dd95SBruce Richardson 		return -EINVAL;
18899a2dd95SBruce Richardson 
18999a2dd95SBruce Richardson 	rsn->window[bucket] |= bit;
19099a2dd95SBruce Richardson 	return 0;
19199a2dd95SBruce Richardson }
19299a2dd95SBruce Richardson 
19399a2dd95SBruce Richardson /**
19499a2dd95SBruce Richardson  * To achieve ability to do multiple readers single writer for
19599a2dd95SBruce Richardson  * SA replay window information and sequence number (RSN)
19699a2dd95SBruce Richardson  * basic RCU schema is used:
19799a2dd95SBruce Richardson  * SA have 2 copies of RSN (one for readers, another for writers).
19899a2dd95SBruce Richardson  * Each RSN contains a rwlock that has to be grabbed (for read/write)
19999a2dd95SBruce Richardson  * to avoid races between readers and writer.
20099a2dd95SBruce Richardson  * Writer is responsible to make a copy or reader RSN, update it
20199a2dd95SBruce Richardson  * and mark newly updated RSN as readers one.
20299a2dd95SBruce Richardson  * That approach is intended to minimize contention and cache sharing
20399a2dd95SBruce Richardson  * between writer and readers.
20499a2dd95SBruce Richardson  */
20599a2dd95SBruce Richardson 
20699a2dd95SBruce Richardson /**
20799a2dd95SBruce Richardson  * Copy replay window and SQN.
20899a2dd95SBruce Richardson  */
20999a2dd95SBruce Richardson static inline void
rsn_copy(const struct rte_ipsec_sa * sa,uint32_t dst,uint32_t src)21099a2dd95SBruce Richardson rsn_copy(const struct rte_ipsec_sa *sa, uint32_t dst, uint32_t src)
21199a2dd95SBruce Richardson {
21299a2dd95SBruce Richardson 	uint32_t i, n;
21399a2dd95SBruce Richardson 	struct replay_sqn *d;
21499a2dd95SBruce Richardson 	const struct replay_sqn *s;
21599a2dd95SBruce Richardson 
21699a2dd95SBruce Richardson 	d = sa->sqn.inb.rsn[dst];
21799a2dd95SBruce Richardson 	s = sa->sqn.inb.rsn[src];
21899a2dd95SBruce Richardson 
21999a2dd95SBruce Richardson 	n = sa->replay.nb_bucket;
22099a2dd95SBruce Richardson 
22199a2dd95SBruce Richardson 	d->sqn = s->sqn;
22299a2dd95SBruce Richardson 	for (i = 0; i != n; i++)
22399a2dd95SBruce Richardson 		d->window[i] = s->window[i];
22499a2dd95SBruce Richardson }
22599a2dd95SBruce Richardson 
22699a2dd95SBruce Richardson /**
22799a2dd95SBruce Richardson  * Get RSN for read-only access.
22899a2dd95SBruce Richardson  */
22999a2dd95SBruce Richardson static inline struct replay_sqn *
rsn_acquire(struct rte_ipsec_sa * sa)23099a2dd95SBruce Richardson rsn_acquire(struct rte_ipsec_sa *sa)
23199a2dd95SBruce Richardson {
23299a2dd95SBruce Richardson 	uint32_t n;
23399a2dd95SBruce Richardson 	struct replay_sqn *rsn;
23499a2dd95SBruce Richardson 
23599a2dd95SBruce Richardson 	n = sa->sqn.inb.rdidx;
23699a2dd95SBruce Richardson 	rsn = sa->sqn.inb.rsn[n];
23799a2dd95SBruce Richardson 
23899a2dd95SBruce Richardson 	if (!SQN_ATOMIC(sa))
23999a2dd95SBruce Richardson 		return rsn;
24099a2dd95SBruce Richardson 
24199a2dd95SBruce Richardson 	/* check there are no writers */
24299a2dd95SBruce Richardson 	while (rte_rwlock_read_trylock(&rsn->rwl) < 0) {
24399a2dd95SBruce Richardson 		rte_pause();
24499a2dd95SBruce Richardson 		n = sa->sqn.inb.rdidx;
24599a2dd95SBruce Richardson 		rsn = sa->sqn.inb.rsn[n];
24699a2dd95SBruce Richardson 		rte_compiler_barrier();
24799a2dd95SBruce Richardson 	}
24899a2dd95SBruce Richardson 
24999a2dd95SBruce Richardson 	return rsn;
25099a2dd95SBruce Richardson }
25199a2dd95SBruce Richardson 
25299a2dd95SBruce Richardson /**
25399a2dd95SBruce Richardson  * Release read-only access for RSN.
25499a2dd95SBruce Richardson  */
25599a2dd95SBruce Richardson static inline void
rsn_release(struct rte_ipsec_sa * sa,struct replay_sqn * rsn)25699a2dd95SBruce Richardson rsn_release(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
25799a2dd95SBruce Richardson {
25899a2dd95SBruce Richardson 	if (SQN_ATOMIC(sa))
25999a2dd95SBruce Richardson 		rte_rwlock_read_unlock(&rsn->rwl);
26099a2dd95SBruce Richardson }
26199a2dd95SBruce Richardson 
26299a2dd95SBruce Richardson /**
26399a2dd95SBruce Richardson  * Start RSN update.
26499a2dd95SBruce Richardson  */
26599a2dd95SBruce Richardson static inline struct replay_sqn *
rsn_update_start(struct rte_ipsec_sa * sa)26699a2dd95SBruce Richardson rsn_update_start(struct rte_ipsec_sa *sa)
26799a2dd95SBruce Richardson {
26899a2dd95SBruce Richardson 	uint32_t k, n;
26999a2dd95SBruce Richardson 	struct replay_sqn *rsn;
27099a2dd95SBruce Richardson 
27199a2dd95SBruce Richardson 	n = sa->sqn.inb.wridx;
27299a2dd95SBruce Richardson 
27399a2dd95SBruce Richardson 	/* no active writers */
27499a2dd95SBruce Richardson 	RTE_ASSERT(n == sa->sqn.inb.rdidx);
27599a2dd95SBruce Richardson 
27699a2dd95SBruce Richardson 	if (!SQN_ATOMIC(sa))
27799a2dd95SBruce Richardson 		return sa->sqn.inb.rsn[n];
27899a2dd95SBruce Richardson 
27999a2dd95SBruce Richardson 	k = REPLAY_SQN_NEXT(n);
28099a2dd95SBruce Richardson 	sa->sqn.inb.wridx = k;
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 	rsn = sa->sqn.inb.rsn[k];
28399a2dd95SBruce Richardson 	rte_rwlock_write_lock(&rsn->rwl);
28499a2dd95SBruce Richardson 	rsn_copy(sa, k, n);
28599a2dd95SBruce Richardson 
28699a2dd95SBruce Richardson 	return rsn;
28799a2dd95SBruce Richardson }
28899a2dd95SBruce Richardson 
28999a2dd95SBruce Richardson /**
29099a2dd95SBruce Richardson  * Finish RSN update.
29199a2dd95SBruce Richardson  */
29299a2dd95SBruce Richardson static inline void
rsn_update_finish(struct rte_ipsec_sa * sa,struct replay_sqn * rsn)29399a2dd95SBruce Richardson rsn_update_finish(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
29499a2dd95SBruce Richardson {
29599a2dd95SBruce Richardson 	uint32_t n;
29699a2dd95SBruce Richardson 
29799a2dd95SBruce Richardson 	if (!SQN_ATOMIC(sa))
29899a2dd95SBruce Richardson 		return;
29999a2dd95SBruce Richardson 
30099a2dd95SBruce Richardson 	n = sa->sqn.inb.wridx;
30199a2dd95SBruce Richardson 	RTE_ASSERT(n != sa->sqn.inb.rdidx);
30299a2dd95SBruce Richardson 	RTE_ASSERT(rsn == sa->sqn.inb.rsn[n]);
30399a2dd95SBruce Richardson 
30499a2dd95SBruce Richardson 	rte_rwlock_write_unlock(&rsn->rwl);
30599a2dd95SBruce Richardson 	sa->sqn.inb.rdidx = n;
30699a2dd95SBruce Richardson }
30799a2dd95SBruce Richardson 
30899a2dd95SBruce Richardson 
30999a2dd95SBruce Richardson #endif /* _IPSEC_SQN_H_ */
310