]>
Commit | Line | Data |
---|---|---|
a61127c2 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9fdc4883 SK |
2 | /* |
3 | * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c. | |
97e15c3a SK |
4 | * |
5 | * Copyright (C) 2010 secunet Security Networks AG | |
6 | * Copyright (C) 2010 Steffen Klassert <[email protected]> | |
9fdc4883 SK |
7 | */ |
8 | ||
bc3b2d7f | 9 | #include <linux/export.h> |
9fdc4883 SK |
10 | #include <net/xfrm.h> |
11 | ||
2cd08467 SK |
12 | u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq) |
13 | { | |
14 | u32 seq, seq_hi, bottom; | |
15 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
16 | ||
17 | if (!(x->props.flags & XFRM_STATE_ESN)) | |
18 | return 0; | |
19 | ||
20 | seq = ntohl(net_seq); | |
21 | seq_hi = replay_esn->seq_hi; | |
22 | bottom = replay_esn->seq - replay_esn->replay_window + 1; | |
23 | ||
24 | if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) { | |
25 | /* A. same subspace */ | |
26 | if (unlikely(seq < bottom)) | |
27 | seq_hi++; | |
28 | } else { | |
29 | /* B. window spans two subspaces */ | |
30 | if (unlikely(seq >= bottom)) | |
31 | seq_hi--; | |
32 | } | |
33 | ||
34 | return seq_hi; | |
35 | } | |
7862b405 | 36 | EXPORT_SYMBOL(xfrm_replay_seqhi); |
cfc61c59 FW |
37 | |
38 | static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event); | |
39 | static void xfrm_replay_notify_esn(struct xfrm_state *x, int event); | |
40 | ||
41 | void xfrm_replay_notify(struct xfrm_state *x, int event) | |
9fdc4883 SK |
42 | { |
43 | struct km_event c; | |
44 | /* we send notify messages in case | |
45 | * 1. we updated on of the sequence numbers, and the seqno difference | |
46 | * is at least x->replay_maxdiff, in this case we also update the | |
47 | * timeout of our timer function | |
48 | * 2. if x->replay_maxage has elapsed since last update, | |
49 | * and there were changes | |
50 | * | |
51 | * The state structure must be locked! | |
52 | */ | |
53 | ||
cfc61c59 FW |
54 | switch (x->repl_mode) { |
55 | case XFRM_REPLAY_MODE_LEGACY: | |
56 | break; | |
57 | case XFRM_REPLAY_MODE_BMP: | |
58 | xfrm_replay_notify_bmp(x, event); | |
59 | return; | |
60 | case XFRM_REPLAY_MODE_ESN: | |
61 | xfrm_replay_notify_esn(x, event); | |
62 | return; | |
63 | } | |
64 | ||
9fdc4883 SK |
65 | switch (event) { |
66 | case XFRM_REPLAY_UPDATE: | |
cd808fc9 TE |
67 | if (!x->replay_maxdiff || |
68 | ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) && | |
69 | (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) { | |
9fdc4883 SK |
70 | if (x->xflags & XFRM_TIME_DEFER) |
71 | event = XFRM_REPLAY_TIMEOUT; | |
72 | else | |
73 | return; | |
74 | } | |
75 | ||
76 | break; | |
77 | ||
78 | case XFRM_REPLAY_TIMEOUT: | |
79 | if (memcmp(&x->replay, &x->preplay, | |
80 | sizeof(struct xfrm_replay_state)) == 0) { | |
81 | x->xflags |= XFRM_TIME_DEFER; | |
82 | return; | |
83 | } | |
84 | ||
85 | break; | |
86 | } | |
87 | ||
88 | memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); | |
89 | c.event = XFRM_MSG_NEWAE; | |
90 | c.data.aevent = event; | |
91 | km_state_notify(x, &c); | |
92 | ||
93 | if (x->replay_maxage && | |
94 | !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) | |
95 | x->xflags &= ~XFRM_TIME_DEFER; | |
96 | } | |
97 | ||
b5a1d1fe | 98 | static int __xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) |
9fdc4883 SK |
99 | { |
100 | int err = 0; | |
101 | struct net *net = xs_net(x); | |
102 | ||
103 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
104 | XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq; | |
407d34ef | 105 | XFRM_SKB_CB(skb)->seq.output.hi = 0; |
428d2459 PV |
106 | if (unlikely(x->replay.oseq == 0) && |
107 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { | |
9fdc4883 SK |
108 | x->replay.oseq--; |
109 | xfrm_audit_state_replay_overflow(x, skb); | |
110 | err = -EOVERFLOW; | |
111 | ||
112 | return err; | |
113 | } | |
114 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 115 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
9fdc4883 SK |
116 | } |
117 | ||
118 | return err; | |
119 | } | |
120 | ||
adfc2fdb FW |
121 | static int xfrm_replay_check_legacy(struct xfrm_state *x, |
122 | struct sk_buff *skb, __be32 net_seq) | |
9fdc4883 SK |
123 | { |
124 | u32 diff; | |
125 | u32 seq = ntohl(net_seq); | |
126 | ||
36ae0148 SK |
127 | if (!x->props.replay_window) |
128 | return 0; | |
129 | ||
9fdc4883 SK |
130 | if (unlikely(seq == 0)) |
131 | goto err; | |
132 | ||
133 | if (likely(seq > x->replay.seq)) | |
134 | return 0; | |
135 | ||
136 | diff = x->replay.seq - seq; | |
33fce60d | 137 | if (diff >= x->props.replay_window) { |
9fdc4883 SK |
138 | x->stats.replay_window++; |
139 | goto err; | |
140 | } | |
141 | ||
142 | if (x->replay.bitmap & (1U << diff)) { | |
143 | x->stats.replay++; | |
144 | goto err; | |
145 | } | |
146 | return 0; | |
147 | ||
148 | err: | |
149 | xfrm_audit_state_replay(x, skb, net_seq); | |
150 | return -EINVAL; | |
151 | } | |
152 | ||
c7f87783 FW |
153 | static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq); |
154 | static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq); | |
155 | ||
156 | void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) | |
9fdc4883 | 157 | { |
c7f87783 FW |
158 | u32 diff, seq; |
159 | ||
160 | switch (x->repl_mode) { | |
161 | case XFRM_REPLAY_MODE_LEGACY: | |
162 | break; | |
163 | case XFRM_REPLAY_MODE_BMP: | |
164 | return xfrm_replay_advance_bmp(x, net_seq); | |
165 | case XFRM_REPLAY_MODE_ESN: | |
166 | return xfrm_replay_advance_esn(x, net_seq); | |
167 | } | |
9fdc4883 SK |
168 | |
169 | if (!x->props.replay_window) | |
170 | return; | |
171 | ||
c7f87783 | 172 | seq = ntohl(net_seq); |
9fdc4883 SK |
173 | if (seq > x->replay.seq) { |
174 | diff = seq - x->replay.seq; | |
175 | if (diff < x->props.replay_window) | |
176 | x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; | |
177 | else | |
178 | x->replay.bitmap = 1; | |
179 | x->replay.seq = seq; | |
180 | } else { | |
181 | diff = x->replay.seq - seq; | |
182 | x->replay.bitmap |= (1U << diff); | |
183 | } | |
184 | ||
185 | if (xfrm_aevent_is_on(xs_net(x))) | |
cfc61c59 | 186 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
9fdc4883 SK |
187 | } |
188 | ||
97e15c3a SK |
189 | static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb) |
190 | { | |
191 | int err = 0; | |
192 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
193 | struct net *net = xs_net(x); | |
194 | ||
195 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
196 | XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; | |
407d34ef | 197 | XFRM_SKB_CB(skb)->seq.output.hi = 0; |
428d2459 PV |
198 | if (unlikely(replay_esn->oseq == 0) && |
199 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { | |
97e15c3a SK |
200 | replay_esn->oseq--; |
201 | xfrm_audit_state_replay_overflow(x, skb); | |
202 | err = -EOVERFLOW; | |
203 | ||
204 | return err; | |
205 | } | |
206 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 207 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
97e15c3a SK |
208 | } |
209 | ||
210 | return err; | |
211 | } | |
212 | ||
213 | static int xfrm_replay_check_bmp(struct xfrm_state *x, | |
214 | struct sk_buff *skb, __be32 net_seq) | |
215 | { | |
216 | unsigned int bitnr, nr; | |
217 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
36ae0148 | 218 | u32 pos; |
97e15c3a SK |
219 | u32 seq = ntohl(net_seq); |
220 | u32 diff = replay_esn->seq - seq; | |
36ae0148 SK |
221 | |
222 | if (!replay_esn->replay_window) | |
223 | return 0; | |
224 | ||
97e15c3a SK |
225 | if (unlikely(seq == 0)) |
226 | goto err; | |
227 | ||
228 | if (likely(seq > replay_esn->seq)) | |
229 | return 0; | |
230 | ||
231 | if (diff >= replay_esn->replay_window) { | |
232 | x->stats.replay_window++; | |
233 | goto err; | |
234 | } | |
235 | ||
1d974374 SK |
236 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
237 | ||
238 | if (pos >= diff) | |
97e15c3a | 239 | bitnr = (pos - diff) % replay_esn->replay_window; |
1d974374 | 240 | else |
97e15c3a | 241 | bitnr = replay_esn->replay_window - (diff - pos); |
1d974374 SK |
242 | |
243 | nr = bitnr >> 5; | |
244 | bitnr = bitnr & 0x1F; | |
245 | if (replay_esn->bmp[nr] & (1U << bitnr)) | |
246 | goto err_replay; | |
247 | ||
97e15c3a SK |
248 | return 0; |
249 | ||
250 | err_replay: | |
251 | x->stats.replay++; | |
252 | err: | |
253 | xfrm_audit_state_replay(x, skb, net_seq); | |
254 | return -EINVAL; | |
255 | } | |
256 | ||
257 | static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq) | |
258 | { | |
259 | unsigned int bitnr, nr, i; | |
260 | u32 diff; | |
261 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
262 | u32 seq = ntohl(net_seq); | |
e2f67259 | 263 | u32 pos; |
97e15c3a SK |
264 | |
265 | if (!replay_esn->replay_window) | |
266 | return; | |
267 | ||
e2f67259 NZ |
268 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
269 | ||
97e15c3a SK |
270 | if (seq > replay_esn->seq) { |
271 | diff = seq - replay_esn->seq; | |
272 | ||
273 | if (diff < replay_esn->replay_window) { | |
274 | for (i = 1; i < diff; i++) { | |
275 | bitnr = (pos + i) % replay_esn->replay_window; | |
276 | nr = bitnr >> 5; | |
277 | bitnr = bitnr & 0x1F; | |
278 | replay_esn->bmp[nr] &= ~(1U << bitnr); | |
279 | } | |
97e15c3a | 280 | } else { |
e756682c | 281 | nr = (replay_esn->replay_window - 1) >> 5; |
97e15c3a SK |
282 | for (i = 0; i <= nr; i++) |
283 | replay_esn->bmp[i] = 0; | |
97e15c3a SK |
284 | } |
285 | ||
1d974374 | 286 | bitnr = (pos + diff) % replay_esn->replay_window; |
97e15c3a SK |
287 | replay_esn->seq = seq; |
288 | } else { | |
289 | diff = replay_esn->seq - seq; | |
290 | ||
1d974374 | 291 | if (pos >= diff) |
97e15c3a | 292 | bitnr = (pos - diff) % replay_esn->replay_window; |
1d974374 | 293 | else |
97e15c3a | 294 | bitnr = replay_esn->replay_window - (diff - pos); |
97e15c3a SK |
295 | } |
296 | ||
1d974374 SK |
297 | nr = bitnr >> 5; |
298 | bitnr = bitnr & 0x1F; | |
299 | replay_esn->bmp[nr] |= (1U << bitnr); | |
300 | ||
97e15c3a | 301 | if (xfrm_aevent_is_on(xs_net(x))) |
cfc61c59 | 302 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
97e15c3a SK |
303 | } |
304 | ||
305 | static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event) | |
306 | { | |
307 | struct km_event c; | |
308 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
309 | struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn; | |
310 | ||
311 | /* we send notify messages in case | |
312 | * 1. we updated on of the sequence numbers, and the seqno difference | |
313 | * is at least x->replay_maxdiff, in this case we also update the | |
314 | * timeout of our timer function | |
315 | * 2. if x->replay_maxage has elapsed since last update, | |
316 | * and there were changes | |
317 | * | |
318 | * The state structure must be locked! | |
319 | */ | |
320 | ||
321 | switch (event) { | |
322 | case XFRM_REPLAY_UPDATE: | |
cd808fc9 TE |
323 | if (!x->replay_maxdiff || |
324 | ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && | |
325 | (replay_esn->oseq - preplay_esn->oseq | |
326 | < x->replay_maxdiff))) { | |
97e15c3a SK |
327 | if (x->xflags & XFRM_TIME_DEFER) |
328 | event = XFRM_REPLAY_TIMEOUT; | |
329 | else | |
330 | return; | |
331 | } | |
332 | ||
333 | break; | |
334 | ||
335 | case XFRM_REPLAY_TIMEOUT: | |
336 | if (memcmp(x->replay_esn, x->preplay_esn, | |
337 | xfrm_replay_state_esn_len(replay_esn)) == 0) { | |
338 | x->xflags |= XFRM_TIME_DEFER; | |
339 | return; | |
340 | } | |
341 | ||
342 | break; | |
343 | } | |
344 | ||
345 | memcpy(x->preplay_esn, x->replay_esn, | |
346 | xfrm_replay_state_esn_len(replay_esn)); | |
347 | c.event = XFRM_MSG_NEWAE; | |
348 | c.data.aevent = event; | |
349 | km_state_notify(x, &c); | |
350 | ||
351 | if (x->replay_maxage && | |
352 | !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) | |
353 | x->xflags &= ~XFRM_TIME_DEFER; | |
354 | } | |
355 | ||
0017c0b5 SK |
356 | static void xfrm_replay_notify_esn(struct xfrm_state *x, int event) |
357 | { | |
358 | u32 seq_diff, oseq_diff; | |
359 | struct km_event c; | |
360 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
361 | struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn; | |
362 | ||
363 | /* we send notify messages in case | |
364 | * 1. we updated on of the sequence numbers, and the seqno difference | |
365 | * is at least x->replay_maxdiff, in this case we also update the | |
366 | * timeout of our timer function | |
367 | * 2. if x->replay_maxage has elapsed since last update, | |
368 | * and there were changes | |
369 | * | |
370 | * The state structure must be locked! | |
371 | */ | |
372 | ||
373 | switch (event) { | |
374 | case XFRM_REPLAY_UPDATE: | |
cd808fc9 TE |
375 | if (x->replay_maxdiff) { |
376 | if (replay_esn->seq_hi == preplay_esn->seq_hi) | |
377 | seq_diff = replay_esn->seq - preplay_esn->seq; | |
378 | else | |
379 | seq_diff = ~preplay_esn->seq + replay_esn->seq | |
380 | + 1; | |
0017c0b5 | 381 | |
cd808fc9 TE |
382 | if (replay_esn->oseq_hi == preplay_esn->oseq_hi) |
383 | oseq_diff = replay_esn->oseq | |
384 | - preplay_esn->oseq; | |
0017c0b5 | 385 | else |
cd808fc9 TE |
386 | oseq_diff = ~preplay_esn->oseq |
387 | + replay_esn->oseq + 1; | |
388 | ||
389 | if (seq_diff >= x->replay_maxdiff || | |
390 | oseq_diff >= x->replay_maxdiff) | |
391 | break; | |
0017c0b5 SK |
392 | } |
393 | ||
cd808fc9 TE |
394 | if (x->xflags & XFRM_TIME_DEFER) |
395 | event = XFRM_REPLAY_TIMEOUT; | |
396 | else | |
397 | return; | |
398 | ||
0017c0b5 SK |
399 | break; |
400 | ||
401 | case XFRM_REPLAY_TIMEOUT: | |
402 | if (memcmp(x->replay_esn, x->preplay_esn, | |
403 | xfrm_replay_state_esn_len(replay_esn)) == 0) { | |
404 | x->xflags |= XFRM_TIME_DEFER; | |
405 | return; | |
406 | } | |
407 | ||
408 | break; | |
409 | } | |
410 | ||
411 | memcpy(x->preplay_esn, x->replay_esn, | |
412 | xfrm_replay_state_esn_len(replay_esn)); | |
413 | c.event = XFRM_MSG_NEWAE; | |
414 | c.data.aevent = event; | |
415 | km_state_notify(x, &c); | |
416 | ||
417 | if (x->replay_maxage && | |
418 | !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) | |
419 | x->xflags &= ~XFRM_TIME_DEFER; | |
420 | } | |
421 | ||
2cd08467 SK |
422 | static int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb) |
423 | { | |
424 | int err = 0; | |
425 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
426 | struct net *net = xs_net(x); | |
427 | ||
428 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
429 | XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; | |
430 | XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi; | |
431 | ||
432 | if (unlikely(replay_esn->oseq == 0)) { | |
433 | XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi; | |
434 | ||
435 | if (replay_esn->oseq_hi == 0) { | |
436 | replay_esn->oseq--; | |
437 | replay_esn->oseq_hi--; | |
438 | xfrm_audit_state_replay_overflow(x, skb); | |
439 | err = -EOVERFLOW; | |
440 | ||
441 | return err; | |
442 | } | |
443 | } | |
444 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 445 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
2cd08467 SK |
446 | } |
447 | ||
448 | return err; | |
449 | } | |
450 | ||
451 | static int xfrm_replay_check_esn(struct xfrm_state *x, | |
452 | struct sk_buff *skb, __be32 net_seq) | |
453 | { | |
454 | unsigned int bitnr, nr; | |
455 | u32 diff; | |
456 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
36ae0148 | 457 | u32 pos; |
2cd08467 | 458 | u32 seq = ntohl(net_seq); |
2cd08467 SK |
459 | u32 wsize = replay_esn->replay_window; |
460 | u32 top = replay_esn->seq; | |
461 | u32 bottom = top - wsize + 1; | |
462 | ||
36ae0148 SK |
463 | if (!wsize) |
464 | return 0; | |
465 | ||
2cd08467 SK |
466 | if (unlikely(seq == 0 && replay_esn->seq_hi == 0 && |
467 | (replay_esn->seq < replay_esn->replay_window - 1))) | |
468 | goto err; | |
469 | ||
470 | diff = top - seq; | |
471 | ||
472 | if (likely(top >= wsize - 1)) { | |
473 | /* A. same subspace */ | |
474 | if (likely(seq > top) || seq < bottom) | |
475 | return 0; | |
476 | } else { | |
477 | /* B. window spans two subspaces */ | |
478 | if (likely(seq > top && seq < bottom)) | |
479 | return 0; | |
480 | if (seq >= bottom) | |
481 | diff = ~seq + top + 1; | |
482 | } | |
483 | ||
484 | if (diff >= replay_esn->replay_window) { | |
485 | x->stats.replay_window++; | |
486 | goto err; | |
487 | } | |
488 | ||
1d974374 SK |
489 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
490 | ||
491 | if (pos >= diff) | |
2cd08467 | 492 | bitnr = (pos - diff) % replay_esn->replay_window; |
1d974374 | 493 | else |
2cd08467 | 494 | bitnr = replay_esn->replay_window - (diff - pos); |
1d974374 SK |
495 | |
496 | nr = bitnr >> 5; | |
497 | bitnr = bitnr & 0x1F; | |
498 | if (replay_esn->bmp[nr] & (1U << bitnr)) | |
499 | goto err_replay; | |
500 | ||
2cd08467 SK |
501 | return 0; |
502 | ||
503 | err_replay: | |
504 | x->stats.replay++; | |
505 | err: | |
506 | xfrm_audit_state_replay(x, skb, net_seq); | |
507 | return -EINVAL; | |
508 | } | |
509 | ||
adfc2fdb FW |
510 | int xfrm_replay_check(struct xfrm_state *x, |
511 | struct sk_buff *skb, __be32 net_seq) | |
512 | { | |
513 | switch (x->repl_mode) { | |
514 | case XFRM_REPLAY_MODE_LEGACY: | |
515 | break; | |
516 | case XFRM_REPLAY_MODE_BMP: | |
517 | return xfrm_replay_check_bmp(x, skb, net_seq); | |
518 | case XFRM_REPLAY_MODE_ESN: | |
519 | return xfrm_replay_check_esn(x, skb, net_seq); | |
520 | } | |
521 | ||
522 | return xfrm_replay_check_legacy(x, skb, net_seq); | |
523 | } | |
524 | ||
3b59df46 SK |
525 | static int xfrm_replay_recheck_esn(struct xfrm_state *x, |
526 | struct sk_buff *skb, __be32 net_seq) | |
527 | { | |
528 | if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi != | |
529 | htonl(xfrm_replay_seqhi(x, net_seq)))) { | |
530 | x->stats.replay_window++; | |
531 | return -EINVAL; | |
532 | } | |
533 | ||
534 | return xfrm_replay_check_esn(x, skb, net_seq); | |
535 | } | |
536 | ||
25cfb8bc FW |
537 | int xfrm_replay_recheck(struct xfrm_state *x, |
538 | struct sk_buff *skb, __be32 net_seq) | |
539 | { | |
540 | switch (x->repl_mode) { | |
541 | case XFRM_REPLAY_MODE_LEGACY: | |
542 | break; | |
543 | case XFRM_REPLAY_MODE_BMP: | |
544 | /* no special recheck treatment */ | |
545 | return xfrm_replay_check_bmp(x, skb, net_seq); | |
546 | case XFRM_REPLAY_MODE_ESN: | |
547 | return xfrm_replay_recheck_esn(x, skb, net_seq); | |
548 | } | |
549 | ||
adfc2fdb | 550 | return xfrm_replay_check_legacy(x, skb, net_seq); |
25cfb8bc FW |
551 | } |
552 | ||
2cd08467 SK |
553 | static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq) |
554 | { | |
555 | unsigned int bitnr, nr, i; | |
556 | int wrap; | |
557 | u32 diff, pos, seq, seq_hi; | |
558 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
559 | ||
560 | if (!replay_esn->replay_window) | |
561 | return; | |
562 | ||
563 | seq = ntohl(net_seq); | |
564 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; | |
565 | seq_hi = xfrm_replay_seqhi(x, net_seq); | |
566 | wrap = seq_hi - replay_esn->seq_hi; | |
567 | ||
568 | if ((!wrap && seq > replay_esn->seq) || wrap > 0) { | |
569 | if (likely(!wrap)) | |
570 | diff = seq - replay_esn->seq; | |
571 | else | |
572 | diff = ~replay_esn->seq + seq + 1; | |
573 | ||
574 | if (diff < replay_esn->replay_window) { | |
575 | for (i = 1; i < diff; i++) { | |
576 | bitnr = (pos + i) % replay_esn->replay_window; | |
577 | nr = bitnr >> 5; | |
578 | bitnr = bitnr & 0x1F; | |
579 | replay_esn->bmp[nr] &= ~(1U << bitnr); | |
580 | } | |
2cd08467 | 581 | } else { |
e756682c | 582 | nr = (replay_esn->replay_window - 1) >> 5; |
2cd08467 SK |
583 | for (i = 0; i <= nr; i++) |
584 | replay_esn->bmp[i] = 0; | |
2cd08467 SK |
585 | } |
586 | ||
1d974374 | 587 | bitnr = (pos + diff) % replay_esn->replay_window; |
2cd08467 SK |
588 | replay_esn->seq = seq; |
589 | ||
590 | if (unlikely(wrap > 0)) | |
591 | replay_esn->seq_hi++; | |
592 | } else { | |
593 | diff = replay_esn->seq - seq; | |
594 | ||
1d974374 | 595 | if (pos >= diff) |
2cd08467 | 596 | bitnr = (pos - diff) % replay_esn->replay_window; |
1d974374 | 597 | else |
2cd08467 | 598 | bitnr = replay_esn->replay_window - (diff - pos); |
2cd08467 SK |
599 | } |
600 | ||
50bd870a YE |
601 | xfrm_dev_state_advance_esn(x); |
602 | ||
1d974374 SK |
603 | nr = bitnr >> 5; |
604 | bitnr = bitnr & 0x1F; | |
605 | replay_esn->bmp[nr] |= (1U << bitnr); | |
606 | ||
2cd08467 | 607 | if (xfrm_aevent_is_on(xs_net(x))) |
cfc61c59 | 608 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
2cd08467 SK |
609 | } |
610 | ||
d7dbefc4 SK |
611 | #ifdef CONFIG_XFRM_OFFLOAD |
612 | static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb) | |
613 | { | |
614 | int err = 0; | |
615 | struct net *net = xs_net(x); | |
616 | struct xfrm_offload *xo = xfrm_offload(skb); | |
617 | __u32 oseq = x->replay.oseq; | |
618 | ||
619 | if (!xo) | |
b5a1d1fe | 620 | return __xfrm_replay_overflow(x, skb); |
d7dbefc4 SK |
621 | |
622 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
623 | if (!skb_is_gso(skb)) { | |
624 | XFRM_SKB_CB(skb)->seq.output.low = ++oseq; | |
625 | xo->seq.low = oseq; | |
626 | } else { | |
627 | XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; | |
628 | xo->seq.low = oseq + 1; | |
629 | oseq += skb_shinfo(skb)->gso_segs; | |
630 | } | |
631 | ||
632 | XFRM_SKB_CB(skb)->seq.output.hi = 0; | |
633 | xo->seq.hi = 0; | |
428d2459 PV |
634 | if (unlikely(oseq < x->replay.oseq) && |
635 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { | |
d7dbefc4 SK |
636 | xfrm_audit_state_replay_overflow(x, skb); |
637 | err = -EOVERFLOW; | |
638 | ||
639 | return err; | |
640 | } | |
641 | ||
642 | x->replay.oseq = oseq; | |
643 | ||
644 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 645 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
d7dbefc4 SK |
646 | } |
647 | ||
648 | return err; | |
649 | } | |
650 | ||
651 | static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb) | |
652 | { | |
653 | int err = 0; | |
654 | struct xfrm_offload *xo = xfrm_offload(skb); | |
655 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
656 | struct net *net = xs_net(x); | |
657 | __u32 oseq = replay_esn->oseq; | |
658 | ||
659 | if (!xo) | |
660 | return xfrm_replay_overflow_bmp(x, skb); | |
661 | ||
662 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
663 | if (!skb_is_gso(skb)) { | |
664 | XFRM_SKB_CB(skb)->seq.output.low = ++oseq; | |
665 | xo->seq.low = oseq; | |
666 | } else { | |
667 | XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; | |
668 | xo->seq.low = oseq + 1; | |
669 | oseq += skb_shinfo(skb)->gso_segs; | |
670 | } | |
671 | ||
672 | XFRM_SKB_CB(skb)->seq.output.hi = 0; | |
673 | xo->seq.hi = 0; | |
428d2459 PV |
674 | if (unlikely(oseq < replay_esn->oseq) && |
675 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { | |
d7dbefc4 SK |
676 | xfrm_audit_state_replay_overflow(x, skb); |
677 | err = -EOVERFLOW; | |
678 | ||
679 | return err; | |
680 | } else { | |
681 | replay_esn->oseq = oseq; | |
682 | } | |
683 | ||
684 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 685 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
d7dbefc4 SK |
686 | } |
687 | ||
688 | return err; | |
689 | } | |
690 | ||
691 | static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb) | |
692 | { | |
693 | int err = 0; | |
694 | struct xfrm_offload *xo = xfrm_offload(skb); | |
695 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | |
696 | struct net *net = xs_net(x); | |
697 | __u32 oseq = replay_esn->oseq; | |
698 | __u32 oseq_hi = replay_esn->oseq_hi; | |
699 | ||
700 | if (!xo) | |
701 | return xfrm_replay_overflow_esn(x, skb); | |
702 | ||
703 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | |
704 | if (!skb_is_gso(skb)) { | |
705 | XFRM_SKB_CB(skb)->seq.output.low = ++oseq; | |
706 | XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; | |
707 | xo->seq.low = oseq; | |
708 | xo->seq.hi = oseq_hi; | |
709 | } else { | |
710 | XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; | |
711 | XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; | |
b8b549ee | 712 | xo->seq.low = oseq + 1; |
d7dbefc4 SK |
713 | xo->seq.hi = oseq_hi; |
714 | oseq += skb_shinfo(skb)->gso_segs; | |
715 | } | |
716 | ||
717 | if (unlikely(oseq < replay_esn->oseq)) { | |
718 | XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; | |
719 | xo->seq.hi = oseq_hi; | |
0ba23a21 | 720 | replay_esn->oseq_hi = oseq_hi; |
d7dbefc4 SK |
721 | if (replay_esn->oseq_hi == 0) { |
722 | replay_esn->oseq--; | |
723 | replay_esn->oseq_hi--; | |
724 | xfrm_audit_state_replay_overflow(x, skb); | |
725 | err = -EOVERFLOW; | |
726 | ||
727 | return err; | |
728 | } | |
729 | } | |
730 | ||
731 | replay_esn->oseq = oseq; | |
d7dbefc4 SK |
732 | |
733 | if (xfrm_aevent_is_on(net)) | |
cfc61c59 | 734 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
d7dbefc4 SK |
735 | } |
736 | ||
737 | return err; | |
738 | } | |
739 | ||
b5a1d1fe FW |
740 | int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) |
741 | { | |
742 | switch (x->repl_mode) { | |
743 | case XFRM_REPLAY_MODE_LEGACY: | |
744 | break; | |
745 | case XFRM_REPLAY_MODE_BMP: | |
746 | return xfrm_replay_overflow_offload_bmp(x, skb); | |
747 | case XFRM_REPLAY_MODE_ESN: | |
748 | return xfrm_replay_overflow_offload_esn(x, skb); | |
749 | } | |
d7dbefc4 | 750 | |
b5a1d1fe FW |
751 | return xfrm_replay_overflow_offload(x, skb); |
752 | } | |
d7dbefc4 | 753 | #else |
b5a1d1fe FW |
754 | int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) |
755 | { | |
756 | switch (x->repl_mode) { | |
757 | case XFRM_REPLAY_MODE_LEGACY: | |
758 | break; | |
759 | case XFRM_REPLAY_MODE_BMP: | |
760 | return xfrm_replay_overflow_bmp(x, skb); | |
761 | case XFRM_REPLAY_MODE_ESN: | |
762 | return xfrm_replay_overflow_esn(x, skb); | |
763 | } | |
97e15c3a | 764 | |
b5a1d1fe FW |
765 | return __xfrm_replay_overflow(x, skb); |
766 | } | |
d7dbefc4 | 767 | #endif |
2cd08467 | 768 | |
9fdc4883 SK |
769 | int xfrm_init_replay(struct xfrm_state *x) |
770 | { | |
97e15c3a SK |
771 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; |
772 | ||
773 | if (replay_esn) { | |
774 | if (replay_esn->replay_window > | |
3f602b08 | 775 | replay_esn->bmp_len * sizeof(__u32) * 8) |
97e15c3a SK |
776 | return -EINVAL; |
777 | ||
aafd0d88 UW |
778 | if (x->props.flags & XFRM_STATE_ESN) { |
779 | if (replay_esn->replay_window == 0) | |
780 | return -EINVAL; | |
cfc61c59 | 781 | x->repl_mode = XFRM_REPLAY_MODE_ESN; |
d7dbefc4 | 782 | } else { |
cfc61c59 | 783 | x->repl_mode = XFRM_REPLAY_MODE_BMP; |
d7dbefc4 SK |
784 | } |
785 | } else { | |
cfc61c59 | 786 | x->repl_mode = XFRM_REPLAY_MODE_LEGACY; |
d7dbefc4 | 787 | } |
97e15c3a | 788 | |
9fdc4883 SK |
789 | return 0; |
790 | } | |
791 | EXPORT_SYMBOL(xfrm_init_replay); |