1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c. |
4 | * |
5 | * Copyright (C) 2010 secunet Security Networks AG |
6 | * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com> |
7 | */ |
8 | |
9 | #include <linux/export.h> |
10 | #include <net/xfrm.h> |
11 | |
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 | } |
36 | EXPORT_SYMBOL(xfrm_replay_seqhi); |
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) |
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 | |
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 | |
65 | switch (event) { |
66 | case XFRM_REPLAY_UPDATE: |
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))) { |
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(p: &x->replay, q: &x->preplay, |
80 | size: 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: &c); |
92 | |
93 | if (x->replay_maxage && |
94 | !mod_timer(timer: &x->rtimer, expires: jiffies + x->replay_maxage)) |
95 | x->xflags &= ~XFRM_TIME_DEFER; |
96 | } |
97 | |
98 | static int __xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) |
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; |
105 | XFRM_SKB_CB(skb)->seq.output.hi = 0; |
106 | if (unlikely(x->replay.oseq == 0) && |
107 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { |
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)) |
115 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
116 | } |
117 | |
118 | return err; |
119 | } |
120 | |
121 | static int xfrm_replay_check_legacy(struct xfrm_state *x, |
122 | struct sk_buff *skb, __be32 net_seq) |
123 | { |
124 | u32 diff; |
125 | u32 seq = ntohl(net_seq); |
126 | |
127 | if (!x->props.replay_window) |
128 | return 0; |
129 | |
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; |
137 | if (diff >= x->props.replay_window) { |
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 | |
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) |
157 | { |
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 | } |
168 | |
169 | if (!x->props.replay_window) |
170 | return; |
171 | |
172 | seq = ntohl(net_seq); |
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(net: xs_net(x))) |
186 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
187 | } |
188 | |
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; |
197 | XFRM_SKB_CB(skb)->seq.output.hi = 0; |
198 | if (unlikely(replay_esn->oseq == 0) && |
199 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { |
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)) |
207 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
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; |
218 | u32 pos; |
219 | u32 seq = ntohl(net_seq); |
220 | u32 diff = replay_esn->seq - seq; |
221 | |
222 | if (!replay_esn->replay_window) |
223 | return 0; |
224 | |
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 | |
236 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
237 | |
238 | if (pos >= diff) |
239 | bitnr = (pos - diff) % replay_esn->replay_window; |
240 | else |
241 | bitnr = replay_esn->replay_window - (diff - pos); |
242 | |
243 | nr = bitnr >> 5; |
244 | bitnr = bitnr & 0x1F; |
245 | if (replay_esn->bmp[nr] & (1U << bitnr)) |
246 | goto err_replay; |
247 | |
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); |
263 | u32 pos; |
264 | |
265 | if (!replay_esn->replay_window) |
266 | return; |
267 | |
268 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
269 | |
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 | } |
280 | } else { |
281 | nr = (replay_esn->replay_window - 1) >> 5; |
282 | for (i = 0; i <= nr; i++) |
283 | replay_esn->bmp[i] = 0; |
284 | } |
285 | |
286 | bitnr = (pos + diff) % replay_esn->replay_window; |
287 | replay_esn->seq = seq; |
288 | } else { |
289 | diff = replay_esn->seq - seq; |
290 | |
291 | if (pos >= diff) |
292 | bitnr = (pos - diff) % replay_esn->replay_window; |
293 | else |
294 | bitnr = replay_esn->replay_window - (diff - pos); |
295 | } |
296 | |
297 | nr = bitnr >> 5; |
298 | bitnr = bitnr & 0x1F; |
299 | replay_esn->bmp[nr] |= (1U << bitnr); |
300 | |
301 | if (xfrm_aevent_is_on(net: xs_net(x))) |
302 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
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: |
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))) { |
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(p: x->replay_esn, q: x->preplay_esn, |
337 | size: 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: &c); |
350 | |
351 | if (x->replay_maxage && |
352 | !mod_timer(timer: &x->rtimer, expires: jiffies + x->replay_maxage)) |
353 | x->xflags &= ~XFRM_TIME_DEFER; |
354 | } |
355 | |
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: |
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; |
381 | |
382 | if (replay_esn->oseq_hi == preplay_esn->oseq_hi) |
383 | oseq_diff = replay_esn->oseq |
384 | - preplay_esn->oseq; |
385 | else |
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; |
392 | } |
393 | |
394 | if (x->xflags & XFRM_TIME_DEFER) |
395 | event = XFRM_REPLAY_TIMEOUT; |
396 | else |
397 | return; |
398 | |
399 | break; |
400 | |
401 | case XFRM_REPLAY_TIMEOUT: |
402 | if (memcmp(p: x->replay_esn, q: x->preplay_esn, |
403 | size: 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: &c); |
416 | |
417 | if (x->replay_maxage && |
418 | !mod_timer(timer: &x->rtimer, expires: jiffies + x->replay_maxage)) |
419 | x->xflags &= ~XFRM_TIME_DEFER; |
420 | } |
421 | |
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)) |
445 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
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; |
457 | u32 pos; |
458 | u32 seq = ntohl(net_seq); |
459 | u32 wsize = replay_esn->replay_window; |
460 | u32 top = replay_esn->seq; |
461 | u32 bottom = top - wsize + 1; |
462 | |
463 | if (!wsize) |
464 | return 0; |
465 | |
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 | |
489 | pos = (replay_esn->seq - 1) % replay_esn->replay_window; |
490 | |
491 | if (pos >= diff) |
492 | bitnr = (pos - diff) % replay_esn->replay_window; |
493 | else |
494 | bitnr = replay_esn->replay_window - (diff - pos); |
495 | |
496 | nr = bitnr >> 5; |
497 | bitnr = bitnr & 0x1F; |
498 | if (replay_esn->bmp[nr] & (1U << bitnr)) |
499 | goto err_replay; |
500 | |
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 | |
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 | |
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 | |
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 | |
550 | return xfrm_replay_check_legacy(x, skb, net_seq); |
551 | } |
552 | |
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 | } |
581 | } else { |
582 | nr = (replay_esn->replay_window - 1) >> 5; |
583 | for (i = 0; i <= nr; i++) |
584 | replay_esn->bmp[i] = 0; |
585 | } |
586 | |
587 | bitnr = (pos + diff) % replay_esn->replay_window; |
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 | |
595 | if (pos >= diff) |
596 | bitnr = (pos - diff) % replay_esn->replay_window; |
597 | else |
598 | bitnr = replay_esn->replay_window - (diff - pos); |
599 | } |
600 | |
601 | xfrm_dev_state_advance_esn(x); |
602 | |
603 | nr = bitnr >> 5; |
604 | bitnr = bitnr & 0x1F; |
605 | replay_esn->bmp[nr] |= (1U << bitnr); |
606 | |
607 | if (xfrm_aevent_is_on(net: xs_net(x))) |
608 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
609 | } |
610 | |
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) |
620 | return __xfrm_replay_overflow(x, skb); |
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; |
634 | if (unlikely(oseq < x->replay.oseq) && |
635 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { |
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)) |
645 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
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; |
674 | if (unlikely(oseq < replay_esn->oseq) && |
675 | !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { |
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)) |
685 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
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; |
712 | xo->seq.low = oseq + 1; |
713 | xo->seq.hi = oseq_hi; |
714 | oseq += skb_shinfo(skb)->gso_segs; |
715 | } |
716 | |
717 | if (unlikely(xo->seq.low < replay_esn->oseq)) { |
718 | XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; |
719 | xo->seq.hi = oseq_hi; |
720 | replay_esn->oseq_hi = oseq_hi; |
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; |
732 | |
733 | if (xfrm_aevent_is_on(net)) |
734 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
735 | } |
736 | |
737 | return err; |
738 | } |
739 | |
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 | } |
750 | |
751 | return xfrm_replay_overflow_offload(x, skb); |
752 | } |
753 | #else |
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 | } |
764 | |
765 | return __xfrm_replay_overflow(x, skb); |
766 | } |
767 | #endif |
768 | |
769 | int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack) |
770 | { |
771 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; |
772 | |
773 | if (replay_esn) { |
774 | if (replay_esn->replay_window > |
775 | replay_esn->bmp_len * sizeof(__u32) * 8) { |
776 | NL_SET_ERR_MSG(extack, "ESN replay window is too large for the chosen bitmap size" ); |
777 | return -EINVAL; |
778 | } |
779 | |
780 | if (x->props.flags & XFRM_STATE_ESN) { |
781 | if (replay_esn->replay_window == 0) { |
782 | NL_SET_ERR_MSG(extack, "ESN replay window must be > 0" ); |
783 | return -EINVAL; |
784 | } |
785 | x->repl_mode = XFRM_REPLAY_MODE_ESN; |
786 | } else { |
787 | x->repl_mode = XFRM_REPLAY_MODE_BMP; |
788 | } |
789 | } else { |
790 | x->repl_mode = XFRM_REPLAY_MODE_LEGACY; |
791 | } |
792 | |
793 | return 0; |
794 | } |
795 | EXPORT_SYMBOL(xfrm_init_replay); |
796 | |