6e18c34ebbae7c0d84ac3f1a66e8e38a4e890b02
[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 /*
19  * gets SQN.hi32 bits, SQN supposed to be in network byte order.
20  */
21 static inline rte_be32_t
22 sqn_hi32(rte_be64_t sqn)
23 {
24 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
25         return (sqn >> 32);
26 #else
27         return sqn;
28 #endif
29 }
30
31 /*
32  * gets SQN.low32 bits, SQN supposed to be in network byte order.
33  */
34 static inline rte_be32_t
35 sqn_low32(rte_be64_t sqn)
36 {
37 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
38         return sqn;
39 #else
40         return (sqn >> 32);
41 #endif
42 }
43
44 /*
45  * gets SQN.low16 bits, SQN supposed to be in network byte order.
46  */
47 static inline rte_be16_t
48 sqn_low16(rte_be64_t sqn)
49 {
50 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
51         return sqn;
52 #else
53         return (sqn >> 48);
54 #endif
55 }
56
57 /*
58  * for given size, calculate required number of buckets.
59  */
60 static uint32_t
61 replay_num_bucket(uint32_t wsz)
62 {
63         uint32_t nb;
64
65         nb = rte_align32pow2(RTE_ALIGN_MUL_CEIL(wsz, WINDOW_BUCKET_SIZE) /
66                 WINDOW_BUCKET_SIZE);
67         nb = RTE_MAX(nb, (uint32_t)WINDOW_BUCKET_MIN);
68
69         return nb;
70 }
71
72 /*
73  * According to RFC4303 A2.1, determine the high-order bit of sequence number.
74  * use 32bit arithmetic inside, return uint64_t.
75  */
76 static inline uint64_t
77 reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
78 {
79         uint32_t th, tl, bl;
80
81         tl = t;
82         th = t >> 32;
83         bl = tl - w + 1;
84
85         /* case A: window is within one sequence number subspace */
86         if (tl >= (w - 1))
87                 th += (sqn < bl);
88         /* case B: window spans two sequence number subspaces */
89         else if (th != 0)
90                 th -= (sqn >= bl);
91
92         /* return constructed sequence with proper high-order bits */
93         return (uint64_t)th << 32 | sqn;
94 }
95
96 /**
97  * Perform the replay checking.
98  *
99  * struct rte_ipsec_sa contains the window and window related parameters,
100  * such as the window size, bitmask, and the last acknowledged sequence number.
101  *
102  * Based on RFC 6479.
103  * Blocks are 64 bits unsigned integers
104  */
105 static inline int32_t
106 esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
107         uint64_t sqn)
108 {
109         uint32_t bit, bucket;
110
111         /* replay not enabled */
112         if (sa->replay.win_sz == 0)
113                 return 0;
114
115         /* seq is larger than lastseq */
116         if (sqn > rsn->sqn)
117                 return 0;
118
119         /* seq is outside window */
120         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
121                 return -EINVAL;
122
123         /* seq is inside the window */
124         bit = sqn & WINDOW_BIT_LOC_MASK;
125         bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
126
127         /* already seen packet */
128         if (rsn->window[bucket] & ((uint64_t)1 << bit))
129                 return -EINVAL;
130
131         return 0;
132 }
133
134 /**
135  * For outbound SA perform the sequence number update.
136  */
137 static inline uint64_t
138 esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
139 {
140         uint64_t n, s, sqn;
141
142         n = *num;
143         sqn = sa->sqn.outb + n;
144         sa->sqn.outb = sqn;
145
146         /* overflow */
147         if (sqn > sa->sqn_mask) {
148                 s = sqn - sa->sqn_mask;
149                 *num = (s < n) ?  n - s : 0;
150         }
151
152         return sqn - n;
153 }
154
155 /**
156  * For inbound SA perform the sequence number and replay window update.
157  */
158 static inline int32_t
159 esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
160         uint64_t sqn)
161 {
162         uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
163
164         /* replay not enabled */
165         if (sa->replay.win_sz == 0)
166                 return 0;
167
168         /* handle ESN */
169         if (IS_ESN(sa))
170                 sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
171
172         /* seq is outside window*/
173         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
174                 return -EINVAL;
175
176         /* update the bit */
177         bucket = (sqn >> WINDOW_BUCKET_BITS);
178
179         /* check if the seq is within the range */
180         if (sqn > rsn->sqn) {
181                 last_bucket = rsn->sqn >> WINDOW_BUCKET_BITS;
182                 diff = bucket - last_bucket;
183                 /* seq is way after the range of WINDOW_SIZE */
184                 if (diff > sa->replay.nb_bucket)
185                         diff = sa->replay.nb_bucket;
186
187                 for (i = 0; i != diff; i++) {
188                         new_bucket = (i + last_bucket + 1) &
189                                 sa->replay.bucket_index_mask;
190                         rsn->window[new_bucket] = 0;
191                 }
192                 rsn->sqn = sqn;
193         }
194
195         bucket &= sa->replay.bucket_index_mask;
196         bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
197
198         /* already seen packet */
199         if (rsn->window[bucket] & bit)
200                 return -EINVAL;
201
202         rsn->window[bucket] |= bit;
203         return 0;
204 }
205
206 /**
207  * To achieve ability to do multiple readers single writer for
208  * SA replay window information and sequence number (RSN)
209  * basic RCU schema is used:
210  * SA have 2 copies of RSN (one for readers, another for writers).
211  * Each RSN contains a rwlock that has to be grabbed (for read/write)
212  * to avoid races between readers and writer.
213  * Writer is responsible to make a copy or reader RSN, update it
214  * and mark newly updated RSN as readers one.
215  * That approach is intended to minimize contention and cache sharing
216  * between writer and readers.
217  */
218
219 /**
220  * Based on number of buckets calculated required size for the
221  * structure that holds replay window and sequence number (RSN) information.
222  */
223 static size_t
224 rsn_size(uint32_t nb_bucket)
225 {
226         size_t sz;
227         struct replay_sqn *rsn;
228
229         sz = sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0]);
230         sz = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
231         return sz;
232 }
233
234 #endif /* _IPSEC_SQN_H_ */