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