2 * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c.
4 * Copyright (C) 2010 secunet Security Networks AG
5 * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23 u32
xfrm_replay_seqhi(struct xfrm_state
*x
, __be32 net_seq
)
25 u32 seq
, seq_hi
, bottom
;
26 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
28 if (!(x
->props
.flags
& XFRM_STATE_ESN
))
32 seq_hi
= replay_esn
->seq_hi
;
33 bottom
= replay_esn
->seq
- replay_esn
->replay_window
+ 1;
35 if (likely(replay_esn
->seq
>= replay_esn
->replay_window
- 1)) {
36 /* A. same subspace */
37 if (unlikely(seq
< bottom
))
40 /* B. window spans two subspaces */
41 if (unlikely(seq
>= bottom
))
48 static void xfrm_replay_notify(struct xfrm_state
*x
, int event
)
51 /* we send notify messages in case
52 * 1. we updated on of the sequence numbers, and the seqno difference
53 * is at least x->replay_maxdiff, in this case we also update the
54 * timeout of our timer function
55 * 2. if x->replay_maxage has elapsed since last update,
56 * and there were changes
58 * The state structure must be locked!
62 case XFRM_REPLAY_UPDATE
:
63 if (x
->replay_maxdiff
&&
64 (x
->replay
.seq
- x
->preplay
.seq
< x
->replay_maxdiff
) &&
65 (x
->replay
.oseq
- x
->preplay
.oseq
< x
->replay_maxdiff
)) {
66 if (x
->xflags
& XFRM_TIME_DEFER
)
67 event
= XFRM_REPLAY_TIMEOUT
;
74 case XFRM_REPLAY_TIMEOUT
:
75 if (memcmp(&x
->replay
, &x
->preplay
,
76 sizeof(struct xfrm_replay_state
)) == 0) {
77 x
->xflags
|= XFRM_TIME_DEFER
;
84 memcpy(&x
->preplay
, &x
->replay
, sizeof(struct xfrm_replay_state
));
85 c
.event
= XFRM_MSG_NEWAE
;
86 c
.data
.aevent
= event
;
87 km_state_notify(x
, &c
);
89 if (x
->replay_maxage
&&
90 !mod_timer(&x
->rtimer
, jiffies
+ x
->replay_maxage
))
91 x
->xflags
&= ~XFRM_TIME_DEFER
;
94 static int xfrm_replay_overflow(struct xfrm_state
*x
, struct sk_buff
*skb
)
97 struct net
*net
= xs_net(x
);
99 if (x
->type
->flags
& XFRM_TYPE_REPLAY_PROT
) {
100 XFRM_SKB_CB(skb
)->seq
.output
.low
= ++x
->replay
.oseq
;
101 if (unlikely(x
->replay
.oseq
== 0)) {
103 xfrm_audit_state_replay_overflow(x
, skb
);
108 if (xfrm_aevent_is_on(net
))
109 x
->repl
->notify(x
, XFRM_REPLAY_UPDATE
);
115 static int xfrm_replay_check(struct xfrm_state
*x
,
116 struct sk_buff
*skb
, __be32 net_seq
)
119 u32 seq
= ntohl(net_seq
);
121 if (!x
->props
.replay_window
)
124 if (unlikely(seq
== 0))
127 if (likely(seq
> x
->replay
.seq
))
130 diff
= x
->replay
.seq
- seq
;
131 if (diff
>= min_t(unsigned int, x
->props
.replay_window
,
132 sizeof(x
->replay
.bitmap
) * 8)) {
133 x
->stats
.replay_window
++;
137 if (x
->replay
.bitmap
& (1U << diff
)) {
144 xfrm_audit_state_replay(x
, skb
, net_seq
);
148 static void xfrm_replay_advance(struct xfrm_state
*x
, __be32 net_seq
)
151 u32 seq
= ntohl(net_seq
);
153 if (!x
->props
.replay_window
)
156 if (seq
> x
->replay
.seq
) {
157 diff
= seq
- x
->replay
.seq
;
158 if (diff
< x
->props
.replay_window
)
159 x
->replay
.bitmap
= ((x
->replay
.bitmap
) << diff
) | 1;
161 x
->replay
.bitmap
= 1;
164 diff
= x
->replay
.seq
- seq
;
165 x
->replay
.bitmap
|= (1U << diff
);
168 if (xfrm_aevent_is_on(xs_net(x
)))
169 xfrm_replay_notify(x
, XFRM_REPLAY_UPDATE
);
172 static int xfrm_replay_overflow_bmp(struct xfrm_state
*x
, struct sk_buff
*skb
)
175 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
176 struct net
*net
= xs_net(x
);
178 if (x
->type
->flags
& XFRM_TYPE_REPLAY_PROT
) {
179 XFRM_SKB_CB(skb
)->seq
.output
.low
= ++replay_esn
->oseq
;
180 if (unlikely(replay_esn
->oseq
== 0)) {
182 xfrm_audit_state_replay_overflow(x
, skb
);
187 if (xfrm_aevent_is_on(net
))
188 x
->repl
->notify(x
, XFRM_REPLAY_UPDATE
);
194 static int xfrm_replay_check_bmp(struct xfrm_state
*x
,
195 struct sk_buff
*skb
, __be32 net_seq
)
197 unsigned int bitnr
, nr
;
198 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
200 u32 seq
= ntohl(net_seq
);
201 u32 diff
= replay_esn
->seq
- seq
;
203 if (!replay_esn
->replay_window
)
206 pos
= (replay_esn
->seq
- 1) % replay_esn
->replay_window
;
208 if (unlikely(seq
== 0))
211 if (likely(seq
> replay_esn
->seq
))
214 if (diff
>= replay_esn
->replay_window
) {
215 x
->stats
.replay_window
++;
220 bitnr
= (pos
- diff
) % replay_esn
->replay_window
;
222 bitnr
= bitnr
& 0x1F;
223 if (replay_esn
->bmp
[nr
] & (1U << bitnr
))
226 bitnr
= replay_esn
->replay_window
- (diff
- pos
);
228 bitnr
= bitnr
& 0x1F;
229 if (replay_esn
->bmp
[nr
] & (1U << bitnr
))
237 xfrm_audit_state_replay(x
, skb
, net_seq
);
241 static void xfrm_replay_advance_bmp(struct xfrm_state
*x
, __be32 net_seq
)
243 unsigned int bitnr
, nr
, i
;
245 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
246 u32 seq
= ntohl(net_seq
);
247 u32 pos
= (replay_esn
->seq
- 1) % replay_esn
->replay_window
;
249 if (!replay_esn
->replay_window
)
252 if (seq
> replay_esn
->seq
) {
253 diff
= seq
- replay_esn
->seq
;
255 if (diff
< replay_esn
->replay_window
) {
256 for (i
= 1; i
< diff
; i
++) {
257 bitnr
= (pos
+ i
) % replay_esn
->replay_window
;
259 bitnr
= bitnr
& 0x1F;
260 replay_esn
->bmp
[nr
] &= ~(1U << bitnr
);
263 bitnr
= (pos
+ diff
) % replay_esn
->replay_window
;
265 bitnr
= bitnr
& 0x1F;
266 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
268 nr
= (replay_esn
->replay_window
- 1) >> 5;
269 for (i
= 0; i
<= nr
; i
++)
270 replay_esn
->bmp
[i
] = 0;
272 bitnr
= (pos
+ diff
) % replay_esn
->replay_window
;
274 bitnr
= bitnr
& 0x1F;
275 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
278 replay_esn
->seq
= seq
;
280 diff
= replay_esn
->seq
- seq
;
283 bitnr
= (pos
- diff
) % replay_esn
->replay_window
;
285 bitnr
= bitnr
& 0x1F;
286 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
288 bitnr
= replay_esn
->replay_window
- (diff
- pos
);
290 bitnr
= bitnr
& 0x1F;
291 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
295 if (xfrm_aevent_is_on(xs_net(x
)))
296 xfrm_replay_notify(x
, XFRM_REPLAY_UPDATE
);
299 static void xfrm_replay_notify_bmp(struct xfrm_state
*x
, int event
)
302 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
303 struct xfrm_replay_state_esn
*preplay_esn
= x
->preplay_esn
;
305 /* we send notify messages in case
306 * 1. we updated on of the sequence numbers, and the seqno difference
307 * is at least x->replay_maxdiff, in this case we also update the
308 * timeout of our timer function
309 * 2. if x->replay_maxage has elapsed since last update,
310 * and there were changes
312 * The state structure must be locked!
316 case XFRM_REPLAY_UPDATE
:
317 if (x
->replay_maxdiff
&&
318 (replay_esn
->seq
- preplay_esn
->seq
< x
->replay_maxdiff
) &&
319 (replay_esn
->oseq
- preplay_esn
->oseq
< x
->replay_maxdiff
)) {
320 if (x
->xflags
& XFRM_TIME_DEFER
)
321 event
= XFRM_REPLAY_TIMEOUT
;
328 case XFRM_REPLAY_TIMEOUT
:
329 if (memcmp(x
->replay_esn
, x
->preplay_esn
,
330 xfrm_replay_state_esn_len(replay_esn
)) == 0) {
331 x
->xflags
|= XFRM_TIME_DEFER
;
338 memcpy(x
->preplay_esn
, x
->replay_esn
,
339 xfrm_replay_state_esn_len(replay_esn
));
340 c
.event
= XFRM_MSG_NEWAE
;
341 c
.data
.aevent
= event
;
342 km_state_notify(x
, &c
);
344 if (x
->replay_maxage
&&
345 !mod_timer(&x
->rtimer
, jiffies
+ x
->replay_maxage
))
346 x
->xflags
&= ~XFRM_TIME_DEFER
;
349 static int xfrm_replay_overflow_esn(struct xfrm_state
*x
, struct sk_buff
*skb
)
352 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
353 struct net
*net
= xs_net(x
);
355 if (x
->type
->flags
& XFRM_TYPE_REPLAY_PROT
) {
356 XFRM_SKB_CB(skb
)->seq
.output
.low
= ++replay_esn
->oseq
;
357 XFRM_SKB_CB(skb
)->seq
.output
.hi
= replay_esn
->oseq_hi
;
359 if (unlikely(replay_esn
->oseq
== 0)) {
360 XFRM_SKB_CB(skb
)->seq
.output
.hi
= ++replay_esn
->oseq_hi
;
362 if (replay_esn
->oseq_hi
== 0) {
364 replay_esn
->oseq_hi
--;
365 xfrm_audit_state_replay_overflow(x
, skb
);
371 if (xfrm_aevent_is_on(net
))
372 x
->repl
->notify(x
, XFRM_REPLAY_UPDATE
);
378 static int xfrm_replay_check_esn(struct xfrm_state
*x
,
379 struct sk_buff
*skb
, __be32 net_seq
)
381 unsigned int bitnr
, nr
;
383 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
385 u32 seq
= ntohl(net_seq
);
386 u32 wsize
= replay_esn
->replay_window
;
387 u32 top
= replay_esn
->seq
;
388 u32 bottom
= top
- wsize
+ 1;
393 pos
= (replay_esn
->seq
- 1) % replay_esn
->replay_window
;
395 if (unlikely(seq
== 0 && replay_esn
->seq_hi
== 0 &&
396 (replay_esn
->seq
< replay_esn
->replay_window
- 1)))
401 if (likely(top
>= wsize
- 1)) {
402 /* A. same subspace */
403 if (likely(seq
> top
) || seq
< bottom
)
406 /* B. window spans two subspaces */
407 if (likely(seq
> top
&& seq
< bottom
))
410 diff
= ~seq
+ top
+ 1;
413 if (diff
>= replay_esn
->replay_window
) {
414 x
->stats
.replay_window
++;
419 bitnr
= (pos
- diff
) % replay_esn
->replay_window
;
421 bitnr
= bitnr
& 0x1F;
422 if (replay_esn
->bmp
[nr
] & (1U << bitnr
))
425 bitnr
= replay_esn
->replay_window
- (diff
- pos
);
427 bitnr
= bitnr
& 0x1F;
428 if (replay_esn
->bmp
[nr
] & (1U << bitnr
))
436 xfrm_audit_state_replay(x
, skb
, net_seq
);
440 static void xfrm_replay_advance_esn(struct xfrm_state
*x
, __be32 net_seq
)
442 unsigned int bitnr
, nr
, i
;
444 u32 diff
, pos
, seq
, seq_hi
;
445 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
447 if (!replay_esn
->replay_window
)
450 seq
= ntohl(net_seq
);
451 pos
= (replay_esn
->seq
- 1) % replay_esn
->replay_window
;
452 seq_hi
= xfrm_replay_seqhi(x
, net_seq
);
453 wrap
= seq_hi
- replay_esn
->seq_hi
;
455 if ((!wrap
&& seq
> replay_esn
->seq
) || wrap
> 0) {
457 diff
= seq
- replay_esn
->seq
;
459 diff
= ~replay_esn
->seq
+ seq
+ 1;
461 if (diff
< replay_esn
->replay_window
) {
462 for (i
= 1; i
< diff
; i
++) {
463 bitnr
= (pos
+ i
) % replay_esn
->replay_window
;
465 bitnr
= bitnr
& 0x1F;
466 replay_esn
->bmp
[nr
] &= ~(1U << bitnr
);
469 bitnr
= (pos
+ diff
) % replay_esn
->replay_window
;
471 bitnr
= bitnr
& 0x1F;
472 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
474 nr
= (replay_esn
->replay_window
- 1) >> 5;
475 for (i
= 0; i
<= nr
; i
++)
476 replay_esn
->bmp
[i
] = 0;
478 bitnr
= (pos
+ diff
) % replay_esn
->replay_window
;
480 bitnr
= bitnr
& 0x1F;
481 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
484 replay_esn
->seq
= seq
;
486 if (unlikely(wrap
> 0))
487 replay_esn
->seq_hi
++;
489 diff
= replay_esn
->seq
- seq
;
492 bitnr
= (pos
- diff
) % replay_esn
->replay_window
;
494 bitnr
= bitnr
& 0x1F;
495 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
497 bitnr
= replay_esn
->replay_window
- (diff
- pos
);
499 bitnr
= bitnr
& 0x1F;
500 replay_esn
->bmp
[nr
] |= (1U << bitnr
);
504 if (xfrm_aevent_is_on(xs_net(x
)))
505 xfrm_replay_notify(x
, XFRM_REPLAY_UPDATE
);
508 static struct xfrm_replay xfrm_replay_legacy
= {
509 .advance
= xfrm_replay_advance
,
510 .check
= xfrm_replay_check
,
511 .notify
= xfrm_replay_notify
,
512 .overflow
= xfrm_replay_overflow
,
515 static struct xfrm_replay xfrm_replay_bmp
= {
516 .advance
= xfrm_replay_advance_bmp
,
517 .check
= xfrm_replay_check_bmp
,
518 .notify
= xfrm_replay_notify_bmp
,
519 .overflow
= xfrm_replay_overflow_bmp
,
522 static struct xfrm_replay xfrm_replay_esn
= {
523 .advance
= xfrm_replay_advance_esn
,
524 .check
= xfrm_replay_check_esn
,
525 .notify
= xfrm_replay_notify_bmp
,
526 .overflow
= xfrm_replay_overflow_esn
,
529 int xfrm_init_replay(struct xfrm_state
*x
)
531 struct xfrm_replay_state_esn
*replay_esn
= x
->replay_esn
;
534 if (replay_esn
->replay_window
>
535 replay_esn
->bmp_len
* sizeof(__u32
) * 8)
538 if ((x
->props
.flags
& XFRM_STATE_ESN
) && replay_esn
->replay_window
== 0)
541 if ((x
->props
.flags
& XFRM_STATE_ESN
) && x
->replay_esn
)
542 x
->repl
= &xfrm_replay_esn
;
544 x
->repl
= &xfrm_replay_bmp
;
546 x
->repl
= &xfrm_replay_legacy
;
550 EXPORT_SYMBOL(xfrm_init_replay
);