858ce5b15f747c9869c06a27a12e39ba2c536c9d
[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(uint64_t seq, struct otx2_ipsec_fp_in_sa *sa)
20 {
21         struct otx2_ipsec_replay *replay = sa->replay;
22         uint64_t *window = &replay->window[0];
23         uint64_t winsz = sa->replay_win_sz;
24         uint64_t ex_winsz = winsz + WORD_SIZE;
25         uint64_t winwords = ex_winsz >> WORD_SHIFT;
26         uint64_t base = replay->base;
27         uint32_t winb = replay->winb;
28         uint32_t wint = replay->wint;
29         uint64_t seqword, shiftwords;
30         uint64_t bit_pos;
31         uint64_t shift;
32         uint64_t *wptr;
33         uint64_t tmp;
34
35         if (winsz > 64)
36                 goto slow_shift;
37         /* Check if the seq is the biggest one yet */
38         if (likely(seq > base)) {
39                 shift = seq - base;
40                 if (shift < winsz) {  /* In window */
41                         /*
42                          * If more than 64-bit anti-replay window,
43                          * use slow shift routine
44                          */
45                         wptr = window + (shift >> WORD_SHIFT);
46                         *wptr <<= shift;
47                         *wptr |= 1ull;
48                 } else {
49                         /* No special handling of window size > 64 */
50                         wptr = window + ((winsz - 1) >> WORD_SHIFT);
51                         /*
52                          * Zero out the whole window (especially for
53                          * bigger than 64b window) till the last 64b word
54                          * as the incoming sequence number minus
55                          * base sequence is more than the window size.
56                          */
57                         while (window != wptr)
58                                 *window++ = 0ull;
59                         /*
60                          * Set the last bit (of the window) to 1
61                          * as that corresponds to the base sequence number.
62                          * Now any incoming sequence number which is
63                          * (base - window size - 1) will pass anti-replay check
64                          */
65                         *wptr = 1ull;
66                 }
67                 /*
68                  * Set the base to incoming sequence number as
69                  * that is the biggest sequence number seen yet
70                  */
71                 replay->base = seq;
72                 return 0;
73         }
74
75         bit_pos = base - seq;
76
77         /* If seq falls behind the window, return failure */
78         if (bit_pos >= winsz)
79                 return IPSEC_ANTI_REPLAY_FAILED;
80
81         /* seq is within anti-replay window */
82         wptr = window + ((winsz - bit_pos - 1) >> WORD_SHIFT);
83         bit_pos &= WORD_MASK;
84
85         /* Check if this is a replayed packet */
86         if (*wptr & ((1ull) << bit_pos))
87                 return IPSEC_ANTI_REPLAY_FAILED;
88
89         /* mark as seen */
90         *wptr |= ((1ull) << bit_pos);
91         return 0;
92
93 slow_shift:
94         if (likely(seq > base)) {
95                 uint32_t i;
96
97                 shift = seq - base;
98                 if (unlikely(shift >= winsz)) {
99                         /*
100                          * shift is bigger than the window,
101                          * so just zero out everything
102                          */
103                         for (i = 0; i < winwords; i++)
104                                 window[i] = 0;
105 winupdate:
106                         /* Find out the word */
107                         seqword = ((seq - 1) % ex_winsz) >> WORD_SHIFT;
108
109                         /* Find out the bit in the word */
110                         bit_pos = (seq - 1) & WORD_MASK;
111
112                         /*
113                          * Set the bit corresponding to sequence number
114                          * in window to mark it as received
115                          */
116                         window[seqword] |= (1ull << (63 - bit_pos));
117
118                         /* wint and winb range from 1 to ex_winsz */
119                         replay->wint = ((wint + shift - 1) % ex_winsz) + 1;
120                         replay->winb = ((winb + shift - 1) % ex_winsz) + 1;
121
122                         replay->base = seq;
123                         return 0;
124                 }
125
126                 /*
127                  * New sequence number is bigger than the base but
128                  * it's not bigger than base + window size
129                  */
130
131                 shiftwords = ((wint + shift - 1) >> WORD_SHIFT) -
132                              ((wint - 1) >> WORD_SHIFT);
133                 if (unlikely(shiftwords)) {
134                         tmp = (wint + WORD_SIZE - 1) / WORD_SIZE;
135                         for (i = 0; i < shiftwords; i++) {
136                                 tmp %= winwords;
137                                 window[tmp++] = 0;
138                         }
139                 }
140
141                 goto winupdate;
142         }
143
144         /* Sequence number is before the window */
145         if (unlikely((seq + winsz) <= base))
146                 return IPSEC_ANTI_REPLAY_FAILED;
147
148         /* Sequence number is within the window */
149
150         /* Find out the word */
151         seqword = ((seq - 1) % ex_winsz) >> WORD_SHIFT;
152
153         /* Find out the bit in the word */
154         bit_pos = (seq - 1) & WORD_MASK;
155
156         /* Check if this is a replayed packet */
157         if (window[seqword] & (1ull << (63 - bit_pos)))
158                 return IPSEC_ANTI_REPLAY_FAILED;
159
160         /*
161          * Set the bit corresponding to sequence number
162          * in window to mark it as received
163          */
164         window[seqword] |= (1ull << (63 - bit_pos));
165
166         return 0;
167 }
168
169 static int
170 cpt_ipsec_antireplay_check(struct otx2_ipsec_fp_in_sa *sa, char *data)
171 {
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(*((uint32_t *)(data +
181                         OTX2_IPSEC_SEQNO_LO_INDEX)));
182
183         if (!esn)
184                 seq = (uint64_t)seql;
185         else {
186                 seqh = rte_be_to_cpu_32(*((uint32_t *)(data +
187                                 OTX2_IPSEC_SEQNO_HI_INDEX)));
188                 seq = ((uint64_t)seqh << 32) | seql;
189         }
190
191         if (unlikely(seq == 0))
192                 return IPSEC_ANTI_REPLAY_FAILED;
193
194         rte_spinlock_lock(&sa->replay->lock);
195         ret = anti_replay_check(seq, sa);
196         if (esn && (ret == 0)) {
197                 seq_in_sa = ((uint64_t)rte_be_to_cpu_32(sa->esn_hi) << 32) |
198                                 rte_be_to_cpu_32(sa->esn_low);
199                 if (seq > seq_in_sa) {
200                         sa->esn_low = rte_cpu_to_be_32(seql);
201                         sa->esn_hi = rte_cpu_to_be_32(seqh);
202                 }
203         }
204         rte_spinlock_unlock(&sa->replay->lock);
205
206         return ret;
207 }
208 #endif /* __OTX2_IPSEC_ANTI_REPLAY_H__ */