blob: 51866b7fab3c858c5c97bf02f1fdb35e9119b5d0 [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);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800507 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
508 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
509 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800510 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 x->lft.soft_byte_limit = XFRM_INF;
512 x->lft.soft_packet_limit = XFRM_INF;
513 x->lft.hard_byte_limit = XFRM_INF;
514 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800515 x->replay_maxage = 0;
516 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 spin_lock_init(&x->lock);
518 }
519 return x;
520}
521EXPORT_SYMBOL(xfrm_state_alloc);
522
523void __xfrm_state_destroy(struct xfrm_state *x)
524{
525 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
526
527 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700528 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 spin_unlock_bh(&xfrm_state_gc_lock);
530 schedule_work(&xfrm_state_gc_work);
531}
532EXPORT_SYMBOL(__xfrm_state_destroy);
533
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800534int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700536 int err = -ESRCH;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (x->km.state != XFRM_STATE_DEAD) {
539 x->km.state = XFRM_STATE_DEAD;
540 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700541 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700542 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700543 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700544 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700545 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 /* All xfrm_state objects are created by xfrm_state_alloc.
549 * The xfrm_state_alloc call gives a reference, and that
550 * is what we are dropping here.
551 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800552 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700553 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700555
556 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800558EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700560int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700562 int err;
563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700565 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700567
568 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570EXPORT_SYMBOL(xfrm_state_delete);
571
Joy Latten4aa2e622007-06-04 19:05:57 -0400572#ifdef CONFIG_SECURITY_NETWORK_XFRM
573static inline int
574xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Joy Latten4aa2e622007-06-04 19:05:57 -0400576 int i, err = 0;
577
578 for (i = 0; i <= xfrm_state_hmask; i++) {
579 struct hlist_node *entry;
580 struct xfrm_state *x;
581
582 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
583 if (xfrm_id_proto_match(x->id.proto, proto) &&
584 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700585 xfrm_audit_state_delete(x, 0,
586 audit_info->loginuid,
587 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400588 return err;
589 }
590 }
591 }
592
593 return err;
594}
595#else
596static inline int
597xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
598{
599 return 0;
600}
601#endif
602
603int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
604{
605 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400608 err = xfrm_state_flush_secctx_check(proto, audit_info);
609 if (err)
610 goto out;
611
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700612 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700613 struct hlist_node *entry;
614 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700616 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700618 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 xfrm_state_hold(x);
620 spin_unlock_bh(&xfrm_state_lock);
621
Joy Latten161a09e2006-11-27 13:11:54 -0600622 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700623 xfrm_audit_state_delete(x, err ? 0 : 1,
624 audit_info->loginuid,
625 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 xfrm_state_put(x);
627
628 spin_lock_bh(&xfrm_state_lock);
629 goto restart;
630 }
631 }
632 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400633 err = 0;
634
635out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 spin_unlock_bh(&xfrm_state_lock);
637 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400638 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639}
640EXPORT_SYMBOL(xfrm_state_flush);
641
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700642void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700643{
644 spin_lock_bh(&xfrm_state_lock);
645 si->sadcnt = xfrm_state_num;
646 si->sadhcnt = xfrm_state_hmask;
647 si->sadhmcnt = xfrm_state_hashmax;
648 spin_unlock_bh(&xfrm_state_lock);
649}
650EXPORT_SYMBOL(xfrm_sad_getinfo);
651
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652static int
653xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
654 struct xfrm_tmpl *tmpl,
655 xfrm_address_t *daddr, xfrm_address_t *saddr,
656 unsigned short family)
657{
658 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
659 if (!afinfo)
660 return -1;
661 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
662 xfrm_state_put_afinfo(afinfo);
663 return 0;
664}
665
Al Viroa94cfd12006-09-27 18:47:24 -0700666static 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 -0700667{
668 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
669 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700670 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700671
David S. Miller8f126e32006-08-24 02:45:07 -0700672 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700673 if (x->props.family != family ||
674 x->id.spi != spi ||
675 x->id.proto != proto)
676 continue;
677
678 switch (family) {
679 case AF_INET:
680 if (x->id.daddr.a4 != daddr->a4)
681 continue;
682 break;
683 case AF_INET6:
684 if (!ipv6_addr_equal((struct in6_addr *)daddr,
685 (struct in6_addr *)
686 x->id.daddr.a6))
687 continue;
688 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700689 }
David S. Milleredcd5822006-08-24 00:42:45 -0700690
691 xfrm_state_hold(x);
692 return x;
693 }
694
695 return NULL;
696}
697
698static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
699{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700700 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700701 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700702 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700703
David S. Miller8f126e32006-08-24 02:45:07 -0700704 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700705 if (x->props.family != family ||
706 x->id.proto != proto)
707 continue;
708
709 switch (family) {
710 case AF_INET:
711 if (x->id.daddr.a4 != daddr->a4 ||
712 x->props.saddr.a4 != saddr->a4)
713 continue;
714 break;
715 case AF_INET6:
716 if (!ipv6_addr_equal((struct in6_addr *)daddr,
717 (struct in6_addr *)
718 x->id.daddr.a6) ||
719 !ipv6_addr_equal((struct in6_addr *)saddr,
720 (struct in6_addr *)
721 x->props.saddr.a6))
722 continue;
723 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700724 }
David S. Milleredcd5822006-08-24 00:42:45 -0700725
726 xfrm_state_hold(x);
727 return x;
728 }
729
730 return NULL;
731}
732
733static inline struct xfrm_state *
734__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
735{
736 if (use_spi)
737 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
738 x->id.proto, family);
739 else
740 return __xfrm_state_lookup_byaddr(&x->id.daddr,
741 &x->props.saddr,
742 x->id.proto, family);
743}
744
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700745static void xfrm_hash_grow_check(int have_hash_collision)
746{
747 if (have_hash_collision &&
748 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
749 xfrm_state_num > xfrm_state_hmask)
750 schedule_work(&xfrm_hash_work);
751}
752
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900754xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 struct flowi *fl, struct xfrm_tmpl *tmpl,
756 struct xfrm_policy *pol, int *err,
757 unsigned short family)
758{
David S. Millerc1969f22006-08-24 04:00:03 -0700759 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700760 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 struct xfrm_state *x, *x0;
762 int acquire_in_progress = 0;
763 int error = 0;
764 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700767 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 if (x->props.family == family &&
769 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700770 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 xfrm_state_addr_check(x, daddr, saddr, family) &&
772 tmpl->mode == x->props.mode &&
773 tmpl->id.proto == x->id.proto &&
774 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
775 /* Resolution logic:
776 1. There is a valid state with matching selector.
777 Done.
778 2. Valid state with inappropriate selector. Skip.
779
780 Entering area of "sysdeps".
781
782 3. If state is not valid, selector is temporary,
783 it selects only session which triggered
784 previous resolution. Key manager will do
785 something to install a state with proper
786 selector.
787 */
788 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700789 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700790 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 continue;
792 if (!best ||
793 best->km.dying > x->km.dying ||
794 (best->km.dying == x->km.dying &&
795 best->curlft.add_time < x->curlft.add_time))
796 best = x;
797 } else if (x->km.state == XFRM_STATE_ACQ) {
798 acquire_in_progress = 1;
799 } else if (x->km.state == XFRM_STATE_ERROR ||
800 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700801 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700802 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 error = -ESRCH;
804 }
805 }
806 }
807
808 x = best;
809 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700810 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700811 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
812 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 xfrm_state_put(x0);
814 error = -EEXIST;
815 goto out;
816 }
817 x = xfrm_state_alloc();
818 if (x == NULL) {
819 error = -ENOMEM;
820 goto out;
821 }
822 /* Initialize temporary selector matching only
823 * to current session. */
824 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
825
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700826 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
827 if (error) {
828 x->km.state = XFRM_STATE_DEAD;
829 xfrm_state_put(x);
830 x = NULL;
831 goto out;
832 }
833
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 if (km_query(x, tmpl, pol) == 0) {
835 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700836 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700837 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700838 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 if (x->id.spi) {
840 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700841 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 }
David S. Miller01e67d02007-05-25 00:41:38 -0700843 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
844 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700846 xfrm_state_num++;
847 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 } else {
849 x->km.state = XFRM_STATE_DEAD;
850 xfrm_state_put(x);
851 x = NULL;
852 error = -ESRCH;
853 }
854 }
855out:
856 if (x)
857 xfrm_state_hold(x);
858 else
859 *err = acquire_in_progress ? -EAGAIN : error;
860 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 return x;
862}
863
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700864struct xfrm_state *
865xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
866 unsigned short family, u8 mode, u8 proto, u32 reqid)
867{
868 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
869 struct xfrm_state *rx = NULL, *x = NULL;
870 struct hlist_node *entry;
871
872 spin_lock(&xfrm_state_lock);
873 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
874 if (x->props.family == family &&
875 x->props.reqid == reqid &&
876 !(x->props.flags & XFRM_STATE_WILDRECV) &&
877 xfrm_state_addr_check(x, daddr, saddr, family) &&
878 mode == x->props.mode &&
879 proto == x->id.proto &&
880 x->km.state == XFRM_STATE_VALID) {
881 rx = x;
882 break;
883 }
884 }
885
886 if (rx)
887 xfrm_state_hold(rx);
888 spin_unlock(&xfrm_state_lock);
889
890
891 return rx;
892}
893EXPORT_SYMBOL(xfrm_stateonly_find);
894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895static void __xfrm_state_insert(struct xfrm_state *x)
896{
David S. Millera624c102006-08-24 03:24:33 -0700897 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
David S. Miller9d4a7062006-08-24 03:18:09 -0700899 x->genid = ++xfrm_state_genid;
900
David S. Millerc1969f22006-08-24 04:00:03 -0700901 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
902 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700903 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700905 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700906 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700908 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700909 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
910 x->props.family);
911
David S. Miller8f126e32006-08-24 02:45:07 -0700912 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700913 }
914
David S. Millera47f0ce2006-08-24 03:54:22 -0700915 mod_timer(&x->timer, jiffies + HZ);
916 if (x->replay_maxage)
917 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800918
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700920
921 xfrm_state_num++;
922
David S. Miller918049f2006-10-12 22:03:24 -0700923 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
David S. Millerc7f5ea32006-08-24 03:29:04 -0700926/* xfrm_state_lock is held */
927static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
928{
929 unsigned short family = xnew->props.family;
930 u32 reqid = xnew->props.reqid;
931 struct xfrm_state *x;
932 struct hlist_node *entry;
933 unsigned int h;
934
David S. Millerc1969f22006-08-24 04:00:03 -0700935 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700936 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
937 if (x->props.family == family &&
938 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700939 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
940 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700941 x->genid = xfrm_state_genid;
942 }
943}
944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945void xfrm_state_insert(struct xfrm_state *x)
946{
947 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700948 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 __xfrm_state_insert(x);
950 spin_unlock_bh(&xfrm_state_lock);
951}
952EXPORT_SYMBOL(xfrm_state_insert);
953
David S. Miller27708342006-08-24 00:13:10 -0700954/* xfrm_state_lock is held */
955static 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)
956{
David S. Millerc1969f22006-08-24 04:00:03 -0700957 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700958 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700959 struct xfrm_state *x;
960
David S. Miller8f126e32006-08-24 02:45:07 -0700961 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700962 if (x->props.reqid != reqid ||
963 x->props.mode != mode ||
964 x->props.family != family ||
965 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700966 x->id.spi != 0 ||
967 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700968 continue;
969
970 switch (family) {
971 case AF_INET:
972 if (x->id.daddr.a4 != daddr->a4 ||
973 x->props.saddr.a4 != saddr->a4)
974 continue;
975 break;
976 case AF_INET6:
977 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
978 (struct in6_addr *)daddr) ||
979 !ipv6_addr_equal((struct in6_addr *)
980 x->props.saddr.a6,
981 (struct in6_addr *)saddr))
982 continue;
983 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700984 }
David S. Miller27708342006-08-24 00:13:10 -0700985
986 xfrm_state_hold(x);
987 return x;
988 }
989
990 if (!create)
991 return NULL;
992
993 x = xfrm_state_alloc();
994 if (likely(x)) {
995 switch (family) {
996 case AF_INET:
997 x->sel.daddr.a4 = daddr->a4;
998 x->sel.saddr.a4 = saddr->a4;
999 x->sel.prefixlen_d = 32;
1000 x->sel.prefixlen_s = 32;
1001 x->props.saddr.a4 = saddr->a4;
1002 x->id.daddr.a4 = daddr->a4;
1003 break;
1004
1005 case AF_INET6:
1006 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1007 (struct in6_addr *)daddr);
1008 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1009 (struct in6_addr *)saddr);
1010 x->sel.prefixlen_d = 128;
1011 x->sel.prefixlen_s = 128;
1012 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1013 (struct in6_addr *)saddr);
1014 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1015 (struct in6_addr *)daddr);
1016 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001017 }
David S. Miller27708342006-08-24 00:13:10 -07001018
1019 x->km.state = XFRM_STATE_ACQ;
1020 x->id.proto = proto;
1021 x->props.family = family;
1022 x->props.mode = mode;
1023 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001024 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001025 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001026 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001027 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001028 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001029 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001030 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001031
1032 xfrm_state_num++;
1033
1034 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001035 }
1036
1037 return x;
1038}
1039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1041
1042int xfrm_state_add(struct xfrm_state *x)
1043{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 struct xfrm_state *x1;
1045 int family;
1046 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001047 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
1049 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
1051 spin_lock_bh(&xfrm_state_lock);
1052
David S. Milleredcd5822006-08-24 00:42:45 -07001053 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 if (x1) {
1055 xfrm_state_put(x1);
1056 x1 = NULL;
1057 err = -EEXIST;
1058 goto out;
1059 }
1060
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001061 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001063 if (x1 && ((x1->id.proto != x->id.proto) ||
1064 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 xfrm_state_put(x1);
1066 x1 = NULL;
1067 }
1068 }
1069
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001070 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001071 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1072 x->id.proto,
1073 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
David S. Millerc7f5ea32006-08-24 03:29:04 -07001075 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 __xfrm_state_insert(x);
1077 err = 0;
1078
1079out:
1080 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
1082 if (x1) {
1083 xfrm_state_delete(x1);
1084 xfrm_state_put(x1);
1085 }
1086
1087 return err;
1088}
1089EXPORT_SYMBOL(xfrm_state_add);
1090
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001091#ifdef CONFIG_XFRM_MIGRATE
1092struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
1093{
1094 int err = -ENOMEM;
1095 struct xfrm_state *x = xfrm_state_alloc();
1096 if (!x)
1097 goto error;
1098
1099 memcpy(&x->id, &orig->id, sizeof(x->id));
1100 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1101 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1102 x->props.mode = orig->props.mode;
1103 x->props.replay_window = orig->props.replay_window;
1104 x->props.reqid = orig->props.reqid;
1105 x->props.family = orig->props.family;
1106 x->props.saddr = orig->props.saddr;
1107
1108 if (orig->aalg) {
1109 x->aalg = xfrm_algo_clone(orig->aalg);
1110 if (!x->aalg)
1111 goto error;
1112 }
1113 x->props.aalgo = orig->props.aalgo;
1114
1115 if (orig->ealg) {
1116 x->ealg = xfrm_algo_clone(orig->ealg);
1117 if (!x->ealg)
1118 goto error;
1119 }
1120 x->props.ealgo = orig->props.ealgo;
1121
1122 if (orig->calg) {
1123 x->calg = xfrm_algo_clone(orig->calg);
1124 if (!x->calg)
1125 goto error;
1126 }
1127 x->props.calgo = orig->props.calgo;
1128
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001129 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001130 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1131 if (!x->encap)
1132 goto error;
1133 }
1134
1135 if (orig->coaddr) {
1136 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1137 GFP_KERNEL);
1138 if (!x->coaddr)
1139 goto error;
1140 }
1141
1142 err = xfrm_init_state(x);
1143 if (err)
1144 goto error;
1145
1146 x->props.flags = orig->props.flags;
1147
1148 x->curlft.add_time = orig->curlft.add_time;
1149 x->km.state = orig->km.state;
1150 x->km.seq = orig->km.seq;
1151
1152 return x;
1153
1154 error:
1155 if (errp)
1156 *errp = err;
1157 if (x) {
1158 kfree(x->aalg);
1159 kfree(x->ealg);
1160 kfree(x->calg);
1161 kfree(x->encap);
1162 kfree(x->coaddr);
1163 }
1164 kfree(x);
1165 return NULL;
1166}
1167EXPORT_SYMBOL(xfrm_state_clone);
1168
1169/* xfrm_state_lock is held */
1170struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1171{
1172 unsigned int h;
1173 struct xfrm_state *x;
1174 struct hlist_node *entry;
1175
1176 if (m->reqid) {
1177 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1178 m->reqid, m->old_family);
1179 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1180 if (x->props.mode != m->mode ||
1181 x->id.proto != m->proto)
1182 continue;
1183 if (m->reqid && x->props.reqid != m->reqid)
1184 continue;
1185 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1186 m->old_family) ||
1187 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1188 m->old_family))
1189 continue;
1190 xfrm_state_hold(x);
1191 return x;
1192 }
1193 } else {
1194 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1195 m->old_family);
1196 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1197 if (x->props.mode != m->mode ||
1198 x->id.proto != m->proto)
1199 continue;
1200 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1201 m->old_family) ||
1202 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1203 m->old_family))
1204 continue;
1205 xfrm_state_hold(x);
1206 return x;
1207 }
1208 }
1209
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001210 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001211}
1212EXPORT_SYMBOL(xfrm_migrate_state_find);
1213
1214struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1215 struct xfrm_migrate *m)
1216{
1217 struct xfrm_state *xc;
1218 int err;
1219
1220 xc = xfrm_state_clone(x, &err);
1221 if (!xc)
1222 return NULL;
1223
1224 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1225 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1226
1227 /* add state */
1228 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1229 /* a care is needed when the destination address of the
1230 state is to be updated as it is a part of triplet */
1231 xfrm_state_insert(xc);
1232 } else {
1233 if ((err = xfrm_state_add(xc)) < 0)
1234 goto error;
1235 }
1236
1237 return xc;
1238error:
1239 kfree(xc);
1240 return NULL;
1241}
1242EXPORT_SYMBOL(xfrm_state_migrate);
1243#endif
1244
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245int xfrm_state_update(struct xfrm_state *x)
1246{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 struct xfrm_state *x1;
1248 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001249 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001252 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
1254 err = -ESRCH;
1255 if (!x1)
1256 goto out;
1257
1258 if (xfrm_state_kern(x1)) {
1259 xfrm_state_put(x1);
1260 err = -EEXIST;
1261 goto out;
1262 }
1263
1264 if (x1->km.state == XFRM_STATE_ACQ) {
1265 __xfrm_state_insert(x);
1266 x = NULL;
1267 }
1268 err = 0;
1269
1270out:
1271 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272
1273 if (err)
1274 return err;
1275
1276 if (!x) {
1277 xfrm_state_delete(x1);
1278 xfrm_state_put(x1);
1279 return 0;
1280 }
1281
1282 err = -EINVAL;
1283 spin_lock_bh(&x1->lock);
1284 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1285 if (x->encap && x1->encap)
1286 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001287 if (x->coaddr && x1->coaddr) {
1288 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1289 }
1290 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1291 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1293 x1->km.dying = 0;
1294
David S. Millera47f0ce2006-08-24 03:54:22 -07001295 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (x1->curlft.use_time)
1297 xfrm_state_check_expire(x1);
1298
1299 err = 0;
1300 }
1301 spin_unlock_bh(&x1->lock);
1302
1303 xfrm_state_put(x1);
1304
1305 return err;
1306}
1307EXPORT_SYMBOL(xfrm_state_update);
1308
1309int xfrm_state_check_expire(struct xfrm_state *x)
1310{
1311 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001312 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
1314 if (x->km.state != XFRM_STATE_VALID)
1315 return -EINVAL;
1316
1317 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1318 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001319 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001320 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 return -EINVAL;
1322 }
1323
1324 if (!x->km.dying &&
1325 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001326 x->curlft.packets >= x->lft.soft_packet_limit)) {
1327 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001328 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001329 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 return 0;
1331}
1332EXPORT_SYMBOL(xfrm_state_check_expire);
1333
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001335xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 unsigned short family)
1337{
1338 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001341 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 return x;
1344}
1345EXPORT_SYMBOL(xfrm_state_lookup);
1346
1347struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001348xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1349 u8 proto, unsigned short family)
1350{
1351 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001352
1353 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001354 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001355 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001356 return x;
1357}
1358EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1359
1360struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001361xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1362 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 int create, unsigned short family)
1364{
1365 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
1367 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001368 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return x;
1372}
1373EXPORT_SYMBOL(xfrm_find_acq);
1374
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001375#ifdef CONFIG_XFRM_SUB_POLICY
1376int
1377xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1378 unsigned short family)
1379{
1380 int err = 0;
1381 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1382 if (!afinfo)
1383 return -EAFNOSUPPORT;
1384
1385 spin_lock_bh(&xfrm_state_lock);
1386 if (afinfo->tmpl_sort)
1387 err = afinfo->tmpl_sort(dst, src, n);
1388 spin_unlock_bh(&xfrm_state_lock);
1389 xfrm_state_put_afinfo(afinfo);
1390 return err;
1391}
1392EXPORT_SYMBOL(xfrm_tmpl_sort);
1393
1394int
1395xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1396 unsigned short family)
1397{
1398 int err = 0;
1399 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1400 if (!afinfo)
1401 return -EAFNOSUPPORT;
1402
1403 spin_lock_bh(&xfrm_state_lock);
1404 if (afinfo->state_sort)
1405 err = afinfo->state_sort(dst, src, n);
1406 spin_unlock_bh(&xfrm_state_lock);
1407 xfrm_state_put_afinfo(afinfo);
1408 return err;
1409}
1410EXPORT_SYMBOL(xfrm_state_sort);
1411#endif
1412
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413/* Silly enough, but I'm lazy to build resolution list */
1414
1415static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1416{
1417 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
David S. Millerf034b5d2006-08-24 03:08:07 -07001419 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001420 struct hlist_node *entry;
1421 struct xfrm_state *x;
1422
1423 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1424 if (x->km.seq == seq &&
1425 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 xfrm_state_hold(x);
1427 return x;
1428 }
1429 }
1430 }
1431 return NULL;
1432}
1433
1434struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1435{
1436 struct xfrm_state *x;
1437
1438 spin_lock_bh(&xfrm_state_lock);
1439 x = __xfrm_find_acq_byseq(seq);
1440 spin_unlock_bh(&xfrm_state_lock);
1441 return x;
1442}
1443EXPORT_SYMBOL(xfrm_find_acq_byseq);
1444
1445u32 xfrm_get_acqseq(void)
1446{
1447 u32 res;
1448 static u32 acqseq;
1449 static DEFINE_SPINLOCK(acqseq_lock);
1450
1451 spin_lock_bh(&acqseq_lock);
1452 res = (++acqseq ? : ++acqseq);
1453 spin_unlock_bh(&acqseq_lock);
1454 return res;
1455}
1456EXPORT_SYMBOL(xfrm_get_acqseq);
1457
Herbert Xu658b2192007-10-09 13:29:52 -07001458int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459{
David S. Millerf034b5d2006-08-24 03:08:07 -07001460 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001462 int err = -ENOENT;
1463 __be32 minspi = htonl(low);
1464 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
Herbert Xu658b2192007-10-09 13:29:52 -07001466 spin_lock_bh(&x->lock);
1467 if (x->km.state == XFRM_STATE_DEAD)
1468 goto unlock;
1469
1470 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001472 goto unlock;
1473
1474 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
1476 if (minspi == maxspi) {
1477 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1478 if (x0) {
1479 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001480 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 }
1482 x->id.spi = minspi;
1483 } else {
1484 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001485 for (h=0; h<high-low+1; h++) {
1486 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1488 if (x0 == NULL) {
1489 x->id.spi = htonl(spi);
1490 break;
1491 }
1492 xfrm_state_put(x0);
1493 }
1494 }
1495 if (x->id.spi) {
1496 spin_lock_bh(&xfrm_state_lock);
1497 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001498 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001500
1501 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 }
Herbert Xu658b2192007-10-09 13:29:52 -07001503
1504unlock:
1505 spin_unlock_bh(&x->lock);
1506
1507 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508}
1509EXPORT_SYMBOL(xfrm_alloc_spi);
1510
1511int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1512 void *data)
1513{
1514 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001515 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001516 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 int count = 0;
1518 int err = 0;
1519
1520 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001521 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001522 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001523 if (!xfrm_id_proto_match(x->id.proto, proto))
1524 continue;
1525 if (last) {
1526 err = func(last, count, data);
1527 if (err)
1528 goto out;
1529 }
1530 last = x;
1531 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 }
1533 }
1534 if (count == 0) {
1535 err = -ENOENT;
1536 goto out;
1537 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001538 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539out:
1540 spin_unlock_bh(&xfrm_state_lock);
1541 return err;
1542}
1543EXPORT_SYMBOL(xfrm_state_walk);
1544
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001545
1546void xfrm_replay_notify(struct xfrm_state *x, int event)
1547{
1548 struct km_event c;
1549 /* we send notify messages in case
1550 * 1. we updated on of the sequence numbers, and the seqno difference
1551 * is at least x->replay_maxdiff, in this case we also update the
1552 * timeout of our timer function
1553 * 2. if x->replay_maxage has elapsed since last update,
1554 * and there were changes
1555 *
1556 * The state structure must be locked!
1557 */
1558
1559 switch (event) {
1560 case XFRM_REPLAY_UPDATE:
1561 if (x->replay_maxdiff &&
1562 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001563 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1564 if (x->xflags & XFRM_TIME_DEFER)
1565 event = XFRM_REPLAY_TIMEOUT;
1566 else
1567 return;
1568 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001569
1570 break;
1571
1572 case XFRM_REPLAY_TIMEOUT:
1573 if ((x->replay.seq == x->preplay.seq) &&
1574 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001575 (x->replay.oseq == x->preplay.oseq)) {
1576 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001577 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001578 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001579
1580 break;
1581 }
1582
1583 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1584 c.event = XFRM_MSG_NEWAE;
1585 c.data.aevent = event;
1586 km_state_notify(x, &c);
1587
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001588 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001589 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001590 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001591}
1592
1593static void xfrm_replay_timer_handler(unsigned long data)
1594{
1595 struct xfrm_state *x = (struct xfrm_state*)data;
1596
1597 spin_lock(&x->lock);
1598
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001599 if (x->km.state == XFRM_STATE_VALID) {
1600 if (xfrm_aevent_is_on())
1601 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1602 else
1603 x->xflags |= XFRM_TIME_DEFER;
1604 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001605
1606 spin_unlock(&x->lock);
1607}
1608
Al Viroa252cc22006-09-27 18:48:18 -07001609int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610{
1611 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001612 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
1614 if (unlikely(seq == 0))
1615 return -EINVAL;
1616
1617 if (likely(seq > x->replay.seq))
1618 return 0;
1619
1620 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001621 if (diff >= min_t(unsigned int, x->props.replay_window,
1622 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 x->stats.replay_window++;
1624 return -EINVAL;
1625 }
1626
1627 if (x->replay.bitmap & (1U << diff)) {
1628 x->stats.replay++;
1629 return -EINVAL;
1630 }
1631 return 0;
1632}
1633EXPORT_SYMBOL(xfrm_replay_check);
1634
Al Viro61f46272006-09-27 18:48:33 -07001635void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636{
1637 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001638 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639
1640 if (seq > x->replay.seq) {
1641 diff = seq - x->replay.seq;
1642 if (diff < x->props.replay_window)
1643 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1644 else
1645 x->replay.bitmap = 1;
1646 x->replay.seq = seq;
1647 } else {
1648 diff = x->replay.seq - seq;
1649 x->replay.bitmap |= (1U << diff);
1650 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001651
1652 if (xfrm_aevent_is_on())
1653 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654}
1655EXPORT_SYMBOL(xfrm_replay_advance);
1656
1657static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1658static DEFINE_RWLOCK(xfrm_km_lock);
1659
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001660void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
1662 struct xfrm_mgr *km;
1663
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001664 read_lock(&xfrm_km_lock);
1665 list_for_each_entry(km, &xfrm_km_list, list)
1666 if (km->notify_policy)
1667 km->notify_policy(xp, dir, c);
1668 read_unlock(&xfrm_km_lock);
1669}
1670
1671void km_state_notify(struct xfrm_state *x, struct km_event *c)
1672{
1673 struct xfrm_mgr *km;
1674 read_lock(&xfrm_km_lock);
1675 list_for_each_entry(km, &xfrm_km_list, list)
1676 if (km->notify)
1677 km->notify(x, c);
1678 read_unlock(&xfrm_km_lock);
1679}
1680
1681EXPORT_SYMBOL(km_policy_notify);
1682EXPORT_SYMBOL(km_state_notify);
1683
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001684void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001685{
1686 struct km_event c;
1687
Herbert Xubf088672005-06-18 22:44:00 -07001688 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001689 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001690 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001691 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692
1693 if (hard)
1694 wake_up(&km_waitq);
1695}
1696
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001697EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001698/*
1699 * We send to all registered managers regardless of failure
1700 * We are happy with one success
1701*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001702int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001704 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 struct xfrm_mgr *km;
1706
1707 read_lock(&xfrm_km_lock);
1708 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001709 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1710 if (!acqret)
1711 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 }
1713 read_unlock(&xfrm_km_lock);
1714 return err;
1715}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001716EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717
Al Viro5d36b182006-11-08 00:24:06 -08001718int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
1720 int err = -EINVAL;
1721 struct xfrm_mgr *km;
1722
1723 read_lock(&xfrm_km_lock);
1724 list_for_each_entry(km, &xfrm_km_list, list) {
1725 if (km->new_mapping)
1726 err = km->new_mapping(x, ipaddr, sport);
1727 if (!err)
1728 break;
1729 }
1730 read_unlock(&xfrm_km_lock);
1731 return err;
1732}
1733EXPORT_SYMBOL(km_new_mapping);
1734
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001735void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001737 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
Herbert Xubf088672005-06-18 22:44:00 -07001739 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001740 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001741 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001742 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
1744 if (hard)
1745 wake_up(&km_waitq);
1746}
David S. Millera70fcb02006-03-20 19:18:52 -08001747EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001749#ifdef CONFIG_XFRM_MIGRATE
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);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001769#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001770
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001771int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1772{
1773 int err = -EINVAL;
1774 int ret;
1775 struct xfrm_mgr *km;
1776
1777 read_lock(&xfrm_km_lock);
1778 list_for_each_entry(km, &xfrm_km_list, list) {
1779 if (km->report) {
1780 ret = km->report(proto, sel, addr);
1781 if (!ret)
1782 err = ret;
1783 }
1784 }
1785 read_unlock(&xfrm_km_lock);
1786 return err;
1787}
1788EXPORT_SYMBOL(km_report);
1789
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1791{
1792 int err;
1793 u8 *data;
1794 struct xfrm_mgr *km;
1795 struct xfrm_policy *pol = NULL;
1796
1797 if (optlen <= 0 || optlen > PAGE_SIZE)
1798 return -EMSGSIZE;
1799
1800 data = kmalloc(optlen, GFP_KERNEL);
1801 if (!data)
1802 return -ENOMEM;
1803
1804 err = -EFAULT;
1805 if (copy_from_user(data, optval, optlen))
1806 goto out;
1807
1808 err = -EINVAL;
1809 read_lock(&xfrm_km_lock);
1810 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001811 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812 optlen, &err);
1813 if (err >= 0)
1814 break;
1815 }
1816 read_unlock(&xfrm_km_lock);
1817
1818 if (err >= 0) {
1819 xfrm_sk_policy_insert(sk, err, pol);
1820 xfrm_pol_put(pol);
1821 err = 0;
1822 }
1823
1824out:
1825 kfree(data);
1826 return err;
1827}
1828EXPORT_SYMBOL(xfrm_user_policy);
1829
1830int xfrm_register_km(struct xfrm_mgr *km)
1831{
1832 write_lock_bh(&xfrm_km_lock);
1833 list_add_tail(&km->list, &xfrm_km_list);
1834 write_unlock_bh(&xfrm_km_lock);
1835 return 0;
1836}
1837EXPORT_SYMBOL(xfrm_register_km);
1838
1839int xfrm_unregister_km(struct xfrm_mgr *km)
1840{
1841 write_lock_bh(&xfrm_km_lock);
1842 list_del(&km->list);
1843 write_unlock_bh(&xfrm_km_lock);
1844 return 0;
1845}
1846EXPORT_SYMBOL(xfrm_unregister_km);
1847
1848int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1849{
1850 int err = 0;
1851 if (unlikely(afinfo == NULL))
1852 return -EINVAL;
1853 if (unlikely(afinfo->family >= NPROTO))
1854 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001855 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1857 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001858 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001860 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 return err;
1862}
1863EXPORT_SYMBOL(xfrm_state_register_afinfo);
1864
1865int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1866{
1867 int err = 0;
1868 if (unlikely(afinfo == NULL))
1869 return -EINVAL;
1870 if (unlikely(afinfo->family >= NPROTO))
1871 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001872 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1874 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1875 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001876 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001879 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 return err;
1881}
1882EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1883
Herbert Xu17c2a422007-10-17 21:33:12 -07001884static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885{
1886 struct xfrm_state_afinfo *afinfo;
1887 if (unlikely(family >= NPROTO))
1888 return NULL;
1889 read_lock(&xfrm_state_afinfo_lock);
1890 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001891 if (unlikely(!afinfo))
1892 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 return afinfo;
1894}
1895
Herbert Xu17c2a422007-10-17 21:33:12 -07001896static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897{
Herbert Xu546be242006-05-27 23:03:58 -07001898 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899}
1900
1901/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1902void xfrm_state_delete_tunnel(struct xfrm_state *x)
1903{
1904 if (x->tunnel) {
1905 struct xfrm_state *t = x->tunnel;
1906
1907 if (atomic_read(&t->tunnel_users) == 2)
1908 xfrm_state_delete(t);
1909 atomic_dec(&t->tunnel_users);
1910 xfrm_state_put(t);
1911 x->tunnel = NULL;
1912 }
1913}
1914EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1915
1916int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1917{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001918 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
Patrick McHardyc5c25232007-04-09 11:47:18 -07001920 spin_lock_bh(&x->lock);
1921 if (x->km.state == XFRM_STATE_VALID &&
1922 x->type && x->type->get_mtu)
1923 res = x->type->get_mtu(x, mtu);
1924 else
Patrick McHardy28121612007-06-18 22:30:15 -07001925 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001926 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 return res;
1928}
1929
Herbert Xu72cb6962005-06-20 13:18:08 -07001930int xfrm_init_state(struct xfrm_state *x)
1931{
Herbert Xud094cd82005-06-20 13:19:41 -07001932 struct xfrm_state_afinfo *afinfo;
1933 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001934 int err;
1935
Herbert Xud094cd82005-06-20 13:19:41 -07001936 err = -EAFNOSUPPORT;
1937 afinfo = xfrm_state_get_afinfo(family);
1938 if (!afinfo)
1939 goto error;
1940
1941 err = 0;
1942 if (afinfo->init_flags)
1943 err = afinfo->init_flags(x);
1944
1945 xfrm_state_put_afinfo(afinfo);
1946
1947 if (err)
1948 goto error;
1949
1950 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001951 x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1952 if (x->inner_mode == NULL)
1953 goto error;
1954
1955 if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1956 family != x->sel.family)
1957 goto error;
1958
Herbert Xud094cd82005-06-20 13:19:41 -07001959 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001960 if (x->type == NULL)
1961 goto error;
1962
1963 err = x->type->init_state(x);
1964 if (err)
1965 goto error;
1966
Herbert Xu13996372007-10-17 21:35:51 -07001967 x->outer_mode = xfrm_get_mode(x->props.mode, family);
1968 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07001969 goto error;
1970
Herbert Xu72cb6962005-06-20 13:18:08 -07001971 x->km.state = XFRM_STATE_VALID;
1972
1973error:
1974 return err;
1975}
1976
1977EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001978
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979void __init xfrm_state_init(void)
1980{
David S. Millerf034b5d2006-08-24 03:08:07 -07001981 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982
David S. Millerf034b5d2006-08-24 03:08:07 -07001983 sz = sizeof(struct hlist_head) * 8;
1984
David S. Miller44e36b42006-08-24 04:50:50 -07001985 xfrm_state_bydst = xfrm_hash_alloc(sz);
1986 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1987 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001988 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1989 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1990 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1991
David Howellsc4028952006-11-22 14:57:56 +00001992 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993}
1994
Joy Lattenab5f5e82007-09-17 11:51:22 -07001995#ifdef CONFIG_AUDITSYSCALL
1996static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
1997 struct audit_buffer *audit_buf)
1998{
1999 if (x->security)
2000 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
2001 x->security->ctx_alg, x->security->ctx_doi,
2002 x->security->ctx_str);
2003
2004 switch(x->props.family) {
2005 case AF_INET:
2006 audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
2007 NIPQUAD(x->props.saddr.a4),
2008 NIPQUAD(x->id.daddr.a4));
2009 break;
2010 case AF_INET6:
2011 {
2012 struct in6_addr saddr6, daddr6;
2013
2014 memcpy(&saddr6, x->props.saddr.a6,
2015 sizeof(struct in6_addr));
2016 memcpy(&daddr6, x->id.daddr.a6,
2017 sizeof(struct in6_addr));
2018 audit_log_format(audit_buf,
2019 " src=" NIP6_FMT " dst=" NIP6_FMT,
2020 NIP6(saddr6), NIP6(daddr6));
2021 }
2022 break;
2023 }
2024}
2025
2026void
2027xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
2028{
2029 struct audit_buffer *audit_buf;
Paul Moore9ab4c952007-12-12 11:10:16 -08002030 u32 spi;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002031 extern int audit_enabled;
2032
2033 if (audit_enabled == 0)
2034 return;
Paul Moore5951cab2007-12-20 00:00:45 -08002035 audit_buf = xfrm_audit_start(auid, sid);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002036 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);
Paul Moore9ab4c952007-12-12 11:10:16 -08002040 spi = ntohl(x->id.spi);
2041 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002042 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;
Paul Moore9ab4c952007-12-12 11:10:16 -08002050 u32 spi;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002051 extern int audit_enabled;
2052
2053 if (audit_enabled == 0)
2054 return;
Paul Moore5951cab2007-12-20 00:00:45 -08002055 audit_buf = xfrm_audit_start(auid, sid);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002056 if (audit_buf == NULL)
2057 return;
2058 audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
2059 xfrm_audit_common_stateinfo(x, audit_buf);
Paul Moore9ab4c952007-12-12 11:10:16 -08002060 spi = ntohl(x->id.spi);
2061 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002062 audit_log_end(audit_buf);
2063}
2064EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2065#endif /* CONFIG_AUDITSYSCALL */