blob: 2a9992894e69d77193ef1ac20510fb02eea5c35f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * xfrm_state.c
3 *
4 * Changes:
5 * Mitsuru KANDA @USAGI
6 * Kazunori MIYAZAWA @USAGI
7 * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8 * IPv6 support
9 * YOSHIFUJI Hideaki @USAGI
10 * Split up af-specific functions
11 * Derek Atkins <derek@ihtfp.com>
12 * Add UDP Encapsulation
Trent Jaegerdf718372005-12-13 23:12:27 -080013 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 */
15
16#include <linux/workqueue.h>
17#include <net/xfrm.h>
18#include <linux/pfkeyv2.h>
19#include <linux/ipsec.h>
20#include <linux/module.h>
21#include <asm/uaccess.h>
22
David S. Milleree857a72006-03-20 19:18:37 -080023struct sock *xfrm_nl;
24EXPORT_SYMBOL(xfrm_nl);
25
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080026u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080027EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
28
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080029u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080030EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
31
Linus Torvalds1da177e2005-04-16 15:20:36 -070032/* Each xfrm_state may be linked to two tables:
33
34 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
35 2. Hash table by daddr to find what SAs exist for given
36 destination/tunnel endpoint. (output)
37 */
38
39static DEFINE_SPINLOCK(xfrm_state_lock);
40
41/* Hash table to find appropriate SA towards given target (endpoint
42 * of tunnel or destination of transport mode) allowed by selector.
43 *
44 * Main use is finding SA after policy selected tunnel or transport mode.
45 * Also, it can be used by ah/esp icmp error handler to find offending SA.
46 */
47static struct list_head xfrm_state_bydst[XFRM_DST_HSIZE];
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -070048static struct list_head xfrm_state_bysrc[XFRM_DST_HSIZE];
Linus Torvalds1da177e2005-04-16 15:20:36 -070049static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
50
51DECLARE_WAIT_QUEUE_HEAD(km_waitq);
52EXPORT_SYMBOL(km_waitq);
53
54static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
55static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
56
57static struct work_struct xfrm_state_gc_work;
58static struct list_head xfrm_state_gc_list = LIST_HEAD_INIT(xfrm_state_gc_list);
59static DEFINE_SPINLOCK(xfrm_state_gc_lock);
60
61static int xfrm_state_gc_flush_bundles;
62
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -080063int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
66static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
67
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -080068int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -080069void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71static void xfrm_state_gc_destroy(struct xfrm_state *x)
72{
73 if (del_timer(&x->timer))
74 BUG();
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080075 if (del_timer(&x->rtimer))
76 BUG();
Jesper Juhla51482b2005-11-08 09:41:34 -080077 kfree(x->aalg);
78 kfree(x->ealg);
79 kfree(x->calg);
80 kfree(x->encap);
Herbert Xub59f45d2006-05-27 23:05:54 -070081 if (x->mode)
82 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 if (x->type) {
84 x->type->destructor(x);
85 xfrm_put_type(x->type);
86 }
Trent Jaegerdf718372005-12-13 23:12:27 -080087 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 kfree(x);
89}
90
91static void xfrm_state_gc_task(void *data)
92{
93 struct xfrm_state *x;
94 struct list_head *entry, *tmp;
95 struct list_head gc_list = LIST_HEAD_INIT(gc_list);
96
97 if (xfrm_state_gc_flush_bundles) {
98 xfrm_state_gc_flush_bundles = 0;
99 xfrm_flush_bundles();
100 }
101
102 spin_lock_bh(&xfrm_state_gc_lock);
103 list_splice_init(&xfrm_state_gc_list, &gc_list);
104 spin_unlock_bh(&xfrm_state_gc_lock);
105
106 list_for_each_safe(entry, tmp, &gc_list) {
107 x = list_entry(entry, struct xfrm_state, bydst);
108 xfrm_state_gc_destroy(x);
109 }
110 wake_up(&km_waitq);
111}
112
113static inline unsigned long make_jiffies(long secs)
114{
115 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
116 return MAX_SCHEDULE_TIMEOUT-1;
117 else
118 return secs*HZ;
119}
120
121static void xfrm_timer_handler(unsigned long data)
122{
123 struct xfrm_state *x = (struct xfrm_state*)data;
124 unsigned long now = (unsigned long)xtime.tv_sec;
125 long next = LONG_MAX;
126 int warn = 0;
127
128 spin_lock(&x->lock);
129 if (x->km.state == XFRM_STATE_DEAD)
130 goto out;
131 if (x->km.state == XFRM_STATE_EXPIRED)
132 goto expired;
133 if (x->lft.hard_add_expires_seconds) {
134 long tmo = x->lft.hard_add_expires_seconds +
135 x->curlft.add_time - now;
136 if (tmo <= 0)
137 goto expired;
138 if (tmo < next)
139 next = tmo;
140 }
141 if (x->lft.hard_use_expires_seconds) {
142 long tmo = x->lft.hard_use_expires_seconds +
143 (x->curlft.use_time ? : now) - now;
144 if (tmo <= 0)
145 goto expired;
146 if (tmo < next)
147 next = tmo;
148 }
149 if (x->km.dying)
150 goto resched;
151 if (x->lft.soft_add_expires_seconds) {
152 long tmo = x->lft.soft_add_expires_seconds +
153 x->curlft.add_time - now;
154 if (tmo <= 0)
155 warn = 1;
156 else if (tmo < next)
157 next = tmo;
158 }
159 if (x->lft.soft_use_expires_seconds) {
160 long tmo = x->lft.soft_use_expires_seconds +
161 (x->curlft.use_time ? : now) - now;
162 if (tmo <= 0)
163 warn = 1;
164 else if (tmo < next)
165 next = tmo;
166 }
167
Herbert Xu4666faa2005-06-18 22:43:22 -0700168 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800170 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171resched:
172 if (next != LONG_MAX &&
173 !mod_timer(&x->timer, jiffies + make_jiffies(next)))
174 xfrm_state_hold(x);
175 goto out;
176
177expired:
178 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
179 x->km.state = XFRM_STATE_EXPIRED;
180 wake_up(&km_waitq);
181 next = 2;
182 goto resched;
183 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700184 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800185 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187out:
188 spin_unlock(&x->lock);
189 xfrm_state_put(x);
190}
191
David S. Miller0ac84752006-03-20 19:18:23 -0800192static void xfrm_replay_timer_handler(unsigned long data);
193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194struct xfrm_state *xfrm_state_alloc(void)
195{
196 struct xfrm_state *x;
197
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700198 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
200 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 atomic_set(&x->refcnt, 1);
202 atomic_set(&x->tunnel_users, 0);
203 INIT_LIST_HEAD(&x->bydst);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700204 INIT_LIST_HEAD(&x->bysrc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 INIT_LIST_HEAD(&x->byspi);
206 init_timer(&x->timer);
207 x->timer.function = xfrm_timer_handler;
208 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800209 init_timer(&x->rtimer);
210 x->rtimer.function = xfrm_replay_timer_handler;
211 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 x->curlft.add_time = (unsigned long)xtime.tv_sec;
213 x->lft.soft_byte_limit = XFRM_INF;
214 x->lft.soft_packet_limit = XFRM_INF;
215 x->lft.hard_byte_limit = XFRM_INF;
216 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800217 x->replay_maxage = 0;
218 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 spin_lock_init(&x->lock);
220 }
221 return x;
222}
223EXPORT_SYMBOL(xfrm_state_alloc);
224
225void __xfrm_state_destroy(struct xfrm_state *x)
226{
227 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
228
229 spin_lock_bh(&xfrm_state_gc_lock);
230 list_add(&x->bydst, &xfrm_state_gc_list);
231 spin_unlock_bh(&xfrm_state_gc_lock);
232 schedule_work(&xfrm_state_gc_work);
233}
234EXPORT_SYMBOL(__xfrm_state_destroy);
235
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800236int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700238 int err = -ESRCH;
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (x->km.state != XFRM_STATE_DEAD) {
241 x->km.state = XFRM_STATE_DEAD;
242 spin_lock(&xfrm_state_lock);
243 list_del(&x->bydst);
Herbert Xu21380b82006-02-22 14:47:13 -0800244 __xfrm_state_put(x);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700245 list_del(&x->bysrc);
246 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 if (x->id.spi) {
248 list_del(&x->byspi);
Herbert Xu21380b82006-02-22 14:47:13 -0800249 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251 spin_unlock(&xfrm_state_lock);
252 if (del_timer(&x->timer))
Herbert Xu21380b82006-02-22 14:47:13 -0800253 __xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800254 if (del_timer(&x->rtimer))
255 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 /* The number two in this test is the reference
258 * mentioned in the comment below plus the reference
259 * our caller holds. A larger value means that
260 * there are DSTs attached to this xfrm_state.
261 */
262 if (atomic_read(&x->refcnt) > 2) {
263 xfrm_state_gc_flush_bundles = 1;
264 schedule_work(&xfrm_state_gc_work);
265 }
266
267 /* All xfrm_state objects are created by xfrm_state_alloc.
268 * The xfrm_state_alloc call gives a reference, and that
269 * is what we are dropping here.
270 */
Herbert Xu21380b82006-02-22 14:47:13 -0800271 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700272 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700274
275 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800277EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700279int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700281 int err;
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700284 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700286
287 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288}
289EXPORT_SYMBOL(xfrm_state_delete);
290
291void xfrm_state_flush(u8 proto)
292{
293 int i;
294 struct xfrm_state *x;
295
296 spin_lock_bh(&xfrm_state_lock);
297 for (i = 0; i < XFRM_DST_HSIZE; i++) {
298restart:
299 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
300 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700301 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 xfrm_state_hold(x);
303 spin_unlock_bh(&xfrm_state_lock);
304
305 xfrm_state_delete(x);
306 xfrm_state_put(x);
307
308 spin_lock_bh(&xfrm_state_lock);
309 goto restart;
310 }
311 }
312 }
313 spin_unlock_bh(&xfrm_state_lock);
314 wake_up(&km_waitq);
315}
316EXPORT_SYMBOL(xfrm_state_flush);
317
318static int
319xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
320 struct xfrm_tmpl *tmpl,
321 xfrm_address_t *daddr, xfrm_address_t *saddr,
322 unsigned short family)
323{
324 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
325 if (!afinfo)
326 return -1;
327 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
328 xfrm_state_put_afinfo(afinfo);
329 return 0;
330}
331
332struct xfrm_state *
333xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
334 struct flowi *fl, struct xfrm_tmpl *tmpl,
335 struct xfrm_policy *pol, int *err,
336 unsigned short family)
337{
338 unsigned h = xfrm_dst_hash(daddr, family);
339 struct xfrm_state *x, *x0;
340 int acquire_in_progress = 0;
341 int error = 0;
342 struct xfrm_state *best = NULL;
343 struct xfrm_state_afinfo *afinfo;
344
345 afinfo = xfrm_state_get_afinfo(family);
346 if (afinfo == NULL) {
347 *err = -EAFNOSUPPORT;
348 return NULL;
349 }
350
351 spin_lock_bh(&xfrm_state_lock);
352 list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
353 if (x->props.family == family &&
354 x->props.reqid == tmpl->reqid &&
355 xfrm_state_addr_check(x, daddr, saddr, family) &&
356 tmpl->mode == x->props.mode &&
357 tmpl->id.proto == x->id.proto &&
358 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
359 /* Resolution logic:
360 1. There is a valid state with matching selector.
361 Done.
362 2. Valid state with inappropriate selector. Skip.
363
364 Entering area of "sysdeps".
365
366 3. If state is not valid, selector is temporary,
367 it selects only session which triggered
368 previous resolution. Key manager will do
369 something to install a state with proper
370 selector.
371 */
372 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800373 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700374 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 continue;
376 if (!best ||
377 best->km.dying > x->km.dying ||
378 (best->km.dying == x->km.dying &&
379 best->curlft.add_time < x->curlft.add_time))
380 best = x;
381 } else if (x->km.state == XFRM_STATE_ACQ) {
382 acquire_in_progress = 1;
383 } else if (x->km.state == XFRM_STATE_ERROR ||
384 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800385 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700386 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 error = -ESRCH;
388 }
389 }
390 }
391
392 x = best;
393 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700394 if (tmpl->id.spi &&
395 (x0 = afinfo->state_lookup(daddr, tmpl->id.spi,
396 tmpl->id.proto)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 xfrm_state_put(x0);
398 error = -EEXIST;
399 goto out;
400 }
401 x = xfrm_state_alloc();
402 if (x == NULL) {
403 error = -ENOMEM;
404 goto out;
405 }
406 /* Initialize temporary selector matching only
407 * to current session. */
408 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
409
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700410 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
411 if (error) {
412 x->km.state = XFRM_STATE_DEAD;
413 xfrm_state_put(x);
414 x = NULL;
415 goto out;
416 }
417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 if (km_query(x, tmpl, pol) == 0) {
419 x->km.state = XFRM_STATE_ACQ;
420 list_add_tail(&x->bydst, xfrm_state_bydst+h);
421 xfrm_state_hold(x);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700422 list_add_tail(&x->bysrc, xfrm_state_bysrc+h);
423 xfrm_state_hold(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if (x->id.spi) {
425 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
426 list_add(&x->byspi, xfrm_state_byspi+h);
427 xfrm_state_hold(x);
428 }
429 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
430 xfrm_state_hold(x);
431 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
432 add_timer(&x->timer);
433 } else {
434 x->km.state = XFRM_STATE_DEAD;
435 xfrm_state_put(x);
436 x = NULL;
437 error = -ESRCH;
438 }
439 }
440out:
441 if (x)
442 xfrm_state_hold(x);
443 else
444 *err = acquire_in_progress ? -EAGAIN : error;
445 spin_unlock_bh(&xfrm_state_lock);
446 xfrm_state_put_afinfo(afinfo);
447 return x;
448}
449
450static void __xfrm_state_insert(struct xfrm_state *x)
451{
452 unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
453
454 list_add(&x->bydst, xfrm_state_bydst+h);
455 xfrm_state_hold(x);
456
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700457 h = xfrm_src_hash(&x->props.saddr, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700459 list_add(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 xfrm_state_hold(x);
461
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700462 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
463 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
464 x->props.family);
465
466 list_add(&x->byspi, xfrm_state_byspi+h);
467 xfrm_state_hold(x);
468 }
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 if (!mod_timer(&x->timer, jiffies + HZ))
471 xfrm_state_hold(x);
472
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800473 if (x->replay_maxage &&
474 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
475 xfrm_state_hold(x);
476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 wake_up(&km_waitq);
478}
479
480void xfrm_state_insert(struct xfrm_state *x)
481{
482 spin_lock_bh(&xfrm_state_lock);
483 __xfrm_state_insert(x);
484 spin_unlock_bh(&xfrm_state_lock);
David S. Miller399c1802005-12-19 14:23:23 -0800485
486 xfrm_flush_all_bundles();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488EXPORT_SYMBOL(xfrm_state_insert);
489
490static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
491
492int xfrm_state_add(struct xfrm_state *x)
493{
494 struct xfrm_state_afinfo *afinfo;
495 struct xfrm_state *x1;
496 int family;
497 int err;
498
499 family = x->props.family;
500 afinfo = xfrm_state_get_afinfo(family);
501 if (unlikely(afinfo == NULL))
502 return -EAFNOSUPPORT;
503
504 spin_lock_bh(&xfrm_state_lock);
505
506 x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
507 if (x1) {
508 xfrm_state_put(x1);
509 x1 = NULL;
510 err = -EEXIST;
511 goto out;
512 }
513
514 if (x->km.seq) {
515 x1 = __xfrm_find_acq_byseq(x->km.seq);
516 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
517 xfrm_state_put(x1);
518 x1 = NULL;
519 }
520 }
521
522 if (!x1)
523 x1 = afinfo->find_acq(
524 x->props.mode, x->props.reqid, x->id.proto,
525 &x->id.daddr, &x->props.saddr, 0);
526
527 __xfrm_state_insert(x);
528 err = 0;
529
530out:
531 spin_unlock_bh(&xfrm_state_lock);
532 xfrm_state_put_afinfo(afinfo);
533
David S. Miller399c1802005-12-19 14:23:23 -0800534 if (!err)
535 xfrm_flush_all_bundles();
536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 if (x1) {
538 xfrm_state_delete(x1);
539 xfrm_state_put(x1);
540 }
541
542 return err;
543}
544EXPORT_SYMBOL(xfrm_state_add);
545
546int xfrm_state_update(struct xfrm_state *x)
547{
548 struct xfrm_state_afinfo *afinfo;
549 struct xfrm_state *x1;
550 int err;
551
552 afinfo = xfrm_state_get_afinfo(x->props.family);
553 if (unlikely(afinfo == NULL))
554 return -EAFNOSUPPORT;
555
556 spin_lock_bh(&xfrm_state_lock);
557 x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
558
559 err = -ESRCH;
560 if (!x1)
561 goto out;
562
563 if (xfrm_state_kern(x1)) {
564 xfrm_state_put(x1);
565 err = -EEXIST;
566 goto out;
567 }
568
569 if (x1->km.state == XFRM_STATE_ACQ) {
570 __xfrm_state_insert(x);
571 x = NULL;
572 }
573 err = 0;
574
575out:
576 spin_unlock_bh(&xfrm_state_lock);
577 xfrm_state_put_afinfo(afinfo);
578
579 if (err)
580 return err;
581
582 if (!x) {
583 xfrm_state_delete(x1);
584 xfrm_state_put(x1);
585 return 0;
586 }
587
588 err = -EINVAL;
589 spin_lock_bh(&x1->lock);
590 if (likely(x1->km.state == XFRM_STATE_VALID)) {
591 if (x->encap && x1->encap)
592 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
593 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
594 x1->km.dying = 0;
595
596 if (!mod_timer(&x1->timer, jiffies + HZ))
597 xfrm_state_hold(x1);
598 if (x1->curlft.use_time)
599 xfrm_state_check_expire(x1);
600
601 err = 0;
602 }
603 spin_unlock_bh(&x1->lock);
604
605 xfrm_state_put(x1);
606
607 return err;
608}
609EXPORT_SYMBOL(xfrm_state_update);
610
611int xfrm_state_check_expire(struct xfrm_state *x)
612{
613 if (!x->curlft.use_time)
614 x->curlft.use_time = (unsigned long)xtime.tv_sec;
615
616 if (x->km.state != XFRM_STATE_VALID)
617 return -EINVAL;
618
619 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
620 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700621 x->km.state = XFRM_STATE_EXPIRED;
622 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 xfrm_state_hold(x);
624 return -EINVAL;
625 }
626
627 if (!x->km.dying &&
628 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700629 x->curlft.packets >= x->lft.soft_packet_limit)) {
630 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800631 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return 0;
634}
635EXPORT_SYMBOL(xfrm_state_check_expire);
636
637static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
638{
639 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
640 - skb_headroom(skb);
641
642 if (nhead > 0)
643 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
644
645 /* Check tail too... */
646 return 0;
647}
648
649int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
650{
651 int err = xfrm_state_check_expire(x);
652 if (err < 0)
653 goto err;
654 err = xfrm_state_check_space(x, skb);
655err:
656 return err;
657}
658EXPORT_SYMBOL(xfrm_state_check);
659
660struct xfrm_state *
661xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
662 unsigned short family)
663{
664 struct xfrm_state *x;
665 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
666 if (!afinfo)
667 return NULL;
668
669 spin_lock_bh(&xfrm_state_lock);
670 x = afinfo->state_lookup(daddr, spi, proto);
671 spin_unlock_bh(&xfrm_state_lock);
672 xfrm_state_put_afinfo(afinfo);
673 return x;
674}
675EXPORT_SYMBOL(xfrm_state_lookup);
676
677struct xfrm_state *
678xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
679 xfrm_address_t *daddr, xfrm_address_t *saddr,
680 int create, unsigned short family)
681{
682 struct xfrm_state *x;
683 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
684 if (!afinfo)
685 return NULL;
686
687 spin_lock_bh(&xfrm_state_lock);
688 x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
689 spin_unlock_bh(&xfrm_state_lock);
690 xfrm_state_put_afinfo(afinfo);
691 return x;
692}
693EXPORT_SYMBOL(xfrm_find_acq);
694
695/* Silly enough, but I'm lazy to build resolution list */
696
697static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
698{
699 int i;
700 struct xfrm_state *x;
701
702 for (i = 0; i < XFRM_DST_HSIZE; i++) {
703 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
704 if (x->km.seq == seq && x->km.state == XFRM_STATE_ACQ) {
705 xfrm_state_hold(x);
706 return x;
707 }
708 }
709 }
710 return NULL;
711}
712
713struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
714{
715 struct xfrm_state *x;
716
717 spin_lock_bh(&xfrm_state_lock);
718 x = __xfrm_find_acq_byseq(seq);
719 spin_unlock_bh(&xfrm_state_lock);
720 return x;
721}
722EXPORT_SYMBOL(xfrm_find_acq_byseq);
723
724u32 xfrm_get_acqseq(void)
725{
726 u32 res;
727 static u32 acqseq;
728 static DEFINE_SPINLOCK(acqseq_lock);
729
730 spin_lock_bh(&acqseq_lock);
731 res = (++acqseq ? : ++acqseq);
732 spin_unlock_bh(&acqseq_lock);
733 return res;
734}
735EXPORT_SYMBOL(xfrm_get_acqseq);
736
737void
738xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
739{
740 u32 h;
741 struct xfrm_state *x0;
742
743 if (x->id.spi)
744 return;
745
746 if (minspi == maxspi) {
747 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
748 if (x0) {
749 xfrm_state_put(x0);
750 return;
751 }
752 x->id.spi = minspi;
753 } else {
754 u32 spi = 0;
755 minspi = ntohl(minspi);
756 maxspi = ntohl(maxspi);
757 for (h=0; h<maxspi-minspi+1; h++) {
758 spi = minspi + net_random()%(maxspi-minspi+1);
759 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
760 if (x0 == NULL) {
761 x->id.spi = htonl(spi);
762 break;
763 }
764 xfrm_state_put(x0);
765 }
766 }
767 if (x->id.spi) {
768 spin_lock_bh(&xfrm_state_lock);
769 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
770 list_add(&x->byspi, xfrm_state_byspi+h);
771 xfrm_state_hold(x);
772 spin_unlock_bh(&xfrm_state_lock);
773 wake_up(&km_waitq);
774 }
775}
776EXPORT_SYMBOL(xfrm_alloc_spi);
777
778int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
779 void *data)
780{
781 int i;
782 struct xfrm_state *x;
783 int count = 0;
784 int err = 0;
785
786 spin_lock_bh(&xfrm_state_lock);
787 for (i = 0; i < XFRM_DST_HSIZE; i++) {
788 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700789 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 count++;
791 }
792 }
793 if (count == 0) {
794 err = -ENOENT;
795 goto out;
796 }
797
798 for (i = 0; i < XFRM_DST_HSIZE; i++) {
799 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700800 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 continue;
802 err = func(x, --count, data);
803 if (err)
804 goto out;
805 }
806 }
807out:
808 spin_unlock_bh(&xfrm_state_lock);
809 return err;
810}
811EXPORT_SYMBOL(xfrm_state_walk);
812
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800813
814void xfrm_replay_notify(struct xfrm_state *x, int event)
815{
816 struct km_event c;
817 /* we send notify messages in case
818 * 1. we updated on of the sequence numbers, and the seqno difference
819 * is at least x->replay_maxdiff, in this case we also update the
820 * timeout of our timer function
821 * 2. if x->replay_maxage has elapsed since last update,
822 * and there were changes
823 *
824 * The state structure must be locked!
825 */
826
827 switch (event) {
828 case XFRM_REPLAY_UPDATE:
829 if (x->replay_maxdiff &&
830 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700831 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
832 if (x->xflags & XFRM_TIME_DEFER)
833 event = XFRM_REPLAY_TIMEOUT;
834 else
835 return;
836 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800837
838 break;
839
840 case XFRM_REPLAY_TIMEOUT:
841 if ((x->replay.seq == x->preplay.seq) &&
842 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700843 (x->replay.oseq == x->preplay.oseq)) {
844 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800845 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700846 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800847
848 break;
849 }
850
851 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
852 c.event = XFRM_MSG_NEWAE;
853 c.data.aevent = event;
854 km_state_notify(x, &c);
855
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800856 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700857 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800858 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700859 x->xflags &= ~XFRM_TIME_DEFER;
860 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800861}
David S. Millera70fcb02006-03-20 19:18:52 -0800862EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800863
864static void xfrm_replay_timer_handler(unsigned long data)
865{
866 struct xfrm_state *x = (struct xfrm_state*)data;
867
868 spin_lock(&x->lock);
869
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700870 if (x->km.state == XFRM_STATE_VALID) {
871 if (xfrm_aevent_is_on())
872 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
873 else
874 x->xflags |= XFRM_TIME_DEFER;
875 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800876
877 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700878 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800879}
880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881int xfrm_replay_check(struct xfrm_state *x, u32 seq)
882{
883 u32 diff;
884
885 seq = ntohl(seq);
886
887 if (unlikely(seq == 0))
888 return -EINVAL;
889
890 if (likely(seq > x->replay.seq))
891 return 0;
892
893 diff = x->replay.seq - seq;
894 if (diff >= x->props.replay_window) {
895 x->stats.replay_window++;
896 return -EINVAL;
897 }
898
899 if (x->replay.bitmap & (1U << diff)) {
900 x->stats.replay++;
901 return -EINVAL;
902 }
903 return 0;
904}
905EXPORT_SYMBOL(xfrm_replay_check);
906
907void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
908{
909 u32 diff;
910
911 seq = ntohl(seq);
912
913 if (seq > x->replay.seq) {
914 diff = seq - x->replay.seq;
915 if (diff < x->props.replay_window)
916 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
917 else
918 x->replay.bitmap = 1;
919 x->replay.seq = seq;
920 } else {
921 diff = x->replay.seq - seq;
922 x->replay.bitmap |= (1U << diff);
923 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800924
925 if (xfrm_aevent_is_on())
926 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927}
928EXPORT_SYMBOL(xfrm_replay_advance);
929
930static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
931static DEFINE_RWLOCK(xfrm_km_lock);
932
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700933void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934{
935 struct xfrm_mgr *km;
936
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700937 read_lock(&xfrm_km_lock);
938 list_for_each_entry(km, &xfrm_km_list, list)
939 if (km->notify_policy)
940 km->notify_policy(xp, dir, c);
941 read_unlock(&xfrm_km_lock);
942}
943
944void km_state_notify(struct xfrm_state *x, struct km_event *c)
945{
946 struct xfrm_mgr *km;
947 read_lock(&xfrm_km_lock);
948 list_for_each_entry(km, &xfrm_km_list, list)
949 if (km->notify)
950 km->notify(x, c);
951 read_unlock(&xfrm_km_lock);
952}
953
954EXPORT_SYMBOL(km_policy_notify);
955EXPORT_SYMBOL(km_state_notify);
956
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800957void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700958{
959 struct km_event c;
960
Herbert Xubf088672005-06-18 22:44:00 -0700961 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800962 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -0700963 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700964 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 if (hard)
967 wake_up(&km_waitq);
968}
969
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800970EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700971/*
972 * We send to all registered managers regardless of failure
973 * We are happy with one success
974*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800975int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700977 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 struct xfrm_mgr *km;
979
980 read_lock(&xfrm_km_lock);
981 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700982 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
983 if (!acqret)
984 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 }
986 read_unlock(&xfrm_km_lock);
987 return err;
988}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800989EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
992{
993 int err = -EINVAL;
994 struct xfrm_mgr *km;
995
996 read_lock(&xfrm_km_lock);
997 list_for_each_entry(km, &xfrm_km_list, list) {
998 if (km->new_mapping)
999 err = km->new_mapping(x, ipaddr, sport);
1000 if (!err)
1001 break;
1002 }
1003 read_unlock(&xfrm_km_lock);
1004 return err;
1005}
1006EXPORT_SYMBOL(km_new_mapping);
1007
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001008void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001010 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
Herbert Xubf088672005-06-18 22:44:00 -07001012 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001013 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001014 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001015 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017 if (hard)
1018 wake_up(&km_waitq);
1019}
David S. Millera70fcb02006-03-20 19:18:52 -08001020EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
1022int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1023{
1024 int err;
1025 u8 *data;
1026 struct xfrm_mgr *km;
1027 struct xfrm_policy *pol = NULL;
1028
1029 if (optlen <= 0 || optlen > PAGE_SIZE)
1030 return -EMSGSIZE;
1031
1032 data = kmalloc(optlen, GFP_KERNEL);
1033 if (!data)
1034 return -ENOMEM;
1035
1036 err = -EFAULT;
1037 if (copy_from_user(data, optval, optlen))
1038 goto out;
1039
1040 err = -EINVAL;
1041 read_lock(&xfrm_km_lock);
1042 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001043 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 optlen, &err);
1045 if (err >= 0)
1046 break;
1047 }
1048 read_unlock(&xfrm_km_lock);
1049
1050 if (err >= 0) {
1051 xfrm_sk_policy_insert(sk, err, pol);
1052 xfrm_pol_put(pol);
1053 err = 0;
1054 }
1055
1056out:
1057 kfree(data);
1058 return err;
1059}
1060EXPORT_SYMBOL(xfrm_user_policy);
1061
1062int xfrm_register_km(struct xfrm_mgr *km)
1063{
1064 write_lock_bh(&xfrm_km_lock);
1065 list_add_tail(&km->list, &xfrm_km_list);
1066 write_unlock_bh(&xfrm_km_lock);
1067 return 0;
1068}
1069EXPORT_SYMBOL(xfrm_register_km);
1070
1071int xfrm_unregister_km(struct xfrm_mgr *km)
1072{
1073 write_lock_bh(&xfrm_km_lock);
1074 list_del(&km->list);
1075 write_unlock_bh(&xfrm_km_lock);
1076 return 0;
1077}
1078EXPORT_SYMBOL(xfrm_unregister_km);
1079
1080int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1081{
1082 int err = 0;
1083 if (unlikely(afinfo == NULL))
1084 return -EINVAL;
1085 if (unlikely(afinfo->family >= NPROTO))
1086 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001087 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1089 err = -ENOBUFS;
1090 else {
1091 afinfo->state_bydst = xfrm_state_bydst;
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001092 afinfo->state_bysrc = xfrm_state_bysrc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 afinfo->state_byspi = xfrm_state_byspi;
1094 xfrm_state_afinfo[afinfo->family] = afinfo;
1095 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001096 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 return err;
1098}
1099EXPORT_SYMBOL(xfrm_state_register_afinfo);
1100
1101int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1102{
1103 int err = 0;
1104 if (unlikely(afinfo == NULL))
1105 return -EINVAL;
1106 if (unlikely(afinfo->family >= NPROTO))
1107 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001108 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1110 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1111 err = -EINVAL;
1112 else {
1113 xfrm_state_afinfo[afinfo->family] = NULL;
1114 afinfo->state_byspi = NULL;
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001115 afinfo->state_bysrc = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 afinfo->state_bydst = NULL;
1117 }
1118 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001119 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 return err;
1121}
1122EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1123
1124static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1125{
1126 struct xfrm_state_afinfo *afinfo;
1127 if (unlikely(family >= NPROTO))
1128 return NULL;
1129 read_lock(&xfrm_state_afinfo_lock);
1130 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001131 if (unlikely(!afinfo))
1132 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 return afinfo;
1134}
1135
1136static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1137{
Herbert Xu546be242006-05-27 23:03:58 -07001138 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139}
1140
1141/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1142void xfrm_state_delete_tunnel(struct xfrm_state *x)
1143{
1144 if (x->tunnel) {
1145 struct xfrm_state *t = x->tunnel;
1146
1147 if (atomic_read(&t->tunnel_users) == 2)
1148 xfrm_state_delete(t);
1149 atomic_dec(&t->tunnel_users);
1150 xfrm_state_put(t);
1151 x->tunnel = NULL;
1152 }
1153}
1154EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1155
Herbert Xu80b30c12005-10-15 10:58:30 +10001156/*
1157 * This function is NOT optimal. For example, with ESP it will give an
1158 * MTU that's usually two bytes short of being optimal. However, it will
1159 * usually give an answer that's a multiple of 4 provided the input is
1160 * also a multiple of 4.
1161 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1163{
1164 int res = mtu;
1165
1166 res -= x->props.header_len;
1167
1168 for (;;) {
1169 int m = res;
1170
1171 if (m < 68)
1172 return 68;
1173
1174 spin_lock_bh(&x->lock);
1175 if (x->km.state == XFRM_STATE_VALID &&
1176 x->type && x->type->get_max_size)
1177 m = x->type->get_max_size(x, m);
1178 else
1179 m += x->props.header_len;
1180 spin_unlock_bh(&x->lock);
1181
1182 if (m <= mtu)
1183 break;
1184 res -= (m - mtu);
1185 }
1186
1187 return res;
1188}
1189
Herbert Xu72cb6962005-06-20 13:18:08 -07001190int xfrm_init_state(struct xfrm_state *x)
1191{
Herbert Xud094cd82005-06-20 13:19:41 -07001192 struct xfrm_state_afinfo *afinfo;
1193 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001194 int err;
1195
Herbert Xud094cd82005-06-20 13:19:41 -07001196 err = -EAFNOSUPPORT;
1197 afinfo = xfrm_state_get_afinfo(family);
1198 if (!afinfo)
1199 goto error;
1200
1201 err = 0;
1202 if (afinfo->init_flags)
1203 err = afinfo->init_flags(x);
1204
1205 xfrm_state_put_afinfo(afinfo);
1206
1207 if (err)
1208 goto error;
1209
1210 err = -EPROTONOSUPPORT;
1211 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001212 if (x->type == NULL)
1213 goto error;
1214
1215 err = x->type->init_state(x);
1216 if (err)
1217 goto error;
1218
Herbert Xub59f45d2006-05-27 23:05:54 -07001219 x->mode = xfrm_get_mode(x->props.mode, family);
1220 if (x->mode == NULL)
1221 goto error;
1222
Herbert Xu72cb6962005-06-20 13:18:08 -07001223 x->km.state = XFRM_STATE_VALID;
1224
1225error:
1226 return err;
1227}
1228
1229EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
1231void __init xfrm_state_init(void)
1232{
1233 int i;
1234
1235 for (i=0; i<XFRM_DST_HSIZE; i++) {
1236 INIT_LIST_HEAD(&xfrm_state_bydst[i]);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001237 INIT_LIST_HEAD(&xfrm_state_bysrc[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 INIT_LIST_HEAD(&xfrm_state_byspi[i]);
1239 }
1240 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1241}
1242