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