blob: abbe2702c4007e42e5ad7b53986adb5776328867 [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>
Paul Moore68277ac2007-12-20 20:49:33 -080022#include <linux/audit.h>
Jesper Juhlb5890d82007-08-10 15:20:21 -070023#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
David S. Miller44e36b42006-08-24 04:50:50 -070025#include "xfrm_hash.h"
26
David S. Milleree857a72006-03-20 19:18:37 -080027struct sock *xfrm_nl;
28EXPORT_SYMBOL(xfrm_nl);
29
David S. Miller01e67d02007-05-25 00:41:38 -070030u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080031EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32
David S. Miller01e67d02007-05-25 00:41:38 -070033u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080034EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35
David S. Miller01e67d02007-05-25 00:41:38 -070036u32 sysctl_xfrm_acq_expires __read_mostly = 30;
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038/* Each xfrm_state may be linked to two tables:
39
40 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070041 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 destination/tunnel endpoint. (output)
43 */
44
45static DEFINE_SPINLOCK(xfrm_state_lock);
46
47/* Hash table to find appropriate SA towards given target (endpoint
48 * of tunnel or destination of transport mode) allowed by selector.
49 *
50 * Main use is finding SA after policy selected tunnel or transport mode.
51 * Also, it can be used by ah/esp icmp error handler to find offending SA.
52 */
Timo Teras4c563f72008-02-28 21:31:08 -080053static LIST_HEAD(xfrm_state_all);
David S. Millerf034b5d2006-08-24 03:08:07 -070054static struct hlist_head *xfrm_state_bydst __read_mostly;
55static struct hlist_head *xfrm_state_bysrc __read_mostly;
56static struct hlist_head *xfrm_state_byspi __read_mostly;
57static unsigned int xfrm_state_hmask __read_mostly;
58static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
59static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070060static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Herbert Xuabb81c42008-09-09 19:58:29 -070062/* Counter indicating ongoing walk, protected by xfrm_state_lock. */
63static unsigned long xfrm_state_walk_ongoing;
64/* Counter indicating walk completion, protected by xfrm_cfg_mutex. */
65static unsigned long xfrm_state_walk_completed;
66
Herbert Xu17c2a422007-10-17 21:33:12 -070067static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
68static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
69
Paul Mooreafeb14b2007-12-21 14:58:11 -080070#ifdef CONFIG_AUDITSYSCALL
71static void xfrm_audit_state_replay(struct xfrm_state *x,
72 struct sk_buff *skb, __be32 net_seq);
73#else
74#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
75#endif /* CONFIG_AUDITSYSCALL */
76
David S. Millerc1969f22006-08-24 04:00:03 -070077static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
78 xfrm_address_t *saddr,
79 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070080 unsigned short family)
81{
David S. Millerc1969f22006-08-24 04:00:03 -070082 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070083}
84
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070085static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
86 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070087 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070088{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070089 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070090}
91
David S. Miller2575b652006-08-24 03:26:44 -070092static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070093xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070094{
David S. Millerc1969f22006-08-24 04:00:03 -070095 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070096}
97
David S. Millerf034b5d2006-08-24 03:08:07 -070098static void xfrm_hash_transfer(struct hlist_head *list,
99 struct hlist_head *ndsttable,
100 struct hlist_head *nsrctable,
101 struct hlist_head *nspitable,
102 unsigned int nhashmask)
103{
104 struct hlist_node *entry, *tmp;
105 struct xfrm_state *x;
106
107 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
108 unsigned int h;
109
David S. Millerc1969f22006-08-24 04:00:03 -0700110 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
111 x->props.reqid, x->props.family,
112 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700113 hlist_add_head(&x->bydst, ndsttable+h);
114
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700115 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
116 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700117 nhashmask);
118 hlist_add_head(&x->bysrc, nsrctable+h);
119
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700120 if (x->id.spi) {
121 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
122 x->id.proto, x->props.family,
123 nhashmask);
124 hlist_add_head(&x->byspi, nspitable+h);
125 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700126 }
127}
128
129static unsigned long xfrm_hash_new_size(void)
130{
131 return ((xfrm_state_hmask + 1) << 1) *
132 sizeof(struct hlist_head);
133}
134
135static DEFINE_MUTEX(hash_resize_mutex);
136
David Howellsc4028952006-11-22 14:57:56 +0000137static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700138{
139 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
140 unsigned long nsize, osize;
141 unsigned int nhashmask, ohashmask;
142 int i;
143
144 mutex_lock(&hash_resize_mutex);
145
146 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700147 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700148 if (!ndst)
149 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700150 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700151 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700152 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700153 goto out_unlock;
154 }
David S. Miller44e36b42006-08-24 04:50:50 -0700155 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700156 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700157 xfrm_hash_free(ndst, nsize);
158 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700159 goto out_unlock;
160 }
161
162 spin_lock_bh(&xfrm_state_lock);
163
164 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
165 for (i = xfrm_state_hmask; i >= 0; i--)
166 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
167 nhashmask);
168
169 odst = xfrm_state_bydst;
170 osrc = xfrm_state_bysrc;
171 ospi = xfrm_state_byspi;
172 ohashmask = xfrm_state_hmask;
173
174 xfrm_state_bydst = ndst;
175 xfrm_state_bysrc = nsrc;
176 xfrm_state_byspi = nspi;
177 xfrm_state_hmask = nhashmask;
178
179 spin_unlock_bh(&xfrm_state_lock);
180
181 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700182 xfrm_hash_free(odst, osize);
183 xfrm_hash_free(osrc, osize);
184 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700185
186out_unlock:
187 mutex_unlock(&hash_resize_mutex);
188}
189
David Howellsc4028952006-11-22 14:57:56 +0000190static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192DECLARE_WAIT_QUEUE_HEAD(km_waitq);
193EXPORT_SYMBOL(km_waitq);
194
195static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
196static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
197
198static struct work_struct xfrm_state_gc_work;
Herbert Xuabb81c42008-09-09 19:58:29 -0700199static LIST_HEAD(xfrm_state_gc_leftovers);
200static LIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201static DEFINE_SPINLOCK(xfrm_state_gc_lock);
202
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800203int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800205int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800206void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700208static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
209{
210 struct xfrm_state_afinfo *afinfo;
211 if (unlikely(family >= NPROTO))
212 return NULL;
213 write_lock_bh(&xfrm_state_afinfo_lock);
214 afinfo = xfrm_state_afinfo[family];
215 if (unlikely(!afinfo))
216 write_unlock_bh(&xfrm_state_afinfo_lock);
217 return afinfo;
218}
219
220static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800221 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700222{
223 write_unlock_bh(&xfrm_state_afinfo_lock);
224}
225
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800226int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700227{
228 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800229 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700230 int err = 0;
231
232 if (unlikely(afinfo == NULL))
233 return -EAFNOSUPPORT;
234 typemap = afinfo->type_map;
235
236 if (likely(typemap[type->proto] == NULL))
237 typemap[type->proto] = type;
238 else
239 err = -EEXIST;
240 xfrm_state_unlock_afinfo(afinfo);
241 return err;
242}
243EXPORT_SYMBOL(xfrm_register_type);
244
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800245int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700246{
247 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800248 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700249 int err = 0;
250
251 if (unlikely(afinfo == NULL))
252 return -EAFNOSUPPORT;
253 typemap = afinfo->type_map;
254
255 if (unlikely(typemap[type->proto] != type))
256 err = -ENOENT;
257 else
258 typemap[type->proto] = NULL;
259 xfrm_state_unlock_afinfo(afinfo);
260 return err;
261}
262EXPORT_SYMBOL(xfrm_unregister_type);
263
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800264static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700265{
266 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800267 const struct xfrm_type **typemap;
268 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700269 int modload_attempted = 0;
270
271retry:
272 afinfo = xfrm_state_get_afinfo(family);
273 if (unlikely(afinfo == NULL))
274 return NULL;
275 typemap = afinfo->type_map;
276
277 type = typemap[proto];
278 if (unlikely(type && !try_module_get(type->owner)))
279 type = NULL;
280 if (!type && !modload_attempted) {
281 xfrm_state_put_afinfo(afinfo);
282 request_module("xfrm-type-%d-%d", family, proto);
283 modload_attempted = 1;
284 goto retry;
285 }
286
287 xfrm_state_put_afinfo(afinfo);
288 return type;
289}
290
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800291static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700292{
293 module_put(type->owner);
294}
295
296int xfrm_register_mode(struct xfrm_mode *mode, int family)
297{
298 struct xfrm_state_afinfo *afinfo;
299 struct xfrm_mode **modemap;
300 int err;
301
302 if (unlikely(mode->encap >= XFRM_MODE_MAX))
303 return -EINVAL;
304
305 afinfo = xfrm_state_lock_afinfo(family);
306 if (unlikely(afinfo == NULL))
307 return -EAFNOSUPPORT;
308
309 err = -EEXIST;
310 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700311 if (modemap[mode->encap])
312 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700313
Herbert Xu17c2a422007-10-17 21:33:12 -0700314 err = -ENOENT;
315 if (!try_module_get(afinfo->owner))
316 goto out;
317
318 mode->afinfo = afinfo;
319 modemap[mode->encap] = mode;
320 err = 0;
321
322out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700323 xfrm_state_unlock_afinfo(afinfo);
324 return err;
325}
326EXPORT_SYMBOL(xfrm_register_mode);
327
328int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
329{
330 struct xfrm_state_afinfo *afinfo;
331 struct xfrm_mode **modemap;
332 int err;
333
334 if (unlikely(mode->encap >= XFRM_MODE_MAX))
335 return -EINVAL;
336
337 afinfo = xfrm_state_lock_afinfo(family);
338 if (unlikely(afinfo == NULL))
339 return -EAFNOSUPPORT;
340
341 err = -ENOENT;
342 modemap = afinfo->mode_map;
343 if (likely(modemap[mode->encap] == mode)) {
344 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700345 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700346 err = 0;
347 }
348
349 xfrm_state_unlock_afinfo(afinfo);
350 return err;
351}
352EXPORT_SYMBOL(xfrm_unregister_mode);
353
354static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
355{
356 struct xfrm_state_afinfo *afinfo;
357 struct xfrm_mode *mode;
358 int modload_attempted = 0;
359
360 if (unlikely(encap >= XFRM_MODE_MAX))
361 return NULL;
362
363retry:
364 afinfo = xfrm_state_get_afinfo(family);
365 if (unlikely(afinfo == NULL))
366 return NULL;
367
368 mode = afinfo->mode_map[encap];
369 if (unlikely(mode && !try_module_get(mode->owner)))
370 mode = NULL;
371 if (!mode && !modload_attempted) {
372 xfrm_state_put_afinfo(afinfo);
373 request_module("xfrm-mode-%d-%d", family, encap);
374 modload_attempted = 1;
375 goto retry;
376 }
377
378 xfrm_state_put_afinfo(afinfo);
379 return mode;
380}
381
382static void xfrm_put_mode(struct xfrm_mode *mode)
383{
384 module_put(mode->owner);
385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387static void xfrm_state_gc_destroy(struct xfrm_state *x)
388{
David S. Millera47f0ce2006-08-24 03:54:22 -0700389 del_timer_sync(&x->timer);
390 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800391 kfree(x->aalg);
392 kfree(x->ealg);
393 kfree(x->calg);
394 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700395 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700396 if (x->inner_mode)
397 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700398 if (x->inner_mode_iaf)
399 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700400 if (x->outer_mode)
401 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 if (x->type) {
403 x->type->destructor(x);
404 xfrm_put_type(x->type);
405 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800406 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 kfree(x);
408}
409
David Howellsc4028952006-11-22 14:57:56 +0000410static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Herbert Xuabb81c42008-09-09 19:58:29 -0700412 struct xfrm_state *x, *tmp;
413 unsigned long completed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
Herbert Xuabb81c42008-09-09 19:58:29 -0700415 mutex_lock(&xfrm_cfg_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 spin_lock_bh(&xfrm_state_gc_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -0700417 list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 spin_unlock_bh(&xfrm_state_gc_lock);
419
Herbert Xuabb81c42008-09-09 19:58:29 -0700420 completed = xfrm_state_walk_completed;
421 mutex_unlock(&xfrm_cfg_mutex);
422
423 list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) {
424 if ((long)(x->lastused - completed) > 0)
425 break;
David S. Miller08569902008-09-09 22:13:28 -0700426 list_del(&x->gclist);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 xfrm_state_gc_destroy(x);
Herbert Xuabb81c42008-09-09 19:58:29 -0700428 }
David S. Miller8f126e32006-08-24 02:45:07 -0700429
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 wake_up(&km_waitq);
431}
432
433static inline unsigned long make_jiffies(long secs)
434{
435 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
436 return MAX_SCHEDULE_TIMEOUT-1;
437 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900438 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
441static void xfrm_timer_handler(unsigned long data)
442{
443 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800444 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 long next = LONG_MAX;
446 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600447 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
449 spin_lock(&x->lock);
450 if (x->km.state == XFRM_STATE_DEAD)
451 goto out;
452 if (x->km.state == XFRM_STATE_EXPIRED)
453 goto expired;
454 if (x->lft.hard_add_expires_seconds) {
455 long tmo = x->lft.hard_add_expires_seconds +
456 x->curlft.add_time - now;
457 if (tmo <= 0)
458 goto expired;
459 if (tmo < next)
460 next = tmo;
461 }
462 if (x->lft.hard_use_expires_seconds) {
463 long tmo = x->lft.hard_use_expires_seconds +
464 (x->curlft.use_time ? : now) - now;
465 if (tmo <= 0)
466 goto expired;
467 if (tmo < next)
468 next = tmo;
469 }
470 if (x->km.dying)
471 goto resched;
472 if (x->lft.soft_add_expires_seconds) {
473 long tmo = x->lft.soft_add_expires_seconds +
474 x->curlft.add_time - now;
475 if (tmo <= 0)
476 warn = 1;
477 else if (tmo < next)
478 next = tmo;
479 }
480 if (x->lft.soft_use_expires_seconds) {
481 long tmo = x->lft.soft_use_expires_seconds +
482 (x->curlft.use_time ? : now) - now;
483 if (tmo <= 0)
484 warn = 1;
485 else if (tmo < next)
486 next = tmo;
487 }
488
Herbert Xu4666faa2005-06-18 22:43:22 -0700489 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800491 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700493 if (next != LONG_MAX)
494 mod_timer(&x->timer, jiffies + make_jiffies(next));
495
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 goto out;
497
498expired:
499 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
500 x->km.state = XFRM_STATE_EXPIRED;
501 wake_up(&km_waitq);
502 next = 2;
503 goto resched;
504 }
Joy Latten161a09e2006-11-27 13:11:54 -0600505
506 err = __xfrm_state_delete(x);
507 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800508 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Joy Lattenab5f5e82007-09-17 11:51:22 -0700510 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400511 audit_get_loginuid(current),
512 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514out:
515 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
David S. Miller0ac84752006-03-20 19:18:23 -0800518static void xfrm_replay_timer_handler(unsigned long data);
519
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520struct xfrm_state *xfrm_state_alloc(void)
521{
522 struct xfrm_state *x;
523
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700524 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
526 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 atomic_set(&x->refcnt, 1);
528 atomic_set(&x->tunnel_users, 0);
Timo Teras4c563f72008-02-28 21:31:08 -0800529 INIT_LIST_HEAD(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700530 INIT_HLIST_NODE(&x->bydst);
531 INIT_HLIST_NODE(&x->bysrc);
532 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800533 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
534 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
535 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800536 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 x->lft.soft_byte_limit = XFRM_INF;
538 x->lft.soft_packet_limit = XFRM_INF;
539 x->lft.hard_byte_limit = XFRM_INF;
540 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800541 x->replay_maxage = 0;
542 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700543 x->inner_mode = NULL;
544 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 spin_lock_init(&x->lock);
546 }
547 return x;
548}
549EXPORT_SYMBOL(xfrm_state_alloc);
550
551void __xfrm_state_destroy(struct xfrm_state *x)
552{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700553 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555 spin_lock_bh(&xfrm_state_gc_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -0700556 list_add_tail(&x->gclist, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 spin_unlock_bh(&xfrm_state_gc_lock);
558 schedule_work(&xfrm_state_gc_work);
559}
560EXPORT_SYMBOL(__xfrm_state_destroy);
561
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800562int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700564 int err = -ESRCH;
565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if (x->km.state != XFRM_STATE_DEAD) {
567 x->km.state = XFRM_STATE_DEAD;
568 spin_lock(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -0700569 x->lastused = xfrm_state_walk_ongoing;
570 list_del_rcu(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700571 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700572 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700573 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700574 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700575 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 /* All xfrm_state objects are created by xfrm_state_alloc.
579 * The xfrm_state_alloc call gives a reference, and that
580 * is what we are dropping here.
581 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800582 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700583 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700585
586 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800588EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700590int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700592 int err;
593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700595 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700597
598 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600EXPORT_SYMBOL(xfrm_state_delete);
601
Joy Latten4aa2e622007-06-04 19:05:57 -0400602#ifdef CONFIG_SECURITY_NETWORK_XFRM
603static inline int
604xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
Joy Latten4aa2e622007-06-04 19:05:57 -0400606 int i, err = 0;
607
608 for (i = 0; i <= xfrm_state_hmask; i++) {
609 struct hlist_node *entry;
610 struct xfrm_state *x;
611
612 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
613 if (xfrm_id_proto_match(x->id.proto, proto) &&
614 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700615 xfrm_audit_state_delete(x, 0,
616 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400617 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700618 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400619 return err;
620 }
621 }
622 }
623
624 return err;
625}
626#else
627static inline int
628xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
629{
630 return 0;
631}
632#endif
633
634int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
635{
636 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400639 err = xfrm_state_flush_secctx_check(proto, audit_info);
640 if (err)
641 goto out;
642
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700643 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700644 struct hlist_node *entry;
645 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700647 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700649 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 xfrm_state_hold(x);
651 spin_unlock_bh(&xfrm_state_lock);
652
Joy Latten161a09e2006-11-27 13:11:54 -0600653 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700654 xfrm_audit_state_delete(x, err ? 0 : 1,
655 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400656 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700657 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 xfrm_state_put(x);
659
660 spin_lock_bh(&xfrm_state_lock);
661 goto restart;
662 }
663 }
664 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400665 err = 0;
666
667out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 spin_unlock_bh(&xfrm_state_lock);
669 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400670 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671}
672EXPORT_SYMBOL(xfrm_state_flush);
673
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700674void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700675{
676 spin_lock_bh(&xfrm_state_lock);
677 si->sadcnt = xfrm_state_num;
678 si->sadhcnt = xfrm_state_hmask;
679 si->sadhmcnt = xfrm_state_hashmax;
680 spin_unlock_bh(&xfrm_state_lock);
681}
682EXPORT_SYMBOL(xfrm_sad_getinfo);
683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684static int
685xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
686 struct xfrm_tmpl *tmpl,
687 xfrm_address_t *daddr, xfrm_address_t *saddr,
688 unsigned short family)
689{
690 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
691 if (!afinfo)
692 return -1;
693 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
694 xfrm_state_put_afinfo(afinfo);
695 return 0;
696}
697
Al Viroa94cfd12006-09-27 18:47:24 -0700698static 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 -0700699{
700 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
701 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_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700705 if (x->props.family != family ||
706 x->id.spi != spi ||
707 x->id.proto != proto)
708 continue;
709
710 switch (family) {
711 case AF_INET:
712 if (x->id.daddr.a4 != daddr->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 continue;
720 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700721 }
David S. Milleredcd5822006-08-24 00:42:45 -0700722
723 xfrm_state_hold(x);
724 return x;
725 }
726
727 return NULL;
728}
729
730static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
731{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700732 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700733 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700734 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700735
David S. Miller8f126e32006-08-24 02:45:07 -0700736 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700737 if (x->props.family != family ||
738 x->id.proto != proto)
739 continue;
740
741 switch (family) {
742 case AF_INET:
743 if (x->id.daddr.a4 != daddr->a4 ||
744 x->props.saddr.a4 != saddr->a4)
745 continue;
746 break;
747 case AF_INET6:
748 if (!ipv6_addr_equal((struct in6_addr *)daddr,
749 (struct in6_addr *)
750 x->id.daddr.a6) ||
751 !ipv6_addr_equal((struct in6_addr *)saddr,
752 (struct in6_addr *)
753 x->props.saddr.a6))
754 continue;
755 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700756 }
David S. Milleredcd5822006-08-24 00:42:45 -0700757
758 xfrm_state_hold(x);
759 return x;
760 }
761
762 return NULL;
763}
764
765static inline struct xfrm_state *
766__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
767{
768 if (use_spi)
769 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
770 x->id.proto, family);
771 else
772 return __xfrm_state_lookup_byaddr(&x->id.daddr,
773 &x->props.saddr,
774 x->id.proto, family);
775}
776
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700777static void xfrm_hash_grow_check(int have_hash_collision)
778{
779 if (have_hash_collision &&
780 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
781 xfrm_state_num > xfrm_state_hmask)
782 schedule_work(&xfrm_hash_work);
783}
784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900786xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 struct flowi *fl, struct xfrm_tmpl *tmpl,
788 struct xfrm_policy *pol, int *err,
789 unsigned short family)
790{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800791 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700792 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700793 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 int acquire_in_progress = 0;
795 int error = 0;
796 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900797
David S. Miller37b08e32008-09-02 20:14:15 -0700798 to_put = NULL;
799
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800801 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700802 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 if (x->props.family == family &&
804 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700805 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 xfrm_state_addr_check(x, daddr, saddr, family) &&
807 tmpl->mode == x->props.mode &&
808 tmpl->id.proto == x->id.proto &&
809 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
810 /* Resolution logic:
811 1. There is a valid state with matching selector.
812 Done.
813 2. Valid state with inappropriate selector. Skip.
814
815 Entering area of "sysdeps".
816
817 3. If state is not valid, selector is temporary,
818 it selects only session which triggered
819 previous resolution. Key manager will do
820 something to install a state with proper
821 selector.
822 */
823 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700824 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700825 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 continue;
827 if (!best ||
828 best->km.dying > x->km.dying ||
829 (best->km.dying == x->km.dying &&
830 best->curlft.add_time < x->curlft.add_time))
831 best = x;
832 } else if (x->km.state == XFRM_STATE_ACQ) {
833 acquire_in_progress = 1;
834 } else if (x->km.state == XFRM_STATE_ERROR ||
835 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700836 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700837 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 error = -ESRCH;
839 }
840 }
841 }
842
843 x = best;
844 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700845 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700846 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
847 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700848 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 error = -EEXIST;
850 goto out;
851 }
852 x = xfrm_state_alloc();
853 if (x == NULL) {
854 error = -ENOMEM;
855 goto out;
856 }
857 /* Initialize temporary selector matching only
858 * to current session. */
859 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
860
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700861 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
862 if (error) {
863 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700864 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700865 x = NULL;
866 goto out;
867 }
868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 if (km_query(x, tmpl, pol) == 0) {
870 x->km.state = XFRM_STATE_ACQ;
Herbert Xu225f4002008-09-09 05:23:37 -0700871 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -0700872 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700873 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700874 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 if (x->id.spi) {
876 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700877 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 }
David S. Miller01e67d02007-05-25 00:41:38 -0700879 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
880 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700882 xfrm_state_num++;
883 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 } else {
885 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700886 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 x = NULL;
888 error = -ESRCH;
889 }
890 }
891out:
892 if (x)
893 xfrm_state_hold(x);
894 else
895 *err = acquire_in_progress ? -EAGAIN : error;
896 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700897 if (to_put)
898 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 return x;
900}
901
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700902struct xfrm_state *
903xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
904 unsigned short family, u8 mode, u8 proto, u32 reqid)
905{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800906 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700907 struct xfrm_state *rx = NULL, *x = NULL;
908 struct hlist_node *entry;
909
910 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800911 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700912 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
913 if (x->props.family == family &&
914 x->props.reqid == reqid &&
915 !(x->props.flags & XFRM_STATE_WILDRECV) &&
916 xfrm_state_addr_check(x, daddr, saddr, family) &&
917 mode == x->props.mode &&
918 proto == x->id.proto &&
919 x->km.state == XFRM_STATE_VALID) {
920 rx = x;
921 break;
922 }
923 }
924
925 if (rx)
926 xfrm_state_hold(rx);
927 spin_unlock(&xfrm_state_lock);
928
929
930 return rx;
931}
932EXPORT_SYMBOL(xfrm_stateonly_find);
933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934static void __xfrm_state_insert(struct xfrm_state *x)
935{
David S. Millera624c102006-08-24 03:24:33 -0700936 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
David S. Miller9d4a7062006-08-24 03:18:09 -0700938 x->genid = ++xfrm_state_genid;
939
Timo Teras4c563f72008-02-28 21:31:08 -0800940 list_add_tail(&x->all, &xfrm_state_all);
941
David S. Millerc1969f22006-08-24 04:00:03 -0700942 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
943 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700944 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700946 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700947 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700949 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700950 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
951 x->props.family);
952
David S. Miller8f126e32006-08-24 02:45:07 -0700953 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700954 }
955
David S. Millera47f0ce2006-08-24 03:54:22 -0700956 mod_timer(&x->timer, jiffies + HZ);
957 if (x->replay_maxage)
958 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800959
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700961
962 xfrm_state_num++;
963
David S. Miller918049f2006-10-12 22:03:24 -0700964 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965}
966
David S. Millerc7f5ea32006-08-24 03:29:04 -0700967/* xfrm_state_lock is held */
968static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
969{
970 unsigned short family = xnew->props.family;
971 u32 reqid = xnew->props.reqid;
972 struct xfrm_state *x;
973 struct hlist_node *entry;
974 unsigned int h;
975
David S. Millerc1969f22006-08-24 04:00:03 -0700976 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700977 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
978 if (x->props.family == family &&
979 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700980 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
981 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700982 x->genid = xfrm_state_genid;
983 }
984}
985
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986void xfrm_state_insert(struct xfrm_state *x)
987{
988 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700989 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 __xfrm_state_insert(x);
991 spin_unlock_bh(&xfrm_state_lock);
992}
993EXPORT_SYMBOL(xfrm_state_insert);
994
David S. Miller27708342006-08-24 00:13:10 -0700995/* xfrm_state_lock is held */
996static 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)
997{
David S. Millerc1969f22006-08-24 04:00:03 -0700998 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700999 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -07001000 struct xfrm_state *x;
1001
David S. Miller8f126e32006-08-24 02:45:07 -07001002 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -07001003 if (x->props.reqid != reqid ||
1004 x->props.mode != mode ||
1005 x->props.family != family ||
1006 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -07001007 x->id.spi != 0 ||
1008 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -07001009 continue;
1010
1011 switch (family) {
1012 case AF_INET:
1013 if (x->id.daddr.a4 != daddr->a4 ||
1014 x->props.saddr.a4 != saddr->a4)
1015 continue;
1016 break;
1017 case AF_INET6:
1018 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
1019 (struct in6_addr *)daddr) ||
1020 !ipv6_addr_equal((struct in6_addr *)
1021 x->props.saddr.a6,
1022 (struct in6_addr *)saddr))
1023 continue;
1024 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001025 }
David S. Miller27708342006-08-24 00:13:10 -07001026
1027 xfrm_state_hold(x);
1028 return x;
1029 }
1030
1031 if (!create)
1032 return NULL;
1033
1034 x = xfrm_state_alloc();
1035 if (likely(x)) {
1036 switch (family) {
1037 case AF_INET:
1038 x->sel.daddr.a4 = daddr->a4;
1039 x->sel.saddr.a4 = saddr->a4;
1040 x->sel.prefixlen_d = 32;
1041 x->sel.prefixlen_s = 32;
1042 x->props.saddr.a4 = saddr->a4;
1043 x->id.daddr.a4 = daddr->a4;
1044 break;
1045
1046 case AF_INET6:
1047 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1048 (struct in6_addr *)daddr);
1049 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1050 (struct in6_addr *)saddr);
1051 x->sel.prefixlen_d = 128;
1052 x->sel.prefixlen_s = 128;
1053 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1054 (struct in6_addr *)saddr);
1055 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1056 (struct in6_addr *)daddr);
1057 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001058 }
David S. Miller27708342006-08-24 00:13:10 -07001059
1060 x->km.state = XFRM_STATE_ACQ;
1061 x->id.proto = proto;
1062 x->props.family = family;
1063 x->props.mode = mode;
1064 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001065 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001066 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001067 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001068 add_timer(&x->timer);
Herbert Xu225f4002008-09-09 05:23:37 -07001069 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -07001070 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001071 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001072 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001073
1074 xfrm_state_num++;
1075
1076 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001077 }
1078
1079 return x;
1080}
1081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1083
1084int xfrm_state_add(struct xfrm_state *x)
1085{
David S. Miller37b08e32008-09-02 20:14:15 -07001086 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 int family;
1088 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001089 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
David S. Miller37b08e32008-09-02 20:14:15 -07001093 to_put = NULL;
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 spin_lock_bh(&xfrm_state_lock);
1096
David S. Milleredcd5822006-08-24 00:42:45 -07001097 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001099 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 x1 = NULL;
1101 err = -EEXIST;
1102 goto out;
1103 }
1104
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001105 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001107 if (x1 && ((x1->id.proto != x->id.proto) ||
1108 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001109 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 x1 = NULL;
1111 }
1112 }
1113
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001114 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001115 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1116 x->id.proto,
1117 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
David S. Millerc7f5ea32006-08-24 03:29:04 -07001119 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 __xfrm_state_insert(x);
1121 err = 0;
1122
1123out:
1124 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 if (x1) {
1127 xfrm_state_delete(x1);
1128 xfrm_state_put(x1);
1129 }
1130
David S. Miller37b08e32008-09-02 20:14:15 -07001131 if (to_put)
1132 xfrm_state_put(to_put);
1133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 return err;
1135}
1136EXPORT_SYMBOL(xfrm_state_add);
1137
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001138#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001139static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001140{
1141 int err = -ENOMEM;
1142 struct xfrm_state *x = xfrm_state_alloc();
1143 if (!x)
1144 goto error;
1145
1146 memcpy(&x->id, &orig->id, sizeof(x->id));
1147 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1148 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1149 x->props.mode = orig->props.mode;
1150 x->props.replay_window = orig->props.replay_window;
1151 x->props.reqid = orig->props.reqid;
1152 x->props.family = orig->props.family;
1153 x->props.saddr = orig->props.saddr;
1154
1155 if (orig->aalg) {
1156 x->aalg = xfrm_algo_clone(orig->aalg);
1157 if (!x->aalg)
1158 goto error;
1159 }
1160 x->props.aalgo = orig->props.aalgo;
1161
1162 if (orig->ealg) {
1163 x->ealg = xfrm_algo_clone(orig->ealg);
1164 if (!x->ealg)
1165 goto error;
1166 }
1167 x->props.ealgo = orig->props.ealgo;
1168
1169 if (orig->calg) {
1170 x->calg = xfrm_algo_clone(orig->calg);
1171 if (!x->calg)
1172 goto error;
1173 }
1174 x->props.calgo = orig->props.calgo;
1175
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001176 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001177 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1178 if (!x->encap)
1179 goto error;
1180 }
1181
1182 if (orig->coaddr) {
1183 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1184 GFP_KERNEL);
1185 if (!x->coaddr)
1186 goto error;
1187 }
1188
1189 err = xfrm_init_state(x);
1190 if (err)
1191 goto error;
1192
1193 x->props.flags = orig->props.flags;
1194
1195 x->curlft.add_time = orig->curlft.add_time;
1196 x->km.state = orig->km.state;
1197 x->km.seq = orig->km.seq;
1198
1199 return x;
1200
1201 error:
1202 if (errp)
1203 *errp = err;
1204 if (x) {
1205 kfree(x->aalg);
1206 kfree(x->ealg);
1207 kfree(x->calg);
1208 kfree(x->encap);
1209 kfree(x->coaddr);
1210 }
1211 kfree(x);
1212 return NULL;
1213}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001214
1215/* xfrm_state_lock is held */
1216struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1217{
1218 unsigned int h;
1219 struct xfrm_state *x;
1220 struct hlist_node *entry;
1221
1222 if (m->reqid) {
1223 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1224 m->reqid, m->old_family);
1225 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1226 if (x->props.mode != m->mode ||
1227 x->id.proto != m->proto)
1228 continue;
1229 if (m->reqid && x->props.reqid != m->reqid)
1230 continue;
1231 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1232 m->old_family) ||
1233 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1234 m->old_family))
1235 continue;
1236 xfrm_state_hold(x);
1237 return x;
1238 }
1239 } else {
1240 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1241 m->old_family);
1242 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1243 if (x->props.mode != m->mode ||
1244 x->id.proto != m->proto)
1245 continue;
1246 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1247 m->old_family) ||
1248 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1249 m->old_family))
1250 continue;
1251 xfrm_state_hold(x);
1252 return x;
1253 }
1254 }
1255
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001256 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001257}
1258EXPORT_SYMBOL(xfrm_migrate_state_find);
1259
1260struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1261 struct xfrm_migrate *m)
1262{
1263 struct xfrm_state *xc;
1264 int err;
1265
1266 xc = xfrm_state_clone(x, &err);
1267 if (!xc)
1268 return NULL;
1269
1270 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1271 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1272
1273 /* add state */
1274 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1275 /* a care is needed when the destination address of the
1276 state is to be updated as it is a part of triplet */
1277 xfrm_state_insert(xc);
1278 } else {
1279 if ((err = xfrm_state_add(xc)) < 0)
1280 goto error;
1281 }
1282
1283 return xc;
1284error:
1285 kfree(xc);
1286 return NULL;
1287}
1288EXPORT_SYMBOL(xfrm_state_migrate);
1289#endif
1290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291int xfrm_state_update(struct xfrm_state *x)
1292{
David S. Miller37b08e32008-09-02 20:14:15 -07001293 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001295 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
David S. Miller37b08e32008-09-02 20:14:15 -07001297 to_put = NULL;
1298
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001300 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
1302 err = -ESRCH;
1303 if (!x1)
1304 goto out;
1305
1306 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001307 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 err = -EEXIST;
1309 goto out;
1310 }
1311
1312 if (x1->km.state == XFRM_STATE_ACQ) {
1313 __xfrm_state_insert(x);
1314 x = NULL;
1315 }
1316 err = 0;
1317
1318out:
1319 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
David S. Miller37b08e32008-09-02 20:14:15 -07001321 if (to_put)
1322 xfrm_state_put(to_put);
1323
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 if (err)
1325 return err;
1326
1327 if (!x) {
1328 xfrm_state_delete(x1);
1329 xfrm_state_put(x1);
1330 return 0;
1331 }
1332
1333 err = -EINVAL;
1334 spin_lock_bh(&x1->lock);
1335 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1336 if (x->encap && x1->encap)
1337 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001338 if (x->coaddr && x1->coaddr) {
1339 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1340 }
1341 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1342 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1344 x1->km.dying = 0;
1345
David S. Millera47f0ce2006-08-24 03:54:22 -07001346 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if (x1->curlft.use_time)
1348 xfrm_state_check_expire(x1);
1349
1350 err = 0;
1351 }
1352 spin_unlock_bh(&x1->lock);
1353
1354 xfrm_state_put(x1);
1355
1356 return err;
1357}
1358EXPORT_SYMBOL(xfrm_state_update);
1359
1360int xfrm_state_check_expire(struct xfrm_state *x)
1361{
1362 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001363 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 if (x->km.state != XFRM_STATE_VALID)
1366 return -EINVAL;
1367
1368 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1369 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001370 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001371 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 return -EINVAL;
1373 }
1374
1375 if (!x->km.dying &&
1376 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001377 x->curlft.packets >= x->lft.soft_packet_limit)) {
1378 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001379 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001380 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 return 0;
1382}
1383EXPORT_SYMBOL(xfrm_state_check_expire);
1384
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001386xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 unsigned short family)
1388{
1389 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
1391 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001392 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 return x;
1395}
1396EXPORT_SYMBOL(xfrm_state_lookup);
1397
1398struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001399xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1400 u8 proto, unsigned short family)
1401{
1402 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001403
1404 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001405 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001406 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001407 return x;
1408}
1409EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1410
1411struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001412xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1413 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 int create, unsigned short family)
1415{
1416 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
1418 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001419 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001421
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 return x;
1423}
1424EXPORT_SYMBOL(xfrm_find_acq);
1425
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001426#ifdef CONFIG_XFRM_SUB_POLICY
1427int
1428xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1429 unsigned short family)
1430{
1431 int err = 0;
1432 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1433 if (!afinfo)
1434 return -EAFNOSUPPORT;
1435
1436 spin_lock_bh(&xfrm_state_lock);
1437 if (afinfo->tmpl_sort)
1438 err = afinfo->tmpl_sort(dst, src, n);
1439 spin_unlock_bh(&xfrm_state_lock);
1440 xfrm_state_put_afinfo(afinfo);
1441 return err;
1442}
1443EXPORT_SYMBOL(xfrm_tmpl_sort);
1444
1445int
1446xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1447 unsigned short family)
1448{
1449 int err = 0;
1450 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1451 if (!afinfo)
1452 return -EAFNOSUPPORT;
1453
1454 spin_lock_bh(&xfrm_state_lock);
1455 if (afinfo->state_sort)
1456 err = afinfo->state_sort(dst, src, n);
1457 spin_unlock_bh(&xfrm_state_lock);
1458 xfrm_state_put_afinfo(afinfo);
1459 return err;
1460}
1461EXPORT_SYMBOL(xfrm_state_sort);
1462#endif
1463
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464/* Silly enough, but I'm lazy to build resolution list */
1465
1466static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1467{
1468 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469
David S. Millerf034b5d2006-08-24 03:08:07 -07001470 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001471 struct hlist_node *entry;
1472 struct xfrm_state *x;
1473
1474 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1475 if (x->km.seq == seq &&
1476 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 xfrm_state_hold(x);
1478 return x;
1479 }
1480 }
1481 }
1482 return NULL;
1483}
1484
1485struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1486{
1487 struct xfrm_state *x;
1488
1489 spin_lock_bh(&xfrm_state_lock);
1490 x = __xfrm_find_acq_byseq(seq);
1491 spin_unlock_bh(&xfrm_state_lock);
1492 return x;
1493}
1494EXPORT_SYMBOL(xfrm_find_acq_byseq);
1495
1496u32 xfrm_get_acqseq(void)
1497{
1498 u32 res;
1499 static u32 acqseq;
1500 static DEFINE_SPINLOCK(acqseq_lock);
1501
1502 spin_lock_bh(&acqseq_lock);
1503 res = (++acqseq ? : ++acqseq);
1504 spin_unlock_bh(&acqseq_lock);
1505 return res;
1506}
1507EXPORT_SYMBOL(xfrm_get_acqseq);
1508
Herbert Xu658b2192007-10-09 13:29:52 -07001509int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510{
David S. Millerf034b5d2006-08-24 03:08:07 -07001511 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001513 int err = -ENOENT;
1514 __be32 minspi = htonl(low);
1515 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Herbert Xu658b2192007-10-09 13:29:52 -07001517 spin_lock_bh(&x->lock);
1518 if (x->km.state == XFRM_STATE_DEAD)
1519 goto unlock;
1520
1521 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001523 goto unlock;
1524
1525 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
1527 if (minspi == maxspi) {
1528 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1529 if (x0) {
1530 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001531 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 }
1533 x->id.spi = minspi;
1534 } else {
1535 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001536 for (h=0; h<high-low+1; h++) {
1537 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1539 if (x0 == NULL) {
1540 x->id.spi = htonl(spi);
1541 break;
1542 }
1543 xfrm_state_put(x0);
1544 }
1545 }
1546 if (x->id.spi) {
1547 spin_lock_bh(&xfrm_state_lock);
1548 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001549 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001551
1552 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 }
Herbert Xu658b2192007-10-09 13:29:52 -07001554
1555unlock:
1556 spin_unlock_bh(&x->lock);
1557
1558 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559}
1560EXPORT_SYMBOL(xfrm_alloc_spi);
1561
Timo Teras4c563f72008-02-28 21:31:08 -08001562int xfrm_state_walk(struct xfrm_state_walk *walk,
1563 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 void *data)
1565{
Timo Teras4c563f72008-02-28 21:31:08 -08001566 struct xfrm_state *old, *x, *last = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 int err = 0;
1568
Timo Teras4c563f72008-02-28 21:31:08 -08001569 if (walk->state == NULL && walk->count != 0)
1570 return 0;
1571
1572 old = x = walk->state;
1573 walk->state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 spin_lock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001575 if (x == NULL)
1576 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1577 list_for_each_entry_from(x, &xfrm_state_all, all) {
1578 if (x->km.state == XFRM_STATE_DEAD)
1579 continue;
1580 if (!xfrm_id_proto_match(x->id.proto, walk->proto))
1581 continue;
1582 if (last) {
1583 err = func(last, walk->count, data);
1584 if (err) {
1585 xfrm_state_hold(last);
1586 walk->state = last;
Herbert Xuabb81c42008-09-09 19:58:29 -07001587 xfrm_state_walk_ongoing++;
Timo Teras4c563f72008-02-28 21:31:08 -08001588 goto out;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001589 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 }
Timo Teras4c563f72008-02-28 21:31:08 -08001591 last = x;
1592 walk->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 }
Timo Teras4c563f72008-02-28 21:31:08 -08001594 if (walk->count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 err = -ENOENT;
1596 goto out;
1597 }
Timo Teras4c563f72008-02-28 21:31:08 -08001598 if (last)
1599 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600out:
1601 spin_unlock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001602 if (old != NULL) {
Timo Teras4c563f72008-02-28 21:31:08 -08001603 xfrm_state_put(old);
Herbert Xuabb81c42008-09-09 19:58:29 -07001604 xfrm_state_walk_completed++;
1605 if (!list_empty(&xfrm_state_gc_leftovers))
1606 schedule_work(&xfrm_state_gc_work);
1607 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 return err;
1609}
1610EXPORT_SYMBOL(xfrm_state_walk);
1611
Herbert Xuabb81c42008-09-09 19:58:29 -07001612void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1613{
1614 if (walk->state != NULL) {
1615 xfrm_state_put(walk->state);
1616 walk->state = NULL;
1617 xfrm_state_walk_completed++;
1618 if (!list_empty(&xfrm_state_gc_leftovers))
1619 schedule_work(&xfrm_state_gc_work);
1620 }
1621}
1622EXPORT_SYMBOL(xfrm_state_walk_done);
1623
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001624
1625void xfrm_replay_notify(struct xfrm_state *x, int event)
1626{
1627 struct km_event c;
1628 /* we send notify messages in case
1629 * 1. we updated on of the sequence numbers, and the seqno difference
1630 * is at least x->replay_maxdiff, in this case we also update the
1631 * timeout of our timer function
1632 * 2. if x->replay_maxage has elapsed since last update,
1633 * and there were changes
1634 *
1635 * The state structure must be locked!
1636 */
1637
1638 switch (event) {
1639 case XFRM_REPLAY_UPDATE:
1640 if (x->replay_maxdiff &&
1641 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001642 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1643 if (x->xflags & XFRM_TIME_DEFER)
1644 event = XFRM_REPLAY_TIMEOUT;
1645 else
1646 return;
1647 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001648
1649 break;
1650
1651 case XFRM_REPLAY_TIMEOUT:
1652 if ((x->replay.seq == x->preplay.seq) &&
1653 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001654 (x->replay.oseq == x->preplay.oseq)) {
1655 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001656 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001657 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001658
1659 break;
1660 }
1661
1662 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1663 c.event = XFRM_MSG_NEWAE;
1664 c.data.aevent = event;
1665 km_state_notify(x, &c);
1666
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001667 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001668 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001669 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001670}
1671
1672static void xfrm_replay_timer_handler(unsigned long data)
1673{
1674 struct xfrm_state *x = (struct xfrm_state*)data;
1675
1676 spin_lock(&x->lock);
1677
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001678 if (x->km.state == XFRM_STATE_VALID) {
1679 if (xfrm_aevent_is_on())
1680 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1681 else
1682 x->xflags |= XFRM_TIME_DEFER;
1683 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001684
1685 spin_unlock(&x->lock);
1686}
1687
Paul Mooreafeb14b2007-12-21 14:58:11 -08001688int xfrm_replay_check(struct xfrm_state *x,
1689 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690{
1691 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001692 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001695 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696
1697 if (likely(seq > x->replay.seq))
1698 return 0;
1699
1700 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001701 if (diff >= min_t(unsigned int, x->props.replay_window,
1702 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001704 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 }
1706
1707 if (x->replay.bitmap & (1U << diff)) {
1708 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001709 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 }
1711 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001712
1713err:
1714 xfrm_audit_state_replay(x, skb, net_seq);
1715 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717
Al Viro61f46272006-09-27 18:48:33 -07001718void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
1720 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001721 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722
1723 if (seq > x->replay.seq) {
1724 diff = seq - x->replay.seq;
1725 if (diff < x->props.replay_window)
1726 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1727 else
1728 x->replay.bitmap = 1;
1729 x->replay.seq = seq;
1730 } else {
1731 diff = x->replay.seq - seq;
1732 x->replay.bitmap |= (1U << diff);
1733 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001734
1735 if (xfrm_aevent_is_on())
1736 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
Denis Chengdf018122007-12-07 00:51:11 -08001739static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740static DEFINE_RWLOCK(xfrm_km_lock);
1741
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001742void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743{
1744 struct xfrm_mgr *km;
1745
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001746 read_lock(&xfrm_km_lock);
1747 list_for_each_entry(km, &xfrm_km_list, list)
1748 if (km->notify_policy)
1749 km->notify_policy(xp, dir, c);
1750 read_unlock(&xfrm_km_lock);
1751}
1752
1753void km_state_notify(struct xfrm_state *x, struct km_event *c)
1754{
1755 struct xfrm_mgr *km;
1756 read_lock(&xfrm_km_lock);
1757 list_for_each_entry(km, &xfrm_km_list, list)
1758 if (km->notify)
1759 km->notify(x, c);
1760 read_unlock(&xfrm_km_lock);
1761}
1762
1763EXPORT_SYMBOL(km_policy_notify);
1764EXPORT_SYMBOL(km_state_notify);
1765
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001766void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001767{
1768 struct km_event c;
1769
Herbert Xubf088672005-06-18 22:44:00 -07001770 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001771 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001772 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001773 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
1775 if (hard)
1776 wake_up(&km_waitq);
1777}
1778
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001779EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001780/*
1781 * We send to all registered managers regardless of failure
1782 * We are happy with one success
1783*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001784int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001786 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 struct xfrm_mgr *km;
1788
1789 read_lock(&xfrm_km_lock);
1790 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001791 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1792 if (!acqret)
1793 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 }
1795 read_unlock(&xfrm_km_lock);
1796 return err;
1797}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001798EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Al Viro5d36b182006-11-08 00:24:06 -08001800int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801{
1802 int err = -EINVAL;
1803 struct xfrm_mgr *km;
1804
1805 read_lock(&xfrm_km_lock);
1806 list_for_each_entry(km, &xfrm_km_list, list) {
1807 if (km->new_mapping)
1808 err = km->new_mapping(x, ipaddr, sport);
1809 if (!err)
1810 break;
1811 }
1812 read_unlock(&xfrm_km_lock);
1813 return err;
1814}
1815EXPORT_SYMBOL(km_new_mapping);
1816
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001817void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001819 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Herbert Xubf088672005-06-18 22:44:00 -07001821 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001822 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001823 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001824 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
1826 if (hard)
1827 wake_up(&km_waitq);
1828}
David S. Millera70fcb02006-03-20 19:18:52 -08001829EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001831#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001832int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1833 struct xfrm_migrate *m, int num_migrate)
1834{
1835 int err = -EINVAL;
1836 int ret;
1837 struct xfrm_mgr *km;
1838
1839 read_lock(&xfrm_km_lock);
1840 list_for_each_entry(km, &xfrm_km_list, list) {
1841 if (km->migrate) {
1842 ret = km->migrate(sel, dir, type, m, num_migrate);
1843 if (!ret)
1844 err = ret;
1845 }
1846 }
1847 read_unlock(&xfrm_km_lock);
1848 return err;
1849}
1850EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001851#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001852
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001853int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1854{
1855 int err = -EINVAL;
1856 int ret;
1857 struct xfrm_mgr *km;
1858
1859 read_lock(&xfrm_km_lock);
1860 list_for_each_entry(km, &xfrm_km_list, list) {
1861 if (km->report) {
1862 ret = km->report(proto, sel, addr);
1863 if (!ret)
1864 err = ret;
1865 }
1866 }
1867 read_unlock(&xfrm_km_lock);
1868 return err;
1869}
1870EXPORT_SYMBOL(km_report);
1871
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1873{
1874 int err;
1875 u8 *data;
1876 struct xfrm_mgr *km;
1877 struct xfrm_policy *pol = NULL;
1878
1879 if (optlen <= 0 || optlen > PAGE_SIZE)
1880 return -EMSGSIZE;
1881
1882 data = kmalloc(optlen, GFP_KERNEL);
1883 if (!data)
1884 return -ENOMEM;
1885
1886 err = -EFAULT;
1887 if (copy_from_user(data, optval, optlen))
1888 goto out;
1889
1890 err = -EINVAL;
1891 read_lock(&xfrm_km_lock);
1892 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001893 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 optlen, &err);
1895 if (err >= 0)
1896 break;
1897 }
1898 read_unlock(&xfrm_km_lock);
1899
1900 if (err >= 0) {
1901 xfrm_sk_policy_insert(sk, err, pol);
1902 xfrm_pol_put(pol);
1903 err = 0;
1904 }
1905
1906out:
1907 kfree(data);
1908 return err;
1909}
1910EXPORT_SYMBOL(xfrm_user_policy);
1911
1912int xfrm_register_km(struct xfrm_mgr *km)
1913{
1914 write_lock_bh(&xfrm_km_lock);
1915 list_add_tail(&km->list, &xfrm_km_list);
1916 write_unlock_bh(&xfrm_km_lock);
1917 return 0;
1918}
1919EXPORT_SYMBOL(xfrm_register_km);
1920
1921int xfrm_unregister_km(struct xfrm_mgr *km)
1922{
1923 write_lock_bh(&xfrm_km_lock);
1924 list_del(&km->list);
1925 write_unlock_bh(&xfrm_km_lock);
1926 return 0;
1927}
1928EXPORT_SYMBOL(xfrm_unregister_km);
1929
1930int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1931{
1932 int err = 0;
1933 if (unlikely(afinfo == NULL))
1934 return -EINVAL;
1935 if (unlikely(afinfo->family >= NPROTO))
1936 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001937 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1939 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001940 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001942 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 return err;
1944}
1945EXPORT_SYMBOL(xfrm_state_register_afinfo);
1946
1947int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1948{
1949 int err = 0;
1950 if (unlikely(afinfo == NULL))
1951 return -EINVAL;
1952 if (unlikely(afinfo->family >= NPROTO))
1953 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001954 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1956 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1957 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001958 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001961 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 return err;
1963}
1964EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1965
Herbert Xu17c2a422007-10-17 21:33:12 -07001966static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967{
1968 struct xfrm_state_afinfo *afinfo;
1969 if (unlikely(family >= NPROTO))
1970 return NULL;
1971 read_lock(&xfrm_state_afinfo_lock);
1972 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001973 if (unlikely(!afinfo))
1974 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 return afinfo;
1976}
1977
Herbert Xu17c2a422007-10-17 21:33:12 -07001978static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001979 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980{
Herbert Xu546be242006-05-27 23:03:58 -07001981 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982}
1983
1984/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1985void xfrm_state_delete_tunnel(struct xfrm_state *x)
1986{
1987 if (x->tunnel) {
1988 struct xfrm_state *t = x->tunnel;
1989
1990 if (atomic_read(&t->tunnel_users) == 2)
1991 xfrm_state_delete(t);
1992 atomic_dec(&t->tunnel_users);
1993 xfrm_state_put(t);
1994 x->tunnel = NULL;
1995 }
1996}
1997EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1998
1999int xfrm_state_mtu(struct xfrm_state *x, int mtu)
2000{
Patrick McHardyc5c25232007-04-09 11:47:18 -07002001 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002
Patrick McHardyc5c25232007-04-09 11:47:18 -07002003 spin_lock_bh(&x->lock);
2004 if (x->km.state == XFRM_STATE_VALID &&
2005 x->type && x->type->get_mtu)
2006 res = x->type->get_mtu(x, mtu);
2007 else
Patrick McHardy28121612007-06-18 22:30:15 -07002008 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07002009 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 return res;
2011}
2012
Herbert Xu72cb6962005-06-20 13:18:08 -07002013int xfrm_init_state(struct xfrm_state *x)
2014{
Herbert Xud094cd82005-06-20 13:19:41 -07002015 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002016 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07002017 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07002018 int err;
2019
Herbert Xud094cd82005-06-20 13:19:41 -07002020 err = -EAFNOSUPPORT;
2021 afinfo = xfrm_state_get_afinfo(family);
2022 if (!afinfo)
2023 goto error;
2024
2025 err = 0;
2026 if (afinfo->init_flags)
2027 err = afinfo->init_flags(x);
2028
2029 xfrm_state_put_afinfo(afinfo);
2030
2031 if (err)
2032 goto error;
2033
2034 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002035
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002036 if (x->sel.family != AF_UNSPEC) {
2037 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2038 if (inner_mode == NULL)
2039 goto error;
2040
2041 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2042 family != x->sel.family) {
2043 xfrm_put_mode(inner_mode);
2044 goto error;
2045 }
2046
2047 x->inner_mode = inner_mode;
2048 } else {
2049 struct xfrm_mode *inner_mode_iaf;
2050
2051 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2052 if (inner_mode == NULL)
2053 goto error;
2054
2055 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2056 xfrm_put_mode(inner_mode);
2057 goto error;
2058 }
2059
2060 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2061 if (inner_mode_iaf == NULL)
2062 goto error;
2063
2064 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2065 xfrm_put_mode(inner_mode_iaf);
2066 goto error;
2067 }
2068
2069 if (x->props.family == AF_INET) {
2070 x->inner_mode = inner_mode;
2071 x->inner_mode_iaf = inner_mode_iaf;
2072 } else {
2073 x->inner_mode = inner_mode_iaf;
2074 x->inner_mode_iaf = inner_mode;
2075 }
2076 }
Herbert Xu13996372007-10-17 21:35:51 -07002077
Herbert Xud094cd82005-06-20 13:19:41 -07002078 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002079 if (x->type == NULL)
2080 goto error;
2081
2082 err = x->type->init_state(x);
2083 if (err)
2084 goto error;
2085
Herbert Xu13996372007-10-17 21:35:51 -07002086 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2087 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002088 goto error;
2089
Herbert Xu72cb6962005-06-20 13:18:08 -07002090 x->km.state = XFRM_STATE_VALID;
2091
2092error:
2093 return err;
2094}
2095
2096EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002097
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098void __init xfrm_state_init(void)
2099{
David S. Millerf034b5d2006-08-24 03:08:07 -07002100 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101
David S. Millerf034b5d2006-08-24 03:08:07 -07002102 sz = sizeof(struct hlist_head) * 8;
2103
David S. Miller44e36b42006-08-24 04:50:50 -07002104 xfrm_state_bydst = xfrm_hash_alloc(sz);
2105 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2106 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002107 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2108 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2109 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2110
David Howellsc4028952006-11-22 14:57:56 +00002111 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112}
2113
Joy Lattenab5f5e82007-09-17 11:51:22 -07002114#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002115static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2116 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002117{
Paul Moore68277ac2007-12-20 20:49:33 -08002118 struct xfrm_sec_ctx *ctx = x->security;
2119 u32 spi = ntohl(x->id.spi);
2120
2121 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002122 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002123 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002124
2125 switch(x->props.family) {
2126 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002127 audit_log_format(audit_buf,
2128 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002129 NIPQUAD(x->props.saddr.a4),
2130 NIPQUAD(x->id.daddr.a4));
2131 break;
2132 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002133 audit_log_format(audit_buf,
2134 " src=" NIP6_FMT " dst=" NIP6_FMT,
2135 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2136 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002137 break;
2138 }
Paul Moore68277ac2007-12-20 20:49:33 -08002139
2140 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002141}
2142
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002143static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2144 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002145{
2146 struct iphdr *iph4;
2147 struct ipv6hdr *iph6;
2148
2149 switch (family) {
2150 case AF_INET:
2151 iph4 = ip_hdr(skb);
2152 audit_log_format(audit_buf,
2153 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2154 NIPQUAD(iph4->saddr),
2155 NIPQUAD(iph4->daddr));
2156 break;
2157 case AF_INET6:
2158 iph6 = ipv6_hdr(skb);
2159 audit_log_format(audit_buf,
2160 " src=" NIP6_FMT " dst=" NIP6_FMT
YOSHIFUJI Hideaki5e2c4332008-04-26 22:24:10 -07002161 " flowlbl=0x%x%02x%02x",
Paul Mooreafeb14b2007-12-21 14:58:11 -08002162 NIP6(iph6->saddr),
2163 NIP6(iph6->daddr),
2164 iph6->flow_lbl[0] & 0x0f,
2165 iph6->flow_lbl[1],
2166 iph6->flow_lbl[2]);
2167 break;
2168 }
2169}
2170
Paul Moore68277ac2007-12-20 20:49:33 -08002171void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002172 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002173{
2174 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002175
Paul Mooreafeb14b2007-12-21 14:58:11 -08002176 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002177 if (audit_buf == NULL)
2178 return;
Eric Paris25323862008-04-18 10:09:25 -04002179 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002180 xfrm_audit_helper_sainfo(x, audit_buf);
2181 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002182 audit_log_end(audit_buf);
2183}
2184EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2185
Paul Moore68277ac2007-12-20 20:49:33 -08002186void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002187 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002188{
2189 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002190
Paul Mooreafeb14b2007-12-21 14:58:11 -08002191 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002192 if (audit_buf == NULL)
2193 return;
Eric Paris25323862008-04-18 10:09:25 -04002194 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002195 xfrm_audit_helper_sainfo(x, audit_buf);
2196 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002197 audit_log_end(audit_buf);
2198}
2199EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002200
2201void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2202 struct sk_buff *skb)
2203{
2204 struct audit_buffer *audit_buf;
2205 u32 spi;
2206
2207 audit_buf = xfrm_audit_start("SA-replay-overflow");
2208 if (audit_buf == NULL)
2209 return;
2210 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2211 /* don't record the sequence number because it's inherent in this kind
2212 * of audit message */
2213 spi = ntohl(x->id.spi);
2214 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2215 audit_log_end(audit_buf);
2216}
2217EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2218
2219static void xfrm_audit_state_replay(struct xfrm_state *x,
2220 struct sk_buff *skb, __be32 net_seq)
2221{
2222 struct audit_buffer *audit_buf;
2223 u32 spi;
2224
2225 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2226 if (audit_buf == NULL)
2227 return;
2228 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2229 spi = ntohl(x->id.spi);
2230 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2231 spi, spi, ntohl(net_seq));
2232 audit_log_end(audit_buf);
2233}
2234
2235void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2236{
2237 struct audit_buffer *audit_buf;
2238
2239 audit_buf = xfrm_audit_start("SA-notfound");
2240 if (audit_buf == NULL)
2241 return;
2242 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2243 audit_log_end(audit_buf);
2244}
2245EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2246
2247void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2248 __be32 net_spi, __be32 net_seq)
2249{
2250 struct audit_buffer *audit_buf;
2251 u32 spi;
2252
2253 audit_buf = xfrm_audit_start("SA-notfound");
2254 if (audit_buf == NULL)
2255 return;
2256 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2257 spi = ntohl(net_spi);
2258 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2259 spi, spi, ntohl(net_seq));
2260 audit_log_end(audit_buf);
2261}
2262EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2263
2264void xfrm_audit_state_icvfail(struct xfrm_state *x,
2265 struct sk_buff *skb, u8 proto)
2266{
2267 struct audit_buffer *audit_buf;
2268 __be32 net_spi;
2269 __be32 net_seq;
2270
2271 audit_buf = xfrm_audit_start("SA-icv-failure");
2272 if (audit_buf == NULL)
2273 return;
2274 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2275 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2276 u32 spi = ntohl(net_spi);
2277 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2278 spi, spi, ntohl(net_seq));
2279 }
2280 audit_log_end(audit_buf);
2281}
2282EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002283#endif /* CONFIG_AUDITSYSCALL */