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)19anti_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)170cpt_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