a3ae7e2de471ca5cb639d9f40bb1a2ac1e561aaa
[dpdk.git] / lib / librte_ipsec / ipsec_sqn.h
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #ifndef _IPSEC_SQN_H_
6 #define _IPSEC_SQN_H_
7
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)
11
12 /* minimum number of bucket, power of 2*/
13 #define WINDOW_BUCKET_MIN               2
14 #define WINDOW_BUCKET_MAX               (INT16_MAX + 1)
15
16 #define IS_ESN(sa)      ((sa)->sqn_mask == UINT64_MAX)
17
18 #define SQN_ATOMIC(sa)  ((sa)->type & RTE_IPSEC_SATP_SQN_ATOM)
19
20 /*
21  * gets SQN.hi32 bits, SQN supposed to be in network byte order.
22  */
23 static inline rte_be32_t
24 sqn_hi32(rte_be64_t sqn)
25 {
26 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
27         return (sqn >> 32);
28 #else
29         return sqn;
30 #endif
31 }
32
33 /*
34  * gets SQN.low32 bits, SQN supposed to be in network byte order.
35  */
36 static inline rte_be32_t
37 sqn_low32(rte_be64_t sqn)
38 {
39 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
40         return sqn;
41 #else
42         return (sqn >> 32);
43 #endif
44 }
45
46 /*
47  * gets SQN.low16 bits, SQN supposed to be in network byte order.
48  */
49 static inline rte_be16_t
50 sqn_low16(rte_be64_t sqn)
51 {
52 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
53         return sqn;
54 #else
55         return (sqn >> 48);
56 #endif
57 }
58
59 /*
60  * for given size, calculate required number of buckets.
61  */
62 static uint32_t
63 replay_num_bucket(uint32_t wsz)
64 {
65         uint32_t nb;
66
67         nb = rte_align32pow2(RTE_ALIGN_MUL_CEIL(wsz, WINDOW_BUCKET_SIZE) /
68                 WINDOW_BUCKET_SIZE);
69         nb = RTE_MAX(nb, (uint32_t)WINDOW_BUCKET_MIN);
70
71         return nb;
72 }
73
74 /*
75  * According to RFC4303 A2.1, determine the high-order bit of sequence number.
76  * use 32bit arithmetic inside, return uint64_t.
77  */
78 static inline uint64_t
79 reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
80 {
81         uint32_t th, tl, bl;
82
83         tl = t;
84         th = t >> 32;
85         bl = tl - w + 1;
86
87         /* case A: window is within one sequence number subspace */
88         if (tl >= (w - 1))
89                 th += (sqn < bl);
90         /* case B: window spans two sequence number subspaces */
91         else if (th != 0)
92                 th -= (sqn >= bl);
93
94         /* return constructed sequence with proper high-order bits */
95         return (uint64_t)th << 32 | sqn;
96 }
97
98 /**
99  * Perform the replay checking.
100  *
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.
103  *
104  * Based on RFC 6479.
105  * Blocks are 64 bits unsigned integers
106  */
107 static inline int32_t
108 esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
109         uint64_t sqn)
110 {
111         uint32_t bit, bucket;
112
113         /* replay not enabled */
114         if (sa->replay.win_sz == 0)
115                 return 0;
116
117         /* seq is larger than lastseq */
118         if (sqn > rsn->sqn)
119                 return 0;
120
121         /* seq is outside window */
122         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
123                 return -EINVAL;
124
125         /* seq is inside the window */
126         bit = sqn & WINDOW_BIT_LOC_MASK;
127         bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
128
129         /* already seen packet */
130         if (rsn->window[bucket] & ((uint64_t)1 << bit))
131                 return -EINVAL;
132
133         return 0;
134 }
135
136 /**
137  * For outbound SA perform the sequence number update.
138  */
139 static inline uint64_t
140 esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
141 {
142         uint64_t n, s, sqn;
143
144         n = *num;
145         if (SQN_ATOMIC(sa))
146                 sqn = (uint64_t)rte_atomic64_add_return(&sa->sqn.outb.atom, n);
147         else {
148                 sqn = sa->sqn.outb.raw + n;
149                 sa->sqn.outb.raw = sqn;
150         }
151
152         /* overflow */
153         if (sqn > sa->sqn_mask) {
154                 s = sqn - sa->sqn_mask;
155                 *num = (s < n) ?  n - s : 0;
156         }
157
158         return sqn - n;
159 }
160
161 /**
162  * For inbound SA perform the sequence number and replay window update.
163  */
164 static inline int32_t
165 esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
166         uint64_t sqn)
167 {
168         uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
169
170         /* replay not enabled */
171         if (sa->replay.win_sz == 0)
172                 return 0;
173
174         /* handle ESN */
175         if (IS_ESN(sa))
176                 sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
177
178         /* seq is outside window*/
179         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
180                 return -EINVAL;
181
182         /* update the bit */
183         bucket = (sqn >> WINDOW_BUCKET_BITS);
184
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;
192
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;
197                 }
198                 rsn->sqn = sqn;
199         }
200
201         bucket &= sa->replay.bucket_index_mask;
202         bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
203
204         /* already seen packet */
205         if (rsn->window[bucket] & bit)
206                 return -EINVAL;
207
208         rsn->window[bucket] |= bit;
209         return 0;
210 }
211
212 /**
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.
223  */
224
225 /**
226  * Based on number of buckets calculated required size for the
227  * structure that holds replay window and sequence number (RSN) information.
228  */
229 static size_t
230 rsn_size(uint32_t nb_bucket)
231 {
232         size_t sz;
233         struct replay_sqn *rsn;
234
235         sz = sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0]);
236         sz = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
237         return sz;
238 }
239
240 /**
241  * Copy replay window and SQN.
242  */
243 static inline void
244 rsn_copy(const struct rte_ipsec_sa *sa, uint32_t dst, uint32_t src)
245 {
246         uint32_t i, n;
247         struct replay_sqn *d;
248         const struct replay_sqn *s;
249
250         d = sa->sqn.inb.rsn[dst];
251         s = sa->sqn.inb.rsn[src];
252
253         n = sa->replay.nb_bucket;
254
255         d->sqn = s->sqn;
256         for (i = 0; i != n; i++)
257                 d->window[i] = s->window[i];
258 }
259
260 /**
261  * Get RSN for read-only access.
262  */
263 static inline struct replay_sqn *
264 rsn_acquire(struct rte_ipsec_sa *sa)
265 {
266         uint32_t n;
267         struct replay_sqn *rsn;
268
269         n = sa->sqn.inb.rdidx;
270         rsn = sa->sqn.inb.rsn[n];
271
272         if (!SQN_ATOMIC(sa))
273                 return rsn;
274
275         /* check there are no writers */
276         while (rte_rwlock_read_trylock(&rsn->rwl) < 0) {
277                 rte_pause();
278                 n = sa->sqn.inb.rdidx;
279                 rsn = sa->sqn.inb.rsn[n];
280                 rte_compiler_barrier();
281         }
282
283         return rsn;
284 }
285
286 /**
287  * Release read-only access for RSN.
288  */
289 static inline void
290 rsn_release(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
291 {
292         if (SQN_ATOMIC(sa))
293                 rte_rwlock_read_unlock(&rsn->rwl);
294 }
295
296 /**
297  * Start RSN update.
298  */
299 static inline struct replay_sqn *
300 rsn_update_start(struct rte_ipsec_sa *sa)
301 {
302         uint32_t k, n;
303         struct replay_sqn *rsn;
304
305         n = sa->sqn.inb.wridx;
306
307         /* no active writers */
308         RTE_ASSERT(n == sa->sqn.inb.rdidx);
309
310         if (!SQN_ATOMIC(sa))
311                 return sa->sqn.inb.rsn[n];
312
313         k = REPLAY_SQN_NEXT(n);
314         sa->sqn.inb.wridx = k;
315
316         rsn = sa->sqn.inb.rsn[k];
317         rte_rwlock_write_lock(&rsn->rwl);
318         rsn_copy(sa, k, n);
319
320         return rsn;
321 }
322
323 /**
324  * Finish RSN update.
325  */
326 static inline void
327 rsn_update_finish(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
328 {
329         uint32_t n;
330
331         if (!SQN_ATOMIC(sa))
332                 return;
333
334         n = sa->sqn.inb.wridx;
335         RTE_ASSERT(n != sa->sqn.inb.rdidx);
336         RTE_ASSERT(rsn == sa->sqn.inb.rsn[n]);
337
338         rte_rwlock_write_unlock(&rsn->rwl);
339         sa->sqn.inb.rdidx = n;
340 }
341
342
343 #endif /* _IPSEC_SQN_H_ */