net/octeontx2: support non-ethernet L2 header
[dpdk.git] / drivers / crypto / octeontx2 / otx2_ipsec_anti_replay.h
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (C) 2020 Marvell International Ltd.
3  */
4
5 #ifndef __OTX2_IPSEC_ANTI_REPLAY_H__
6 #define __OTX2_IPSEC_ANTI_REPLAY_H__
7
8 #include <rte_mbuf.h>
9
10 #include "otx2_ipsec_fp.h"
11
12 #define WORD_SHIFT      6
13 #define WORD_SIZE       (1 << WORD_SHIFT)
14 #define WORD_MASK       (WORD_SIZE - 1)
15
16 #define IPSEC_ANTI_REPLAY_FAILED        (-1)
17
18 static inline int
19 anti_replay_check(struct otx2_ipsec_replay *replay, uint64_t seq,
20                         uint64_t winsz)
21 {
22         uint64_t *window = &replay->window[0];
23         uint64_t ex_winsz = winsz + WORD_SIZE;
24         uint64_t winwords = ex_winsz >> WORD_SHIFT;
25         uint64_t base = replay->base;
26         uint32_t winb = replay->winb;
27         uint32_t wint = replay->wint;
28         uint64_t seqword, shiftwords;
29         uint64_t bit_pos;
30         uint64_t shift;
31         uint64_t *wptr;
32         uint64_t tmp;
33
34         if (winsz > 64)
35                 goto slow_shift;
36         /* Check if the seq is the biggest one yet */
37         if (likely(seq > base)) {
38                 shift = seq - base;
39                 if (shift < winsz) {  /* In window */
40                         /*
41                          * If more than 64-bit anti-replay window,
42                          * use slow shift routine
43                          */
44                         wptr = window + (shift >> WORD_SHIFT);
45                         *wptr <<= shift;
46                         *wptr |= 1ull;
47                 } else {
48                         /* No special handling of window size > 64 */
49                         wptr = window + ((winsz - 1) >> WORD_SHIFT);
50                         /*
51                          * Zero out the whole window (especially for
52                          * bigger than 64b window) till the last 64b word
53                          * as the incoming sequence number minus
54                          * base sequence is more than the window size.
55                          */
56                         while (window != wptr)
57                                 *window++ = 0ull;
58                         /*
59                          * Set the last bit (of the window) to 1
60                          * as that corresponds to the base sequence number.
61                          * Now any incoming sequence number which is
62                          * (base - window size - 1) will pass anti-replay check
63                          */
64                         *wptr = 1ull;
65                 }
66                 /*
67                  * Set the base to incoming sequence number as
68                  * that is the biggest sequence number seen yet
69                  */
70                 replay->base = seq;
71                 return 0;
72         }
73
74         bit_pos = base - seq;
75
76         /* If seq falls behind the window, return failure */
77         if (bit_pos >= winsz)
78                 return IPSEC_ANTI_REPLAY_FAILED;
79
80         /* seq is within anti-replay window */
81         wptr = window + ((winsz - bit_pos - 1) >> WORD_SHIFT);
82         bit_pos &= WORD_MASK;
83
84         /* Check if this is a replayed packet */
85         if (*wptr & ((1ull) << bit_pos))
86                 return IPSEC_ANTI_REPLAY_FAILED;
87
88         /* mark as seen */
89         *wptr |= ((1ull) << bit_pos);
90         return 0;
91
92 slow_shift:
93         if (likely(seq > base)) {
94                 uint32_t i;
95
96                 shift = seq - base;
97                 if (unlikely(shift >= winsz)) {
98                         /*
99                          * shift is bigger than the window,
100                          * so just zero out everything
101                          */
102                         for (i = 0; i < winwords; i++)
103                                 window[i] = 0;
104 winupdate:
105                         /* Find out the word */
106                         seqword = ((seq - 1) % ex_winsz) >> WORD_SHIFT;
107
108                         /* Find out the bit in the word */
109                         bit_pos = (seq - 1) & WORD_MASK;
110
111                         /*
112                          * Set the bit corresponding to sequence number
113                          * in window to mark it as received
114                          */
115                         window[seqword] |= (1ull << (63 - bit_pos));
116
117                         /* wint and winb range from 1 to ex_winsz */
118                         replay->wint = ((wint + shift - 1) % ex_winsz) + 1;
119                         replay->winb = ((winb + shift - 1) % ex_winsz) + 1;
120
121                         replay->base = seq;
122                         return 0;
123                 }
124
125                 /*
126                  * New sequence number is bigger than the base but
127                  * it's not bigger than base + window size
128                  */
129
130                 shiftwords = ((wint + shift - 1) >> WORD_SHIFT) -
131                              ((wint - 1) >> WORD_SHIFT);
132                 if (unlikely(shiftwords)) {
133                         tmp = (wint + WORD_SIZE - 1) / WORD_SIZE;
134                         for (i = 0; i < shiftwords; i++) {
135                                 tmp %= winwords;
136                                 window[tmp++] = 0;
137                         }
138                 }
139
140                 goto winupdate;
141         }
142
143         /* Sequence number is before the window */
144         if (unlikely((seq + winsz) <= base))
145                 return IPSEC_ANTI_REPLAY_FAILED;
146
147         /* Sequence number is within the window */
148
149         /* Find out the word */
150         seqword = ((seq - 1) % ex_winsz) >> WORD_SHIFT;
151
152         /* Find out the bit in the word */
153         bit_pos = (seq - 1) & WORD_MASK;
154
155         /* Check if this is a replayed packet */
156         if (window[seqword] & (1ull << (63 - bit_pos)))
157                 return IPSEC_ANTI_REPLAY_FAILED;
158
159         /*
160          * Set the bit corresponding to sequence number
161          * in window to mark it as received
162          */
163         window[seqword] |= (1ull << (63 - bit_pos));
164
165         return 0;
166 }
167
168 static inline int
169 cpt_ipsec_ip_antireplay_check(struct otx2_ipsec_fp_in_sa *sa, void *l3_ptr)
170 {
171         struct otx2_ipsec_fp_res_hdr *hdr = l3_ptr;
172         uint64_t seq_in_sa;
173         uint32_t seqh = 0;
174         uint32_t seql;
175         uint64_t seq;
176         uint8_t esn;
177         int ret;
178
179         esn = sa->ctl.esn_en;
180         seql = rte_be_to_cpu_32(hdr->seq_no_lo);
181
182         if (!esn)
183                 seq = (uint64_t)seql;
184         else {
185                 seqh = rte_be_to_cpu_32(hdr->seq_no_hi);
186                 seq = ((uint64_t)seqh << 32) | seql;
187         }
188
189         if (unlikely(seq == 0))
190                 return IPSEC_ANTI_REPLAY_FAILED;
191
192         rte_spinlock_lock(&sa->replay->lock);
193         ret = anti_replay_check(sa->replay, seq, sa->replay_win_sz);
194         if (esn && (ret == 0)) {
195                 seq_in_sa = ((uint64_t)rte_be_to_cpu_32(sa->esn_hi) << 32) |
196                                 rte_be_to_cpu_32(sa->esn_low);
197                 if (seq > seq_in_sa) {
198                         sa->esn_low = rte_cpu_to_be_32(seql);
199                         sa->esn_hi = rte_cpu_to_be_32(seqh);
200                 }
201         }
202         rte_spinlock_unlock(&sa->replay->lock);
203
204         return ret;
205 }
206
207 static inline uint32_t
208 anti_replay_get_seqh(uint32_t winsz, uint32_t seql,
209                         uint32_t esn_hi, uint32_t esn_low)
210 {
211         uint32_t win_low = esn_low - winsz + 1;
212
213         if (esn_low > winsz - 1) {
214                 /* Window is in one sequence number subspace */
215                 if (seql > win_low)
216                         return esn_hi;
217                 else
218                         return esn_hi + 1;
219         } else {
220                 /* Window is split across two sequence number subspaces */
221                 if (seql > win_low)
222                         return esn_hi - 1;
223                 else
224                         return esn_hi;
225         }
226 }
227 #endif /* __OTX2_IPSEC_ANTI_REPLAY_H__ */