blob: 224b44e31a07b934091f79e42b85888ab143925f [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>
David S. Millerf034b5d2006-08-24 03:08:07 -070021#include <linux/cache.h>
Jesper Juhlb5890d82007-08-10 15:20:21 -070022#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
David S. Miller44e36b42006-08-24 04:50:50 -070024#include "xfrm_hash.h"
25
David S. Milleree857a72006-03-20 19:18:37 -080026struct sock *xfrm_nl;
27EXPORT_SYMBOL(xfrm_nl);
28
David S. Miller01e67d02007-05-25 00:41:38 -070029u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080030EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
31
David S. Miller01e67d02007-05-25 00:41:38 -070032u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080033EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
34
David S. Miller01e67d02007-05-25 00:41:38 -070035u32 sysctl_xfrm_acq_expires __read_mostly = 30;
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037/* Each xfrm_state may be linked to two tables:
38
39 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070040 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 destination/tunnel endpoint. (output)
42 */
43
44static DEFINE_SPINLOCK(xfrm_state_lock);
45
46/* Hash table to find appropriate SA towards given target (endpoint
47 * of tunnel or destination of transport mode) allowed by selector.
48 *
49 * Main use is finding SA after policy selected tunnel or transport mode.
50 * Also, it can be used by ah/esp icmp error handler to find offending SA.
51 */
David S. Millerf034b5d2006-08-24 03:08:07 -070052static struct hlist_head *xfrm_state_bydst __read_mostly;
53static struct hlist_head *xfrm_state_bysrc __read_mostly;
54static struct hlist_head *xfrm_state_byspi __read_mostly;
55static unsigned int xfrm_state_hmask __read_mostly;
56static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
57static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070058static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Herbert Xu17c2a422007-10-17 21:33:12 -070060static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
61static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
62
David S. Millerc1969f22006-08-24 04:00:03 -070063static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
64 xfrm_address_t *saddr,
65 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070066 unsigned short family)
67{
David S. Millerc1969f22006-08-24 04:00:03 -070068 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070069}
70
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070071static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
72 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070073 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070074{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070075 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070076}
77
David S. Miller2575b652006-08-24 03:26:44 -070078static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070079xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070080{
David S. Millerc1969f22006-08-24 04:00:03 -070081 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070082}
83
David S. Millerf034b5d2006-08-24 03:08:07 -070084static void xfrm_hash_transfer(struct hlist_head *list,
85 struct hlist_head *ndsttable,
86 struct hlist_head *nsrctable,
87 struct hlist_head *nspitable,
88 unsigned int nhashmask)
89{
90 struct hlist_node *entry, *tmp;
91 struct xfrm_state *x;
92
93 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
94 unsigned int h;
95
David S. Millerc1969f22006-08-24 04:00:03 -070096 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
97 x->props.reqid, x->props.family,
98 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070099 hlist_add_head(&x->bydst, ndsttable+h);
100
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700101 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
102 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700103 nhashmask);
104 hlist_add_head(&x->bysrc, nsrctable+h);
105
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700106 if (x->id.spi) {
107 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
108 x->id.proto, x->props.family,
109 nhashmask);
110 hlist_add_head(&x->byspi, nspitable+h);
111 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700112 }
113}
114
115static unsigned long xfrm_hash_new_size(void)
116{
117 return ((xfrm_state_hmask + 1) << 1) *
118 sizeof(struct hlist_head);
119}
120
121static DEFINE_MUTEX(hash_resize_mutex);
122
David Howellsc4028952006-11-22 14:57:56 +0000123static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700124{
125 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
126 unsigned long nsize, osize;
127 unsigned int nhashmask, ohashmask;
128 int i;
129
130 mutex_lock(&hash_resize_mutex);
131
132 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700133 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700134 if (!ndst)
135 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700136 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700137 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700138 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700139 goto out_unlock;
140 }
David S. Miller44e36b42006-08-24 04:50:50 -0700141 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700143 xfrm_hash_free(ndst, nsize);
144 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700145 goto out_unlock;
146 }
147
148 spin_lock_bh(&xfrm_state_lock);
149
150 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
151 for (i = xfrm_state_hmask; i >= 0; i--)
152 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
153 nhashmask);
154
155 odst = xfrm_state_bydst;
156 osrc = xfrm_state_bysrc;
157 ospi = xfrm_state_byspi;
158 ohashmask = xfrm_state_hmask;
159
160 xfrm_state_bydst = ndst;
161 xfrm_state_bysrc = nsrc;
162 xfrm_state_byspi = nspi;
163 xfrm_state_hmask = nhashmask;
164
165 spin_unlock_bh(&xfrm_state_lock);
166
167 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700168 xfrm_hash_free(odst, osize);
169 xfrm_hash_free(osrc, osize);
170 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700171
172out_unlock:
173 mutex_unlock(&hash_resize_mutex);
174}
175
David Howellsc4028952006-11-22 14:57:56 +0000176static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178DECLARE_WAIT_QUEUE_HEAD(km_waitq);
179EXPORT_SYMBOL(km_waitq);
180
181static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
182static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
183
184static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700185static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186static DEFINE_SPINLOCK(xfrm_state_gc_lock);
187
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800188int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800190int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800191void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700193static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
194{
195 struct xfrm_state_afinfo *afinfo;
196 if (unlikely(family >= NPROTO))
197 return NULL;
198 write_lock_bh(&xfrm_state_afinfo_lock);
199 afinfo = xfrm_state_afinfo[family];
200 if (unlikely(!afinfo))
201 write_unlock_bh(&xfrm_state_afinfo_lock);
202 return afinfo;
203}
204
205static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
206{
207 write_unlock_bh(&xfrm_state_afinfo_lock);
208}
209
210int xfrm_register_type(struct xfrm_type *type, unsigned short family)
211{
212 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
213 struct xfrm_type **typemap;
214 int err = 0;
215
216 if (unlikely(afinfo == NULL))
217 return -EAFNOSUPPORT;
218 typemap = afinfo->type_map;
219
220 if (likely(typemap[type->proto] == NULL))
221 typemap[type->proto] = type;
222 else
223 err = -EEXIST;
224 xfrm_state_unlock_afinfo(afinfo);
225 return err;
226}
227EXPORT_SYMBOL(xfrm_register_type);
228
229int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
230{
231 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
232 struct xfrm_type **typemap;
233 int err = 0;
234
235 if (unlikely(afinfo == NULL))
236 return -EAFNOSUPPORT;
237 typemap = afinfo->type_map;
238
239 if (unlikely(typemap[type->proto] != type))
240 err = -ENOENT;
241 else
242 typemap[type->proto] = NULL;
243 xfrm_state_unlock_afinfo(afinfo);
244 return err;
245}
246EXPORT_SYMBOL(xfrm_unregister_type);
247
248static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
249{
250 struct xfrm_state_afinfo *afinfo;
251 struct xfrm_type **typemap;
252 struct xfrm_type *type;
253 int modload_attempted = 0;
254
255retry:
256 afinfo = xfrm_state_get_afinfo(family);
257 if (unlikely(afinfo == NULL))
258 return NULL;
259 typemap = afinfo->type_map;
260
261 type = typemap[proto];
262 if (unlikely(type && !try_module_get(type->owner)))
263 type = NULL;
264 if (!type && !modload_attempted) {
265 xfrm_state_put_afinfo(afinfo);
266 request_module("xfrm-type-%d-%d", family, proto);
267 modload_attempted = 1;
268 goto retry;
269 }
270
271 xfrm_state_put_afinfo(afinfo);
272 return type;
273}
274
275static void xfrm_put_type(struct xfrm_type *type)
276{
277 module_put(type->owner);
278}
279
280int xfrm_register_mode(struct xfrm_mode *mode, int family)
281{
282 struct xfrm_state_afinfo *afinfo;
283 struct xfrm_mode **modemap;
284 int err;
285
286 if (unlikely(mode->encap >= XFRM_MODE_MAX))
287 return -EINVAL;
288
289 afinfo = xfrm_state_lock_afinfo(family);
290 if (unlikely(afinfo == NULL))
291 return -EAFNOSUPPORT;
292
293 err = -EEXIST;
294 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700295 if (modemap[mode->encap])
296 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700297
Herbert Xu17c2a422007-10-17 21:33:12 -0700298 err = -ENOENT;
299 if (!try_module_get(afinfo->owner))
300 goto out;
301
302 mode->afinfo = afinfo;
303 modemap[mode->encap] = mode;
304 err = 0;
305
306out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700307 xfrm_state_unlock_afinfo(afinfo);
308 return err;
309}
310EXPORT_SYMBOL(xfrm_register_mode);
311
312int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
313{
314 struct xfrm_state_afinfo *afinfo;
315 struct xfrm_mode **modemap;
316 int err;
317
318 if (unlikely(mode->encap >= XFRM_MODE_MAX))
319 return -EINVAL;
320
321 afinfo = xfrm_state_lock_afinfo(family);
322 if (unlikely(afinfo == NULL))
323 return -EAFNOSUPPORT;
324
325 err = -ENOENT;
326 modemap = afinfo->mode_map;
327 if (likely(modemap[mode->encap] == mode)) {
328 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700329 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700330 err = 0;
331 }
332
333 xfrm_state_unlock_afinfo(afinfo);
334 return err;
335}
336EXPORT_SYMBOL(xfrm_unregister_mode);
337
338static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
339{
340 struct xfrm_state_afinfo *afinfo;
341 struct xfrm_mode *mode;
342 int modload_attempted = 0;
343
344 if (unlikely(encap >= XFRM_MODE_MAX))
345 return NULL;
346
347retry:
348 afinfo = xfrm_state_get_afinfo(family);
349 if (unlikely(afinfo == NULL))
350 return NULL;
351
352 mode = afinfo->mode_map[encap];
353 if (unlikely(mode && !try_module_get(mode->owner)))
354 mode = NULL;
355 if (!mode && !modload_attempted) {
356 xfrm_state_put_afinfo(afinfo);
357 request_module("xfrm-mode-%d-%d", family, encap);
358 modload_attempted = 1;
359 goto retry;
360 }
361
362 xfrm_state_put_afinfo(afinfo);
363 return mode;
364}
365
366static void xfrm_put_mode(struct xfrm_mode *mode)
367{
368 module_put(mode->owner);
369}
370
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371static void xfrm_state_gc_destroy(struct xfrm_state *x)
372{
David S. Millera47f0ce2006-08-24 03:54:22 -0700373 del_timer_sync(&x->timer);
374 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800375 kfree(x->aalg);
376 kfree(x->ealg);
377 kfree(x->calg);
378 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700379 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700380 if (x->inner_mode)
381 xfrm_put_mode(x->inner_mode);
382 if (x->outer_mode)
383 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 if (x->type) {
385 x->type->destructor(x);
386 xfrm_put_type(x->type);
387 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800388 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 kfree(x);
390}
391
David Howellsc4028952006-11-22 14:57:56 +0000392static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393{
394 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700395 struct hlist_node *entry, *tmp;
396 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700399 gc_list.first = xfrm_state_gc_list.first;
400 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 spin_unlock_bh(&xfrm_state_gc_lock);
402
David S. Miller8f126e32006-08-24 02:45:07 -0700403 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 wake_up(&km_waitq);
407}
408
409static inline unsigned long make_jiffies(long secs)
410{
411 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
412 return MAX_SCHEDULE_TIMEOUT-1;
413 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900414 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415}
416
417static void xfrm_timer_handler(unsigned long data)
418{
419 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800420 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 long next = LONG_MAX;
422 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600423 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 spin_lock(&x->lock);
426 if (x->km.state == XFRM_STATE_DEAD)
427 goto out;
428 if (x->km.state == XFRM_STATE_EXPIRED)
429 goto expired;
430 if (x->lft.hard_add_expires_seconds) {
431 long tmo = x->lft.hard_add_expires_seconds +
432 x->curlft.add_time - now;
433 if (tmo <= 0)
434 goto expired;
435 if (tmo < next)
436 next = tmo;
437 }
438 if (x->lft.hard_use_expires_seconds) {
439 long tmo = x->lft.hard_use_expires_seconds +
440 (x->curlft.use_time ? : now) - now;
441 if (tmo <= 0)
442 goto expired;
443 if (tmo < next)
444 next = tmo;
445 }
446 if (x->km.dying)
447 goto resched;
448 if (x->lft.soft_add_expires_seconds) {
449 long tmo = x->lft.soft_add_expires_seconds +
450 x->curlft.add_time - now;
451 if (tmo <= 0)
452 warn = 1;
453 else if (tmo < next)
454 next = tmo;
455 }
456 if (x->lft.soft_use_expires_seconds) {
457 long tmo = x->lft.soft_use_expires_seconds +
458 (x->curlft.use_time ? : now) - now;
459 if (tmo <= 0)
460 warn = 1;
461 else if (tmo < next)
462 next = tmo;
463 }
464
Herbert Xu4666faa2005-06-18 22:43:22 -0700465 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800467 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700469 if (next != LONG_MAX)
470 mod_timer(&x->timer, jiffies + make_jiffies(next));
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 goto out;
473
474expired:
475 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
476 x->km.state = XFRM_STATE_EXPIRED;
477 wake_up(&km_waitq);
478 next = 2;
479 goto resched;
480 }
Joy Latten161a09e2006-11-27 13:11:54 -0600481
482 err = __xfrm_state_delete(x);
483 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800484 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Joy Lattenab5f5e82007-09-17 11:51:22 -0700486 xfrm_audit_state_delete(x, err ? 0 : 1,
487 audit_get_loginuid(current->audit_context), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600488
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489out:
490 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}
492
David S. Miller0ac84752006-03-20 19:18:23 -0800493static void xfrm_replay_timer_handler(unsigned long data);
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495struct xfrm_state *xfrm_state_alloc(void)
496{
497 struct xfrm_state *x;
498
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700499 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
501 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 atomic_set(&x->refcnt, 1);
503 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700504 INIT_HLIST_NODE(&x->bydst);
505 INIT_HLIST_NODE(&x->bysrc);
506 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 init_timer(&x->timer);
508 x->timer.function = xfrm_timer_handler;
509 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800510 init_timer(&x->rtimer);
511 x->rtimer.function = xfrm_replay_timer_handler;
512 x->rtimer.data = (unsigned long)x;
James Morris9d729f72007-03-04 16:12:44 -0800513 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 x->lft.soft_byte_limit = XFRM_INF;
515 x->lft.soft_packet_limit = XFRM_INF;
516 x->lft.hard_byte_limit = XFRM_INF;
517 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800518 x->replay_maxage = 0;
519 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 spin_lock_init(&x->lock);
521 }
522 return x;
523}
524EXPORT_SYMBOL(xfrm_state_alloc);
525
526void __xfrm_state_destroy(struct xfrm_state *x)
527{
528 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
529
530 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700531 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 spin_unlock_bh(&xfrm_state_gc_lock);
533 schedule_work(&xfrm_state_gc_work);
534}
535EXPORT_SYMBOL(__xfrm_state_destroy);
536
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800537int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700539 int err = -ESRCH;
540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 if (x->km.state != XFRM_STATE_DEAD) {
542 x->km.state = XFRM_STATE_DEAD;
543 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700544 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700545 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700546 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700547 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700548 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 /* All xfrm_state objects are created by xfrm_state_alloc.
552 * The xfrm_state_alloc call gives a reference, and that
553 * is what we are dropping here.
554 */
Herbert Xu21380b82006-02-22 14:47:13 -0800555 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700556 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700558
559 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800561EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700563int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700565 int err;
566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700568 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700570
571 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573EXPORT_SYMBOL(xfrm_state_delete);
574
Joy Latten4aa2e622007-06-04 19:05:57 -0400575#ifdef CONFIG_SECURITY_NETWORK_XFRM
576static inline int
577xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
Joy Latten4aa2e622007-06-04 19:05:57 -0400579 int i, err = 0;
580
581 for (i = 0; i <= xfrm_state_hmask; i++) {
582 struct hlist_node *entry;
583 struct xfrm_state *x;
584
585 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
586 if (xfrm_id_proto_match(x->id.proto, proto) &&
587 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700588 xfrm_audit_state_delete(x, 0,
589 audit_info->loginuid,
590 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400591 return err;
592 }
593 }
594 }
595
596 return err;
597}
598#else
599static inline int
600xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
601{
602 return 0;
603}
604#endif
605
606int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
607{
608 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
610 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400611 err = xfrm_state_flush_secctx_check(proto, audit_info);
612 if (err)
613 goto out;
614
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700615 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700616 struct hlist_node *entry;
617 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700619 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700621 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 xfrm_state_hold(x);
623 spin_unlock_bh(&xfrm_state_lock);
624
Joy Latten161a09e2006-11-27 13:11:54 -0600625 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700626 xfrm_audit_state_delete(x, err ? 0 : 1,
627 audit_info->loginuid,
628 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 xfrm_state_put(x);
630
631 spin_lock_bh(&xfrm_state_lock);
632 goto restart;
633 }
634 }
635 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400636 err = 0;
637
638out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 spin_unlock_bh(&xfrm_state_lock);
640 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400641 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643EXPORT_SYMBOL(xfrm_state_flush);
644
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700645void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700646{
647 spin_lock_bh(&xfrm_state_lock);
648 si->sadcnt = xfrm_state_num;
649 si->sadhcnt = xfrm_state_hmask;
650 si->sadhmcnt = xfrm_state_hashmax;
651 spin_unlock_bh(&xfrm_state_lock);
652}
653EXPORT_SYMBOL(xfrm_sad_getinfo);
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655static int
656xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
657 struct xfrm_tmpl *tmpl,
658 xfrm_address_t *daddr, xfrm_address_t *saddr,
659 unsigned short family)
660{
661 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
662 if (!afinfo)
663 return -1;
664 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
665 xfrm_state_put_afinfo(afinfo);
666 return 0;
667}
668
Al Viroa94cfd12006-09-27 18:47:24 -0700669static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700670{
671 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
672 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700673 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700674
David S. Miller8f126e32006-08-24 02:45:07 -0700675 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700676 if (x->props.family != family ||
677 x->id.spi != spi ||
678 x->id.proto != proto)
679 continue;
680
681 switch (family) {
682 case AF_INET:
683 if (x->id.daddr.a4 != daddr->a4)
684 continue;
685 break;
686 case AF_INET6:
687 if (!ipv6_addr_equal((struct in6_addr *)daddr,
688 (struct in6_addr *)
689 x->id.daddr.a6))
690 continue;
691 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700692 }
David S. Milleredcd5822006-08-24 00:42:45 -0700693
694 xfrm_state_hold(x);
695 return x;
696 }
697
698 return NULL;
699}
700
701static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
702{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700703 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700704 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700705 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700706
David S. Miller8f126e32006-08-24 02:45:07 -0700707 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700708 if (x->props.family != family ||
709 x->id.proto != proto)
710 continue;
711
712 switch (family) {
713 case AF_INET:
714 if (x->id.daddr.a4 != daddr->a4 ||
715 x->props.saddr.a4 != saddr->a4)
716 continue;
717 break;
718 case AF_INET6:
719 if (!ipv6_addr_equal((struct in6_addr *)daddr,
720 (struct in6_addr *)
721 x->id.daddr.a6) ||
722 !ipv6_addr_equal((struct in6_addr *)saddr,
723 (struct in6_addr *)
724 x->props.saddr.a6))
725 continue;
726 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700727 }
David S. Milleredcd5822006-08-24 00:42:45 -0700728
729 xfrm_state_hold(x);
730 return x;
731 }
732
733 return NULL;
734}
735
736static inline struct xfrm_state *
737__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
738{
739 if (use_spi)
740 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
741 x->id.proto, family);
742 else
743 return __xfrm_state_lookup_byaddr(&x->id.daddr,
744 &x->props.saddr,
745 x->id.proto, family);
746}
747
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700748static void xfrm_hash_grow_check(int have_hash_collision)
749{
750 if (have_hash_collision &&
751 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
752 xfrm_state_num > xfrm_state_hmask)
753 schedule_work(&xfrm_hash_work);
754}
755
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900757xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 struct flowi *fl, struct xfrm_tmpl *tmpl,
759 struct xfrm_policy *pol, int *err,
760 unsigned short family)
761{
David S. Millerc1969f22006-08-24 04:00:03 -0700762 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700763 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 struct xfrm_state *x, *x0;
765 int acquire_in_progress = 0;
766 int error = 0;
767 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700770 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 if (x->props.family == family &&
772 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700773 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 xfrm_state_addr_check(x, daddr, saddr, family) &&
775 tmpl->mode == x->props.mode &&
776 tmpl->id.proto == x->id.proto &&
777 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
778 /* Resolution logic:
779 1. There is a valid state with matching selector.
780 Done.
781 2. Valid state with inappropriate selector. Skip.
782
783 Entering area of "sysdeps".
784
785 3. If state is not valid, selector is temporary,
786 it selects only session which triggered
787 previous resolution. Key manager will do
788 something to install a state with proper
789 selector.
790 */
791 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700792 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700793 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 continue;
795 if (!best ||
796 best->km.dying > x->km.dying ||
797 (best->km.dying == x->km.dying &&
798 best->curlft.add_time < x->curlft.add_time))
799 best = x;
800 } else if (x->km.state == XFRM_STATE_ACQ) {
801 acquire_in_progress = 1;
802 } else if (x->km.state == XFRM_STATE_ERROR ||
803 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700804 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700805 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 error = -ESRCH;
807 }
808 }
809 }
810
811 x = best;
812 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700813 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700814 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
815 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 xfrm_state_put(x0);
817 error = -EEXIST;
818 goto out;
819 }
820 x = xfrm_state_alloc();
821 if (x == NULL) {
822 error = -ENOMEM;
823 goto out;
824 }
825 /* Initialize temporary selector matching only
826 * to current session. */
827 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
828
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700829 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
830 if (error) {
831 x->km.state = XFRM_STATE_DEAD;
832 xfrm_state_put(x);
833 x = NULL;
834 goto out;
835 }
836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 if (km_query(x, tmpl, pol) == 0) {
838 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700839 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700840 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700841 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 if (x->id.spi) {
843 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700844 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 }
David S. Miller01e67d02007-05-25 00:41:38 -0700846 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
847 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700849 xfrm_state_num++;
850 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else {
852 x->km.state = XFRM_STATE_DEAD;
853 xfrm_state_put(x);
854 x = NULL;
855 error = -ESRCH;
856 }
857 }
858out:
859 if (x)
860 xfrm_state_hold(x);
861 else
862 *err = acquire_in_progress ? -EAGAIN : error;
863 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 return x;
865}
866
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700867struct xfrm_state *
868xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
869 unsigned short family, u8 mode, u8 proto, u32 reqid)
870{
871 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
872 struct xfrm_state *rx = NULL, *x = NULL;
873 struct hlist_node *entry;
874
875 spin_lock(&xfrm_state_lock);
876 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
877 if (x->props.family == family &&
878 x->props.reqid == reqid &&
879 !(x->props.flags & XFRM_STATE_WILDRECV) &&
880 xfrm_state_addr_check(x, daddr, saddr, family) &&
881 mode == x->props.mode &&
882 proto == x->id.proto &&
883 x->km.state == XFRM_STATE_VALID) {
884 rx = x;
885 break;
886 }
887 }
888
889 if (rx)
890 xfrm_state_hold(rx);
891 spin_unlock(&xfrm_state_lock);
892
893
894 return rx;
895}
896EXPORT_SYMBOL(xfrm_stateonly_find);
897
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898static void __xfrm_state_insert(struct xfrm_state *x)
899{
David S. Millera624c102006-08-24 03:24:33 -0700900 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
David S. Miller9d4a7062006-08-24 03:18:09 -0700902 x->genid = ++xfrm_state_genid;
903
David S. Millerc1969f22006-08-24 04:00:03 -0700904 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
905 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700906 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700908 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700909 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700911 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700912 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
913 x->props.family);
914
David S. Miller8f126e32006-08-24 02:45:07 -0700915 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700916 }
917
David S. Millera47f0ce2006-08-24 03:54:22 -0700918 mod_timer(&x->timer, jiffies + HZ);
919 if (x->replay_maxage)
920 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800921
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700923
924 xfrm_state_num++;
925
David S. Miller918049f2006-10-12 22:03:24 -0700926 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927}
928
David S. Millerc7f5ea32006-08-24 03:29:04 -0700929/* xfrm_state_lock is held */
930static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
931{
932 unsigned short family = xnew->props.family;
933 u32 reqid = xnew->props.reqid;
934 struct xfrm_state *x;
935 struct hlist_node *entry;
936 unsigned int h;
937
David S. Millerc1969f22006-08-24 04:00:03 -0700938 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700939 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
940 if (x->props.family == family &&
941 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700942 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
943 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700944 x->genid = xfrm_state_genid;
945 }
946}
947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948void xfrm_state_insert(struct xfrm_state *x)
949{
950 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700951 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 __xfrm_state_insert(x);
953 spin_unlock_bh(&xfrm_state_lock);
954}
955EXPORT_SYMBOL(xfrm_state_insert);
956
David S. Miller27708342006-08-24 00:13:10 -0700957/* xfrm_state_lock is held */
958static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
959{
David S. Millerc1969f22006-08-24 04:00:03 -0700960 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700961 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700962 struct xfrm_state *x;
963
David S. Miller8f126e32006-08-24 02:45:07 -0700964 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700965 if (x->props.reqid != reqid ||
966 x->props.mode != mode ||
967 x->props.family != family ||
968 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700969 x->id.spi != 0 ||
970 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700971 continue;
972
973 switch (family) {
974 case AF_INET:
975 if (x->id.daddr.a4 != daddr->a4 ||
976 x->props.saddr.a4 != saddr->a4)
977 continue;
978 break;
979 case AF_INET6:
980 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
981 (struct in6_addr *)daddr) ||
982 !ipv6_addr_equal((struct in6_addr *)
983 x->props.saddr.a6,
984 (struct in6_addr *)saddr))
985 continue;
986 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700987 }
David S. Miller27708342006-08-24 00:13:10 -0700988
989 xfrm_state_hold(x);
990 return x;
991 }
992
993 if (!create)
994 return NULL;
995
996 x = xfrm_state_alloc();
997 if (likely(x)) {
998 switch (family) {
999 case AF_INET:
1000 x->sel.daddr.a4 = daddr->a4;
1001 x->sel.saddr.a4 = saddr->a4;
1002 x->sel.prefixlen_d = 32;
1003 x->sel.prefixlen_s = 32;
1004 x->props.saddr.a4 = saddr->a4;
1005 x->id.daddr.a4 = daddr->a4;
1006 break;
1007
1008 case AF_INET6:
1009 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1010 (struct in6_addr *)daddr);
1011 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1012 (struct in6_addr *)saddr);
1013 x->sel.prefixlen_d = 128;
1014 x->sel.prefixlen_s = 128;
1015 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1016 (struct in6_addr *)saddr);
1017 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1018 (struct in6_addr *)daddr);
1019 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001020 }
David S. Miller27708342006-08-24 00:13:10 -07001021
1022 x->km.state = XFRM_STATE_ACQ;
1023 x->id.proto = proto;
1024 x->props.family = family;
1025 x->props.mode = mode;
1026 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001027 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001028 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001029 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001030 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001031 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001032 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001033 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001034
1035 xfrm_state_num++;
1036
1037 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001038 }
1039
1040 return x;
1041}
1042
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1044
1045int xfrm_state_add(struct xfrm_state *x)
1046{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 struct xfrm_state *x1;
1048 int family;
1049 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001050 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
1052 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
1054 spin_lock_bh(&xfrm_state_lock);
1055
David S. Milleredcd5822006-08-24 00:42:45 -07001056 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 if (x1) {
1058 xfrm_state_put(x1);
1059 x1 = NULL;
1060 err = -EEXIST;
1061 goto out;
1062 }
1063
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001064 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001066 if (x1 && ((x1->id.proto != x->id.proto) ||
1067 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 xfrm_state_put(x1);
1069 x1 = NULL;
1070 }
1071 }
1072
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001073 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001074 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1075 x->id.proto,
1076 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
David S. Millerc7f5ea32006-08-24 03:29:04 -07001078 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 __xfrm_state_insert(x);
1080 err = 0;
1081
1082out:
1083 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
1085 if (x1) {
1086 xfrm_state_delete(x1);
1087 xfrm_state_put(x1);
1088 }
1089
1090 return err;
1091}
1092EXPORT_SYMBOL(xfrm_state_add);
1093
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001094#ifdef CONFIG_XFRM_MIGRATE
1095struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
1096{
1097 int err = -ENOMEM;
1098 struct xfrm_state *x = xfrm_state_alloc();
1099 if (!x)
1100 goto error;
1101
1102 memcpy(&x->id, &orig->id, sizeof(x->id));
1103 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1104 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1105 x->props.mode = orig->props.mode;
1106 x->props.replay_window = orig->props.replay_window;
1107 x->props.reqid = orig->props.reqid;
1108 x->props.family = orig->props.family;
1109 x->props.saddr = orig->props.saddr;
1110
1111 if (orig->aalg) {
1112 x->aalg = xfrm_algo_clone(orig->aalg);
1113 if (!x->aalg)
1114 goto error;
1115 }
1116 x->props.aalgo = orig->props.aalgo;
1117
1118 if (orig->ealg) {
1119 x->ealg = xfrm_algo_clone(orig->ealg);
1120 if (!x->ealg)
1121 goto error;
1122 }
1123 x->props.ealgo = orig->props.ealgo;
1124
1125 if (orig->calg) {
1126 x->calg = xfrm_algo_clone(orig->calg);
1127 if (!x->calg)
1128 goto error;
1129 }
1130 x->props.calgo = orig->props.calgo;
1131
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001132 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001133 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1134 if (!x->encap)
1135 goto error;
1136 }
1137
1138 if (orig->coaddr) {
1139 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1140 GFP_KERNEL);
1141 if (!x->coaddr)
1142 goto error;
1143 }
1144
1145 err = xfrm_init_state(x);
1146 if (err)
1147 goto error;
1148
1149 x->props.flags = orig->props.flags;
1150
1151 x->curlft.add_time = orig->curlft.add_time;
1152 x->km.state = orig->km.state;
1153 x->km.seq = orig->km.seq;
1154
1155 return x;
1156
1157 error:
1158 if (errp)
1159 *errp = err;
1160 if (x) {
1161 kfree(x->aalg);
1162 kfree(x->ealg);
1163 kfree(x->calg);
1164 kfree(x->encap);
1165 kfree(x->coaddr);
1166 }
1167 kfree(x);
1168 return NULL;
1169}
1170EXPORT_SYMBOL(xfrm_state_clone);
1171
1172/* xfrm_state_lock is held */
1173struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1174{
1175 unsigned int h;
1176 struct xfrm_state *x;
1177 struct hlist_node *entry;
1178
1179 if (m->reqid) {
1180 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1181 m->reqid, m->old_family);
1182 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1183 if (x->props.mode != m->mode ||
1184 x->id.proto != m->proto)
1185 continue;
1186 if (m->reqid && x->props.reqid != m->reqid)
1187 continue;
1188 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1189 m->old_family) ||
1190 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1191 m->old_family))
1192 continue;
1193 xfrm_state_hold(x);
1194 return x;
1195 }
1196 } else {
1197 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1198 m->old_family);
1199 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1200 if (x->props.mode != m->mode ||
1201 x->id.proto != m->proto)
1202 continue;
1203 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1204 m->old_family) ||
1205 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1206 m->old_family))
1207 continue;
1208 xfrm_state_hold(x);
1209 return x;
1210 }
1211 }
1212
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001213 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001214}
1215EXPORT_SYMBOL(xfrm_migrate_state_find);
1216
1217struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1218 struct xfrm_migrate *m)
1219{
1220 struct xfrm_state *xc;
1221 int err;
1222
1223 xc = xfrm_state_clone(x, &err);
1224 if (!xc)
1225 return NULL;
1226
1227 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1228 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1229
1230 /* add state */
1231 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1232 /* a care is needed when the destination address of the
1233 state is to be updated as it is a part of triplet */
1234 xfrm_state_insert(xc);
1235 } else {
1236 if ((err = xfrm_state_add(xc)) < 0)
1237 goto error;
1238 }
1239
1240 return xc;
1241error:
1242 kfree(xc);
1243 return NULL;
1244}
1245EXPORT_SYMBOL(xfrm_state_migrate);
1246#endif
1247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248int xfrm_state_update(struct xfrm_state *x)
1249{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 struct xfrm_state *x1;
1251 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001252 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001255 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
1257 err = -ESRCH;
1258 if (!x1)
1259 goto out;
1260
1261 if (xfrm_state_kern(x1)) {
1262 xfrm_state_put(x1);
1263 err = -EEXIST;
1264 goto out;
1265 }
1266
1267 if (x1->km.state == XFRM_STATE_ACQ) {
1268 __xfrm_state_insert(x);
1269 x = NULL;
1270 }
1271 err = 0;
1272
1273out:
1274 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
1276 if (err)
1277 return err;
1278
1279 if (!x) {
1280 xfrm_state_delete(x1);
1281 xfrm_state_put(x1);
1282 return 0;
1283 }
1284
1285 err = -EINVAL;
1286 spin_lock_bh(&x1->lock);
1287 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1288 if (x->encap && x1->encap)
1289 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001290 if (x->coaddr && x1->coaddr) {
1291 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1292 }
1293 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1294 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1296 x1->km.dying = 0;
1297
David S. Millera47f0ce2006-08-24 03:54:22 -07001298 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 if (x1->curlft.use_time)
1300 xfrm_state_check_expire(x1);
1301
1302 err = 0;
1303 }
1304 spin_unlock_bh(&x1->lock);
1305
1306 xfrm_state_put(x1);
1307
1308 return err;
1309}
1310EXPORT_SYMBOL(xfrm_state_update);
1311
1312int xfrm_state_check_expire(struct xfrm_state *x)
1313{
1314 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001315 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
1317 if (x->km.state != XFRM_STATE_VALID)
1318 return -EINVAL;
1319
1320 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1321 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001322 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001323 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 return -EINVAL;
1325 }
1326
1327 if (!x->km.dying &&
1328 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001329 x->curlft.packets >= x->lft.soft_packet_limit)) {
1330 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001331 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 return 0;
1334}
1335EXPORT_SYMBOL(xfrm_state_check_expire);
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001338xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 unsigned short family)
1340{
1341 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
1343 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001344 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 return x;
1347}
1348EXPORT_SYMBOL(xfrm_state_lookup);
1349
1350struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001351xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1352 u8 proto, unsigned short family)
1353{
1354 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001355
1356 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001357 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001358 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001359 return x;
1360}
1361EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1362
1363struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001364xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1365 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 int create, unsigned short family)
1367{
1368 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
1370 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001371 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001373
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 return x;
1375}
1376EXPORT_SYMBOL(xfrm_find_acq);
1377
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001378#ifdef CONFIG_XFRM_SUB_POLICY
1379int
1380xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1381 unsigned short family)
1382{
1383 int err = 0;
1384 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1385 if (!afinfo)
1386 return -EAFNOSUPPORT;
1387
1388 spin_lock_bh(&xfrm_state_lock);
1389 if (afinfo->tmpl_sort)
1390 err = afinfo->tmpl_sort(dst, src, n);
1391 spin_unlock_bh(&xfrm_state_lock);
1392 xfrm_state_put_afinfo(afinfo);
1393 return err;
1394}
1395EXPORT_SYMBOL(xfrm_tmpl_sort);
1396
1397int
1398xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1399 unsigned short family)
1400{
1401 int err = 0;
1402 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1403 if (!afinfo)
1404 return -EAFNOSUPPORT;
1405
1406 spin_lock_bh(&xfrm_state_lock);
1407 if (afinfo->state_sort)
1408 err = afinfo->state_sort(dst, src, n);
1409 spin_unlock_bh(&xfrm_state_lock);
1410 xfrm_state_put_afinfo(afinfo);
1411 return err;
1412}
1413EXPORT_SYMBOL(xfrm_state_sort);
1414#endif
1415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416/* Silly enough, but I'm lazy to build resolution list */
1417
1418static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1419{
1420 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
David S. Millerf034b5d2006-08-24 03:08:07 -07001422 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001423 struct hlist_node *entry;
1424 struct xfrm_state *x;
1425
1426 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1427 if (x->km.seq == seq &&
1428 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 xfrm_state_hold(x);
1430 return x;
1431 }
1432 }
1433 }
1434 return NULL;
1435}
1436
1437struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1438{
1439 struct xfrm_state *x;
1440
1441 spin_lock_bh(&xfrm_state_lock);
1442 x = __xfrm_find_acq_byseq(seq);
1443 spin_unlock_bh(&xfrm_state_lock);
1444 return x;
1445}
1446EXPORT_SYMBOL(xfrm_find_acq_byseq);
1447
1448u32 xfrm_get_acqseq(void)
1449{
1450 u32 res;
1451 static u32 acqseq;
1452 static DEFINE_SPINLOCK(acqseq_lock);
1453
1454 spin_lock_bh(&acqseq_lock);
1455 res = (++acqseq ? : ++acqseq);
1456 spin_unlock_bh(&acqseq_lock);
1457 return res;
1458}
1459EXPORT_SYMBOL(xfrm_get_acqseq);
1460
Herbert Xu658b2192007-10-09 13:29:52 -07001461int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462{
David S. Millerf034b5d2006-08-24 03:08:07 -07001463 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001465 int err = -ENOENT;
1466 __be32 minspi = htonl(low);
1467 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468
Herbert Xu658b2192007-10-09 13:29:52 -07001469 spin_lock_bh(&x->lock);
1470 if (x->km.state == XFRM_STATE_DEAD)
1471 goto unlock;
1472
1473 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001475 goto unlock;
1476
1477 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478
1479 if (minspi == maxspi) {
1480 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1481 if (x0) {
1482 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001483 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 }
1485 x->id.spi = minspi;
1486 } else {
1487 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001488 for (h=0; h<high-low+1; h++) {
1489 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1491 if (x0 == NULL) {
1492 x->id.spi = htonl(spi);
1493 break;
1494 }
1495 xfrm_state_put(x0);
1496 }
1497 }
1498 if (x->id.spi) {
1499 spin_lock_bh(&xfrm_state_lock);
1500 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001501 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001503
1504 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 }
Herbert Xu658b2192007-10-09 13:29:52 -07001506
1507unlock:
1508 spin_unlock_bh(&x->lock);
1509
1510 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511}
1512EXPORT_SYMBOL(xfrm_alloc_spi);
1513
1514int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1515 void *data)
1516{
1517 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001518 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001519 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 int count = 0;
1521 int err = 0;
1522
1523 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001524 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001525 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001526 if (!xfrm_id_proto_match(x->id.proto, proto))
1527 continue;
1528 if (last) {
1529 err = func(last, count, data);
1530 if (err)
1531 goto out;
1532 }
1533 last = x;
1534 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 }
1536 }
1537 if (count == 0) {
1538 err = -ENOENT;
1539 goto out;
1540 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001541 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542out:
1543 spin_unlock_bh(&xfrm_state_lock);
1544 return err;
1545}
1546EXPORT_SYMBOL(xfrm_state_walk);
1547
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001548
1549void xfrm_replay_notify(struct xfrm_state *x, int event)
1550{
1551 struct km_event c;
1552 /* we send notify messages in case
1553 * 1. we updated on of the sequence numbers, and the seqno difference
1554 * is at least x->replay_maxdiff, in this case we also update the
1555 * timeout of our timer function
1556 * 2. if x->replay_maxage has elapsed since last update,
1557 * and there were changes
1558 *
1559 * The state structure must be locked!
1560 */
1561
1562 switch (event) {
1563 case XFRM_REPLAY_UPDATE:
1564 if (x->replay_maxdiff &&
1565 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001566 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1567 if (x->xflags & XFRM_TIME_DEFER)
1568 event = XFRM_REPLAY_TIMEOUT;
1569 else
1570 return;
1571 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001572
1573 break;
1574
1575 case XFRM_REPLAY_TIMEOUT:
1576 if ((x->replay.seq == x->preplay.seq) &&
1577 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001578 (x->replay.oseq == x->preplay.oseq)) {
1579 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001580 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001581 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001582
1583 break;
1584 }
1585
1586 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1587 c.event = XFRM_MSG_NEWAE;
1588 c.data.aevent = event;
1589 km_state_notify(x, &c);
1590
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001591 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001592 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001593 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001594}
1595
1596static void xfrm_replay_timer_handler(unsigned long data)
1597{
1598 struct xfrm_state *x = (struct xfrm_state*)data;
1599
1600 spin_lock(&x->lock);
1601
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001602 if (x->km.state == XFRM_STATE_VALID) {
1603 if (xfrm_aevent_is_on())
1604 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1605 else
1606 x->xflags |= XFRM_TIME_DEFER;
1607 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001608
1609 spin_unlock(&x->lock);
1610}
1611
Al Viroa252cc22006-09-27 18:48:18 -07001612int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613{
1614 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001615 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
1617 if (unlikely(seq == 0))
1618 return -EINVAL;
1619
1620 if (likely(seq > x->replay.seq))
1621 return 0;
1622
1623 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001624 if (diff >= min_t(unsigned int, x->props.replay_window,
1625 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 x->stats.replay_window++;
1627 return -EINVAL;
1628 }
1629
1630 if (x->replay.bitmap & (1U << diff)) {
1631 x->stats.replay++;
1632 return -EINVAL;
1633 }
1634 return 0;
1635}
1636EXPORT_SYMBOL(xfrm_replay_check);
1637
Al Viro61f46272006-09-27 18:48:33 -07001638void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001641 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
1643 if (seq > x->replay.seq) {
1644 diff = seq - x->replay.seq;
1645 if (diff < x->props.replay_window)
1646 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1647 else
1648 x->replay.bitmap = 1;
1649 x->replay.seq = seq;
1650 } else {
1651 diff = x->replay.seq - seq;
1652 x->replay.bitmap |= (1U << diff);
1653 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001654
1655 if (xfrm_aevent_is_on())
1656 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657}
1658EXPORT_SYMBOL(xfrm_replay_advance);
1659
1660static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1661static DEFINE_RWLOCK(xfrm_km_lock);
1662
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001663void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664{
1665 struct xfrm_mgr *km;
1666
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001667 read_lock(&xfrm_km_lock);
1668 list_for_each_entry(km, &xfrm_km_list, list)
1669 if (km->notify_policy)
1670 km->notify_policy(xp, dir, c);
1671 read_unlock(&xfrm_km_lock);
1672}
1673
1674void km_state_notify(struct xfrm_state *x, struct km_event *c)
1675{
1676 struct xfrm_mgr *km;
1677 read_lock(&xfrm_km_lock);
1678 list_for_each_entry(km, &xfrm_km_list, list)
1679 if (km->notify)
1680 km->notify(x, c);
1681 read_unlock(&xfrm_km_lock);
1682}
1683
1684EXPORT_SYMBOL(km_policy_notify);
1685EXPORT_SYMBOL(km_state_notify);
1686
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001687void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001688{
1689 struct km_event c;
1690
Herbert Xubf088672005-06-18 22:44:00 -07001691 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001692 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001693 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001694 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 if (hard)
1697 wake_up(&km_waitq);
1698}
1699
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001700EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001701/*
1702 * We send to all registered managers regardless of failure
1703 * We are happy with one success
1704*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001705int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001707 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 struct xfrm_mgr *km;
1709
1710 read_lock(&xfrm_km_lock);
1711 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001712 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1713 if (!acqret)
1714 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 }
1716 read_unlock(&xfrm_km_lock);
1717 return err;
1718}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001719EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720
Al Viro5d36b182006-11-08 00:24:06 -08001721int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
1723 int err = -EINVAL;
1724 struct xfrm_mgr *km;
1725
1726 read_lock(&xfrm_km_lock);
1727 list_for_each_entry(km, &xfrm_km_list, list) {
1728 if (km->new_mapping)
1729 err = km->new_mapping(x, ipaddr, sport);
1730 if (!err)
1731 break;
1732 }
1733 read_unlock(&xfrm_km_lock);
1734 return err;
1735}
1736EXPORT_SYMBOL(km_new_mapping);
1737
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001738void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001740 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
Herbert Xubf088672005-06-18 22:44:00 -07001742 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001743 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001744 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001745 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746
1747 if (hard)
1748 wake_up(&km_waitq);
1749}
David S. Millera70fcb02006-03-20 19:18:52 -08001750EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001752int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1753 struct xfrm_migrate *m, int num_migrate)
1754{
1755 int err = -EINVAL;
1756 int ret;
1757 struct xfrm_mgr *km;
1758
1759 read_lock(&xfrm_km_lock);
1760 list_for_each_entry(km, &xfrm_km_list, list) {
1761 if (km->migrate) {
1762 ret = km->migrate(sel, dir, type, m, num_migrate);
1763 if (!ret)
1764 err = ret;
1765 }
1766 }
1767 read_unlock(&xfrm_km_lock);
1768 return err;
1769}
1770EXPORT_SYMBOL(km_migrate);
1771
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001772int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1773{
1774 int err = -EINVAL;
1775 int ret;
1776 struct xfrm_mgr *km;
1777
1778 read_lock(&xfrm_km_lock);
1779 list_for_each_entry(km, &xfrm_km_list, list) {
1780 if (km->report) {
1781 ret = km->report(proto, sel, addr);
1782 if (!ret)
1783 err = ret;
1784 }
1785 }
1786 read_unlock(&xfrm_km_lock);
1787 return err;
1788}
1789EXPORT_SYMBOL(km_report);
1790
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1792{
1793 int err;
1794 u8 *data;
1795 struct xfrm_mgr *km;
1796 struct xfrm_policy *pol = NULL;
1797
1798 if (optlen <= 0 || optlen > PAGE_SIZE)
1799 return -EMSGSIZE;
1800
1801 data = kmalloc(optlen, GFP_KERNEL);
1802 if (!data)
1803 return -ENOMEM;
1804
1805 err = -EFAULT;
1806 if (copy_from_user(data, optval, optlen))
1807 goto out;
1808
1809 err = -EINVAL;
1810 read_lock(&xfrm_km_lock);
1811 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001812 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 optlen, &err);
1814 if (err >= 0)
1815 break;
1816 }
1817 read_unlock(&xfrm_km_lock);
1818
1819 if (err >= 0) {
1820 xfrm_sk_policy_insert(sk, err, pol);
1821 xfrm_pol_put(pol);
1822 err = 0;
1823 }
1824
1825out:
1826 kfree(data);
1827 return err;
1828}
1829EXPORT_SYMBOL(xfrm_user_policy);
1830
1831int xfrm_register_km(struct xfrm_mgr *km)
1832{
1833 write_lock_bh(&xfrm_km_lock);
1834 list_add_tail(&km->list, &xfrm_km_list);
1835 write_unlock_bh(&xfrm_km_lock);
1836 return 0;
1837}
1838EXPORT_SYMBOL(xfrm_register_km);
1839
1840int xfrm_unregister_km(struct xfrm_mgr *km)
1841{
1842 write_lock_bh(&xfrm_km_lock);
1843 list_del(&km->list);
1844 write_unlock_bh(&xfrm_km_lock);
1845 return 0;
1846}
1847EXPORT_SYMBOL(xfrm_unregister_km);
1848
1849int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1850{
1851 int err = 0;
1852 if (unlikely(afinfo == NULL))
1853 return -EINVAL;
1854 if (unlikely(afinfo->family >= NPROTO))
1855 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001856 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1858 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001859 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001861 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 return err;
1863}
1864EXPORT_SYMBOL(xfrm_state_register_afinfo);
1865
1866int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1867{
1868 int err = 0;
1869 if (unlikely(afinfo == NULL))
1870 return -EINVAL;
1871 if (unlikely(afinfo->family >= NPROTO))
1872 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001873 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1875 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1876 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001877 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001880 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 return err;
1882}
1883EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1884
Herbert Xu17c2a422007-10-17 21:33:12 -07001885static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886{
1887 struct xfrm_state_afinfo *afinfo;
1888 if (unlikely(family >= NPROTO))
1889 return NULL;
1890 read_lock(&xfrm_state_afinfo_lock);
1891 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001892 if (unlikely(!afinfo))
1893 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 return afinfo;
1895}
1896
Herbert Xu17c2a422007-10-17 21:33:12 -07001897static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898{
Herbert Xu546be242006-05-27 23:03:58 -07001899 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900}
1901
1902/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1903void xfrm_state_delete_tunnel(struct xfrm_state *x)
1904{
1905 if (x->tunnel) {
1906 struct xfrm_state *t = x->tunnel;
1907
1908 if (atomic_read(&t->tunnel_users) == 2)
1909 xfrm_state_delete(t);
1910 atomic_dec(&t->tunnel_users);
1911 xfrm_state_put(t);
1912 x->tunnel = NULL;
1913 }
1914}
1915EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1916
1917int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1918{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001919 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920
Patrick McHardyc5c25232007-04-09 11:47:18 -07001921 spin_lock_bh(&x->lock);
1922 if (x->km.state == XFRM_STATE_VALID &&
1923 x->type && x->type->get_mtu)
1924 res = x->type->get_mtu(x, mtu);
1925 else
Patrick McHardy28121612007-06-18 22:30:15 -07001926 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001927 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 return res;
1929}
1930
Herbert Xu72cb6962005-06-20 13:18:08 -07001931int xfrm_init_state(struct xfrm_state *x)
1932{
Herbert Xud094cd82005-06-20 13:19:41 -07001933 struct xfrm_state_afinfo *afinfo;
1934 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001935 int err;
1936
Herbert Xud094cd82005-06-20 13:19:41 -07001937 err = -EAFNOSUPPORT;
1938 afinfo = xfrm_state_get_afinfo(family);
1939 if (!afinfo)
1940 goto error;
1941
1942 err = 0;
1943 if (afinfo->init_flags)
1944 err = afinfo->init_flags(x);
1945
1946 xfrm_state_put_afinfo(afinfo);
1947
1948 if (err)
1949 goto error;
1950
1951 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001952 x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1953 if (x->inner_mode == NULL)
1954 goto error;
1955
1956 if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1957 family != x->sel.family)
1958 goto error;
1959
Herbert Xud094cd82005-06-20 13:19:41 -07001960 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001961 if (x->type == NULL)
1962 goto error;
1963
1964 err = x->type->init_state(x);
1965 if (err)
1966 goto error;
1967
Herbert Xu13996372007-10-17 21:35:51 -07001968 x->outer_mode = xfrm_get_mode(x->props.mode, family);
1969 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07001970 goto error;
1971
Herbert Xu72cb6962005-06-20 13:18:08 -07001972 x->km.state = XFRM_STATE_VALID;
1973
1974error:
1975 return err;
1976}
1977
1978EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001979
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980void __init xfrm_state_init(void)
1981{
David S. Millerf034b5d2006-08-24 03:08:07 -07001982 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
David S. Millerf034b5d2006-08-24 03:08:07 -07001984 sz = sizeof(struct hlist_head) * 8;
1985
David S. Miller44e36b42006-08-24 04:50:50 -07001986 xfrm_state_bydst = xfrm_hash_alloc(sz);
1987 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1988 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001989 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1990 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1991 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1992
David Howellsc4028952006-11-22 14:57:56 +00001993 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994}
1995
Joy Lattenab5f5e82007-09-17 11:51:22 -07001996#ifdef CONFIG_AUDITSYSCALL
1997static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
1998 struct audit_buffer *audit_buf)
1999{
2000 if (x->security)
2001 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
2002 x->security->ctx_alg, x->security->ctx_doi,
2003 x->security->ctx_str);
2004
2005 switch(x->props.family) {
2006 case AF_INET:
2007 audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
2008 NIPQUAD(x->props.saddr.a4),
2009 NIPQUAD(x->id.daddr.a4));
2010 break;
2011 case AF_INET6:
2012 {
2013 struct in6_addr saddr6, daddr6;
2014
2015 memcpy(&saddr6, x->props.saddr.a6,
2016 sizeof(struct in6_addr));
2017 memcpy(&daddr6, x->id.daddr.a6,
2018 sizeof(struct in6_addr));
2019 audit_log_format(audit_buf,
2020 " src=" NIP6_FMT " dst=" NIP6_FMT,
2021 NIP6(saddr6), NIP6(daddr6));
2022 }
2023 break;
2024 }
2025}
2026
2027void
2028xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
2029{
2030 struct audit_buffer *audit_buf;
2031 extern int audit_enabled;
2032
2033 if (audit_enabled == 0)
2034 return;
2035 audit_buf = xfrm_audit_start(sid, auid);
2036 if (audit_buf == NULL)
2037 return;
2038 audit_log_format(audit_buf, " op=SAD-add res=%u",result);
2039 xfrm_audit_common_stateinfo(x, audit_buf);
2040 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2041 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2042 audit_log_end(audit_buf);
2043}
2044EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2045
2046void
2047xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid)
2048{
2049 struct audit_buffer *audit_buf;
2050 extern int audit_enabled;
2051
2052 if (audit_enabled == 0)
2053 return;
2054 audit_buf = xfrm_audit_start(sid, auid);
2055 if (audit_buf == NULL)
2056 return;
2057 audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
2058 xfrm_audit_common_stateinfo(x, audit_buf);
2059 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2060 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2061 audit_log_end(audit_buf);
2062}
2063EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2064#endif /* CONFIG_AUDITSYSCALL */