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