1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018 Intel Corporation
8 #define WINDOW_BUCKET_BITS 6 /* uint64_t */
9 #define WINDOW_BUCKET_SIZE (1 << WINDOW_BUCKET_BITS)
10 #define WINDOW_BIT_LOC_MASK (WINDOW_BUCKET_SIZE - 1)
12 /* minimum number of bucket, power of 2*/
13 #define WINDOW_BUCKET_MIN 2
14 #define WINDOW_BUCKET_MAX (INT16_MAX + 1)
16 #define IS_ESN(sa) ((sa)->sqn_mask == UINT64_MAX)
18 #define SQN_ATOMIC(sa) ((sa)->type & RTE_IPSEC_SATP_SQN_ATOM)
21 * gets SQN.hi32 bits, SQN supposed to be in network byte order.
23 static inline rte_be32_t
24 sqn_hi32(rte_be64_t sqn)
26 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
34 * gets SQN.low32 bits, SQN supposed to be in network byte order.
36 static inline rte_be32_t
37 sqn_low32(rte_be64_t sqn)
39 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
47 * gets SQN.low16 bits, SQN supposed to be in network byte order.
49 static inline rte_be16_t
50 sqn_low16(rte_be64_t sqn)
52 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
60 * for given size, calculate required number of buckets.
63 replay_num_bucket(uint32_t wsz)
67 nb = rte_align32pow2(RTE_ALIGN_MUL_CEIL(wsz, WINDOW_BUCKET_SIZE) /
69 nb = RTE_MAX(nb, (uint32_t)WINDOW_BUCKET_MIN);
75 * According to RFC4303 A2.1, determine the high-order bit of sequence number.
76 * use 32bit arithmetic inside, return uint64_t.
78 static inline uint64_t
79 reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
87 /* case A: window is within one sequence number subspace */
90 /* case B: window spans two sequence number subspaces */
94 /* return constructed sequence with proper high-order bits */
95 return (uint64_t)th << 32 | sqn;
99 * Perform the replay checking.
101 * struct rte_ipsec_sa contains the window and window related parameters,
102 * such as the window size, bitmask, and the last acknowledged sequence number.
105 * Blocks are 64 bits unsigned integers
107 static inline int32_t
108 esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
111 uint32_t bit, bucket;
113 /* replay not enabled */
114 if (sa->replay.win_sz == 0)
117 /* seq is larger than lastseq */
121 /* seq is outside window */
122 if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
125 /* seq is inside the window */
126 bit = sqn & WINDOW_BIT_LOC_MASK;
127 bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
129 /* already seen packet */
130 if (rsn->window[bucket] & ((uint64_t)1 << bit))
137 * For outbound SA perform the sequence number update.
139 static inline uint64_t
140 esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
146 sqn = (uint64_t)rte_atomic64_add_return(&sa->sqn.outb.atom, n);
148 sqn = sa->sqn.outb.raw + n;
149 sa->sqn.outb.raw = sqn;
153 if (sqn > sa->sqn_mask) {
154 s = sqn - sa->sqn_mask;
155 *num = (s < n) ? n - s : 0;
162 * For inbound SA perform the sequence number and replay window update.
164 static inline int32_t
165 esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
168 uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
170 /* replay not enabled */
171 if (sa->replay.win_sz == 0)
176 sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
178 /* seq is outside window*/
179 if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
183 bucket = (sqn >> WINDOW_BUCKET_BITS);
185 /* check if the seq is within the range */
186 if (sqn > rsn->sqn) {
187 last_bucket = rsn->sqn >> WINDOW_BUCKET_BITS;
188 diff = bucket - last_bucket;
189 /* seq is way after the range of WINDOW_SIZE */
190 if (diff > sa->replay.nb_bucket)
191 diff = sa->replay.nb_bucket;
193 for (i = 0; i != diff; i++) {
194 new_bucket = (i + last_bucket + 1) &
195 sa->replay.bucket_index_mask;
196 rsn->window[new_bucket] = 0;
201 bucket &= sa->replay.bucket_index_mask;
202 bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
204 /* already seen packet */
205 if (rsn->window[bucket] & bit)
208 rsn->window[bucket] |= bit;
213 * To achieve ability to do multiple readers single writer for
214 * SA replay window information and sequence number (RSN)
215 * basic RCU schema is used:
216 * SA have 2 copies of RSN (one for readers, another for writers).
217 * Each RSN contains a rwlock that has to be grabbed (for read/write)
218 * to avoid races between readers and writer.
219 * Writer is responsible to make a copy or reader RSN, update it
220 * and mark newly updated RSN as readers one.
221 * That approach is intended to minimize contention and cache sharing
222 * between writer and readers.
226 * Based on number of buckets calculated required size for the
227 * structure that holds replay window and sequence number (RSN) information.
230 rsn_size(uint32_t nb_bucket)
233 struct replay_sqn *rsn;
235 sz = sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0]);
236 sz = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
241 * Copy replay window and SQN.
244 rsn_copy(const struct rte_ipsec_sa *sa, uint32_t dst, uint32_t src)
247 struct replay_sqn *d;
248 const struct replay_sqn *s;
250 d = sa->sqn.inb.rsn[dst];
251 s = sa->sqn.inb.rsn[src];
253 n = sa->replay.nb_bucket;
256 for (i = 0; i != n; i++)
257 d->window[i] = s->window[i];
261 * Get RSN for read-only access.
263 static inline struct replay_sqn *
264 rsn_acquire(struct rte_ipsec_sa *sa)
267 struct replay_sqn *rsn;
269 n = sa->sqn.inb.rdidx;
270 rsn = sa->sqn.inb.rsn[n];
275 /* check there are no writers */
276 while (rte_rwlock_read_trylock(&rsn->rwl) < 0) {
278 n = sa->sqn.inb.rdidx;
279 rsn = sa->sqn.inb.rsn[n];
280 rte_compiler_barrier();
287 * Release read-only access for RSN.
290 rsn_release(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
293 rte_rwlock_read_unlock(&rsn->rwl);
299 static inline struct replay_sqn *
300 rsn_update_start(struct rte_ipsec_sa *sa)
303 struct replay_sqn *rsn;
305 n = sa->sqn.inb.wridx;
307 /* no active writers */
308 RTE_ASSERT(n == sa->sqn.inb.rdidx);
311 return sa->sqn.inb.rsn[n];
313 k = REPLAY_SQN_NEXT(n);
314 sa->sqn.inb.wridx = k;
316 rsn = sa->sqn.inb.rsn[k];
317 rte_rwlock_write_lock(&rsn->rwl);
327 rsn_update_finish(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
334 n = sa->sqn.inb.wridx;
335 RTE_ASSERT(n != sa->sqn.inb.rdidx);
336 RTE_ASSERT(rsn == sa->sqn.inb.rsn[n]);
338 rte_rwlock_write_unlock(&rsn->rwl);
339 sa->sqn.inb.rdidx = n;
343 #endif /* _IPSEC_SQN_H_ */