blob: 42d68f324874e65aecae7cf5e0cba0e30aaac80b [file] [log] [blame]
Steffen Klassert9fdc4882011-03-08 00:08:32 +00001/*
2 * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c.
3 */
4
5#include <net/xfrm.h>
6
7static void xfrm_replay_notify(struct xfrm_state *x, int event)
8{
9 struct km_event c;
10 /* we send notify messages in case
11 * 1. we updated on of the sequence numbers, and the seqno difference
12 * is at least x->replay_maxdiff, in this case we also update the
13 * timeout of our timer function
14 * 2. if x->replay_maxage has elapsed since last update,
15 * and there were changes
16 *
17 * The state structure must be locked!
18 */
19
20 switch (event) {
21 case XFRM_REPLAY_UPDATE:
22 if (x->replay_maxdiff &&
23 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
24 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
25 if (x->xflags & XFRM_TIME_DEFER)
26 event = XFRM_REPLAY_TIMEOUT;
27 else
28 return;
29 }
30
31 break;
32
33 case XFRM_REPLAY_TIMEOUT:
34 if (memcmp(&x->replay, &x->preplay,
35 sizeof(struct xfrm_replay_state)) == 0) {
36 x->xflags |= XFRM_TIME_DEFER;
37 return;
38 }
39
40 break;
41 }
42
43 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
44 c.event = XFRM_MSG_NEWAE;
45 c.data.aevent = event;
46 km_state_notify(x, &c);
47
48 if (x->replay_maxage &&
49 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
50 x->xflags &= ~XFRM_TIME_DEFER;
51}
52
53static int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
54{
55 int err = 0;
56 struct net *net = xs_net(x);
57
58 if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
59 XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq;
60 if (unlikely(x->replay.oseq == 0)) {
61 x->replay.oseq--;
62 xfrm_audit_state_replay_overflow(x, skb);
63 err = -EOVERFLOW;
64
65 return err;
66 }
67 if (xfrm_aevent_is_on(net))
68 x->repl->notify(x, XFRM_REPLAY_UPDATE);
69 }
70
71 return err;
72}
73
74static int xfrm_replay_check(struct xfrm_state *x,
75 struct sk_buff *skb, __be32 net_seq)
76{
77 u32 diff;
78 u32 seq = ntohl(net_seq);
79
80 if (unlikely(seq == 0))
81 goto err;
82
83 if (likely(seq > x->replay.seq))
84 return 0;
85
86 diff = x->replay.seq - seq;
87 if (diff >= min_t(unsigned int, x->props.replay_window,
88 sizeof(x->replay.bitmap) * 8)) {
89 x->stats.replay_window++;
90 goto err;
91 }
92
93 if (x->replay.bitmap & (1U << diff)) {
94 x->stats.replay++;
95 goto err;
96 }
97 return 0;
98
99err:
100 xfrm_audit_state_replay(x, skb, net_seq);
101 return -EINVAL;
102}
103
104static void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
105{
106 u32 diff;
107 u32 seq = ntohl(net_seq);
108
109 if (!x->props.replay_window)
110 return;
111
112 if (seq > x->replay.seq) {
113 diff = seq - x->replay.seq;
114 if (diff < x->props.replay_window)
115 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
116 else
117 x->replay.bitmap = 1;
118 x->replay.seq = seq;
119 } else {
120 diff = x->replay.seq - seq;
121 x->replay.bitmap |= (1U << diff);
122 }
123
124 if (xfrm_aevent_is_on(xs_net(x)))
125 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
126}
127
128static struct xfrm_replay xfrm_replay_legacy = {
129 .advance = xfrm_replay_advance,
130 .check = xfrm_replay_check,
131 .notify = xfrm_replay_notify,
132 .overflow = xfrm_replay_overflow,
133};
134
135int xfrm_init_replay(struct xfrm_state *x)
136{
137 x->repl = &xfrm_replay_legacy;
138
139 return 0;
140}
141EXPORT_SYMBOL(xfrm_init_replay);