blob: 48b4a06b3d1a2e15138231055e0e92817440ea8c [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 Xub59f45d2006-05-27 23:05:54 -0700380 if (x->mode)
381 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 if (x->type) {
383 x->type->destructor(x);
384 xfrm_put_type(x->type);
385 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800386 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 kfree(x);
388}
389
David Howellsc4028952006-11-22 14:57:56 +0000390static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391{
392 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700393 struct hlist_node *entry, *tmp;
394 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700397 gc_list.first = xfrm_state_gc_list.first;
398 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 spin_unlock_bh(&xfrm_state_gc_lock);
400
David S. Miller8f126e32006-08-24 02:45:07 -0700401 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 wake_up(&km_waitq);
405}
406
407static inline unsigned long make_jiffies(long secs)
408{
409 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
410 return MAX_SCHEDULE_TIMEOUT-1;
411 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900412 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414
415static void xfrm_timer_handler(unsigned long data)
416{
417 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800418 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 long next = LONG_MAX;
420 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600421 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423 spin_lock(&x->lock);
424 if (x->km.state == XFRM_STATE_DEAD)
425 goto out;
426 if (x->km.state == XFRM_STATE_EXPIRED)
427 goto expired;
428 if (x->lft.hard_add_expires_seconds) {
429 long tmo = x->lft.hard_add_expires_seconds +
430 x->curlft.add_time - now;
431 if (tmo <= 0)
432 goto expired;
433 if (tmo < next)
434 next = tmo;
435 }
436 if (x->lft.hard_use_expires_seconds) {
437 long tmo = x->lft.hard_use_expires_seconds +
438 (x->curlft.use_time ? : now) - now;
439 if (tmo <= 0)
440 goto expired;
441 if (tmo < next)
442 next = tmo;
443 }
444 if (x->km.dying)
445 goto resched;
446 if (x->lft.soft_add_expires_seconds) {
447 long tmo = x->lft.soft_add_expires_seconds +
448 x->curlft.add_time - now;
449 if (tmo <= 0)
450 warn = 1;
451 else if (tmo < next)
452 next = tmo;
453 }
454 if (x->lft.soft_use_expires_seconds) {
455 long tmo = x->lft.soft_use_expires_seconds +
456 (x->curlft.use_time ? : now) - now;
457 if (tmo <= 0)
458 warn = 1;
459 else if (tmo < next)
460 next = tmo;
461 }
462
Herbert Xu4666faa2005-06-18 22:43:22 -0700463 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800465 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700467 if (next != LONG_MAX)
468 mod_timer(&x->timer, jiffies + make_jiffies(next));
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 goto out;
471
472expired:
473 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
474 x->km.state = XFRM_STATE_EXPIRED;
475 wake_up(&km_waitq);
476 next = 2;
477 goto resched;
478 }
Joy Latten161a09e2006-11-27 13:11:54 -0600479
480 err = __xfrm_state_delete(x);
481 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800482 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Joy Lattenab5f5e82007-09-17 11:51:22 -0700484 xfrm_audit_state_delete(x, err ? 0 : 1,
485 audit_get_loginuid(current->audit_context), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600486
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487out:
488 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
David S. Miller0ac84752006-03-20 19:18:23 -0800491static void xfrm_replay_timer_handler(unsigned long data);
492
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493struct xfrm_state *xfrm_state_alloc(void)
494{
495 struct xfrm_state *x;
496
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700497 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 atomic_set(&x->refcnt, 1);
501 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700502 INIT_HLIST_NODE(&x->bydst);
503 INIT_HLIST_NODE(&x->bysrc);
504 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 init_timer(&x->timer);
506 x->timer.function = xfrm_timer_handler;
507 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800508 init_timer(&x->rtimer);
509 x->rtimer.function = xfrm_replay_timer_handler;
510 x->rtimer.data = (unsigned long)x;
James Morris9d729f72007-03-04 16:12:44 -0800511 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 x->lft.soft_byte_limit = XFRM_INF;
513 x->lft.soft_packet_limit = XFRM_INF;
514 x->lft.hard_byte_limit = XFRM_INF;
515 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800516 x->replay_maxage = 0;
517 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 spin_lock_init(&x->lock);
519 }
520 return x;
521}
522EXPORT_SYMBOL(xfrm_state_alloc);
523
524void __xfrm_state_destroy(struct xfrm_state *x)
525{
526 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
527
528 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700529 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 spin_unlock_bh(&xfrm_state_gc_lock);
531 schedule_work(&xfrm_state_gc_work);
532}
533EXPORT_SYMBOL(__xfrm_state_destroy);
534
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800535int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700537 int err = -ESRCH;
538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 if (x->km.state != XFRM_STATE_DEAD) {
540 x->km.state = XFRM_STATE_DEAD;
541 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700542 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700543 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700544 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700545 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700546 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 /* All xfrm_state objects are created by xfrm_state_alloc.
550 * The xfrm_state_alloc call gives a reference, and that
551 * is what we are dropping here.
552 */
Herbert Xu21380b82006-02-22 14:47:13 -0800553 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700554 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700556
557 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800559EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700561int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700563 int err;
564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700566 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700568
569 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570}
571EXPORT_SYMBOL(xfrm_state_delete);
572
Joy Latten4aa2e622007-06-04 19:05:57 -0400573#ifdef CONFIG_SECURITY_NETWORK_XFRM
574static inline int
575xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
Joy Latten4aa2e622007-06-04 19:05:57 -0400577 int i, err = 0;
578
579 for (i = 0; i <= xfrm_state_hmask; i++) {
580 struct hlist_node *entry;
581 struct xfrm_state *x;
582
583 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
584 if (xfrm_id_proto_match(x->id.proto, proto) &&
585 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700586 xfrm_audit_state_delete(x, 0,
587 audit_info->loginuid,
588 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400589 return err;
590 }
591 }
592 }
593
594 return err;
595}
596#else
597static inline int
598xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
599{
600 return 0;
601}
602#endif
603
604int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
605{
606 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400609 err = xfrm_state_flush_secctx_check(proto, audit_info);
610 if (err)
611 goto out;
612
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700613 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700614 struct hlist_node *entry;
615 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700617 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700619 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 xfrm_state_hold(x);
621 spin_unlock_bh(&xfrm_state_lock);
622
Joy Latten161a09e2006-11-27 13:11:54 -0600623 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700624 xfrm_audit_state_delete(x, err ? 0 : 1,
625 audit_info->loginuid,
626 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 xfrm_state_put(x);
628
629 spin_lock_bh(&xfrm_state_lock);
630 goto restart;
631 }
632 }
633 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400634 err = 0;
635
636out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 spin_unlock_bh(&xfrm_state_lock);
638 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400639 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640}
641EXPORT_SYMBOL(xfrm_state_flush);
642
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700643void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700644{
645 spin_lock_bh(&xfrm_state_lock);
646 si->sadcnt = xfrm_state_num;
647 si->sadhcnt = xfrm_state_hmask;
648 si->sadhmcnt = xfrm_state_hashmax;
649 spin_unlock_bh(&xfrm_state_lock);
650}
651EXPORT_SYMBOL(xfrm_sad_getinfo);
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653static int
654xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
655 struct xfrm_tmpl *tmpl,
656 xfrm_address_t *daddr, xfrm_address_t *saddr,
657 unsigned short family)
658{
659 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
660 if (!afinfo)
661 return -1;
662 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
663 xfrm_state_put_afinfo(afinfo);
664 return 0;
665}
666
Al Viroa94cfd12006-09-27 18:47:24 -0700667static 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 -0700668{
669 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
670 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700671 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700672
David S. Miller8f126e32006-08-24 02:45:07 -0700673 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700674 if (x->props.family != family ||
675 x->id.spi != spi ||
676 x->id.proto != proto)
677 continue;
678
679 switch (family) {
680 case AF_INET:
681 if (x->id.daddr.a4 != daddr->a4)
682 continue;
683 break;
684 case AF_INET6:
685 if (!ipv6_addr_equal((struct in6_addr *)daddr,
686 (struct in6_addr *)
687 x->id.daddr.a6))
688 continue;
689 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700690 }
David S. Milleredcd5822006-08-24 00:42:45 -0700691
692 xfrm_state_hold(x);
693 return x;
694 }
695
696 return NULL;
697}
698
699static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
700{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700701 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700702 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700703 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700704
David S. Miller8f126e32006-08-24 02:45:07 -0700705 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700706 if (x->props.family != family ||
707 x->id.proto != proto)
708 continue;
709
710 switch (family) {
711 case AF_INET:
712 if (x->id.daddr.a4 != daddr->a4 ||
713 x->props.saddr.a4 != saddr->a4)
714 continue;
715 break;
716 case AF_INET6:
717 if (!ipv6_addr_equal((struct in6_addr *)daddr,
718 (struct in6_addr *)
719 x->id.daddr.a6) ||
720 !ipv6_addr_equal((struct in6_addr *)saddr,
721 (struct in6_addr *)
722 x->props.saddr.a6))
723 continue;
724 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700725 }
David S. Milleredcd5822006-08-24 00:42:45 -0700726
727 xfrm_state_hold(x);
728 return x;
729 }
730
731 return NULL;
732}
733
734static inline struct xfrm_state *
735__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
736{
737 if (use_spi)
738 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
739 x->id.proto, family);
740 else
741 return __xfrm_state_lookup_byaddr(&x->id.daddr,
742 &x->props.saddr,
743 x->id.proto, family);
744}
745
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700746static void xfrm_hash_grow_check(int have_hash_collision)
747{
748 if (have_hash_collision &&
749 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
750 xfrm_state_num > xfrm_state_hmask)
751 schedule_work(&xfrm_hash_work);
752}
753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900755xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 struct flowi *fl, struct xfrm_tmpl *tmpl,
757 struct xfrm_policy *pol, int *err,
758 unsigned short family)
759{
David S. Millerc1969f22006-08-24 04:00:03 -0700760 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700761 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 struct xfrm_state *x, *x0;
763 int acquire_in_progress = 0;
764 int error = 0;
765 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900766
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700768 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 if (x->props.family == family &&
770 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700771 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 xfrm_state_addr_check(x, daddr, saddr, family) &&
773 tmpl->mode == x->props.mode &&
774 tmpl->id.proto == x->id.proto &&
775 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
776 /* Resolution logic:
777 1. There is a valid state with matching selector.
778 Done.
779 2. Valid state with inappropriate selector. Skip.
780
781 Entering area of "sysdeps".
782
783 3. If state is not valid, selector is temporary,
784 it selects only session which triggered
785 previous resolution. Key manager will do
786 something to install a state with proper
787 selector.
788 */
789 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700790 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700791 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 continue;
793 if (!best ||
794 best->km.dying > x->km.dying ||
795 (best->km.dying == x->km.dying &&
796 best->curlft.add_time < x->curlft.add_time))
797 best = x;
798 } else if (x->km.state == XFRM_STATE_ACQ) {
799 acquire_in_progress = 1;
800 } else if (x->km.state == XFRM_STATE_ERROR ||
801 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700802 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700803 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 error = -ESRCH;
805 }
806 }
807 }
808
809 x = best;
810 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700811 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700812 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
813 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 xfrm_state_put(x0);
815 error = -EEXIST;
816 goto out;
817 }
818 x = xfrm_state_alloc();
819 if (x == NULL) {
820 error = -ENOMEM;
821 goto out;
822 }
823 /* Initialize temporary selector matching only
824 * to current session. */
825 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
826
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700827 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
828 if (error) {
829 x->km.state = XFRM_STATE_DEAD;
830 xfrm_state_put(x);
831 x = NULL;
832 goto out;
833 }
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 if (km_query(x, tmpl, pol) == 0) {
836 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700837 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700838 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700839 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 if (x->id.spi) {
841 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700842 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 }
David S. Miller01e67d02007-05-25 00:41:38 -0700844 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
845 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700847 xfrm_state_num++;
848 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 } else {
850 x->km.state = XFRM_STATE_DEAD;
851 xfrm_state_put(x);
852 x = NULL;
853 error = -ESRCH;
854 }
855 }
856out:
857 if (x)
858 xfrm_state_hold(x);
859 else
860 *err = acquire_in_progress ? -EAGAIN : error;
861 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 return x;
863}
864
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700865struct xfrm_state *
866xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
867 unsigned short family, u8 mode, u8 proto, u32 reqid)
868{
869 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
870 struct xfrm_state *rx = NULL, *x = NULL;
871 struct hlist_node *entry;
872
873 spin_lock(&xfrm_state_lock);
874 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
875 if (x->props.family == family &&
876 x->props.reqid == reqid &&
877 !(x->props.flags & XFRM_STATE_WILDRECV) &&
878 xfrm_state_addr_check(x, daddr, saddr, family) &&
879 mode == x->props.mode &&
880 proto == x->id.proto &&
881 x->km.state == XFRM_STATE_VALID) {
882 rx = x;
883 break;
884 }
885 }
886
887 if (rx)
888 xfrm_state_hold(rx);
889 spin_unlock(&xfrm_state_lock);
890
891
892 return rx;
893}
894EXPORT_SYMBOL(xfrm_stateonly_find);
895
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896static void __xfrm_state_insert(struct xfrm_state *x)
897{
David S. Millera624c102006-08-24 03:24:33 -0700898 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
David S. Miller9d4a7062006-08-24 03:18:09 -0700900 x->genid = ++xfrm_state_genid;
901
David S. Millerc1969f22006-08-24 04:00:03 -0700902 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
903 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700904 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700906 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700907 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700909 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700910 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
911 x->props.family);
912
David S. Miller8f126e32006-08-24 02:45:07 -0700913 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700914 }
915
David S. Millera47f0ce2006-08-24 03:54:22 -0700916 mod_timer(&x->timer, jiffies + HZ);
917 if (x->replay_maxage)
918 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800919
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700921
922 xfrm_state_num++;
923
David S. Miller918049f2006-10-12 22:03:24 -0700924 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925}
926
David S. Millerc7f5ea32006-08-24 03:29:04 -0700927/* xfrm_state_lock is held */
928static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
929{
930 unsigned short family = xnew->props.family;
931 u32 reqid = xnew->props.reqid;
932 struct xfrm_state *x;
933 struct hlist_node *entry;
934 unsigned int h;
935
David S. Millerc1969f22006-08-24 04:00:03 -0700936 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700937 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
938 if (x->props.family == family &&
939 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700940 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
941 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700942 x->genid = xfrm_state_genid;
943 }
944}
945
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946void xfrm_state_insert(struct xfrm_state *x)
947{
948 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700949 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 __xfrm_state_insert(x);
951 spin_unlock_bh(&xfrm_state_lock);
952}
953EXPORT_SYMBOL(xfrm_state_insert);
954
David S. Miller27708342006-08-24 00:13:10 -0700955/* xfrm_state_lock is held */
956static 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)
957{
David S. Millerc1969f22006-08-24 04:00:03 -0700958 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700959 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700960 struct xfrm_state *x;
961
David S. Miller8f126e32006-08-24 02:45:07 -0700962 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700963 if (x->props.reqid != reqid ||
964 x->props.mode != mode ||
965 x->props.family != family ||
966 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700967 x->id.spi != 0 ||
968 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700969 continue;
970
971 switch (family) {
972 case AF_INET:
973 if (x->id.daddr.a4 != daddr->a4 ||
974 x->props.saddr.a4 != saddr->a4)
975 continue;
976 break;
977 case AF_INET6:
978 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
979 (struct in6_addr *)daddr) ||
980 !ipv6_addr_equal((struct in6_addr *)
981 x->props.saddr.a6,
982 (struct in6_addr *)saddr))
983 continue;
984 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700985 }
David S. Miller27708342006-08-24 00:13:10 -0700986
987 xfrm_state_hold(x);
988 return x;
989 }
990
991 if (!create)
992 return NULL;
993
994 x = xfrm_state_alloc();
995 if (likely(x)) {
996 switch (family) {
997 case AF_INET:
998 x->sel.daddr.a4 = daddr->a4;
999 x->sel.saddr.a4 = saddr->a4;
1000 x->sel.prefixlen_d = 32;
1001 x->sel.prefixlen_s = 32;
1002 x->props.saddr.a4 = saddr->a4;
1003 x->id.daddr.a4 = daddr->a4;
1004 break;
1005
1006 case AF_INET6:
1007 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1008 (struct in6_addr *)daddr);
1009 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1010 (struct in6_addr *)saddr);
1011 x->sel.prefixlen_d = 128;
1012 x->sel.prefixlen_s = 128;
1013 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1014 (struct in6_addr *)saddr);
1015 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1016 (struct in6_addr *)daddr);
1017 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001018 }
David S. Miller27708342006-08-24 00:13:10 -07001019
1020 x->km.state = XFRM_STATE_ACQ;
1021 x->id.proto = proto;
1022 x->props.family = family;
1023 x->props.mode = mode;
1024 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001025 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001026 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001027 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001028 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001029 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001030 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001031 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001032
1033 xfrm_state_num++;
1034
1035 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001036 }
1037
1038 return x;
1039}
1040
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1042
1043int xfrm_state_add(struct xfrm_state *x)
1044{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 struct xfrm_state *x1;
1046 int family;
1047 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001048 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
1050 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
1052 spin_lock_bh(&xfrm_state_lock);
1053
David S. Milleredcd5822006-08-24 00:42:45 -07001054 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 if (x1) {
1056 xfrm_state_put(x1);
1057 x1 = NULL;
1058 err = -EEXIST;
1059 goto out;
1060 }
1061
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001062 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001064 if (x1 && ((x1->id.proto != x->id.proto) ||
1065 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 xfrm_state_put(x1);
1067 x1 = NULL;
1068 }
1069 }
1070
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001071 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001072 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1073 x->id.proto,
1074 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
David S. Millerc7f5ea32006-08-24 03:29:04 -07001076 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 __xfrm_state_insert(x);
1078 err = 0;
1079
1080out:
1081 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 if (x1) {
1084 xfrm_state_delete(x1);
1085 xfrm_state_put(x1);
1086 }
1087
1088 return err;
1089}
1090EXPORT_SYMBOL(xfrm_state_add);
1091
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001092#ifdef CONFIG_XFRM_MIGRATE
1093struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
1094{
1095 int err = -ENOMEM;
1096 struct xfrm_state *x = xfrm_state_alloc();
1097 if (!x)
1098 goto error;
1099
1100 memcpy(&x->id, &orig->id, sizeof(x->id));
1101 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1102 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1103 x->props.mode = orig->props.mode;
1104 x->props.replay_window = orig->props.replay_window;
1105 x->props.reqid = orig->props.reqid;
1106 x->props.family = orig->props.family;
1107 x->props.saddr = orig->props.saddr;
1108
1109 if (orig->aalg) {
1110 x->aalg = xfrm_algo_clone(orig->aalg);
1111 if (!x->aalg)
1112 goto error;
1113 }
1114 x->props.aalgo = orig->props.aalgo;
1115
1116 if (orig->ealg) {
1117 x->ealg = xfrm_algo_clone(orig->ealg);
1118 if (!x->ealg)
1119 goto error;
1120 }
1121 x->props.ealgo = orig->props.ealgo;
1122
1123 if (orig->calg) {
1124 x->calg = xfrm_algo_clone(orig->calg);
1125 if (!x->calg)
1126 goto error;
1127 }
1128 x->props.calgo = orig->props.calgo;
1129
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001130 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001131 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1132 if (!x->encap)
1133 goto error;
1134 }
1135
1136 if (orig->coaddr) {
1137 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1138 GFP_KERNEL);
1139 if (!x->coaddr)
1140 goto error;
1141 }
1142
1143 err = xfrm_init_state(x);
1144 if (err)
1145 goto error;
1146
1147 x->props.flags = orig->props.flags;
1148
1149 x->curlft.add_time = orig->curlft.add_time;
1150 x->km.state = orig->km.state;
1151 x->km.seq = orig->km.seq;
1152
1153 return x;
1154
1155 error:
1156 if (errp)
1157 *errp = err;
1158 if (x) {
1159 kfree(x->aalg);
1160 kfree(x->ealg);
1161 kfree(x->calg);
1162 kfree(x->encap);
1163 kfree(x->coaddr);
1164 }
1165 kfree(x);
1166 return NULL;
1167}
1168EXPORT_SYMBOL(xfrm_state_clone);
1169
1170/* xfrm_state_lock is held */
1171struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1172{
1173 unsigned int h;
1174 struct xfrm_state *x;
1175 struct hlist_node *entry;
1176
1177 if (m->reqid) {
1178 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1179 m->reqid, m->old_family);
1180 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1181 if (x->props.mode != m->mode ||
1182 x->id.proto != m->proto)
1183 continue;
1184 if (m->reqid && x->props.reqid != m->reqid)
1185 continue;
1186 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1187 m->old_family) ||
1188 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1189 m->old_family))
1190 continue;
1191 xfrm_state_hold(x);
1192 return x;
1193 }
1194 } else {
1195 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1196 m->old_family);
1197 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1198 if (x->props.mode != m->mode ||
1199 x->id.proto != m->proto)
1200 continue;
1201 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1202 m->old_family) ||
1203 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1204 m->old_family))
1205 continue;
1206 xfrm_state_hold(x);
1207 return x;
1208 }
1209 }
1210
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001211 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001212}
1213EXPORT_SYMBOL(xfrm_migrate_state_find);
1214
1215struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1216 struct xfrm_migrate *m)
1217{
1218 struct xfrm_state *xc;
1219 int err;
1220
1221 xc = xfrm_state_clone(x, &err);
1222 if (!xc)
1223 return NULL;
1224
1225 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1226 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1227
1228 /* add state */
1229 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1230 /* a care is needed when the destination address of the
1231 state is to be updated as it is a part of triplet */
1232 xfrm_state_insert(xc);
1233 } else {
1234 if ((err = xfrm_state_add(xc)) < 0)
1235 goto error;
1236 }
1237
1238 return xc;
1239error:
1240 kfree(xc);
1241 return NULL;
1242}
1243EXPORT_SYMBOL(xfrm_state_migrate);
1244#endif
1245
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246int xfrm_state_update(struct xfrm_state *x)
1247{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 struct xfrm_state *x1;
1249 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001250 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001253 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
1255 err = -ESRCH;
1256 if (!x1)
1257 goto out;
1258
1259 if (xfrm_state_kern(x1)) {
1260 xfrm_state_put(x1);
1261 err = -EEXIST;
1262 goto out;
1263 }
1264
1265 if (x1->km.state == XFRM_STATE_ACQ) {
1266 __xfrm_state_insert(x);
1267 x = NULL;
1268 }
1269 err = 0;
1270
1271out:
1272 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 if (err)
1275 return err;
1276
1277 if (!x) {
1278 xfrm_state_delete(x1);
1279 xfrm_state_put(x1);
1280 return 0;
1281 }
1282
1283 err = -EINVAL;
1284 spin_lock_bh(&x1->lock);
1285 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1286 if (x->encap && x1->encap)
1287 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001288 if (x->coaddr && x1->coaddr) {
1289 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1290 }
1291 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1292 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1294 x1->km.dying = 0;
1295
David S. Millera47f0ce2006-08-24 03:54:22 -07001296 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 if (x1->curlft.use_time)
1298 xfrm_state_check_expire(x1);
1299
1300 err = 0;
1301 }
1302 spin_unlock_bh(&x1->lock);
1303
1304 xfrm_state_put(x1);
1305
1306 return err;
1307}
1308EXPORT_SYMBOL(xfrm_state_update);
1309
1310int xfrm_state_check_expire(struct xfrm_state *x)
1311{
1312 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001313 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
1315 if (x->km.state != XFRM_STATE_VALID)
1316 return -EINVAL;
1317
1318 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1319 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001320 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001321 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 return -EINVAL;
1323 }
1324
1325 if (!x->km.dying &&
1326 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001327 x->curlft.packets >= x->lft.soft_packet_limit)) {
1328 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001329 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001330 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return 0;
1332}
1333EXPORT_SYMBOL(xfrm_state_check_expire);
1334
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001336xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 unsigned short family)
1338{
1339 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
1341 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001342 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 return x;
1345}
1346EXPORT_SYMBOL(xfrm_state_lookup);
1347
1348struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001349xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1350 u8 proto, unsigned short family)
1351{
1352 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001353
1354 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001355 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001356 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001357 return x;
1358}
1359EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1360
1361struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001362xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1363 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 int create, unsigned short family)
1365{
1366 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
1368 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001369 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001371
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 return x;
1373}
1374EXPORT_SYMBOL(xfrm_find_acq);
1375
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001376#ifdef CONFIG_XFRM_SUB_POLICY
1377int
1378xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1379 unsigned short family)
1380{
1381 int err = 0;
1382 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1383 if (!afinfo)
1384 return -EAFNOSUPPORT;
1385
1386 spin_lock_bh(&xfrm_state_lock);
1387 if (afinfo->tmpl_sort)
1388 err = afinfo->tmpl_sort(dst, src, n);
1389 spin_unlock_bh(&xfrm_state_lock);
1390 xfrm_state_put_afinfo(afinfo);
1391 return err;
1392}
1393EXPORT_SYMBOL(xfrm_tmpl_sort);
1394
1395int
1396xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1397 unsigned short family)
1398{
1399 int err = 0;
1400 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1401 if (!afinfo)
1402 return -EAFNOSUPPORT;
1403
1404 spin_lock_bh(&xfrm_state_lock);
1405 if (afinfo->state_sort)
1406 err = afinfo->state_sort(dst, src, n);
1407 spin_unlock_bh(&xfrm_state_lock);
1408 xfrm_state_put_afinfo(afinfo);
1409 return err;
1410}
1411EXPORT_SYMBOL(xfrm_state_sort);
1412#endif
1413
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414/* Silly enough, but I'm lazy to build resolution list */
1415
1416static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1417{
1418 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
David S. Millerf034b5d2006-08-24 03:08:07 -07001420 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001421 struct hlist_node *entry;
1422 struct xfrm_state *x;
1423
1424 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1425 if (x->km.seq == seq &&
1426 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 xfrm_state_hold(x);
1428 return x;
1429 }
1430 }
1431 }
1432 return NULL;
1433}
1434
1435struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1436{
1437 struct xfrm_state *x;
1438
1439 spin_lock_bh(&xfrm_state_lock);
1440 x = __xfrm_find_acq_byseq(seq);
1441 spin_unlock_bh(&xfrm_state_lock);
1442 return x;
1443}
1444EXPORT_SYMBOL(xfrm_find_acq_byseq);
1445
1446u32 xfrm_get_acqseq(void)
1447{
1448 u32 res;
1449 static u32 acqseq;
1450 static DEFINE_SPINLOCK(acqseq_lock);
1451
1452 spin_lock_bh(&acqseq_lock);
1453 res = (++acqseq ? : ++acqseq);
1454 spin_unlock_bh(&acqseq_lock);
1455 return res;
1456}
1457EXPORT_SYMBOL(xfrm_get_acqseq);
1458
Herbert Xu658b2192007-10-09 13:29:52 -07001459int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460{
David S. Millerf034b5d2006-08-24 03:08:07 -07001461 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001463 int err = -ENOENT;
1464 __be32 minspi = htonl(low);
1465 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
Herbert Xu658b2192007-10-09 13:29:52 -07001467 spin_lock_bh(&x->lock);
1468 if (x->km.state == XFRM_STATE_DEAD)
1469 goto unlock;
1470
1471 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001473 goto unlock;
1474
1475 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476
1477 if (minspi == maxspi) {
1478 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1479 if (x0) {
1480 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001481 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 }
1483 x->id.spi = minspi;
1484 } else {
1485 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001486 for (h=0; h<high-low+1; h++) {
1487 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1489 if (x0 == NULL) {
1490 x->id.spi = htonl(spi);
1491 break;
1492 }
1493 xfrm_state_put(x0);
1494 }
1495 }
1496 if (x->id.spi) {
1497 spin_lock_bh(&xfrm_state_lock);
1498 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001499 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001501
1502 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 }
Herbert Xu658b2192007-10-09 13:29:52 -07001504
1505unlock:
1506 spin_unlock_bh(&x->lock);
1507
1508 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509}
1510EXPORT_SYMBOL(xfrm_alloc_spi);
1511
1512int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1513 void *data)
1514{
1515 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001516 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001517 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 int count = 0;
1519 int err = 0;
1520
1521 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001522 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001523 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001524 if (!xfrm_id_proto_match(x->id.proto, proto))
1525 continue;
1526 if (last) {
1527 err = func(last, count, data);
1528 if (err)
1529 goto out;
1530 }
1531 last = x;
1532 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 }
1534 }
1535 if (count == 0) {
1536 err = -ENOENT;
1537 goto out;
1538 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001539 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540out:
1541 spin_unlock_bh(&xfrm_state_lock);
1542 return err;
1543}
1544EXPORT_SYMBOL(xfrm_state_walk);
1545
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001546
1547void xfrm_replay_notify(struct xfrm_state *x, int event)
1548{
1549 struct km_event c;
1550 /* we send notify messages in case
1551 * 1. we updated on of the sequence numbers, and the seqno difference
1552 * is at least x->replay_maxdiff, in this case we also update the
1553 * timeout of our timer function
1554 * 2. if x->replay_maxage has elapsed since last update,
1555 * and there were changes
1556 *
1557 * The state structure must be locked!
1558 */
1559
1560 switch (event) {
1561 case XFRM_REPLAY_UPDATE:
1562 if (x->replay_maxdiff &&
1563 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001564 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1565 if (x->xflags & XFRM_TIME_DEFER)
1566 event = XFRM_REPLAY_TIMEOUT;
1567 else
1568 return;
1569 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001570
1571 break;
1572
1573 case XFRM_REPLAY_TIMEOUT:
1574 if ((x->replay.seq == x->preplay.seq) &&
1575 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001576 (x->replay.oseq == x->preplay.oseq)) {
1577 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001578 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001579 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001580
1581 break;
1582 }
1583
1584 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1585 c.event = XFRM_MSG_NEWAE;
1586 c.data.aevent = event;
1587 km_state_notify(x, &c);
1588
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001589 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001590 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001591 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001592}
1593
1594static void xfrm_replay_timer_handler(unsigned long data)
1595{
1596 struct xfrm_state *x = (struct xfrm_state*)data;
1597
1598 spin_lock(&x->lock);
1599
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001600 if (x->km.state == XFRM_STATE_VALID) {
1601 if (xfrm_aevent_is_on())
1602 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1603 else
1604 x->xflags |= XFRM_TIME_DEFER;
1605 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001606
1607 spin_unlock(&x->lock);
1608}
1609
Al Viroa252cc22006-09-27 18:48:18 -07001610int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611{
1612 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001613 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
1615 if (unlikely(seq == 0))
1616 return -EINVAL;
1617
1618 if (likely(seq > x->replay.seq))
1619 return 0;
1620
1621 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001622 if (diff >= min_t(unsigned int, x->props.replay_window,
1623 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 x->stats.replay_window++;
1625 return -EINVAL;
1626 }
1627
1628 if (x->replay.bitmap & (1U << diff)) {
1629 x->stats.replay++;
1630 return -EINVAL;
1631 }
1632 return 0;
1633}
1634EXPORT_SYMBOL(xfrm_replay_check);
1635
Al Viro61f46272006-09-27 18:48:33 -07001636void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637{
1638 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001639 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641 if (seq > x->replay.seq) {
1642 diff = seq - x->replay.seq;
1643 if (diff < x->props.replay_window)
1644 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1645 else
1646 x->replay.bitmap = 1;
1647 x->replay.seq = seq;
1648 } else {
1649 diff = x->replay.seq - seq;
1650 x->replay.bitmap |= (1U << diff);
1651 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001652
1653 if (xfrm_aevent_is_on())
1654 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655}
1656EXPORT_SYMBOL(xfrm_replay_advance);
1657
1658static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1659static DEFINE_RWLOCK(xfrm_km_lock);
1660
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001661void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
1663 struct xfrm_mgr *km;
1664
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001665 read_lock(&xfrm_km_lock);
1666 list_for_each_entry(km, &xfrm_km_list, list)
1667 if (km->notify_policy)
1668 km->notify_policy(xp, dir, c);
1669 read_unlock(&xfrm_km_lock);
1670}
1671
1672void km_state_notify(struct xfrm_state *x, struct km_event *c)
1673{
1674 struct xfrm_mgr *km;
1675 read_lock(&xfrm_km_lock);
1676 list_for_each_entry(km, &xfrm_km_list, list)
1677 if (km->notify)
1678 km->notify(x, c);
1679 read_unlock(&xfrm_km_lock);
1680}
1681
1682EXPORT_SYMBOL(km_policy_notify);
1683EXPORT_SYMBOL(km_state_notify);
1684
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001685void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001686{
1687 struct km_event c;
1688
Herbert Xubf088672005-06-18 22:44:00 -07001689 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001690 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001691 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001692 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 if (hard)
1695 wake_up(&km_waitq);
1696}
1697
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001698EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001699/*
1700 * We send to all registered managers regardless of failure
1701 * We are happy with one success
1702*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001703int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001705 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 struct xfrm_mgr *km;
1707
1708 read_lock(&xfrm_km_lock);
1709 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001710 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1711 if (!acqret)
1712 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 }
1714 read_unlock(&xfrm_km_lock);
1715 return err;
1716}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001717EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
Al Viro5d36b182006-11-08 00:24:06 -08001719int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720{
1721 int err = -EINVAL;
1722 struct xfrm_mgr *km;
1723
1724 read_lock(&xfrm_km_lock);
1725 list_for_each_entry(km, &xfrm_km_list, list) {
1726 if (km->new_mapping)
1727 err = km->new_mapping(x, ipaddr, sport);
1728 if (!err)
1729 break;
1730 }
1731 read_unlock(&xfrm_km_lock);
1732 return err;
1733}
1734EXPORT_SYMBOL(km_new_mapping);
1735
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001736void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001738 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
Herbert Xubf088672005-06-18 22:44:00 -07001740 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001741 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001742 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001743 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
1745 if (hard)
1746 wake_up(&km_waitq);
1747}
David S. Millera70fcb02006-03-20 19:18:52 -08001748EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001750int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1751 struct xfrm_migrate *m, int num_migrate)
1752{
1753 int err = -EINVAL;
1754 int ret;
1755 struct xfrm_mgr *km;
1756
1757 read_lock(&xfrm_km_lock);
1758 list_for_each_entry(km, &xfrm_km_list, list) {
1759 if (km->migrate) {
1760 ret = km->migrate(sel, dir, type, m, num_migrate);
1761 if (!ret)
1762 err = ret;
1763 }
1764 }
1765 read_unlock(&xfrm_km_lock);
1766 return err;
1767}
1768EXPORT_SYMBOL(km_migrate);
1769
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001770int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1771{
1772 int err = -EINVAL;
1773 int ret;
1774 struct xfrm_mgr *km;
1775
1776 read_lock(&xfrm_km_lock);
1777 list_for_each_entry(km, &xfrm_km_list, list) {
1778 if (km->report) {
1779 ret = km->report(proto, sel, addr);
1780 if (!ret)
1781 err = ret;
1782 }
1783 }
1784 read_unlock(&xfrm_km_lock);
1785 return err;
1786}
1787EXPORT_SYMBOL(km_report);
1788
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1790{
1791 int err;
1792 u8 *data;
1793 struct xfrm_mgr *km;
1794 struct xfrm_policy *pol = NULL;
1795
1796 if (optlen <= 0 || optlen > PAGE_SIZE)
1797 return -EMSGSIZE;
1798
1799 data = kmalloc(optlen, GFP_KERNEL);
1800 if (!data)
1801 return -ENOMEM;
1802
1803 err = -EFAULT;
1804 if (copy_from_user(data, optval, optlen))
1805 goto out;
1806
1807 err = -EINVAL;
1808 read_lock(&xfrm_km_lock);
1809 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001810 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 optlen, &err);
1812 if (err >= 0)
1813 break;
1814 }
1815 read_unlock(&xfrm_km_lock);
1816
1817 if (err >= 0) {
1818 xfrm_sk_policy_insert(sk, err, pol);
1819 xfrm_pol_put(pol);
1820 err = 0;
1821 }
1822
1823out:
1824 kfree(data);
1825 return err;
1826}
1827EXPORT_SYMBOL(xfrm_user_policy);
1828
1829int xfrm_register_km(struct xfrm_mgr *km)
1830{
1831 write_lock_bh(&xfrm_km_lock);
1832 list_add_tail(&km->list, &xfrm_km_list);
1833 write_unlock_bh(&xfrm_km_lock);
1834 return 0;
1835}
1836EXPORT_SYMBOL(xfrm_register_km);
1837
1838int xfrm_unregister_km(struct xfrm_mgr *km)
1839{
1840 write_lock_bh(&xfrm_km_lock);
1841 list_del(&km->list);
1842 write_unlock_bh(&xfrm_km_lock);
1843 return 0;
1844}
1845EXPORT_SYMBOL(xfrm_unregister_km);
1846
1847int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1848{
1849 int err = 0;
1850 if (unlikely(afinfo == NULL))
1851 return -EINVAL;
1852 if (unlikely(afinfo->family >= NPROTO))
1853 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001854 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1856 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001857 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001859 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 return err;
1861}
1862EXPORT_SYMBOL(xfrm_state_register_afinfo);
1863
1864int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1865{
1866 int err = 0;
1867 if (unlikely(afinfo == NULL))
1868 return -EINVAL;
1869 if (unlikely(afinfo->family >= NPROTO))
1870 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001871 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1873 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1874 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001875 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001878 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 return err;
1880}
1881EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1882
Herbert Xu17c2a422007-10-17 21:33:12 -07001883static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884{
1885 struct xfrm_state_afinfo *afinfo;
1886 if (unlikely(family >= NPROTO))
1887 return NULL;
1888 read_lock(&xfrm_state_afinfo_lock);
1889 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001890 if (unlikely(!afinfo))
1891 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 return afinfo;
1893}
1894
Herbert Xu17c2a422007-10-17 21:33:12 -07001895static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896{
Herbert Xu546be242006-05-27 23:03:58 -07001897 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898}
1899
1900/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1901void xfrm_state_delete_tunnel(struct xfrm_state *x)
1902{
1903 if (x->tunnel) {
1904 struct xfrm_state *t = x->tunnel;
1905
1906 if (atomic_read(&t->tunnel_users) == 2)
1907 xfrm_state_delete(t);
1908 atomic_dec(&t->tunnel_users);
1909 xfrm_state_put(t);
1910 x->tunnel = NULL;
1911 }
1912}
1913EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1914
1915int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1916{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001917 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918
Patrick McHardyc5c25232007-04-09 11:47:18 -07001919 spin_lock_bh(&x->lock);
1920 if (x->km.state == XFRM_STATE_VALID &&
1921 x->type && x->type->get_mtu)
1922 res = x->type->get_mtu(x, mtu);
1923 else
Patrick McHardy28121612007-06-18 22:30:15 -07001924 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001925 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 return res;
1927}
1928
Herbert Xu72cb6962005-06-20 13:18:08 -07001929int xfrm_init_state(struct xfrm_state *x)
1930{
Herbert Xud094cd82005-06-20 13:19:41 -07001931 struct xfrm_state_afinfo *afinfo;
1932 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001933 int err;
1934
Herbert Xud094cd82005-06-20 13:19:41 -07001935 err = -EAFNOSUPPORT;
1936 afinfo = xfrm_state_get_afinfo(family);
1937 if (!afinfo)
1938 goto error;
1939
1940 err = 0;
1941 if (afinfo->init_flags)
1942 err = afinfo->init_flags(x);
1943
1944 xfrm_state_put_afinfo(afinfo);
1945
1946 if (err)
1947 goto error;
1948
1949 err = -EPROTONOSUPPORT;
1950 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001951 if (x->type == NULL)
1952 goto error;
1953
1954 err = x->type->init_state(x);
1955 if (err)
1956 goto error;
1957
Herbert Xub59f45d2006-05-27 23:05:54 -07001958 x->mode = xfrm_get_mode(x->props.mode, family);
1959 if (x->mode == NULL)
1960 goto error;
1961
Herbert Xu72cb6962005-06-20 13:18:08 -07001962 x->km.state = XFRM_STATE_VALID;
1963
1964error:
1965 return err;
1966}
1967
1968EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001969
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970void __init xfrm_state_init(void)
1971{
David S. Millerf034b5d2006-08-24 03:08:07 -07001972 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973
David S. Millerf034b5d2006-08-24 03:08:07 -07001974 sz = sizeof(struct hlist_head) * 8;
1975
David S. Miller44e36b42006-08-24 04:50:50 -07001976 xfrm_state_bydst = xfrm_hash_alloc(sz);
1977 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1978 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001979 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1980 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1981 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1982
David Howellsc4028952006-11-22 14:57:56 +00001983 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984}
1985
Joy Lattenab5f5e82007-09-17 11:51:22 -07001986#ifdef CONFIG_AUDITSYSCALL
1987static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
1988 struct audit_buffer *audit_buf)
1989{
1990 if (x->security)
1991 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
1992 x->security->ctx_alg, x->security->ctx_doi,
1993 x->security->ctx_str);
1994
1995 switch(x->props.family) {
1996 case AF_INET:
1997 audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
1998 NIPQUAD(x->props.saddr.a4),
1999 NIPQUAD(x->id.daddr.a4));
2000 break;
2001 case AF_INET6:
2002 {
2003 struct in6_addr saddr6, daddr6;
2004
2005 memcpy(&saddr6, x->props.saddr.a6,
2006 sizeof(struct in6_addr));
2007 memcpy(&daddr6, x->id.daddr.a6,
2008 sizeof(struct in6_addr));
2009 audit_log_format(audit_buf,
2010 " src=" NIP6_FMT " dst=" NIP6_FMT,
2011 NIP6(saddr6), NIP6(daddr6));
2012 }
2013 break;
2014 }
2015}
2016
2017void
2018xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
2019{
2020 struct audit_buffer *audit_buf;
2021 extern int audit_enabled;
2022
2023 if (audit_enabled == 0)
2024 return;
2025 audit_buf = xfrm_audit_start(sid, auid);
2026 if (audit_buf == NULL)
2027 return;
2028 audit_log_format(audit_buf, " op=SAD-add res=%u",result);
2029 xfrm_audit_common_stateinfo(x, audit_buf);
2030 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2031 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2032 audit_log_end(audit_buf);
2033}
2034EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2035
2036void
2037xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid)
2038{
2039 struct audit_buffer *audit_buf;
2040 extern int audit_enabled;
2041
2042 if (audit_enabled == 0)
2043 return;
2044 audit_buf = xfrm_audit_start(sid, auid);
2045 if (audit_buf == NULL)
2046 return;
2047 audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
2048 xfrm_audit_common_stateinfo(x, audit_buf);
2049 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2050 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2051 audit_log_end(audit_buf);
2052}
2053EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2054#endif /* CONFIG_AUDITSYSCALL */