blob: f05371556cced7c8a109d595ceb6b246317aac2c [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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700355 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 xfrm_state_addr_check(x, daddr, saddr, family) &&
357 tmpl->mode == x->props.mode &&
358 tmpl->id.proto == x->id.proto &&
359 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
360 /* Resolution logic:
361 1. There is a valid state with matching selector.
362 Done.
363 2. Valid state with inappropriate selector. Skip.
364
365 Entering area of "sysdeps".
366
367 3. If state is not valid, selector is temporary,
368 it selects only session which triggered
369 previous resolution. Key manager will do
370 something to install a state with proper
371 selector.
372 */
373 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800374 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700375 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 continue;
377 if (!best ||
378 best->km.dying > x->km.dying ||
379 (best->km.dying == x->km.dying &&
380 best->curlft.add_time < x->curlft.add_time))
381 best = x;
382 } else if (x->km.state == XFRM_STATE_ACQ) {
383 acquire_in_progress = 1;
384 } else if (x->km.state == XFRM_STATE_ERROR ||
385 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800386 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700387 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 error = -ESRCH;
389 }
390 }
391 }
392
393 x = best;
394 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700395 if (tmpl->id.spi &&
396 (x0 = afinfo->state_lookup(daddr, tmpl->id.spi,
397 tmpl->id.proto)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 xfrm_state_put(x0);
399 error = -EEXIST;
400 goto out;
401 }
402 x = xfrm_state_alloc();
403 if (x == NULL) {
404 error = -ENOMEM;
405 goto out;
406 }
407 /* Initialize temporary selector matching only
408 * to current session. */
409 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
410
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700411 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
412 if (error) {
413 x->km.state = XFRM_STATE_DEAD;
414 xfrm_state_put(x);
415 x = NULL;
416 goto out;
417 }
418
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 if (km_query(x, tmpl, pol) == 0) {
420 x->km.state = XFRM_STATE_ACQ;
421 list_add_tail(&x->bydst, xfrm_state_bydst+h);
422 xfrm_state_hold(x);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700423 list_add_tail(&x->bysrc, xfrm_state_bysrc+h);
424 xfrm_state_hold(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 if (x->id.spi) {
426 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
427 list_add(&x->byspi, xfrm_state_byspi+h);
428 xfrm_state_hold(x);
429 }
430 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
431 xfrm_state_hold(x);
432 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
433 add_timer(&x->timer);
434 } else {
435 x->km.state = XFRM_STATE_DEAD;
436 xfrm_state_put(x);
437 x = NULL;
438 error = -ESRCH;
439 }
440 }
441out:
442 if (x)
443 xfrm_state_hold(x);
444 else
445 *err = acquire_in_progress ? -EAGAIN : error;
446 spin_unlock_bh(&xfrm_state_lock);
447 xfrm_state_put_afinfo(afinfo);
448 return x;
449}
450
451static void __xfrm_state_insert(struct xfrm_state *x)
452{
453 unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
454
455 list_add(&x->bydst, xfrm_state_bydst+h);
456 xfrm_state_hold(x);
457
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700458 h = xfrm_src_hash(&x->props.saddr, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700460 list_add(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 xfrm_state_hold(x);
462
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700463 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
464 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
465 x->props.family);
466
467 list_add(&x->byspi, xfrm_state_byspi+h);
468 xfrm_state_hold(x);
469 }
470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 if (!mod_timer(&x->timer, jiffies + HZ))
472 xfrm_state_hold(x);
473
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800474 if (x->replay_maxage &&
475 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
476 xfrm_state_hold(x);
477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 wake_up(&km_waitq);
479}
480
481void xfrm_state_insert(struct xfrm_state *x)
482{
483 spin_lock_bh(&xfrm_state_lock);
484 __xfrm_state_insert(x);
485 spin_unlock_bh(&xfrm_state_lock);
David S. Miller399c1802005-12-19 14:23:23 -0800486
487 xfrm_flush_all_bundles();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489EXPORT_SYMBOL(xfrm_state_insert);
490
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700491static inline struct xfrm_state *
492__xfrm_state_locate(struct xfrm_state_afinfo *afinfo, struct xfrm_state *x,
493 int use_spi)
494{
495 if (use_spi)
496 return afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
497 else
498 return afinfo->state_lookup_byaddr(&x->id.daddr, &x->props.saddr, x->id.proto);
499}
500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
502
503int xfrm_state_add(struct xfrm_state *x)
504{
505 struct xfrm_state_afinfo *afinfo;
506 struct xfrm_state *x1;
507 int family;
508 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700509 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 family = x->props.family;
512 afinfo = xfrm_state_get_afinfo(family);
513 if (unlikely(afinfo == NULL))
514 return -EAFNOSUPPORT;
515
516 spin_lock_bh(&xfrm_state_lock);
517
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700518 x1 = __xfrm_state_locate(afinfo, x, use_spi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 if (x1) {
520 xfrm_state_put(x1);
521 x1 = NULL;
522 err = -EEXIST;
523 goto out;
524 }
525
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700526 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 x1 = __xfrm_find_acq_byseq(x->km.seq);
528 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
529 xfrm_state_put(x1);
530 x1 = NULL;
531 }
532 }
533
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700534 if (use_spi && !x1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 x1 = afinfo->find_acq(
536 x->props.mode, x->props.reqid, x->id.proto,
537 &x->id.daddr, &x->props.saddr, 0);
538
539 __xfrm_state_insert(x);
540 err = 0;
541
542out:
543 spin_unlock_bh(&xfrm_state_lock);
544 xfrm_state_put_afinfo(afinfo);
545
David S. Miller399c1802005-12-19 14:23:23 -0800546 if (!err)
547 xfrm_flush_all_bundles();
548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 if (x1) {
550 xfrm_state_delete(x1);
551 xfrm_state_put(x1);
552 }
553
554 return err;
555}
556EXPORT_SYMBOL(xfrm_state_add);
557
558int xfrm_state_update(struct xfrm_state *x)
559{
560 struct xfrm_state_afinfo *afinfo;
561 struct xfrm_state *x1;
562 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700563 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 afinfo = xfrm_state_get_afinfo(x->props.family);
566 if (unlikely(afinfo == NULL))
567 return -EAFNOSUPPORT;
568
569 spin_lock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700570 x1 = __xfrm_state_locate(afinfo, x, use_spi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
572 err = -ESRCH;
573 if (!x1)
574 goto out;
575
576 if (xfrm_state_kern(x1)) {
577 xfrm_state_put(x1);
578 err = -EEXIST;
579 goto out;
580 }
581
582 if (x1->km.state == XFRM_STATE_ACQ) {
583 __xfrm_state_insert(x);
584 x = NULL;
585 }
586 err = 0;
587
588out:
589 spin_unlock_bh(&xfrm_state_lock);
590 xfrm_state_put_afinfo(afinfo);
591
592 if (err)
593 return err;
594
595 if (!x) {
596 xfrm_state_delete(x1);
597 xfrm_state_put(x1);
598 return 0;
599 }
600
601 err = -EINVAL;
602 spin_lock_bh(&x1->lock);
603 if (likely(x1->km.state == XFRM_STATE_VALID)) {
604 if (x->encap && x1->encap)
605 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
606 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
607 x1->km.dying = 0;
608
609 if (!mod_timer(&x1->timer, jiffies + HZ))
610 xfrm_state_hold(x1);
611 if (x1->curlft.use_time)
612 xfrm_state_check_expire(x1);
613
614 err = 0;
615 }
616 spin_unlock_bh(&x1->lock);
617
618 xfrm_state_put(x1);
619
620 return err;
621}
622EXPORT_SYMBOL(xfrm_state_update);
623
624int xfrm_state_check_expire(struct xfrm_state *x)
625{
626 if (!x->curlft.use_time)
627 x->curlft.use_time = (unsigned long)xtime.tv_sec;
628
629 if (x->km.state != XFRM_STATE_VALID)
630 return -EINVAL;
631
632 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
633 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700634 x->km.state = XFRM_STATE_EXPIRED;
635 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 xfrm_state_hold(x);
637 return -EINVAL;
638 }
639
640 if (!x->km.dying &&
641 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700642 x->curlft.packets >= x->lft.soft_packet_limit)) {
643 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800644 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700645 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return 0;
647}
648EXPORT_SYMBOL(xfrm_state_check_expire);
649
650static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
651{
652 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
653 - skb_headroom(skb);
654
655 if (nhead > 0)
656 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
657
658 /* Check tail too... */
659 return 0;
660}
661
662int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
663{
664 int err = xfrm_state_check_expire(x);
665 if (err < 0)
666 goto err;
667 err = xfrm_state_check_space(x, skb);
668err:
669 return err;
670}
671EXPORT_SYMBOL(xfrm_state_check);
672
673struct xfrm_state *
674xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
675 unsigned short family)
676{
677 struct xfrm_state *x;
678 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
679 if (!afinfo)
680 return NULL;
681
682 spin_lock_bh(&xfrm_state_lock);
683 x = afinfo->state_lookup(daddr, spi, proto);
684 spin_unlock_bh(&xfrm_state_lock);
685 xfrm_state_put_afinfo(afinfo);
686 return x;
687}
688EXPORT_SYMBOL(xfrm_state_lookup);
689
690struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700691xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
692 u8 proto, unsigned short family)
693{
694 struct xfrm_state *x;
695 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
696 if (!afinfo)
697 return NULL;
698
699 spin_lock_bh(&xfrm_state_lock);
700 x = afinfo->state_lookup_byaddr(daddr, saddr, proto);
701 spin_unlock_bh(&xfrm_state_lock);
702 xfrm_state_put_afinfo(afinfo);
703 return x;
704}
705EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
706
707struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
709 xfrm_address_t *daddr, xfrm_address_t *saddr,
710 int create, unsigned short family)
711{
712 struct xfrm_state *x;
713 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
714 if (!afinfo)
715 return NULL;
716
717 spin_lock_bh(&xfrm_state_lock);
718 x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
719 spin_unlock_bh(&xfrm_state_lock);
720 xfrm_state_put_afinfo(afinfo);
721 return x;
722}
723EXPORT_SYMBOL(xfrm_find_acq);
724
725/* Silly enough, but I'm lazy to build resolution list */
726
727static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
728{
729 int i;
730 struct xfrm_state *x;
731
732 for (i = 0; i < XFRM_DST_HSIZE; i++) {
733 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
734 if (x->km.seq == seq && x->km.state == XFRM_STATE_ACQ) {
735 xfrm_state_hold(x);
736 return x;
737 }
738 }
739 }
740 return NULL;
741}
742
743struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
744{
745 struct xfrm_state *x;
746
747 spin_lock_bh(&xfrm_state_lock);
748 x = __xfrm_find_acq_byseq(seq);
749 spin_unlock_bh(&xfrm_state_lock);
750 return x;
751}
752EXPORT_SYMBOL(xfrm_find_acq_byseq);
753
754u32 xfrm_get_acqseq(void)
755{
756 u32 res;
757 static u32 acqseq;
758 static DEFINE_SPINLOCK(acqseq_lock);
759
760 spin_lock_bh(&acqseq_lock);
761 res = (++acqseq ? : ++acqseq);
762 spin_unlock_bh(&acqseq_lock);
763 return res;
764}
765EXPORT_SYMBOL(xfrm_get_acqseq);
766
767void
768xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
769{
770 u32 h;
771 struct xfrm_state *x0;
772
773 if (x->id.spi)
774 return;
775
776 if (minspi == maxspi) {
777 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
778 if (x0) {
779 xfrm_state_put(x0);
780 return;
781 }
782 x->id.spi = minspi;
783 } else {
784 u32 spi = 0;
785 minspi = ntohl(minspi);
786 maxspi = ntohl(maxspi);
787 for (h=0; h<maxspi-minspi+1; h++) {
788 spi = minspi + net_random()%(maxspi-minspi+1);
789 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
790 if (x0 == NULL) {
791 x->id.spi = htonl(spi);
792 break;
793 }
794 xfrm_state_put(x0);
795 }
796 }
797 if (x->id.spi) {
798 spin_lock_bh(&xfrm_state_lock);
799 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
800 list_add(&x->byspi, xfrm_state_byspi+h);
801 xfrm_state_hold(x);
802 spin_unlock_bh(&xfrm_state_lock);
803 wake_up(&km_waitq);
804 }
805}
806EXPORT_SYMBOL(xfrm_alloc_spi);
807
808int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
809 void *data)
810{
811 int i;
812 struct xfrm_state *x;
813 int count = 0;
814 int err = 0;
815
816 spin_lock_bh(&xfrm_state_lock);
817 for (i = 0; i < XFRM_DST_HSIZE; i++) {
818 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700819 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 count++;
821 }
822 }
823 if (count == 0) {
824 err = -ENOENT;
825 goto out;
826 }
827
828 for (i = 0; i < XFRM_DST_HSIZE; i++) {
829 list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700830 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 continue;
832 err = func(x, --count, data);
833 if (err)
834 goto out;
835 }
836 }
837out:
838 spin_unlock_bh(&xfrm_state_lock);
839 return err;
840}
841EXPORT_SYMBOL(xfrm_state_walk);
842
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800843
844void xfrm_replay_notify(struct xfrm_state *x, int event)
845{
846 struct km_event c;
847 /* we send notify messages in case
848 * 1. we updated on of the sequence numbers, and the seqno difference
849 * is at least x->replay_maxdiff, in this case we also update the
850 * timeout of our timer function
851 * 2. if x->replay_maxage has elapsed since last update,
852 * and there were changes
853 *
854 * The state structure must be locked!
855 */
856
857 switch (event) {
858 case XFRM_REPLAY_UPDATE:
859 if (x->replay_maxdiff &&
860 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700861 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
862 if (x->xflags & XFRM_TIME_DEFER)
863 event = XFRM_REPLAY_TIMEOUT;
864 else
865 return;
866 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800867
868 break;
869
870 case XFRM_REPLAY_TIMEOUT:
871 if ((x->replay.seq == x->preplay.seq) &&
872 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700873 (x->replay.oseq == x->preplay.oseq)) {
874 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800875 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700876 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800877
878 break;
879 }
880
881 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
882 c.event = XFRM_MSG_NEWAE;
883 c.data.aevent = event;
884 km_state_notify(x, &c);
885
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800886 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700887 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800888 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700889 x->xflags &= ~XFRM_TIME_DEFER;
890 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800891}
David S. Millera70fcb02006-03-20 19:18:52 -0800892EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800893
894static void xfrm_replay_timer_handler(unsigned long data)
895{
896 struct xfrm_state *x = (struct xfrm_state*)data;
897
898 spin_lock(&x->lock);
899
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700900 if (x->km.state == XFRM_STATE_VALID) {
901 if (xfrm_aevent_is_on())
902 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
903 else
904 x->xflags |= XFRM_TIME_DEFER;
905 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800906
907 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -0700908 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800909}
910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911int xfrm_replay_check(struct xfrm_state *x, u32 seq)
912{
913 u32 diff;
914
915 seq = ntohl(seq);
916
917 if (unlikely(seq == 0))
918 return -EINVAL;
919
920 if (likely(seq > x->replay.seq))
921 return 0;
922
923 diff = x->replay.seq - seq;
924 if (diff >= x->props.replay_window) {
925 x->stats.replay_window++;
926 return -EINVAL;
927 }
928
929 if (x->replay.bitmap & (1U << diff)) {
930 x->stats.replay++;
931 return -EINVAL;
932 }
933 return 0;
934}
935EXPORT_SYMBOL(xfrm_replay_check);
936
937void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
938{
939 u32 diff;
940
941 seq = ntohl(seq);
942
943 if (seq > x->replay.seq) {
944 diff = seq - x->replay.seq;
945 if (diff < x->props.replay_window)
946 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
947 else
948 x->replay.bitmap = 1;
949 x->replay.seq = seq;
950 } else {
951 diff = x->replay.seq - seq;
952 x->replay.bitmap |= (1U << diff);
953 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800954
955 if (xfrm_aevent_is_on())
956 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957}
958EXPORT_SYMBOL(xfrm_replay_advance);
959
960static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
961static DEFINE_RWLOCK(xfrm_km_lock);
962
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700963void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964{
965 struct xfrm_mgr *km;
966
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700967 read_lock(&xfrm_km_lock);
968 list_for_each_entry(km, &xfrm_km_list, list)
969 if (km->notify_policy)
970 km->notify_policy(xp, dir, c);
971 read_unlock(&xfrm_km_lock);
972}
973
974void km_state_notify(struct xfrm_state *x, struct km_event *c)
975{
976 struct xfrm_mgr *km;
977 read_lock(&xfrm_km_lock);
978 list_for_each_entry(km, &xfrm_km_list, list)
979 if (km->notify)
980 km->notify(x, c);
981 read_unlock(&xfrm_km_lock);
982}
983
984EXPORT_SYMBOL(km_policy_notify);
985EXPORT_SYMBOL(km_state_notify);
986
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800987void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700988{
989 struct km_event c;
990
Herbert Xubf08867f92005-06-18 22:44:00 -0700991 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800992 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -0700993 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700994 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 if (hard)
997 wake_up(&km_waitq);
998}
999
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001000EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001001/*
1002 * We send to all registered managers regardless of failure
1003 * We are happy with one success
1004*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001005int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001007 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 struct xfrm_mgr *km;
1009
1010 read_lock(&xfrm_km_lock);
1011 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001012 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1013 if (!acqret)
1014 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
1016 read_unlock(&xfrm_km_lock);
1017 return err;
1018}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001019EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
1021int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1022{
1023 int err = -EINVAL;
1024 struct xfrm_mgr *km;
1025
1026 read_lock(&xfrm_km_lock);
1027 list_for_each_entry(km, &xfrm_km_list, list) {
1028 if (km->new_mapping)
1029 err = km->new_mapping(x, ipaddr, sport);
1030 if (!err)
1031 break;
1032 }
1033 read_unlock(&xfrm_km_lock);
1034 return err;
1035}
1036EXPORT_SYMBOL(km_new_mapping);
1037
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001038void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001040 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
Herbert Xubf08867f92005-06-18 22:44:00 -07001042 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001043 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001044 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001045 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047 if (hard)
1048 wake_up(&km_waitq);
1049}
David S. Millera70fcb02006-03-20 19:18:52 -08001050EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
1052int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1053{
1054 int err;
1055 u8 *data;
1056 struct xfrm_mgr *km;
1057 struct xfrm_policy *pol = NULL;
1058
1059 if (optlen <= 0 || optlen > PAGE_SIZE)
1060 return -EMSGSIZE;
1061
1062 data = kmalloc(optlen, GFP_KERNEL);
1063 if (!data)
1064 return -ENOMEM;
1065
1066 err = -EFAULT;
1067 if (copy_from_user(data, optval, optlen))
1068 goto out;
1069
1070 err = -EINVAL;
1071 read_lock(&xfrm_km_lock);
1072 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001073 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 optlen, &err);
1075 if (err >= 0)
1076 break;
1077 }
1078 read_unlock(&xfrm_km_lock);
1079
1080 if (err >= 0) {
1081 xfrm_sk_policy_insert(sk, err, pol);
1082 xfrm_pol_put(pol);
1083 err = 0;
1084 }
1085
1086out:
1087 kfree(data);
1088 return err;
1089}
1090EXPORT_SYMBOL(xfrm_user_policy);
1091
1092int xfrm_register_km(struct xfrm_mgr *km)
1093{
1094 write_lock_bh(&xfrm_km_lock);
1095 list_add_tail(&km->list, &xfrm_km_list);
1096 write_unlock_bh(&xfrm_km_lock);
1097 return 0;
1098}
1099EXPORT_SYMBOL(xfrm_register_km);
1100
1101int xfrm_unregister_km(struct xfrm_mgr *km)
1102{
1103 write_lock_bh(&xfrm_km_lock);
1104 list_del(&km->list);
1105 write_unlock_bh(&xfrm_km_lock);
1106 return 0;
1107}
1108EXPORT_SYMBOL(xfrm_unregister_km);
1109
1110int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1111{
1112 int err = 0;
1113 if (unlikely(afinfo == NULL))
1114 return -EINVAL;
1115 if (unlikely(afinfo->family >= NPROTO))
1116 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001117 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1119 err = -ENOBUFS;
1120 else {
1121 afinfo->state_bydst = xfrm_state_bydst;
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001122 afinfo->state_bysrc = xfrm_state_bysrc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 afinfo->state_byspi = xfrm_state_byspi;
1124 xfrm_state_afinfo[afinfo->family] = afinfo;
1125 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001126 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 return err;
1128}
1129EXPORT_SYMBOL(xfrm_state_register_afinfo);
1130
1131int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1132{
1133 int err = 0;
1134 if (unlikely(afinfo == NULL))
1135 return -EINVAL;
1136 if (unlikely(afinfo->family >= NPROTO))
1137 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001138 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1140 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1141 err = -EINVAL;
1142 else {
1143 xfrm_state_afinfo[afinfo->family] = NULL;
1144 afinfo->state_byspi = NULL;
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001145 afinfo->state_bysrc = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 afinfo->state_bydst = NULL;
1147 }
1148 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001149 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return err;
1151}
1152EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1153
1154static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1155{
1156 struct xfrm_state_afinfo *afinfo;
1157 if (unlikely(family >= NPROTO))
1158 return NULL;
1159 read_lock(&xfrm_state_afinfo_lock);
1160 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001161 if (unlikely(!afinfo))
1162 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 return afinfo;
1164}
1165
1166static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1167{
Herbert Xu546be242006-05-27 23:03:58 -07001168 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169}
1170
1171/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1172void xfrm_state_delete_tunnel(struct xfrm_state *x)
1173{
1174 if (x->tunnel) {
1175 struct xfrm_state *t = x->tunnel;
1176
1177 if (atomic_read(&t->tunnel_users) == 2)
1178 xfrm_state_delete(t);
1179 atomic_dec(&t->tunnel_users);
1180 xfrm_state_put(t);
1181 x->tunnel = NULL;
1182 }
1183}
1184EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1185
Herbert Xu80b30c12005-10-15 10:58:30 +10001186/*
1187 * This function is NOT optimal. For example, with ESP it will give an
1188 * MTU that's usually two bytes short of being optimal. However, it will
1189 * usually give an answer that's a multiple of 4 provided the input is
1190 * also a multiple of 4.
1191 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1193{
1194 int res = mtu;
1195
1196 res -= x->props.header_len;
1197
1198 for (;;) {
1199 int m = res;
1200
1201 if (m < 68)
1202 return 68;
1203
1204 spin_lock_bh(&x->lock);
1205 if (x->km.state == XFRM_STATE_VALID &&
1206 x->type && x->type->get_max_size)
1207 m = x->type->get_max_size(x, m);
1208 else
1209 m += x->props.header_len;
1210 spin_unlock_bh(&x->lock);
1211
1212 if (m <= mtu)
1213 break;
1214 res -= (m - mtu);
1215 }
1216
1217 return res;
1218}
1219
Herbert Xu72cb6962005-06-20 13:18:08 -07001220int xfrm_init_state(struct xfrm_state *x)
1221{
Herbert Xud094cd82005-06-20 13:19:41 -07001222 struct xfrm_state_afinfo *afinfo;
1223 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001224 int err;
1225
Herbert Xud094cd82005-06-20 13:19:41 -07001226 err = -EAFNOSUPPORT;
1227 afinfo = xfrm_state_get_afinfo(family);
1228 if (!afinfo)
1229 goto error;
1230
1231 err = 0;
1232 if (afinfo->init_flags)
1233 err = afinfo->init_flags(x);
1234
1235 xfrm_state_put_afinfo(afinfo);
1236
1237 if (err)
1238 goto error;
1239
1240 err = -EPROTONOSUPPORT;
1241 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001242 if (x->type == NULL)
1243 goto error;
1244
1245 err = x->type->init_state(x);
1246 if (err)
1247 goto error;
1248
Herbert Xub59f45d2006-05-27 23:05:54 -07001249 x->mode = xfrm_get_mode(x->props.mode, family);
1250 if (x->mode == NULL)
1251 goto error;
1252
Herbert Xu72cb6962005-06-20 13:18:08 -07001253 x->km.state = XFRM_STATE_VALID;
1254
1255error:
1256 return err;
1257}
1258
1259EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
1261void __init xfrm_state_init(void)
1262{
1263 int i;
1264
1265 for (i=0; i<XFRM_DST_HSIZE; i++) {
1266 INIT_LIST_HEAD(&xfrm_state_bydst[i]);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -07001267 INIT_LIST_HEAD(&xfrm_state_bysrc[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 INIT_LIST_HEAD(&xfrm_state_byspi[i]);
1269 }
1270 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1271}
1272