diff options
author | Steffen Klassert <steffen.klassert@secunet.com> | 2011-03-07 19:09:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-13 23:22:30 -0400 |
commit | 97e15c3a8504ea39a209778d7dcdbdf440404a91 (patch) | |
tree | 1fb53589ef65caaadbf63a7cd9417f06f4f80a12 /net/xfrm | |
parent | 9fdc4883d92d20842c5acea77a4a21bb1574b495 (diff) |
xfrm: Support anti-replay window size bigger than 32 packets
As it is, the anti-replay bitmap in struct xfrm_replay_state can
only accomodate 32 packets. Even though it is possible to configure
anti-replay window sizes up to 255 packets from userspace. So we
reject any packet with a sequence number within the configured window
but outside the bitmap. With this patch, we represent the anti-replay
window as a bitmap of variable length that can be accessed via the
new struct xfrm_replay_state_esn. Thus, we have no limit on the
window size anymore. To use the new anti-replay window implementantion,
new userspace tools are required. We leave the old implementation
untouched to stay in sync with old userspace tools.
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_replay.c | 207 |
1 files changed, 206 insertions, 1 deletions
diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 42d68f324874..50589ea9d6a8 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c | |||
@@ -1,5 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c. | 2 | * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c. |
3 | * | ||
4 | * Copyright (C) 2010 secunet Security Networks AG | ||
5 | * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com> | ||
6 | * | ||
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. | ||
10 | * | ||
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 | ||
14 | * more details. | ||
15 | * | ||
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. | ||
3 | */ | 19 | */ |
4 | 20 | ||
5 | #include <net/xfrm.h> | 21 | #include <net/xfrm.h> |
@@ -125,6 +141,178 @@ static void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) | |||
125 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); | 141 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
126 | } | 142 | } |
127 | 143 | ||
144 | static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb) | ||
145 | { | ||
146 | int err = 0; | ||
147 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | ||
148 | struct net *net = xs_net(x); | ||
149 | |||
150 | if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | ||
151 | XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; | ||
152 | if (unlikely(replay_esn->oseq == 0)) { | ||
153 | replay_esn->oseq--; | ||
154 | xfrm_audit_state_replay_overflow(x, skb); | ||
155 | err = -EOVERFLOW; | ||
156 | |||
157 | return err; | ||
158 | } | ||
159 | if (xfrm_aevent_is_on(net)) | ||
160 | x->repl->notify(x, XFRM_REPLAY_UPDATE); | ||
161 | } | ||
162 | |||
163 | return err; | ||
164 | } | ||
165 | |||
166 | static int xfrm_replay_check_bmp(struct xfrm_state *x, | ||
167 | struct sk_buff *skb, __be32 net_seq) | ||
168 | { | ||
169 | unsigned int bitnr, nr; | ||
170 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | ||
171 | u32 seq = ntohl(net_seq); | ||
172 | u32 diff = replay_esn->seq - seq; | ||
173 | u32 pos = (replay_esn->seq - 1) % replay_esn->replay_window; | ||
174 | |||
175 | if (unlikely(seq == 0)) | ||
176 | goto err; | ||
177 | |||
178 | if (likely(seq > replay_esn->seq)) | ||
179 | return 0; | ||
180 | |||
181 | if (diff >= replay_esn->replay_window) { | ||
182 | x->stats.replay_window++; | ||
183 | goto err; | ||
184 | } | ||
185 | |||
186 | if (pos >= diff) { | ||
187 | bitnr = (pos - diff) % replay_esn->replay_window; | ||
188 | nr = bitnr >> 5; | ||
189 | bitnr = bitnr & 0x1F; | ||
190 | if (replay_esn->bmp[nr] & (1U << bitnr)) | ||
191 | goto err_replay; | ||
192 | } else { | ||
193 | bitnr = replay_esn->replay_window - (diff - pos); | ||
194 | nr = bitnr >> 5; | ||
195 | bitnr = bitnr & 0x1F; | ||
196 | if (replay_esn->bmp[nr] & (1U << bitnr)) | ||
197 | goto err_replay; | ||
198 | } | ||
199 | return 0; | ||
200 | |||
201 | err_replay: | ||
202 | x->stats.replay++; | ||
203 | err: | ||
204 | xfrm_audit_state_replay(x, skb, net_seq); | ||
205 | return -EINVAL; | ||
206 | } | ||
207 | |||
208 | static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq) | ||
209 | { | ||
210 | unsigned int bitnr, nr, i; | ||
211 | u32 diff; | ||
212 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | ||
213 | u32 seq = ntohl(net_seq); | ||
214 | u32 pos = (replay_esn->seq - 1) % replay_esn->replay_window; | ||
215 | |||
216 | if (!replay_esn->replay_window) | ||
217 | return; | ||
218 | |||
219 | if (seq > replay_esn->seq) { | ||
220 | diff = seq - replay_esn->seq; | ||
221 | |||
222 | if (diff < replay_esn->replay_window) { | ||
223 | for (i = 1; i < diff; i++) { | ||
224 | bitnr = (pos + i) % replay_esn->replay_window; | ||
225 | nr = bitnr >> 5; | ||
226 | bitnr = bitnr & 0x1F; | ||
227 | replay_esn->bmp[nr] &= ~(1U << bitnr); | ||
228 | } | ||
229 | |||
230 | bitnr = (pos + diff) % replay_esn->replay_window; | ||
231 | nr = bitnr >> 5; | ||
232 | bitnr = bitnr & 0x1F; | ||
233 | replay_esn->bmp[nr] |= (1U << bitnr); | ||
234 | } else { | ||
235 | nr = replay_esn->replay_window >> 5; | ||
236 | for (i = 0; i <= nr; i++) | ||
237 | replay_esn->bmp[i] = 0; | ||
238 | |||
239 | bitnr = (pos + diff) % replay_esn->replay_window; | ||
240 | nr = bitnr >> 5; | ||
241 | bitnr = bitnr & 0x1F; | ||
242 | replay_esn->bmp[nr] |= (1U << bitnr); | ||
243 | } | ||
244 | |||
245 | replay_esn->seq = seq; | ||
246 | } else { | ||
247 | diff = replay_esn->seq - seq; | ||
248 | |||
249 | if (pos >= diff) { | ||
250 | bitnr = (pos - diff) % replay_esn->replay_window; | ||
251 | nr = bitnr >> 5; | ||
252 | bitnr = bitnr & 0x1F; | ||
253 | replay_esn->bmp[nr] |= (1U << bitnr); | ||
254 | } else { | ||
255 | bitnr = replay_esn->replay_window - (diff - pos); | ||
256 | nr = bitnr >> 5; | ||
257 | bitnr = bitnr & 0x1F; | ||
258 | replay_esn->bmp[nr] |= (1U << bitnr); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if (xfrm_aevent_is_on(xs_net(x))) | ||
263 | xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); | ||
264 | } | ||
265 | |||
266 | static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event) | ||
267 | { | ||
268 | struct km_event c; | ||
269 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | ||
270 | struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn; | ||
271 | |||
272 | /* we send notify messages in case | ||
273 | * 1. we updated on of the sequence numbers, and the seqno difference | ||
274 | * is at least x->replay_maxdiff, in this case we also update the | ||
275 | * timeout of our timer function | ||
276 | * 2. if x->replay_maxage has elapsed since last update, | ||
277 | * and there were changes | ||
278 | * | ||
279 | * The state structure must be locked! | ||
280 | */ | ||
281 | |||
282 | switch (event) { | ||
283 | case XFRM_REPLAY_UPDATE: | ||
284 | if (x->replay_maxdiff && | ||
285 | (replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && | ||
286 | (replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) { | ||
287 | if (x->xflags & XFRM_TIME_DEFER) | ||
288 | event = XFRM_REPLAY_TIMEOUT; | ||
289 | else | ||
290 | return; | ||
291 | } | ||
292 | |||
293 | break; | ||
294 | |||
295 | case XFRM_REPLAY_TIMEOUT: | ||
296 | if (memcmp(x->replay_esn, x->preplay_esn, | ||
297 | xfrm_replay_state_esn_len(replay_esn)) == 0) { | ||
298 | x->xflags |= XFRM_TIME_DEFER; | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | break; | ||
303 | } | ||
304 | |||
305 | memcpy(x->preplay_esn, x->replay_esn, | ||
306 | xfrm_replay_state_esn_len(replay_esn)); | ||
307 | c.event = XFRM_MSG_NEWAE; | ||
308 | c.data.aevent = event; | ||
309 | km_state_notify(x, &c); | ||
310 | |||
311 | if (x->replay_maxage && | ||
312 | !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) | ||
313 | x->xflags &= ~XFRM_TIME_DEFER; | ||
314 | } | ||
315 | |||
128 | static struct xfrm_replay xfrm_replay_legacy = { | 316 | static struct xfrm_replay xfrm_replay_legacy = { |
129 | .advance = xfrm_replay_advance, | 317 | .advance = xfrm_replay_advance, |
130 | .check = xfrm_replay_check, | 318 | .check = xfrm_replay_check, |
@@ -132,9 +320,26 @@ static struct xfrm_replay xfrm_replay_legacy = { | |||
132 | .overflow = xfrm_replay_overflow, | 320 | .overflow = xfrm_replay_overflow, |
133 | }; | 321 | }; |
134 | 322 | ||
323 | static struct xfrm_replay xfrm_replay_bmp = { | ||
324 | .advance = xfrm_replay_advance_bmp, | ||
325 | .check = xfrm_replay_check_bmp, | ||
326 | .notify = xfrm_replay_notify_bmp, | ||
327 | .overflow = xfrm_replay_overflow_bmp, | ||
328 | }; | ||
329 | |||
135 | int xfrm_init_replay(struct xfrm_state *x) | 330 | int xfrm_init_replay(struct xfrm_state *x) |
136 | { | 331 | { |
137 | x->repl = &xfrm_replay_legacy; | 332 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; |
333 | |||
334 | if (replay_esn) { | ||
335 | if (replay_esn->replay_window > | ||
336 | replay_esn->bmp_len * sizeof(__u32)) | ||
337 | return -EINVAL; | ||
338 | |||
339 | x->repl = &xfrm_replay_bmp; | ||
340 | } else | ||
341 | x->repl = &xfrm_replay_legacy; | ||
342 | |||
138 | 343 | ||
139 | return 0; | 344 | return 0; |
140 | } | 345 | } |