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
anti_replay_check(uint64_t seq,struct otx2_ipsec_fp_in_sa * sa)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
cpt_ipsec_antireplay_check(struct otx2_ipsec_fp_in_sa * sa,char * data)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__ */
209