blob: dc438f2b944226a26ec45033d5ff73cffe8dc433 [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
David S. Millerc1969f22006-08-24 04:00:03 -070060static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
61 xfrm_address_t *saddr,
62 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070063 unsigned short family)
64{
David S. Millerc1969f22006-08-24 04:00:03 -070065 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070066}
67
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070068static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
69 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070070 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070071{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070072 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070073}
74
David S. Miller2575b652006-08-24 03:26:44 -070075static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070076xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070077{
David S. Millerc1969f22006-08-24 04:00:03 -070078 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070079}
80
David S. Millerf034b5d2006-08-24 03:08:07 -070081static void xfrm_hash_transfer(struct hlist_head *list,
82 struct hlist_head *ndsttable,
83 struct hlist_head *nsrctable,
84 struct hlist_head *nspitable,
85 unsigned int nhashmask)
86{
87 struct hlist_node *entry, *tmp;
88 struct xfrm_state *x;
89
90 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
91 unsigned int h;
92
David S. Millerc1969f22006-08-24 04:00:03 -070093 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
94 x->props.reqid, x->props.family,
95 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070096 hlist_add_head(&x->bydst, ndsttable+h);
97
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070098 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
99 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700100 nhashmask);
101 hlist_add_head(&x->bysrc, nsrctable+h);
102
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700103 if (x->id.spi) {
104 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
105 x->id.proto, x->props.family,
106 nhashmask);
107 hlist_add_head(&x->byspi, nspitable+h);
108 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700109 }
110}
111
112static unsigned long xfrm_hash_new_size(void)
113{
114 return ((xfrm_state_hmask + 1) << 1) *
115 sizeof(struct hlist_head);
116}
117
118static DEFINE_MUTEX(hash_resize_mutex);
119
David Howellsc4028952006-11-22 14:57:56 +0000120static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700121{
122 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
123 unsigned long nsize, osize;
124 unsigned int nhashmask, ohashmask;
125 int i;
126
127 mutex_lock(&hash_resize_mutex);
128
129 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700130 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700131 if (!ndst)
132 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700133 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700134 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700135 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700136 goto out_unlock;
137 }
David S. Miller44e36b42006-08-24 04:50:50 -0700138 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700139 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700140 xfrm_hash_free(ndst, nsize);
141 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 goto out_unlock;
143 }
144
145 spin_lock_bh(&xfrm_state_lock);
146
147 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
148 for (i = xfrm_state_hmask; i >= 0; i--)
149 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
150 nhashmask);
151
152 odst = xfrm_state_bydst;
153 osrc = xfrm_state_bysrc;
154 ospi = xfrm_state_byspi;
155 ohashmask = xfrm_state_hmask;
156
157 xfrm_state_bydst = ndst;
158 xfrm_state_bysrc = nsrc;
159 xfrm_state_byspi = nspi;
160 xfrm_state_hmask = nhashmask;
161
162 spin_unlock_bh(&xfrm_state_lock);
163
164 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700165 xfrm_hash_free(odst, osize);
166 xfrm_hash_free(osrc, osize);
167 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700168
169out_unlock:
170 mutex_unlock(&hash_resize_mutex);
171}
172
David Howellsc4028952006-11-22 14:57:56 +0000173static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175DECLARE_WAIT_QUEUE_HEAD(km_waitq);
176EXPORT_SYMBOL(km_waitq);
177
178static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
179static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
180
181static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700182static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183static DEFINE_SPINLOCK(xfrm_state_gc_lock);
184
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800185int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800187int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800188void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700190static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
191{
192 struct xfrm_state_afinfo *afinfo;
193 if (unlikely(family >= NPROTO))
194 return NULL;
195 write_lock_bh(&xfrm_state_afinfo_lock);
196 afinfo = xfrm_state_afinfo[family];
197 if (unlikely(!afinfo))
198 write_unlock_bh(&xfrm_state_afinfo_lock);
199 return afinfo;
200}
201
202static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
203{
204 write_unlock_bh(&xfrm_state_afinfo_lock);
205}
206
207int xfrm_register_type(struct xfrm_type *type, unsigned short family)
208{
209 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
210 struct xfrm_type **typemap;
211 int err = 0;
212
213 if (unlikely(afinfo == NULL))
214 return -EAFNOSUPPORT;
215 typemap = afinfo->type_map;
216
217 if (likely(typemap[type->proto] == NULL))
218 typemap[type->proto] = type;
219 else
220 err = -EEXIST;
221 xfrm_state_unlock_afinfo(afinfo);
222 return err;
223}
224EXPORT_SYMBOL(xfrm_register_type);
225
226int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
227{
228 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
229 struct xfrm_type **typemap;
230 int err = 0;
231
232 if (unlikely(afinfo == NULL))
233 return -EAFNOSUPPORT;
234 typemap = afinfo->type_map;
235
236 if (unlikely(typemap[type->proto] != type))
237 err = -ENOENT;
238 else
239 typemap[type->proto] = NULL;
240 xfrm_state_unlock_afinfo(afinfo);
241 return err;
242}
243EXPORT_SYMBOL(xfrm_unregister_type);
244
245static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
246{
247 struct xfrm_state_afinfo *afinfo;
248 struct xfrm_type **typemap;
249 struct xfrm_type *type;
250 int modload_attempted = 0;
251
252retry:
253 afinfo = xfrm_state_get_afinfo(family);
254 if (unlikely(afinfo == NULL))
255 return NULL;
256 typemap = afinfo->type_map;
257
258 type = typemap[proto];
259 if (unlikely(type && !try_module_get(type->owner)))
260 type = NULL;
261 if (!type && !modload_attempted) {
262 xfrm_state_put_afinfo(afinfo);
263 request_module("xfrm-type-%d-%d", family, proto);
264 modload_attempted = 1;
265 goto retry;
266 }
267
268 xfrm_state_put_afinfo(afinfo);
269 return type;
270}
271
272static void xfrm_put_type(struct xfrm_type *type)
273{
274 module_put(type->owner);
275}
276
277int xfrm_register_mode(struct xfrm_mode *mode, int family)
278{
279 struct xfrm_state_afinfo *afinfo;
280 struct xfrm_mode **modemap;
281 int err;
282
283 if (unlikely(mode->encap >= XFRM_MODE_MAX))
284 return -EINVAL;
285
286 afinfo = xfrm_state_lock_afinfo(family);
287 if (unlikely(afinfo == NULL))
288 return -EAFNOSUPPORT;
289
290 err = -EEXIST;
291 modemap = afinfo->mode_map;
292 if (likely(modemap[mode->encap] == NULL)) {
293 modemap[mode->encap] = mode;
294 err = 0;
295 }
296
297 xfrm_state_unlock_afinfo(afinfo);
298 return err;
299}
300EXPORT_SYMBOL(xfrm_register_mode);
301
302int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
303{
304 struct xfrm_state_afinfo *afinfo;
305 struct xfrm_mode **modemap;
306 int err;
307
308 if (unlikely(mode->encap >= XFRM_MODE_MAX))
309 return -EINVAL;
310
311 afinfo = xfrm_state_lock_afinfo(family);
312 if (unlikely(afinfo == NULL))
313 return -EAFNOSUPPORT;
314
315 err = -ENOENT;
316 modemap = afinfo->mode_map;
317 if (likely(modemap[mode->encap] == mode)) {
318 modemap[mode->encap] = NULL;
319 err = 0;
320 }
321
322 xfrm_state_unlock_afinfo(afinfo);
323 return err;
324}
325EXPORT_SYMBOL(xfrm_unregister_mode);
326
327static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
328{
329 struct xfrm_state_afinfo *afinfo;
330 struct xfrm_mode *mode;
331 int modload_attempted = 0;
332
333 if (unlikely(encap >= XFRM_MODE_MAX))
334 return NULL;
335
336retry:
337 afinfo = xfrm_state_get_afinfo(family);
338 if (unlikely(afinfo == NULL))
339 return NULL;
340
341 mode = afinfo->mode_map[encap];
342 if (unlikely(mode && !try_module_get(mode->owner)))
343 mode = NULL;
344 if (!mode && !modload_attempted) {
345 xfrm_state_put_afinfo(afinfo);
346 request_module("xfrm-mode-%d-%d", family, encap);
347 modload_attempted = 1;
348 goto retry;
349 }
350
351 xfrm_state_put_afinfo(afinfo);
352 return mode;
353}
354
355static void xfrm_put_mode(struct xfrm_mode *mode)
356{
357 module_put(mode->owner);
358}
359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360static void xfrm_state_gc_destroy(struct xfrm_state *x)
361{
David S. Millera47f0ce2006-08-24 03:54:22 -0700362 del_timer_sync(&x->timer);
363 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800364 kfree(x->aalg);
365 kfree(x->ealg);
366 kfree(x->calg);
367 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700368 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700369 if (x->mode)
370 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 if (x->type) {
372 x->type->destructor(x);
373 xfrm_put_type(x->type);
374 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800375 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 kfree(x);
377}
378
David Howellsc4028952006-11-22 14:57:56 +0000379static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
381 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700382 struct hlist_node *entry, *tmp;
383 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700386 gc_list.first = xfrm_state_gc_list.first;
387 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 spin_unlock_bh(&xfrm_state_gc_lock);
389
David S. Miller8f126e32006-08-24 02:45:07 -0700390 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700392
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 wake_up(&km_waitq);
394}
395
396static inline unsigned long make_jiffies(long secs)
397{
398 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
399 return MAX_SCHEDULE_TIMEOUT-1;
400 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900401 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402}
403
404static void xfrm_timer_handler(unsigned long data)
405{
406 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800407 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 long next = LONG_MAX;
409 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600410 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412 spin_lock(&x->lock);
413 if (x->km.state == XFRM_STATE_DEAD)
414 goto out;
415 if (x->km.state == XFRM_STATE_EXPIRED)
416 goto expired;
417 if (x->lft.hard_add_expires_seconds) {
418 long tmo = x->lft.hard_add_expires_seconds +
419 x->curlft.add_time - now;
420 if (tmo <= 0)
421 goto expired;
422 if (tmo < next)
423 next = tmo;
424 }
425 if (x->lft.hard_use_expires_seconds) {
426 long tmo = x->lft.hard_use_expires_seconds +
427 (x->curlft.use_time ? : now) - now;
428 if (tmo <= 0)
429 goto expired;
430 if (tmo < next)
431 next = tmo;
432 }
433 if (x->km.dying)
434 goto resched;
435 if (x->lft.soft_add_expires_seconds) {
436 long tmo = x->lft.soft_add_expires_seconds +
437 x->curlft.add_time - now;
438 if (tmo <= 0)
439 warn = 1;
440 else if (tmo < next)
441 next = tmo;
442 }
443 if (x->lft.soft_use_expires_seconds) {
444 long tmo = x->lft.soft_use_expires_seconds +
445 (x->curlft.use_time ? : now) - now;
446 if (tmo <= 0)
447 warn = 1;
448 else if (tmo < next)
449 next = tmo;
450 }
451
Herbert Xu4666faa2005-06-18 22:43:22 -0700452 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800454 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700456 if (next != LONG_MAX)
457 mod_timer(&x->timer, jiffies + make_jiffies(next));
458
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 goto out;
460
461expired:
462 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
463 x->km.state = XFRM_STATE_EXPIRED;
464 wake_up(&km_waitq);
465 next = 2;
466 goto resched;
467 }
Joy Latten161a09e2006-11-27 13:11:54 -0600468
469 err = __xfrm_state_delete(x);
470 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800471 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Joy Lattenab5f5e82007-09-17 11:51:22 -0700473 xfrm_audit_state_delete(x, err ? 0 : 1,
474 audit_get_loginuid(current->audit_context), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600475
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476out:
477 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478}
479
David S. Miller0ac84752006-03-20 19:18:23 -0800480static void xfrm_replay_timer_handler(unsigned long data);
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482struct xfrm_state *xfrm_state_alloc(void)
483{
484 struct xfrm_state *x;
485
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700486 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 atomic_set(&x->refcnt, 1);
490 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700491 INIT_HLIST_NODE(&x->bydst);
492 INIT_HLIST_NODE(&x->bysrc);
493 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 init_timer(&x->timer);
495 x->timer.function = xfrm_timer_handler;
496 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800497 init_timer(&x->rtimer);
498 x->rtimer.function = xfrm_replay_timer_handler;
499 x->rtimer.data = (unsigned long)x;
James Morris9d729f72007-03-04 16:12:44 -0800500 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 x->lft.soft_byte_limit = XFRM_INF;
502 x->lft.soft_packet_limit = XFRM_INF;
503 x->lft.hard_byte_limit = XFRM_INF;
504 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800505 x->replay_maxage = 0;
506 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 spin_lock_init(&x->lock);
508 }
509 return x;
510}
511EXPORT_SYMBOL(xfrm_state_alloc);
512
513void __xfrm_state_destroy(struct xfrm_state *x)
514{
515 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
516
517 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700518 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 spin_unlock_bh(&xfrm_state_gc_lock);
520 schedule_work(&xfrm_state_gc_work);
521}
522EXPORT_SYMBOL(__xfrm_state_destroy);
523
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800524int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700526 int err = -ESRCH;
527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 if (x->km.state != XFRM_STATE_DEAD) {
529 x->km.state = XFRM_STATE_DEAD;
530 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700531 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700532 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700533 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700534 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700535 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 /* All xfrm_state objects are created by xfrm_state_alloc.
539 * The xfrm_state_alloc call gives a reference, and that
540 * is what we are dropping here.
541 */
Herbert Xu21380b82006-02-22 14:47:13 -0800542 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700543 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700545
546 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800548EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700550int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700552 int err;
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700555 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700557
558 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559}
560EXPORT_SYMBOL(xfrm_state_delete);
561
Joy Latten4aa2e622007-06-04 19:05:57 -0400562#ifdef CONFIG_SECURITY_NETWORK_XFRM
563static inline int
564xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Joy Latten4aa2e622007-06-04 19:05:57 -0400566 int i, err = 0;
567
568 for (i = 0; i <= xfrm_state_hmask; i++) {
569 struct hlist_node *entry;
570 struct xfrm_state *x;
571
572 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
573 if (xfrm_id_proto_match(x->id.proto, proto) &&
574 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700575 xfrm_audit_state_delete(x, 0,
576 audit_info->loginuid,
577 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400578 return err;
579 }
580 }
581 }
582
583 return err;
584}
585#else
586static inline int
587xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
588{
589 return 0;
590}
591#endif
592
593int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
594{
595 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
597 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400598 err = xfrm_state_flush_secctx_check(proto, audit_info);
599 if (err)
600 goto out;
601
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700602 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700603 struct hlist_node *entry;
604 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700606 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700608 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 xfrm_state_hold(x);
610 spin_unlock_bh(&xfrm_state_lock);
611
Joy Latten161a09e2006-11-27 13:11:54 -0600612 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700613 xfrm_audit_state_delete(x, err ? 0 : 1,
614 audit_info->loginuid,
615 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 xfrm_state_put(x);
617
618 spin_lock_bh(&xfrm_state_lock);
619 goto restart;
620 }
621 }
622 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400623 err = 0;
624
625out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 spin_unlock_bh(&xfrm_state_lock);
627 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400628 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629}
630EXPORT_SYMBOL(xfrm_state_flush);
631
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700632void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700633{
634 spin_lock_bh(&xfrm_state_lock);
635 si->sadcnt = xfrm_state_num;
636 si->sadhcnt = xfrm_state_hmask;
637 si->sadhmcnt = xfrm_state_hashmax;
638 spin_unlock_bh(&xfrm_state_lock);
639}
640EXPORT_SYMBOL(xfrm_sad_getinfo);
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642static int
643xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
644 struct xfrm_tmpl *tmpl,
645 xfrm_address_t *daddr, xfrm_address_t *saddr,
646 unsigned short family)
647{
648 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
649 if (!afinfo)
650 return -1;
651 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
652 xfrm_state_put_afinfo(afinfo);
653 return 0;
654}
655
Al Viroa94cfd12006-09-27 18:47:24 -0700656static 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 -0700657{
658 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
659 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700660 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700661
David S. Miller8f126e32006-08-24 02:45:07 -0700662 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700663 if (x->props.family != family ||
664 x->id.spi != spi ||
665 x->id.proto != proto)
666 continue;
667
668 switch (family) {
669 case AF_INET:
670 if (x->id.daddr.a4 != daddr->a4)
671 continue;
672 break;
673 case AF_INET6:
674 if (!ipv6_addr_equal((struct in6_addr *)daddr,
675 (struct in6_addr *)
676 x->id.daddr.a6))
677 continue;
678 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700679 }
David S. Milleredcd5822006-08-24 00:42:45 -0700680
681 xfrm_state_hold(x);
682 return x;
683 }
684
685 return NULL;
686}
687
688static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
689{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700690 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700691 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700692 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700693
David S. Miller8f126e32006-08-24 02:45:07 -0700694 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700695 if (x->props.family != family ||
696 x->id.proto != proto)
697 continue;
698
699 switch (family) {
700 case AF_INET:
701 if (x->id.daddr.a4 != daddr->a4 ||
702 x->props.saddr.a4 != saddr->a4)
703 continue;
704 break;
705 case AF_INET6:
706 if (!ipv6_addr_equal((struct in6_addr *)daddr,
707 (struct in6_addr *)
708 x->id.daddr.a6) ||
709 !ipv6_addr_equal((struct in6_addr *)saddr,
710 (struct in6_addr *)
711 x->props.saddr.a6))
712 continue;
713 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700714 }
David S. Milleredcd5822006-08-24 00:42:45 -0700715
716 xfrm_state_hold(x);
717 return x;
718 }
719
720 return NULL;
721}
722
723static inline struct xfrm_state *
724__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
725{
726 if (use_spi)
727 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
728 x->id.proto, family);
729 else
730 return __xfrm_state_lookup_byaddr(&x->id.daddr,
731 &x->props.saddr,
732 x->id.proto, family);
733}
734
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700735static void xfrm_hash_grow_check(int have_hash_collision)
736{
737 if (have_hash_collision &&
738 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
739 xfrm_state_num > xfrm_state_hmask)
740 schedule_work(&xfrm_hash_work);
741}
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900744xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 struct flowi *fl, struct xfrm_tmpl *tmpl,
746 struct xfrm_policy *pol, int *err,
747 unsigned short family)
748{
David S. Millerc1969f22006-08-24 04:00:03 -0700749 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700750 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 struct xfrm_state *x, *x0;
752 int acquire_in_progress = 0;
753 int error = 0;
754 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900755
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700757 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 if (x->props.family == family &&
759 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700760 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 xfrm_state_addr_check(x, daddr, saddr, family) &&
762 tmpl->mode == x->props.mode &&
763 tmpl->id.proto == x->id.proto &&
764 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
765 /* Resolution logic:
766 1. There is a valid state with matching selector.
767 Done.
768 2. Valid state with inappropriate selector. Skip.
769
770 Entering area of "sysdeps".
771
772 3. If state is not valid, selector is temporary,
773 it selects only session which triggered
774 previous resolution. Key manager will do
775 something to install a state with proper
776 selector.
777 */
778 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700779 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700780 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 continue;
782 if (!best ||
783 best->km.dying > x->km.dying ||
784 (best->km.dying == x->km.dying &&
785 best->curlft.add_time < x->curlft.add_time))
786 best = x;
787 } else if (x->km.state == XFRM_STATE_ACQ) {
788 acquire_in_progress = 1;
789 } else if (x->km.state == XFRM_STATE_ERROR ||
790 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700791 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700792 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 error = -ESRCH;
794 }
795 }
796 }
797
798 x = best;
799 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700800 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700801 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
802 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 xfrm_state_put(x0);
804 error = -EEXIST;
805 goto out;
806 }
807 x = xfrm_state_alloc();
808 if (x == NULL) {
809 error = -ENOMEM;
810 goto out;
811 }
812 /* Initialize temporary selector matching only
813 * to current session. */
814 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
815
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700816 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
817 if (error) {
818 x->km.state = XFRM_STATE_DEAD;
819 xfrm_state_put(x);
820 x = NULL;
821 goto out;
822 }
823
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 if (km_query(x, tmpl, pol) == 0) {
825 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700826 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700827 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700828 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (x->id.spi) {
830 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700831 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 }
David S. Miller01e67d02007-05-25 00:41:38 -0700833 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
834 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700836 xfrm_state_num++;
837 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 } else {
839 x->km.state = XFRM_STATE_DEAD;
840 xfrm_state_put(x);
841 x = NULL;
842 error = -ESRCH;
843 }
844 }
845out:
846 if (x)
847 xfrm_state_hold(x);
848 else
849 *err = acquire_in_progress ? -EAGAIN : error;
850 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return x;
852}
853
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700854struct xfrm_state *
855xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
856 unsigned short family, u8 mode, u8 proto, u32 reqid)
857{
858 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
859 struct xfrm_state *rx = NULL, *x = NULL;
860 struct hlist_node *entry;
861
862 spin_lock(&xfrm_state_lock);
863 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
864 if (x->props.family == family &&
865 x->props.reqid == reqid &&
866 !(x->props.flags & XFRM_STATE_WILDRECV) &&
867 xfrm_state_addr_check(x, daddr, saddr, family) &&
868 mode == x->props.mode &&
869 proto == x->id.proto &&
870 x->km.state == XFRM_STATE_VALID) {
871 rx = x;
872 break;
873 }
874 }
875
876 if (rx)
877 xfrm_state_hold(rx);
878 spin_unlock(&xfrm_state_lock);
879
880
881 return rx;
882}
883EXPORT_SYMBOL(xfrm_stateonly_find);
884
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885static void __xfrm_state_insert(struct xfrm_state *x)
886{
David S. Millera624c102006-08-24 03:24:33 -0700887 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
David S. Miller9d4a7062006-08-24 03:18:09 -0700889 x->genid = ++xfrm_state_genid;
890
David S. Millerc1969f22006-08-24 04:00:03 -0700891 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
892 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700893 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700895 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700896 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700898 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700899 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
900 x->props.family);
901
David S. Miller8f126e32006-08-24 02:45:07 -0700902 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700903 }
904
David S. Millera47f0ce2006-08-24 03:54:22 -0700905 mod_timer(&x->timer, jiffies + HZ);
906 if (x->replay_maxage)
907 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800908
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700910
911 xfrm_state_num++;
912
David S. Miller918049f2006-10-12 22:03:24 -0700913 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914}
915
David S. Millerc7f5ea32006-08-24 03:29:04 -0700916/* xfrm_state_lock is held */
917static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
918{
919 unsigned short family = xnew->props.family;
920 u32 reqid = xnew->props.reqid;
921 struct xfrm_state *x;
922 struct hlist_node *entry;
923 unsigned int h;
924
David S. Millerc1969f22006-08-24 04:00:03 -0700925 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700926 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
927 if (x->props.family == family &&
928 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700929 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
930 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700931 x->genid = xfrm_state_genid;
932 }
933}
934
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935void xfrm_state_insert(struct xfrm_state *x)
936{
937 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700938 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 __xfrm_state_insert(x);
940 spin_unlock_bh(&xfrm_state_lock);
941}
942EXPORT_SYMBOL(xfrm_state_insert);
943
David S. Miller27708342006-08-24 00:13:10 -0700944/* xfrm_state_lock is held */
945static 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)
946{
David S. Millerc1969f22006-08-24 04:00:03 -0700947 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700948 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700949 struct xfrm_state *x;
950
David S. Miller8f126e32006-08-24 02:45:07 -0700951 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700952 if (x->props.reqid != reqid ||
953 x->props.mode != mode ||
954 x->props.family != family ||
955 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700956 x->id.spi != 0 ||
957 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700958 continue;
959
960 switch (family) {
961 case AF_INET:
962 if (x->id.daddr.a4 != daddr->a4 ||
963 x->props.saddr.a4 != saddr->a4)
964 continue;
965 break;
966 case AF_INET6:
967 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
968 (struct in6_addr *)daddr) ||
969 !ipv6_addr_equal((struct in6_addr *)
970 x->props.saddr.a6,
971 (struct in6_addr *)saddr))
972 continue;
973 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700974 }
David S. Miller27708342006-08-24 00:13:10 -0700975
976 xfrm_state_hold(x);
977 return x;
978 }
979
980 if (!create)
981 return NULL;
982
983 x = xfrm_state_alloc();
984 if (likely(x)) {
985 switch (family) {
986 case AF_INET:
987 x->sel.daddr.a4 = daddr->a4;
988 x->sel.saddr.a4 = saddr->a4;
989 x->sel.prefixlen_d = 32;
990 x->sel.prefixlen_s = 32;
991 x->props.saddr.a4 = saddr->a4;
992 x->id.daddr.a4 = daddr->a4;
993 break;
994
995 case AF_INET6:
996 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
997 (struct in6_addr *)daddr);
998 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
999 (struct in6_addr *)saddr);
1000 x->sel.prefixlen_d = 128;
1001 x->sel.prefixlen_s = 128;
1002 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1003 (struct in6_addr *)saddr);
1004 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1005 (struct in6_addr *)daddr);
1006 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001007 }
David S. Miller27708342006-08-24 00:13:10 -07001008
1009 x->km.state = XFRM_STATE_ACQ;
1010 x->id.proto = proto;
1011 x->props.family = family;
1012 x->props.mode = mode;
1013 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001014 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001015 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001016 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001017 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001018 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001019 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001020 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001021
1022 xfrm_state_num++;
1023
1024 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001025 }
1026
1027 return x;
1028}
1029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1031
1032int xfrm_state_add(struct xfrm_state *x)
1033{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 struct xfrm_state *x1;
1035 int family;
1036 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001037 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
1041 spin_lock_bh(&xfrm_state_lock);
1042
David S. Milleredcd5822006-08-24 00:42:45 -07001043 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 if (x1) {
1045 xfrm_state_put(x1);
1046 x1 = NULL;
1047 err = -EEXIST;
1048 goto out;
1049 }
1050
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001051 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001053 if (x1 && ((x1->id.proto != x->id.proto) ||
1054 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 xfrm_state_put(x1);
1056 x1 = NULL;
1057 }
1058 }
1059
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001060 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001061 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1062 x->id.proto,
1063 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
David S. Millerc7f5ea32006-08-24 03:29:04 -07001065 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 __xfrm_state_insert(x);
1067 err = 0;
1068
1069out:
1070 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 if (x1) {
1073 xfrm_state_delete(x1);
1074 xfrm_state_put(x1);
1075 }
1076
1077 return err;
1078}
1079EXPORT_SYMBOL(xfrm_state_add);
1080
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001081#ifdef CONFIG_XFRM_MIGRATE
1082struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
1083{
1084 int err = -ENOMEM;
1085 struct xfrm_state *x = xfrm_state_alloc();
1086 if (!x)
1087 goto error;
1088
1089 memcpy(&x->id, &orig->id, sizeof(x->id));
1090 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1091 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1092 x->props.mode = orig->props.mode;
1093 x->props.replay_window = orig->props.replay_window;
1094 x->props.reqid = orig->props.reqid;
1095 x->props.family = orig->props.family;
1096 x->props.saddr = orig->props.saddr;
1097
1098 if (orig->aalg) {
1099 x->aalg = xfrm_algo_clone(orig->aalg);
1100 if (!x->aalg)
1101 goto error;
1102 }
1103 x->props.aalgo = orig->props.aalgo;
1104
1105 if (orig->ealg) {
1106 x->ealg = xfrm_algo_clone(orig->ealg);
1107 if (!x->ealg)
1108 goto error;
1109 }
1110 x->props.ealgo = orig->props.ealgo;
1111
1112 if (orig->calg) {
1113 x->calg = xfrm_algo_clone(orig->calg);
1114 if (!x->calg)
1115 goto error;
1116 }
1117 x->props.calgo = orig->props.calgo;
1118
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001119 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001120 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1121 if (!x->encap)
1122 goto error;
1123 }
1124
1125 if (orig->coaddr) {
1126 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1127 GFP_KERNEL);
1128 if (!x->coaddr)
1129 goto error;
1130 }
1131
1132 err = xfrm_init_state(x);
1133 if (err)
1134 goto error;
1135
1136 x->props.flags = orig->props.flags;
1137
1138 x->curlft.add_time = orig->curlft.add_time;
1139 x->km.state = orig->km.state;
1140 x->km.seq = orig->km.seq;
1141
1142 return x;
1143
1144 error:
1145 if (errp)
1146 *errp = err;
1147 if (x) {
1148 kfree(x->aalg);
1149 kfree(x->ealg);
1150 kfree(x->calg);
1151 kfree(x->encap);
1152 kfree(x->coaddr);
1153 }
1154 kfree(x);
1155 return NULL;
1156}
1157EXPORT_SYMBOL(xfrm_state_clone);
1158
1159/* xfrm_state_lock is held */
1160struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1161{
1162 unsigned int h;
1163 struct xfrm_state *x;
1164 struct hlist_node *entry;
1165
1166 if (m->reqid) {
1167 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1168 m->reqid, m->old_family);
1169 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1170 if (x->props.mode != m->mode ||
1171 x->id.proto != m->proto)
1172 continue;
1173 if (m->reqid && x->props.reqid != m->reqid)
1174 continue;
1175 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1176 m->old_family) ||
1177 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1178 m->old_family))
1179 continue;
1180 xfrm_state_hold(x);
1181 return x;
1182 }
1183 } else {
1184 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1185 m->old_family);
1186 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1187 if (x->props.mode != m->mode ||
1188 x->id.proto != m->proto)
1189 continue;
1190 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1191 m->old_family) ||
1192 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1193 m->old_family))
1194 continue;
1195 xfrm_state_hold(x);
1196 return x;
1197 }
1198 }
1199
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001200 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001201}
1202EXPORT_SYMBOL(xfrm_migrate_state_find);
1203
1204struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1205 struct xfrm_migrate *m)
1206{
1207 struct xfrm_state *xc;
1208 int err;
1209
1210 xc = xfrm_state_clone(x, &err);
1211 if (!xc)
1212 return NULL;
1213
1214 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1215 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1216
1217 /* add state */
1218 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1219 /* a care is needed when the destination address of the
1220 state is to be updated as it is a part of triplet */
1221 xfrm_state_insert(xc);
1222 } else {
1223 if ((err = xfrm_state_add(xc)) < 0)
1224 goto error;
1225 }
1226
1227 return xc;
1228error:
1229 kfree(xc);
1230 return NULL;
1231}
1232EXPORT_SYMBOL(xfrm_state_migrate);
1233#endif
1234
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235int xfrm_state_update(struct xfrm_state *x)
1236{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 struct xfrm_state *x1;
1238 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001239 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001242 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
1244 err = -ESRCH;
1245 if (!x1)
1246 goto out;
1247
1248 if (xfrm_state_kern(x1)) {
1249 xfrm_state_put(x1);
1250 err = -EEXIST;
1251 goto out;
1252 }
1253
1254 if (x1->km.state == XFRM_STATE_ACQ) {
1255 __xfrm_state_insert(x);
1256 x = NULL;
1257 }
1258 err = 0;
1259
1260out:
1261 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
1263 if (err)
1264 return err;
1265
1266 if (!x) {
1267 xfrm_state_delete(x1);
1268 xfrm_state_put(x1);
1269 return 0;
1270 }
1271
1272 err = -EINVAL;
1273 spin_lock_bh(&x1->lock);
1274 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1275 if (x->encap && x1->encap)
1276 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001277 if (x->coaddr && x1->coaddr) {
1278 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1279 }
1280 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1281 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1283 x1->km.dying = 0;
1284
David S. Millera47f0ce2006-08-24 03:54:22 -07001285 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (x1->curlft.use_time)
1287 xfrm_state_check_expire(x1);
1288
1289 err = 0;
1290 }
1291 spin_unlock_bh(&x1->lock);
1292
1293 xfrm_state_put(x1);
1294
1295 return err;
1296}
1297EXPORT_SYMBOL(xfrm_state_update);
1298
1299int xfrm_state_check_expire(struct xfrm_state *x)
1300{
1301 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001302 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 if (x->km.state != XFRM_STATE_VALID)
1305 return -EINVAL;
1306
1307 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1308 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001309 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001310 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 return -EINVAL;
1312 }
1313
1314 if (!x->km.dying &&
1315 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001316 x->curlft.packets >= x->lft.soft_packet_limit)) {
1317 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001318 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001319 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 return 0;
1321}
1322EXPORT_SYMBOL(xfrm_state_check_expire);
1323
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001325xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 unsigned short family)
1327{
1328 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001331 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 return x;
1334}
1335EXPORT_SYMBOL(xfrm_state_lookup);
1336
1337struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001338xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1339 u8 proto, unsigned short family)
1340{
1341 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001342
1343 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001344 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001345 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001346 return x;
1347}
1348EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1349
1350struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001351xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1352 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 int create, unsigned short family)
1354{
1355 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
1357 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001358 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001360
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 return x;
1362}
1363EXPORT_SYMBOL(xfrm_find_acq);
1364
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001365#ifdef CONFIG_XFRM_SUB_POLICY
1366int
1367xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1368 unsigned short family)
1369{
1370 int err = 0;
1371 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1372 if (!afinfo)
1373 return -EAFNOSUPPORT;
1374
1375 spin_lock_bh(&xfrm_state_lock);
1376 if (afinfo->tmpl_sort)
1377 err = afinfo->tmpl_sort(dst, src, n);
1378 spin_unlock_bh(&xfrm_state_lock);
1379 xfrm_state_put_afinfo(afinfo);
1380 return err;
1381}
1382EXPORT_SYMBOL(xfrm_tmpl_sort);
1383
1384int
1385xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1386 unsigned short family)
1387{
1388 int err = 0;
1389 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1390 if (!afinfo)
1391 return -EAFNOSUPPORT;
1392
1393 spin_lock_bh(&xfrm_state_lock);
1394 if (afinfo->state_sort)
1395 err = afinfo->state_sort(dst, src, n);
1396 spin_unlock_bh(&xfrm_state_lock);
1397 xfrm_state_put_afinfo(afinfo);
1398 return err;
1399}
1400EXPORT_SYMBOL(xfrm_state_sort);
1401#endif
1402
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403/* Silly enough, but I'm lazy to build resolution list */
1404
1405static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1406{
1407 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
David S. Millerf034b5d2006-08-24 03:08:07 -07001409 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001410 struct hlist_node *entry;
1411 struct xfrm_state *x;
1412
1413 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1414 if (x->km.seq == seq &&
1415 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 xfrm_state_hold(x);
1417 return x;
1418 }
1419 }
1420 }
1421 return NULL;
1422}
1423
1424struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1425{
1426 struct xfrm_state *x;
1427
1428 spin_lock_bh(&xfrm_state_lock);
1429 x = __xfrm_find_acq_byseq(seq);
1430 spin_unlock_bh(&xfrm_state_lock);
1431 return x;
1432}
1433EXPORT_SYMBOL(xfrm_find_acq_byseq);
1434
1435u32 xfrm_get_acqseq(void)
1436{
1437 u32 res;
1438 static u32 acqseq;
1439 static DEFINE_SPINLOCK(acqseq_lock);
1440
1441 spin_lock_bh(&acqseq_lock);
1442 res = (++acqseq ? : ++acqseq);
1443 spin_unlock_bh(&acqseq_lock);
1444 return res;
1445}
1446EXPORT_SYMBOL(xfrm_get_acqseq);
1447
Herbert Xu658b2192007-10-09 13:29:52 -07001448int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449{
David S. Millerf034b5d2006-08-24 03:08:07 -07001450 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001452 int err = -ENOENT;
1453 __be32 minspi = htonl(low);
1454 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Herbert Xu658b2192007-10-09 13:29:52 -07001456 spin_lock_bh(&x->lock);
1457 if (x->km.state == XFRM_STATE_DEAD)
1458 goto unlock;
1459
1460 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001462 goto unlock;
1463
1464 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
1466 if (minspi == maxspi) {
1467 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1468 if (x0) {
1469 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001470 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 }
1472 x->id.spi = minspi;
1473 } else {
1474 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001475 for (h=0; h<high-low+1; h++) {
1476 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1478 if (x0 == NULL) {
1479 x->id.spi = htonl(spi);
1480 break;
1481 }
1482 xfrm_state_put(x0);
1483 }
1484 }
1485 if (x->id.spi) {
1486 spin_lock_bh(&xfrm_state_lock);
1487 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001488 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001490
1491 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 }
Herbert Xu658b2192007-10-09 13:29:52 -07001493
1494unlock:
1495 spin_unlock_bh(&x->lock);
1496
1497 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498}
1499EXPORT_SYMBOL(xfrm_alloc_spi);
1500
1501int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1502 void *data)
1503{
1504 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001505 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001506 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 int count = 0;
1508 int err = 0;
1509
1510 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001511 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001512 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001513 if (!xfrm_id_proto_match(x->id.proto, proto))
1514 continue;
1515 if (last) {
1516 err = func(last, count, data);
1517 if (err)
1518 goto out;
1519 }
1520 last = x;
1521 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 }
1523 }
1524 if (count == 0) {
1525 err = -ENOENT;
1526 goto out;
1527 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001528 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529out:
1530 spin_unlock_bh(&xfrm_state_lock);
1531 return err;
1532}
1533EXPORT_SYMBOL(xfrm_state_walk);
1534
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001535
1536void xfrm_replay_notify(struct xfrm_state *x, int event)
1537{
1538 struct km_event c;
1539 /* we send notify messages in case
1540 * 1. we updated on of the sequence numbers, and the seqno difference
1541 * is at least x->replay_maxdiff, in this case we also update the
1542 * timeout of our timer function
1543 * 2. if x->replay_maxage has elapsed since last update,
1544 * and there were changes
1545 *
1546 * The state structure must be locked!
1547 */
1548
1549 switch (event) {
1550 case XFRM_REPLAY_UPDATE:
1551 if (x->replay_maxdiff &&
1552 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001553 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1554 if (x->xflags & XFRM_TIME_DEFER)
1555 event = XFRM_REPLAY_TIMEOUT;
1556 else
1557 return;
1558 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001559
1560 break;
1561
1562 case XFRM_REPLAY_TIMEOUT:
1563 if ((x->replay.seq == x->preplay.seq) &&
1564 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001565 (x->replay.oseq == x->preplay.oseq)) {
1566 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001567 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001568 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001569
1570 break;
1571 }
1572
1573 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1574 c.event = XFRM_MSG_NEWAE;
1575 c.data.aevent = event;
1576 km_state_notify(x, &c);
1577
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001578 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001579 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001580 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001581}
1582
1583static void xfrm_replay_timer_handler(unsigned long data)
1584{
1585 struct xfrm_state *x = (struct xfrm_state*)data;
1586
1587 spin_lock(&x->lock);
1588
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001589 if (x->km.state == XFRM_STATE_VALID) {
1590 if (xfrm_aevent_is_on())
1591 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1592 else
1593 x->xflags |= XFRM_TIME_DEFER;
1594 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001595
1596 spin_unlock(&x->lock);
1597}
1598
Al Viroa252cc22006-09-27 18:48:18 -07001599int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600{
1601 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001602 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603
1604 if (unlikely(seq == 0))
1605 return -EINVAL;
1606
1607 if (likely(seq > x->replay.seq))
1608 return 0;
1609
1610 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001611 if (diff >= min_t(unsigned int, x->props.replay_window,
1612 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 x->stats.replay_window++;
1614 return -EINVAL;
1615 }
1616
1617 if (x->replay.bitmap & (1U << diff)) {
1618 x->stats.replay++;
1619 return -EINVAL;
1620 }
1621 return 0;
1622}
1623EXPORT_SYMBOL(xfrm_replay_check);
1624
Al Viro61f46272006-09-27 18:48:33 -07001625void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626{
1627 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001628 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
1630 if (seq > x->replay.seq) {
1631 diff = seq - x->replay.seq;
1632 if (diff < x->props.replay_window)
1633 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1634 else
1635 x->replay.bitmap = 1;
1636 x->replay.seq = seq;
1637 } else {
1638 diff = x->replay.seq - seq;
1639 x->replay.bitmap |= (1U << diff);
1640 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001641
1642 if (xfrm_aevent_is_on())
1643 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644}
1645EXPORT_SYMBOL(xfrm_replay_advance);
1646
1647static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1648static DEFINE_RWLOCK(xfrm_km_lock);
1649
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001650void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651{
1652 struct xfrm_mgr *km;
1653
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001654 read_lock(&xfrm_km_lock);
1655 list_for_each_entry(km, &xfrm_km_list, list)
1656 if (km->notify_policy)
1657 km->notify_policy(xp, dir, c);
1658 read_unlock(&xfrm_km_lock);
1659}
1660
1661void km_state_notify(struct xfrm_state *x, struct km_event *c)
1662{
1663 struct xfrm_mgr *km;
1664 read_lock(&xfrm_km_lock);
1665 list_for_each_entry(km, &xfrm_km_list, list)
1666 if (km->notify)
1667 km->notify(x, c);
1668 read_unlock(&xfrm_km_lock);
1669}
1670
1671EXPORT_SYMBOL(km_policy_notify);
1672EXPORT_SYMBOL(km_state_notify);
1673
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001674void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001675{
1676 struct km_event c;
1677
Herbert Xubf088672005-06-18 22:44:00 -07001678 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001679 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001680 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001681 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682
1683 if (hard)
1684 wake_up(&km_waitq);
1685}
1686
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001687EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001688/*
1689 * We send to all registered managers regardless of failure
1690 * We are happy with one success
1691*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001692int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001694 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 struct xfrm_mgr *km;
1696
1697 read_lock(&xfrm_km_lock);
1698 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001699 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1700 if (!acqret)
1701 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 }
1703 read_unlock(&xfrm_km_lock);
1704 return err;
1705}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001706EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
Al Viro5d36b182006-11-08 00:24:06 -08001708int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709{
1710 int err = -EINVAL;
1711 struct xfrm_mgr *km;
1712
1713 read_lock(&xfrm_km_lock);
1714 list_for_each_entry(km, &xfrm_km_list, list) {
1715 if (km->new_mapping)
1716 err = km->new_mapping(x, ipaddr, sport);
1717 if (!err)
1718 break;
1719 }
1720 read_unlock(&xfrm_km_lock);
1721 return err;
1722}
1723EXPORT_SYMBOL(km_new_mapping);
1724
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001725void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001727 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
Herbert Xubf088672005-06-18 22:44:00 -07001729 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001730 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001731 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001732 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733
1734 if (hard)
1735 wake_up(&km_waitq);
1736}
David S. Millera70fcb02006-03-20 19:18:52 -08001737EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001739int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1740 struct xfrm_migrate *m, int num_migrate)
1741{
1742 int err = -EINVAL;
1743 int ret;
1744 struct xfrm_mgr *km;
1745
1746 read_lock(&xfrm_km_lock);
1747 list_for_each_entry(km, &xfrm_km_list, list) {
1748 if (km->migrate) {
1749 ret = km->migrate(sel, dir, type, m, num_migrate);
1750 if (!ret)
1751 err = ret;
1752 }
1753 }
1754 read_unlock(&xfrm_km_lock);
1755 return err;
1756}
1757EXPORT_SYMBOL(km_migrate);
1758
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001759int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1760{
1761 int err = -EINVAL;
1762 int ret;
1763 struct xfrm_mgr *km;
1764
1765 read_lock(&xfrm_km_lock);
1766 list_for_each_entry(km, &xfrm_km_list, list) {
1767 if (km->report) {
1768 ret = km->report(proto, sel, addr);
1769 if (!ret)
1770 err = ret;
1771 }
1772 }
1773 read_unlock(&xfrm_km_lock);
1774 return err;
1775}
1776EXPORT_SYMBOL(km_report);
1777
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1779{
1780 int err;
1781 u8 *data;
1782 struct xfrm_mgr *km;
1783 struct xfrm_policy *pol = NULL;
1784
1785 if (optlen <= 0 || optlen > PAGE_SIZE)
1786 return -EMSGSIZE;
1787
1788 data = kmalloc(optlen, GFP_KERNEL);
1789 if (!data)
1790 return -ENOMEM;
1791
1792 err = -EFAULT;
1793 if (copy_from_user(data, optval, optlen))
1794 goto out;
1795
1796 err = -EINVAL;
1797 read_lock(&xfrm_km_lock);
1798 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001799 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 optlen, &err);
1801 if (err >= 0)
1802 break;
1803 }
1804 read_unlock(&xfrm_km_lock);
1805
1806 if (err >= 0) {
1807 xfrm_sk_policy_insert(sk, err, pol);
1808 xfrm_pol_put(pol);
1809 err = 0;
1810 }
1811
1812out:
1813 kfree(data);
1814 return err;
1815}
1816EXPORT_SYMBOL(xfrm_user_policy);
1817
1818int xfrm_register_km(struct xfrm_mgr *km)
1819{
1820 write_lock_bh(&xfrm_km_lock);
1821 list_add_tail(&km->list, &xfrm_km_list);
1822 write_unlock_bh(&xfrm_km_lock);
1823 return 0;
1824}
1825EXPORT_SYMBOL(xfrm_register_km);
1826
1827int xfrm_unregister_km(struct xfrm_mgr *km)
1828{
1829 write_lock_bh(&xfrm_km_lock);
1830 list_del(&km->list);
1831 write_unlock_bh(&xfrm_km_lock);
1832 return 0;
1833}
1834EXPORT_SYMBOL(xfrm_unregister_km);
1835
1836int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1837{
1838 int err = 0;
1839 if (unlikely(afinfo == NULL))
1840 return -EINVAL;
1841 if (unlikely(afinfo->family >= NPROTO))
1842 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001843 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1845 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001846 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001848 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 return err;
1850}
1851EXPORT_SYMBOL(xfrm_state_register_afinfo);
1852
1853int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1854{
1855 int err = 0;
1856 if (unlikely(afinfo == NULL))
1857 return -EINVAL;
1858 if (unlikely(afinfo->family >= NPROTO))
1859 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001860 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1862 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1863 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001864 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001867 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 return err;
1869}
1870EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1871
Miika Komucdca7262007-02-06 14:24:56 -08001872struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873{
1874 struct xfrm_state_afinfo *afinfo;
1875 if (unlikely(family >= NPROTO))
1876 return NULL;
1877 read_lock(&xfrm_state_afinfo_lock);
1878 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001879 if (unlikely(!afinfo))
1880 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 return afinfo;
1882}
1883
Miika Komucdca7262007-02-06 14:24:56 -08001884void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885{
Herbert Xu546be242006-05-27 23:03:58 -07001886 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887}
1888
Miika Komucdca7262007-02-06 14:24:56 -08001889EXPORT_SYMBOL(xfrm_state_get_afinfo);
1890EXPORT_SYMBOL(xfrm_state_put_afinfo);
1891
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1893void xfrm_state_delete_tunnel(struct xfrm_state *x)
1894{
1895 if (x->tunnel) {
1896 struct xfrm_state *t = x->tunnel;
1897
1898 if (atomic_read(&t->tunnel_users) == 2)
1899 xfrm_state_delete(t);
1900 atomic_dec(&t->tunnel_users);
1901 xfrm_state_put(t);
1902 x->tunnel = NULL;
1903 }
1904}
1905EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1906
1907int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1908{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001909 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910
Patrick McHardyc5c25232007-04-09 11:47:18 -07001911 spin_lock_bh(&x->lock);
1912 if (x->km.state == XFRM_STATE_VALID &&
1913 x->type && x->type->get_mtu)
1914 res = x->type->get_mtu(x, mtu);
1915 else
Patrick McHardy28121612007-06-18 22:30:15 -07001916 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001917 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 return res;
1919}
1920
Herbert Xu72cb6962005-06-20 13:18:08 -07001921int xfrm_init_state(struct xfrm_state *x)
1922{
Herbert Xud094cd82005-06-20 13:19:41 -07001923 struct xfrm_state_afinfo *afinfo;
1924 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001925 int err;
1926
Herbert Xud094cd82005-06-20 13:19:41 -07001927 err = -EAFNOSUPPORT;
1928 afinfo = xfrm_state_get_afinfo(family);
1929 if (!afinfo)
1930 goto error;
1931
1932 err = 0;
1933 if (afinfo->init_flags)
1934 err = afinfo->init_flags(x);
1935
1936 xfrm_state_put_afinfo(afinfo);
1937
1938 if (err)
1939 goto error;
1940
1941 err = -EPROTONOSUPPORT;
1942 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001943 if (x->type == NULL)
1944 goto error;
1945
1946 err = x->type->init_state(x);
1947 if (err)
1948 goto error;
1949
Herbert Xub59f45d2006-05-27 23:05:54 -07001950 x->mode = xfrm_get_mode(x->props.mode, family);
1951 if (x->mode == NULL)
1952 goto error;
1953
Herbert Xu72cb6962005-06-20 13:18:08 -07001954 x->km.state = XFRM_STATE_VALID;
1955
1956error:
1957 return err;
1958}
1959
1960EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001961
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962void __init xfrm_state_init(void)
1963{
David S. Millerf034b5d2006-08-24 03:08:07 -07001964 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965
David S. Millerf034b5d2006-08-24 03:08:07 -07001966 sz = sizeof(struct hlist_head) * 8;
1967
David S. Miller44e36b42006-08-24 04:50:50 -07001968 xfrm_state_bydst = xfrm_hash_alloc(sz);
1969 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1970 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001971 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1972 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1973 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1974
David Howellsc4028952006-11-22 14:57:56 +00001975 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976}
1977
Joy Lattenab5f5e82007-09-17 11:51:22 -07001978#ifdef CONFIG_AUDITSYSCALL
1979static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
1980 struct audit_buffer *audit_buf)
1981{
1982 if (x->security)
1983 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
1984 x->security->ctx_alg, x->security->ctx_doi,
1985 x->security->ctx_str);
1986
1987 switch(x->props.family) {
1988 case AF_INET:
1989 audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
1990 NIPQUAD(x->props.saddr.a4),
1991 NIPQUAD(x->id.daddr.a4));
1992 break;
1993 case AF_INET6:
1994 {
1995 struct in6_addr saddr6, daddr6;
1996
1997 memcpy(&saddr6, x->props.saddr.a6,
1998 sizeof(struct in6_addr));
1999 memcpy(&daddr6, x->id.daddr.a6,
2000 sizeof(struct in6_addr));
2001 audit_log_format(audit_buf,
2002 " src=" NIP6_FMT " dst=" NIP6_FMT,
2003 NIP6(saddr6), NIP6(daddr6));
2004 }
2005 break;
2006 }
2007}
2008
2009void
2010xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
2011{
2012 struct audit_buffer *audit_buf;
2013 extern int audit_enabled;
2014
2015 if (audit_enabled == 0)
2016 return;
2017 audit_buf = xfrm_audit_start(sid, auid);
2018 if (audit_buf == NULL)
2019 return;
2020 audit_log_format(audit_buf, " op=SAD-add res=%u",result);
2021 xfrm_audit_common_stateinfo(x, audit_buf);
2022 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2023 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2024 audit_log_end(audit_buf);
2025}
2026EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2027
2028void
2029xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid)
2030{
2031 struct audit_buffer *audit_buf;
2032 extern int audit_enabled;
2033
2034 if (audit_enabled == 0)
2035 return;
2036 audit_buf = xfrm_audit_start(sid, auid);
2037 if (audit_buf == NULL)
2038 return;
2039 audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
2040 xfrm_audit_common_stateinfo(x, audit_buf);
2041 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
2042 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
2043 audit_log_end(audit_buf);
2044}
2045EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2046#endif /* CONFIG_AUDITSYSCALL */