blob: aaafcee02fc596b39d8556e0ed793225fe57b733 [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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 xfrm_state_gc_destroy(x);
Herbert Xuabb81c42008-09-09 19:58:29 -0700427 }
David S. Miller8f126e32006-08-24 02:45:07 -0700428
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 wake_up(&km_waitq);
430}
431
432static inline unsigned long make_jiffies(long secs)
433{
434 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
435 return MAX_SCHEDULE_TIMEOUT-1;
436 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900437 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438}
439
440static void xfrm_timer_handler(unsigned long data)
441{
442 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800443 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 long next = LONG_MAX;
445 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600446 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
448 spin_lock(&x->lock);
449 if (x->km.state == XFRM_STATE_DEAD)
450 goto out;
451 if (x->km.state == XFRM_STATE_EXPIRED)
452 goto expired;
453 if (x->lft.hard_add_expires_seconds) {
454 long tmo = x->lft.hard_add_expires_seconds +
455 x->curlft.add_time - now;
456 if (tmo <= 0)
457 goto expired;
458 if (tmo < next)
459 next = tmo;
460 }
461 if (x->lft.hard_use_expires_seconds) {
462 long tmo = x->lft.hard_use_expires_seconds +
463 (x->curlft.use_time ? : now) - now;
464 if (tmo <= 0)
465 goto expired;
466 if (tmo < next)
467 next = tmo;
468 }
469 if (x->km.dying)
470 goto resched;
471 if (x->lft.soft_add_expires_seconds) {
472 long tmo = x->lft.soft_add_expires_seconds +
473 x->curlft.add_time - now;
474 if (tmo <= 0)
475 warn = 1;
476 else if (tmo < next)
477 next = tmo;
478 }
479 if (x->lft.soft_use_expires_seconds) {
480 long tmo = x->lft.soft_use_expires_seconds +
481 (x->curlft.use_time ? : now) - now;
482 if (tmo <= 0)
483 warn = 1;
484 else if (tmo < next)
485 next = tmo;
486 }
487
Herbert Xu4666faa2005-06-18 22:43:22 -0700488 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 if (warn)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800490 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700492 if (next != LONG_MAX)
493 mod_timer(&x->timer, jiffies + make_jiffies(next));
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 goto out;
496
497expired:
498 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
499 x->km.state = XFRM_STATE_EXPIRED;
500 wake_up(&km_waitq);
501 next = 2;
502 goto resched;
503 }
Joy Latten161a09e2006-11-27 13:11:54 -0600504
505 err = __xfrm_state_delete(x);
506 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800507 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Joy Lattenab5f5e82007-09-17 11:51:22 -0700509 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400510 audit_get_loginuid(current),
511 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513out:
514 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515}
516
David S. Miller0ac84752006-03-20 19:18:23 -0800517static void xfrm_replay_timer_handler(unsigned long data);
518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519struct xfrm_state *xfrm_state_alloc(void)
520{
521 struct xfrm_state *x;
522
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700523 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
525 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 atomic_set(&x->refcnt, 1);
527 atomic_set(&x->tunnel_users, 0);
Timo Teras4c563f72008-02-28 21:31:08 -0800528 INIT_LIST_HEAD(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700529 INIT_HLIST_NODE(&x->bydst);
530 INIT_HLIST_NODE(&x->bysrc);
531 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800532 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
533 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
534 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800535 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 x->lft.soft_byte_limit = XFRM_INF;
537 x->lft.soft_packet_limit = XFRM_INF;
538 x->lft.hard_byte_limit = XFRM_INF;
539 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800540 x->replay_maxage = 0;
541 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700542 x->inner_mode = NULL;
543 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 spin_lock_init(&x->lock);
545 }
546 return x;
547}
548EXPORT_SYMBOL(xfrm_state_alloc);
549
550void __xfrm_state_destroy(struct xfrm_state *x)
551{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700552 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
554 spin_lock_bh(&xfrm_state_gc_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -0700555 list_add_tail(&x->gclist, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 spin_unlock_bh(&xfrm_state_gc_lock);
557 schedule_work(&xfrm_state_gc_work);
558}
559EXPORT_SYMBOL(__xfrm_state_destroy);
560
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800561int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700563 int err = -ESRCH;
564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 if (x->km.state != XFRM_STATE_DEAD) {
566 x->km.state = XFRM_STATE_DEAD;
567 spin_lock(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -0700568 x->lastused = xfrm_state_walk_ongoing;
569 list_del_rcu(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700570 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700571 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700572 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700573 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700574 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 /* All xfrm_state objects are created by xfrm_state_alloc.
578 * The xfrm_state_alloc call gives a reference, and that
579 * is what we are dropping here.
580 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800581 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700582 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700584
585 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800587EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700589int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700591 int err;
592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700594 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700596
597 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599EXPORT_SYMBOL(xfrm_state_delete);
600
Joy Latten4aa2e622007-06-04 19:05:57 -0400601#ifdef CONFIG_SECURITY_NETWORK_XFRM
602static inline int
603xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
Joy Latten4aa2e622007-06-04 19:05:57 -0400605 int i, err = 0;
606
607 for (i = 0; i <= xfrm_state_hmask; i++) {
608 struct hlist_node *entry;
609 struct xfrm_state *x;
610
611 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
612 if (xfrm_id_proto_match(x->id.proto, proto) &&
613 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700614 xfrm_audit_state_delete(x, 0,
615 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400616 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700617 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400618 return err;
619 }
620 }
621 }
622
623 return err;
624}
625#else
626static inline int
627xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
628{
629 return 0;
630}
631#endif
632
633int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
634{
635 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400638 err = xfrm_state_flush_secctx_check(proto, audit_info);
639 if (err)
640 goto out;
641
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700642 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700643 struct hlist_node *entry;
644 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700646 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700648 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 xfrm_state_hold(x);
650 spin_unlock_bh(&xfrm_state_lock);
651
Joy Latten161a09e2006-11-27 13:11:54 -0600652 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700653 xfrm_audit_state_delete(x, err ? 0 : 1,
654 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400655 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700656 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 xfrm_state_put(x);
658
659 spin_lock_bh(&xfrm_state_lock);
660 goto restart;
661 }
662 }
663 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400664 err = 0;
665
666out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 spin_unlock_bh(&xfrm_state_lock);
668 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400669 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670}
671EXPORT_SYMBOL(xfrm_state_flush);
672
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700673void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700674{
675 spin_lock_bh(&xfrm_state_lock);
676 si->sadcnt = xfrm_state_num;
677 si->sadhcnt = xfrm_state_hmask;
678 si->sadhmcnt = xfrm_state_hashmax;
679 spin_unlock_bh(&xfrm_state_lock);
680}
681EXPORT_SYMBOL(xfrm_sad_getinfo);
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683static int
684xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
685 struct xfrm_tmpl *tmpl,
686 xfrm_address_t *daddr, xfrm_address_t *saddr,
687 unsigned short family)
688{
689 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
690 if (!afinfo)
691 return -1;
692 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
693 xfrm_state_put_afinfo(afinfo);
694 return 0;
695}
696
Al Viroa94cfd12006-09-27 18:47:24 -0700697static 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 -0700698{
699 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
700 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700701 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700702
David S. Miller8f126e32006-08-24 02:45:07 -0700703 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700704 if (x->props.family != family ||
705 x->id.spi != spi ||
706 x->id.proto != proto)
707 continue;
708
709 switch (family) {
710 case AF_INET:
711 if (x->id.daddr.a4 != daddr->a4)
712 continue;
713 break;
714 case AF_INET6:
715 if (!ipv6_addr_equal((struct in6_addr *)daddr,
716 (struct in6_addr *)
717 x->id.daddr.a6))
718 continue;
719 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700720 }
David S. Milleredcd5822006-08-24 00:42:45 -0700721
722 xfrm_state_hold(x);
723 return x;
724 }
725
726 return NULL;
727}
728
729static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
730{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700731 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700732 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700733 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700734
David S. Miller8f126e32006-08-24 02:45:07 -0700735 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700736 if (x->props.family != family ||
737 x->id.proto != proto)
738 continue;
739
740 switch (family) {
741 case AF_INET:
742 if (x->id.daddr.a4 != daddr->a4 ||
743 x->props.saddr.a4 != saddr->a4)
744 continue;
745 break;
746 case AF_INET6:
747 if (!ipv6_addr_equal((struct in6_addr *)daddr,
748 (struct in6_addr *)
749 x->id.daddr.a6) ||
750 !ipv6_addr_equal((struct in6_addr *)saddr,
751 (struct in6_addr *)
752 x->props.saddr.a6))
753 continue;
754 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700755 }
David S. Milleredcd5822006-08-24 00:42:45 -0700756
757 xfrm_state_hold(x);
758 return x;
759 }
760
761 return NULL;
762}
763
764static inline struct xfrm_state *
765__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
766{
767 if (use_spi)
768 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
769 x->id.proto, family);
770 else
771 return __xfrm_state_lookup_byaddr(&x->id.daddr,
772 &x->props.saddr,
773 x->id.proto, family);
774}
775
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700776static void xfrm_hash_grow_check(int have_hash_collision)
777{
778 if (have_hash_collision &&
779 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
780 xfrm_state_num > xfrm_state_hmask)
781 schedule_work(&xfrm_hash_work);
782}
783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900785xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 struct flowi *fl, struct xfrm_tmpl *tmpl,
787 struct xfrm_policy *pol, int *err,
788 unsigned short family)
789{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800790 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700791 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700792 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 int acquire_in_progress = 0;
794 int error = 0;
795 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900796
David S. Miller37b08e32008-09-02 20:14:15 -0700797 to_put = NULL;
798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800800 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700801 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 if (x->props.family == family &&
803 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700804 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 xfrm_state_addr_check(x, daddr, saddr, family) &&
806 tmpl->mode == x->props.mode &&
807 tmpl->id.proto == x->id.proto &&
808 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
809 /* Resolution logic:
810 1. There is a valid state with matching selector.
811 Done.
812 2. Valid state with inappropriate selector. Skip.
813
814 Entering area of "sysdeps".
815
816 3. If state is not valid, selector is temporary,
817 it selects only session which triggered
818 previous resolution. Key manager will do
819 something to install a state with proper
820 selector.
821 */
822 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700823 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700824 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 continue;
826 if (!best ||
827 best->km.dying > x->km.dying ||
828 (best->km.dying == x->km.dying &&
829 best->curlft.add_time < x->curlft.add_time))
830 best = x;
831 } else if (x->km.state == XFRM_STATE_ACQ) {
832 acquire_in_progress = 1;
833 } else if (x->km.state == XFRM_STATE_ERROR ||
834 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700835 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700836 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 error = -ESRCH;
838 }
839 }
840 }
841
842 x = best;
843 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700844 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700845 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
846 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700847 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 error = -EEXIST;
849 goto out;
850 }
851 x = xfrm_state_alloc();
852 if (x == NULL) {
853 error = -ENOMEM;
854 goto out;
855 }
856 /* Initialize temporary selector matching only
857 * to current session. */
858 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
859
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700860 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
861 if (error) {
862 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700863 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700864 x = NULL;
865 goto out;
866 }
867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (km_query(x, tmpl, pol) == 0) {
869 x->km.state = XFRM_STATE_ACQ;
Herbert Xu225f4002008-09-09 05:23:37 -0700870 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -0700871 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700872 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700873 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 if (x->id.spi) {
875 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700876 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 }
David S. Miller01e67d02007-05-25 00:41:38 -0700878 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
879 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700881 xfrm_state_num++;
882 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 } else {
884 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700885 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 x = NULL;
887 error = -ESRCH;
888 }
889 }
890out:
891 if (x)
892 xfrm_state_hold(x);
893 else
894 *err = acquire_in_progress ? -EAGAIN : error;
895 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700896 if (to_put)
897 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 return x;
899}
900
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700901struct xfrm_state *
902xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
903 unsigned short family, u8 mode, u8 proto, u32 reqid)
904{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800905 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700906 struct xfrm_state *rx = NULL, *x = NULL;
907 struct hlist_node *entry;
908
909 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800910 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700911 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
912 if (x->props.family == family &&
913 x->props.reqid == reqid &&
914 !(x->props.flags & XFRM_STATE_WILDRECV) &&
915 xfrm_state_addr_check(x, daddr, saddr, family) &&
916 mode == x->props.mode &&
917 proto == x->id.proto &&
918 x->km.state == XFRM_STATE_VALID) {
919 rx = x;
920 break;
921 }
922 }
923
924 if (rx)
925 xfrm_state_hold(rx);
926 spin_unlock(&xfrm_state_lock);
927
928
929 return rx;
930}
931EXPORT_SYMBOL(xfrm_stateonly_find);
932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933static void __xfrm_state_insert(struct xfrm_state *x)
934{
David S. Millera624c102006-08-24 03:24:33 -0700935 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
David S. Miller9d4a7062006-08-24 03:18:09 -0700937 x->genid = ++xfrm_state_genid;
938
Timo Teras4c563f72008-02-28 21:31:08 -0800939 list_add_tail(&x->all, &xfrm_state_all);
940
David S. Millerc1969f22006-08-24 04:00:03 -0700941 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
942 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700943 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700945 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700946 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700948 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700949 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
950 x->props.family);
951
David S. Miller8f126e32006-08-24 02:45:07 -0700952 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700953 }
954
David S. Millera47f0ce2006-08-24 03:54:22 -0700955 mod_timer(&x->timer, jiffies + HZ);
956 if (x->replay_maxage)
957 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800958
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700960
961 xfrm_state_num++;
962
David S. Miller918049f2006-10-12 22:03:24 -0700963 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964}
965
David S. Millerc7f5ea32006-08-24 03:29:04 -0700966/* xfrm_state_lock is held */
967static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
968{
969 unsigned short family = xnew->props.family;
970 u32 reqid = xnew->props.reqid;
971 struct xfrm_state *x;
972 struct hlist_node *entry;
973 unsigned int h;
974
David S. Millerc1969f22006-08-24 04:00:03 -0700975 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700976 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
977 if (x->props.family == family &&
978 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700979 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
980 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700981 x->genid = xfrm_state_genid;
982 }
983}
984
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985void xfrm_state_insert(struct xfrm_state *x)
986{
987 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700988 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 __xfrm_state_insert(x);
990 spin_unlock_bh(&xfrm_state_lock);
991}
992EXPORT_SYMBOL(xfrm_state_insert);
993
David S. Miller27708342006-08-24 00:13:10 -0700994/* xfrm_state_lock is held */
995static 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)
996{
David S. Millerc1969f22006-08-24 04:00:03 -0700997 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700998 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700999 struct xfrm_state *x;
1000
David S. Miller8f126e32006-08-24 02:45:07 -07001001 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -07001002 if (x->props.reqid != reqid ||
1003 x->props.mode != mode ||
1004 x->props.family != family ||
1005 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -07001006 x->id.spi != 0 ||
1007 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -07001008 continue;
1009
1010 switch (family) {
1011 case AF_INET:
1012 if (x->id.daddr.a4 != daddr->a4 ||
1013 x->props.saddr.a4 != saddr->a4)
1014 continue;
1015 break;
1016 case AF_INET6:
1017 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
1018 (struct in6_addr *)daddr) ||
1019 !ipv6_addr_equal((struct in6_addr *)
1020 x->props.saddr.a6,
1021 (struct in6_addr *)saddr))
1022 continue;
1023 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001024 }
David S. Miller27708342006-08-24 00:13:10 -07001025
1026 xfrm_state_hold(x);
1027 return x;
1028 }
1029
1030 if (!create)
1031 return NULL;
1032
1033 x = xfrm_state_alloc();
1034 if (likely(x)) {
1035 switch (family) {
1036 case AF_INET:
1037 x->sel.daddr.a4 = daddr->a4;
1038 x->sel.saddr.a4 = saddr->a4;
1039 x->sel.prefixlen_d = 32;
1040 x->sel.prefixlen_s = 32;
1041 x->props.saddr.a4 = saddr->a4;
1042 x->id.daddr.a4 = daddr->a4;
1043 break;
1044
1045 case AF_INET6:
1046 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1047 (struct in6_addr *)daddr);
1048 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1049 (struct in6_addr *)saddr);
1050 x->sel.prefixlen_d = 128;
1051 x->sel.prefixlen_s = 128;
1052 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1053 (struct in6_addr *)saddr);
1054 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1055 (struct in6_addr *)daddr);
1056 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001057 }
David S. Miller27708342006-08-24 00:13:10 -07001058
1059 x->km.state = XFRM_STATE_ACQ;
1060 x->id.proto = proto;
1061 x->props.family = family;
1062 x->props.mode = mode;
1063 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001064 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001065 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001066 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001067 add_timer(&x->timer);
Herbert Xu225f4002008-09-09 05:23:37 -07001068 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -07001069 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001070 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001071 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001072
1073 xfrm_state_num++;
1074
1075 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001076 }
1077
1078 return x;
1079}
1080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1082
1083int xfrm_state_add(struct xfrm_state *x)
1084{
David S. Miller37b08e32008-09-02 20:14:15 -07001085 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 int family;
1087 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001088 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
David S. Miller37b08e32008-09-02 20:14:15 -07001092 to_put = NULL;
1093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 spin_lock_bh(&xfrm_state_lock);
1095
David S. Milleredcd5822006-08-24 00:42:45 -07001096 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001098 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 x1 = NULL;
1100 err = -EEXIST;
1101 goto out;
1102 }
1103
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001104 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001106 if (x1 && ((x1->id.proto != x->id.proto) ||
1107 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001108 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 x1 = NULL;
1110 }
1111 }
1112
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001113 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001114 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1115 x->id.proto,
1116 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
David S. Millerc7f5ea32006-08-24 03:29:04 -07001118 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 __xfrm_state_insert(x);
1120 err = 0;
1121
1122out:
1123 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
1125 if (x1) {
1126 xfrm_state_delete(x1);
1127 xfrm_state_put(x1);
1128 }
1129
David S. Miller37b08e32008-09-02 20:14:15 -07001130 if (to_put)
1131 xfrm_state_put(to_put);
1132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 return err;
1134}
1135EXPORT_SYMBOL(xfrm_state_add);
1136
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001137#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001138static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001139{
1140 int err = -ENOMEM;
1141 struct xfrm_state *x = xfrm_state_alloc();
1142 if (!x)
1143 goto error;
1144
1145 memcpy(&x->id, &orig->id, sizeof(x->id));
1146 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1147 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1148 x->props.mode = orig->props.mode;
1149 x->props.replay_window = orig->props.replay_window;
1150 x->props.reqid = orig->props.reqid;
1151 x->props.family = orig->props.family;
1152 x->props.saddr = orig->props.saddr;
1153
1154 if (orig->aalg) {
1155 x->aalg = xfrm_algo_clone(orig->aalg);
1156 if (!x->aalg)
1157 goto error;
1158 }
1159 x->props.aalgo = orig->props.aalgo;
1160
1161 if (orig->ealg) {
1162 x->ealg = xfrm_algo_clone(orig->ealg);
1163 if (!x->ealg)
1164 goto error;
1165 }
1166 x->props.ealgo = orig->props.ealgo;
1167
1168 if (orig->calg) {
1169 x->calg = xfrm_algo_clone(orig->calg);
1170 if (!x->calg)
1171 goto error;
1172 }
1173 x->props.calgo = orig->props.calgo;
1174
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001175 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001176 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1177 if (!x->encap)
1178 goto error;
1179 }
1180
1181 if (orig->coaddr) {
1182 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1183 GFP_KERNEL);
1184 if (!x->coaddr)
1185 goto error;
1186 }
1187
1188 err = xfrm_init_state(x);
1189 if (err)
1190 goto error;
1191
1192 x->props.flags = orig->props.flags;
1193
1194 x->curlft.add_time = orig->curlft.add_time;
1195 x->km.state = orig->km.state;
1196 x->km.seq = orig->km.seq;
1197
1198 return x;
1199
1200 error:
1201 if (errp)
1202 *errp = err;
1203 if (x) {
1204 kfree(x->aalg);
1205 kfree(x->ealg);
1206 kfree(x->calg);
1207 kfree(x->encap);
1208 kfree(x->coaddr);
1209 }
1210 kfree(x);
1211 return NULL;
1212}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001213
1214/* xfrm_state_lock is held */
1215struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1216{
1217 unsigned int h;
1218 struct xfrm_state *x;
1219 struct hlist_node *entry;
1220
1221 if (m->reqid) {
1222 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1223 m->reqid, m->old_family);
1224 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1225 if (x->props.mode != m->mode ||
1226 x->id.proto != m->proto)
1227 continue;
1228 if (m->reqid && x->props.reqid != m->reqid)
1229 continue;
1230 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1231 m->old_family) ||
1232 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1233 m->old_family))
1234 continue;
1235 xfrm_state_hold(x);
1236 return x;
1237 }
1238 } else {
1239 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1240 m->old_family);
1241 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1242 if (x->props.mode != m->mode ||
1243 x->id.proto != m->proto)
1244 continue;
1245 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1246 m->old_family) ||
1247 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1248 m->old_family))
1249 continue;
1250 xfrm_state_hold(x);
1251 return x;
1252 }
1253 }
1254
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001255 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001256}
1257EXPORT_SYMBOL(xfrm_migrate_state_find);
1258
1259struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1260 struct xfrm_migrate *m)
1261{
1262 struct xfrm_state *xc;
1263 int err;
1264
1265 xc = xfrm_state_clone(x, &err);
1266 if (!xc)
1267 return NULL;
1268
1269 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1270 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1271
1272 /* add state */
1273 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1274 /* a care is needed when the destination address of the
1275 state is to be updated as it is a part of triplet */
1276 xfrm_state_insert(xc);
1277 } else {
1278 if ((err = xfrm_state_add(xc)) < 0)
1279 goto error;
1280 }
1281
1282 return xc;
1283error:
1284 kfree(xc);
1285 return NULL;
1286}
1287EXPORT_SYMBOL(xfrm_state_migrate);
1288#endif
1289
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290int xfrm_state_update(struct xfrm_state *x)
1291{
David S. Miller37b08e32008-09-02 20:14:15 -07001292 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001294 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
David S. Miller37b08e32008-09-02 20:14:15 -07001296 to_put = NULL;
1297
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001299 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
1301 err = -ESRCH;
1302 if (!x1)
1303 goto out;
1304
1305 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001306 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 err = -EEXIST;
1308 goto out;
1309 }
1310
1311 if (x1->km.state == XFRM_STATE_ACQ) {
1312 __xfrm_state_insert(x);
1313 x = NULL;
1314 }
1315 err = 0;
1316
1317out:
1318 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
David S. Miller37b08e32008-09-02 20:14:15 -07001320 if (to_put)
1321 xfrm_state_put(to_put);
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 if (err)
1324 return err;
1325
1326 if (!x) {
1327 xfrm_state_delete(x1);
1328 xfrm_state_put(x1);
1329 return 0;
1330 }
1331
1332 err = -EINVAL;
1333 spin_lock_bh(&x1->lock);
1334 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1335 if (x->encap && x1->encap)
1336 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001337 if (x->coaddr && x1->coaddr) {
1338 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1339 }
1340 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1341 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1343 x1->km.dying = 0;
1344
David S. Millera47f0ce2006-08-24 03:54:22 -07001345 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 if (x1->curlft.use_time)
1347 xfrm_state_check_expire(x1);
1348
1349 err = 0;
1350 }
1351 spin_unlock_bh(&x1->lock);
1352
1353 xfrm_state_put(x1);
1354
1355 return err;
1356}
1357EXPORT_SYMBOL(xfrm_state_update);
1358
1359int xfrm_state_check_expire(struct xfrm_state *x)
1360{
1361 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001362 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 if (x->km.state != XFRM_STATE_VALID)
1365 return -EINVAL;
1366
1367 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1368 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001369 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001370 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return -EINVAL;
1372 }
1373
1374 if (!x->km.dying &&
1375 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001376 x->curlft.packets >= x->lft.soft_packet_limit)) {
1377 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001378 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return 0;
1381}
1382EXPORT_SYMBOL(xfrm_state_check_expire);
1383
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001385xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 unsigned short family)
1387{
1388 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
1390 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001391 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return x;
1394}
1395EXPORT_SYMBOL(xfrm_state_lookup);
1396
1397struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001398xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1399 u8 proto, unsigned short family)
1400{
1401 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001402
1403 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001404 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001405 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001406 return x;
1407}
1408EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1409
1410struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001411xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1412 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 int create, unsigned short family)
1414{
1415 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001418 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001420
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 return x;
1422}
1423EXPORT_SYMBOL(xfrm_find_acq);
1424
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001425#ifdef CONFIG_XFRM_SUB_POLICY
1426int
1427xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1428 unsigned short family)
1429{
1430 int err = 0;
1431 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1432 if (!afinfo)
1433 return -EAFNOSUPPORT;
1434
1435 spin_lock_bh(&xfrm_state_lock);
1436 if (afinfo->tmpl_sort)
1437 err = afinfo->tmpl_sort(dst, src, n);
1438 spin_unlock_bh(&xfrm_state_lock);
1439 xfrm_state_put_afinfo(afinfo);
1440 return err;
1441}
1442EXPORT_SYMBOL(xfrm_tmpl_sort);
1443
1444int
1445xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1446 unsigned short family)
1447{
1448 int err = 0;
1449 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1450 if (!afinfo)
1451 return -EAFNOSUPPORT;
1452
1453 spin_lock_bh(&xfrm_state_lock);
1454 if (afinfo->state_sort)
1455 err = afinfo->state_sort(dst, src, n);
1456 spin_unlock_bh(&xfrm_state_lock);
1457 xfrm_state_put_afinfo(afinfo);
1458 return err;
1459}
1460EXPORT_SYMBOL(xfrm_state_sort);
1461#endif
1462
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463/* Silly enough, but I'm lazy to build resolution list */
1464
1465static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1466{
1467 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468
David S. Millerf034b5d2006-08-24 03:08:07 -07001469 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001470 struct hlist_node *entry;
1471 struct xfrm_state *x;
1472
1473 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1474 if (x->km.seq == seq &&
1475 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 xfrm_state_hold(x);
1477 return x;
1478 }
1479 }
1480 }
1481 return NULL;
1482}
1483
1484struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1485{
1486 struct xfrm_state *x;
1487
1488 spin_lock_bh(&xfrm_state_lock);
1489 x = __xfrm_find_acq_byseq(seq);
1490 spin_unlock_bh(&xfrm_state_lock);
1491 return x;
1492}
1493EXPORT_SYMBOL(xfrm_find_acq_byseq);
1494
1495u32 xfrm_get_acqseq(void)
1496{
1497 u32 res;
1498 static u32 acqseq;
1499 static DEFINE_SPINLOCK(acqseq_lock);
1500
1501 spin_lock_bh(&acqseq_lock);
1502 res = (++acqseq ? : ++acqseq);
1503 spin_unlock_bh(&acqseq_lock);
1504 return res;
1505}
1506EXPORT_SYMBOL(xfrm_get_acqseq);
1507
Herbert Xu658b2192007-10-09 13:29:52 -07001508int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509{
David S. Millerf034b5d2006-08-24 03:08:07 -07001510 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001512 int err = -ENOENT;
1513 __be32 minspi = htonl(low);
1514 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
Herbert Xu658b2192007-10-09 13:29:52 -07001516 spin_lock_bh(&x->lock);
1517 if (x->km.state == XFRM_STATE_DEAD)
1518 goto unlock;
1519
1520 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001522 goto unlock;
1523
1524 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
1526 if (minspi == maxspi) {
1527 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1528 if (x0) {
1529 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001530 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 }
1532 x->id.spi = minspi;
1533 } else {
1534 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001535 for (h=0; h<high-low+1; h++) {
1536 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1538 if (x0 == NULL) {
1539 x->id.spi = htonl(spi);
1540 break;
1541 }
1542 xfrm_state_put(x0);
1543 }
1544 }
1545 if (x->id.spi) {
1546 spin_lock_bh(&xfrm_state_lock);
1547 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001548 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001550
1551 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 }
Herbert Xu658b2192007-10-09 13:29:52 -07001553
1554unlock:
1555 spin_unlock_bh(&x->lock);
1556
1557 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558}
1559EXPORT_SYMBOL(xfrm_alloc_spi);
1560
Timo Teras4c563f72008-02-28 21:31:08 -08001561int xfrm_state_walk(struct xfrm_state_walk *walk,
1562 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 void *data)
1564{
Timo Teras4c563f72008-02-28 21:31:08 -08001565 struct xfrm_state *old, *x, *last = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 int err = 0;
1567
Timo Teras4c563f72008-02-28 21:31:08 -08001568 if (walk->state == NULL && walk->count != 0)
1569 return 0;
1570
1571 old = x = walk->state;
1572 walk->state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 spin_lock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001574 if (x == NULL)
1575 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1576 list_for_each_entry_from(x, &xfrm_state_all, all) {
1577 if (x->km.state == XFRM_STATE_DEAD)
1578 continue;
1579 if (!xfrm_id_proto_match(x->id.proto, walk->proto))
1580 continue;
1581 if (last) {
1582 err = func(last, walk->count, data);
1583 if (err) {
1584 xfrm_state_hold(last);
1585 walk->state = last;
Herbert Xuabb81c42008-09-09 19:58:29 -07001586 xfrm_state_walk_ongoing++;
Timo Teras4c563f72008-02-28 21:31:08 -08001587 goto out;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001588 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 }
Timo Teras4c563f72008-02-28 21:31:08 -08001590 last = x;
1591 walk->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 }
Timo Teras4c563f72008-02-28 21:31:08 -08001593 if (walk->count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 err = -ENOENT;
1595 goto out;
1596 }
Timo Teras4c563f72008-02-28 21:31:08 -08001597 if (last)
1598 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599out:
1600 spin_unlock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001601 if (old != NULL) {
Timo Teras4c563f72008-02-28 21:31:08 -08001602 xfrm_state_put(old);
Herbert Xuabb81c42008-09-09 19:58:29 -07001603 xfrm_state_walk_completed++;
1604 if (!list_empty(&xfrm_state_gc_leftovers))
1605 schedule_work(&xfrm_state_gc_work);
1606 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 return err;
1608}
1609EXPORT_SYMBOL(xfrm_state_walk);
1610
Herbert Xuabb81c42008-09-09 19:58:29 -07001611void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1612{
1613 if (walk->state != NULL) {
1614 xfrm_state_put(walk->state);
1615 walk->state = NULL;
1616 xfrm_state_walk_completed++;
1617 if (!list_empty(&xfrm_state_gc_leftovers))
1618 schedule_work(&xfrm_state_gc_work);
1619 }
1620}
1621EXPORT_SYMBOL(xfrm_state_walk_done);
1622
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001623
1624void xfrm_replay_notify(struct xfrm_state *x, int event)
1625{
1626 struct km_event c;
1627 /* we send notify messages in case
1628 * 1. we updated on of the sequence numbers, and the seqno difference
1629 * is at least x->replay_maxdiff, in this case we also update the
1630 * timeout of our timer function
1631 * 2. if x->replay_maxage has elapsed since last update,
1632 * and there were changes
1633 *
1634 * The state structure must be locked!
1635 */
1636
1637 switch (event) {
1638 case XFRM_REPLAY_UPDATE:
1639 if (x->replay_maxdiff &&
1640 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001641 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1642 if (x->xflags & XFRM_TIME_DEFER)
1643 event = XFRM_REPLAY_TIMEOUT;
1644 else
1645 return;
1646 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001647
1648 break;
1649
1650 case XFRM_REPLAY_TIMEOUT:
1651 if ((x->replay.seq == x->preplay.seq) &&
1652 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001653 (x->replay.oseq == x->preplay.oseq)) {
1654 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001655 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001656 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001657
1658 break;
1659 }
1660
1661 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1662 c.event = XFRM_MSG_NEWAE;
1663 c.data.aevent = event;
1664 km_state_notify(x, &c);
1665
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001666 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001667 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001668 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001669}
1670
1671static void xfrm_replay_timer_handler(unsigned long data)
1672{
1673 struct xfrm_state *x = (struct xfrm_state*)data;
1674
1675 spin_lock(&x->lock);
1676
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001677 if (x->km.state == XFRM_STATE_VALID) {
1678 if (xfrm_aevent_is_on())
1679 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1680 else
1681 x->xflags |= XFRM_TIME_DEFER;
1682 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001683
1684 spin_unlock(&x->lock);
1685}
1686
Paul Mooreafeb14b2007-12-21 14:58:11 -08001687int xfrm_replay_check(struct xfrm_state *x,
1688 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689{
1690 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001691 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692
1693 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001694 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 if (likely(seq > x->replay.seq))
1697 return 0;
1698
1699 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001700 if (diff >= min_t(unsigned int, x->props.replay_window,
1701 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001703 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 }
1705
1706 if (x->replay.bitmap & (1U << diff)) {
1707 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001708 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 }
1710 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001711
1712err:
1713 xfrm_audit_state_replay(x, skb, net_seq);
1714 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
Al Viro61f46272006-09-27 18:48:33 -07001717void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718{
1719 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001720 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
1722 if (seq > x->replay.seq) {
1723 diff = seq - x->replay.seq;
1724 if (diff < x->props.replay_window)
1725 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1726 else
1727 x->replay.bitmap = 1;
1728 x->replay.seq = seq;
1729 } else {
1730 diff = x->replay.seq - seq;
1731 x->replay.bitmap |= (1U << diff);
1732 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001733
1734 if (xfrm_aevent_is_on())
1735 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
Denis Chengdf018122007-12-07 00:51:11 -08001738static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739static DEFINE_RWLOCK(xfrm_km_lock);
1740
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001741void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742{
1743 struct xfrm_mgr *km;
1744
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001745 read_lock(&xfrm_km_lock);
1746 list_for_each_entry(km, &xfrm_km_list, list)
1747 if (km->notify_policy)
1748 km->notify_policy(xp, dir, c);
1749 read_unlock(&xfrm_km_lock);
1750}
1751
1752void km_state_notify(struct xfrm_state *x, struct km_event *c)
1753{
1754 struct xfrm_mgr *km;
1755 read_lock(&xfrm_km_lock);
1756 list_for_each_entry(km, &xfrm_km_list, list)
1757 if (km->notify)
1758 km->notify(x, c);
1759 read_unlock(&xfrm_km_lock);
1760}
1761
1762EXPORT_SYMBOL(km_policy_notify);
1763EXPORT_SYMBOL(km_state_notify);
1764
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001765void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001766{
1767 struct km_event c;
1768
Herbert Xubf08867f92005-06-18 22:44:00 -07001769 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001770 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001771 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001772 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773
1774 if (hard)
1775 wake_up(&km_waitq);
1776}
1777
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001778EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001779/*
1780 * We send to all registered managers regardless of failure
1781 * We are happy with one success
1782*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001783int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001785 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 struct xfrm_mgr *km;
1787
1788 read_lock(&xfrm_km_lock);
1789 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001790 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1791 if (!acqret)
1792 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 }
1794 read_unlock(&xfrm_km_lock);
1795 return err;
1796}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001797EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Al Viro5d36b182006-11-08 00:24:06 -08001799int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800{
1801 int err = -EINVAL;
1802 struct xfrm_mgr *km;
1803
1804 read_lock(&xfrm_km_lock);
1805 list_for_each_entry(km, &xfrm_km_list, list) {
1806 if (km->new_mapping)
1807 err = km->new_mapping(x, ipaddr, sport);
1808 if (!err)
1809 break;
1810 }
1811 read_unlock(&xfrm_km_lock);
1812 return err;
1813}
1814EXPORT_SYMBOL(km_new_mapping);
1815
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001816void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001818 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819
Herbert Xubf08867f92005-06-18 22:44:00 -07001820 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001821 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001822 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001823 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
1825 if (hard)
1826 wake_up(&km_waitq);
1827}
David S. Millera70fcb02006-03-20 19:18:52 -08001828EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001830#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001831int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1832 struct xfrm_migrate *m, int num_migrate)
1833{
1834 int err = -EINVAL;
1835 int ret;
1836 struct xfrm_mgr *km;
1837
1838 read_lock(&xfrm_km_lock);
1839 list_for_each_entry(km, &xfrm_km_list, list) {
1840 if (km->migrate) {
1841 ret = km->migrate(sel, dir, type, m, num_migrate);
1842 if (!ret)
1843 err = ret;
1844 }
1845 }
1846 read_unlock(&xfrm_km_lock);
1847 return err;
1848}
1849EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001850#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001851
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001852int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1853{
1854 int err = -EINVAL;
1855 int ret;
1856 struct xfrm_mgr *km;
1857
1858 read_lock(&xfrm_km_lock);
1859 list_for_each_entry(km, &xfrm_km_list, list) {
1860 if (km->report) {
1861 ret = km->report(proto, sel, addr);
1862 if (!ret)
1863 err = ret;
1864 }
1865 }
1866 read_unlock(&xfrm_km_lock);
1867 return err;
1868}
1869EXPORT_SYMBOL(km_report);
1870
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1872{
1873 int err;
1874 u8 *data;
1875 struct xfrm_mgr *km;
1876 struct xfrm_policy *pol = NULL;
1877
1878 if (optlen <= 0 || optlen > PAGE_SIZE)
1879 return -EMSGSIZE;
1880
1881 data = kmalloc(optlen, GFP_KERNEL);
1882 if (!data)
1883 return -ENOMEM;
1884
1885 err = -EFAULT;
1886 if (copy_from_user(data, optval, optlen))
1887 goto out;
1888
1889 err = -EINVAL;
1890 read_lock(&xfrm_km_lock);
1891 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001892 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 optlen, &err);
1894 if (err >= 0)
1895 break;
1896 }
1897 read_unlock(&xfrm_km_lock);
1898
1899 if (err >= 0) {
1900 xfrm_sk_policy_insert(sk, err, pol);
1901 xfrm_pol_put(pol);
1902 err = 0;
1903 }
1904
1905out:
1906 kfree(data);
1907 return err;
1908}
1909EXPORT_SYMBOL(xfrm_user_policy);
1910
1911int xfrm_register_km(struct xfrm_mgr *km)
1912{
1913 write_lock_bh(&xfrm_km_lock);
1914 list_add_tail(&km->list, &xfrm_km_list);
1915 write_unlock_bh(&xfrm_km_lock);
1916 return 0;
1917}
1918EXPORT_SYMBOL(xfrm_register_km);
1919
1920int xfrm_unregister_km(struct xfrm_mgr *km)
1921{
1922 write_lock_bh(&xfrm_km_lock);
1923 list_del(&km->list);
1924 write_unlock_bh(&xfrm_km_lock);
1925 return 0;
1926}
1927EXPORT_SYMBOL(xfrm_unregister_km);
1928
1929int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1930{
1931 int err = 0;
1932 if (unlikely(afinfo == NULL))
1933 return -EINVAL;
1934 if (unlikely(afinfo->family >= NPROTO))
1935 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001936 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1938 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001939 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001941 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 return err;
1943}
1944EXPORT_SYMBOL(xfrm_state_register_afinfo);
1945
1946int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1947{
1948 int err = 0;
1949 if (unlikely(afinfo == NULL))
1950 return -EINVAL;
1951 if (unlikely(afinfo->family >= NPROTO))
1952 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001953 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1955 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1956 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001957 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001960 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 return err;
1962}
1963EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1964
Herbert Xu17c2a422007-10-17 21:33:12 -07001965static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966{
1967 struct xfrm_state_afinfo *afinfo;
1968 if (unlikely(family >= NPROTO))
1969 return NULL;
1970 read_lock(&xfrm_state_afinfo_lock);
1971 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001972 if (unlikely(!afinfo))
1973 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 return afinfo;
1975}
1976
Herbert Xu17c2a422007-10-17 21:33:12 -07001977static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001978 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979{
Herbert Xu546be242006-05-27 23:03:58 -07001980 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981}
1982
1983/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1984void xfrm_state_delete_tunnel(struct xfrm_state *x)
1985{
1986 if (x->tunnel) {
1987 struct xfrm_state *t = x->tunnel;
1988
1989 if (atomic_read(&t->tunnel_users) == 2)
1990 xfrm_state_delete(t);
1991 atomic_dec(&t->tunnel_users);
1992 xfrm_state_put(t);
1993 x->tunnel = NULL;
1994 }
1995}
1996EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1997
1998int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1999{
Patrick McHardyc5c25232007-04-09 11:47:18 -07002000 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001
Patrick McHardyc5c25232007-04-09 11:47:18 -07002002 spin_lock_bh(&x->lock);
2003 if (x->km.state == XFRM_STATE_VALID &&
2004 x->type && x->type->get_mtu)
2005 res = x->type->get_mtu(x, mtu);
2006 else
Patrick McHardy28121612007-06-18 22:30:15 -07002007 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07002008 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 return res;
2010}
2011
Herbert Xu72cb6962005-06-20 13:18:08 -07002012int xfrm_init_state(struct xfrm_state *x)
2013{
Herbert Xud094cd82005-06-20 13:19:41 -07002014 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002015 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07002016 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07002017 int err;
2018
Herbert Xud094cd82005-06-20 13:19:41 -07002019 err = -EAFNOSUPPORT;
2020 afinfo = xfrm_state_get_afinfo(family);
2021 if (!afinfo)
2022 goto error;
2023
2024 err = 0;
2025 if (afinfo->init_flags)
2026 err = afinfo->init_flags(x);
2027
2028 xfrm_state_put_afinfo(afinfo);
2029
2030 if (err)
2031 goto error;
2032
2033 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002034
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002035 if (x->sel.family != AF_UNSPEC) {
2036 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2037 if (inner_mode == NULL)
2038 goto error;
2039
2040 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2041 family != x->sel.family) {
2042 xfrm_put_mode(inner_mode);
2043 goto error;
2044 }
2045
2046 x->inner_mode = inner_mode;
2047 } else {
2048 struct xfrm_mode *inner_mode_iaf;
2049
2050 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2051 if (inner_mode == NULL)
2052 goto error;
2053
2054 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2055 xfrm_put_mode(inner_mode);
2056 goto error;
2057 }
2058
2059 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2060 if (inner_mode_iaf == NULL)
2061 goto error;
2062
2063 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2064 xfrm_put_mode(inner_mode_iaf);
2065 goto error;
2066 }
2067
2068 if (x->props.family == AF_INET) {
2069 x->inner_mode = inner_mode;
2070 x->inner_mode_iaf = inner_mode_iaf;
2071 } else {
2072 x->inner_mode = inner_mode_iaf;
2073 x->inner_mode_iaf = inner_mode;
2074 }
2075 }
Herbert Xu13996372007-10-17 21:35:51 -07002076
Herbert Xud094cd82005-06-20 13:19:41 -07002077 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002078 if (x->type == NULL)
2079 goto error;
2080
2081 err = x->type->init_state(x);
2082 if (err)
2083 goto error;
2084
Herbert Xu13996372007-10-17 21:35:51 -07002085 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2086 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002087 goto error;
2088
Herbert Xu72cb6962005-06-20 13:18:08 -07002089 x->km.state = XFRM_STATE_VALID;
2090
2091error:
2092 return err;
2093}
2094
2095EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002096
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097void __init xfrm_state_init(void)
2098{
David S. Millerf034b5d2006-08-24 03:08:07 -07002099 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
David S. Millerf034b5d2006-08-24 03:08:07 -07002101 sz = sizeof(struct hlist_head) * 8;
2102
David S. Miller44e36b42006-08-24 04:50:50 -07002103 xfrm_state_bydst = xfrm_hash_alloc(sz);
2104 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2105 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002106 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2107 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2108 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2109
David Howellsc4028952006-11-22 14:57:56 +00002110 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111}
2112
Joy Lattenab5f5e82007-09-17 11:51:22 -07002113#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002114static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2115 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002116{
Paul Moore68277ac2007-12-20 20:49:33 -08002117 struct xfrm_sec_ctx *ctx = x->security;
2118 u32 spi = ntohl(x->id.spi);
2119
2120 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002121 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002122 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002123
2124 switch(x->props.family) {
2125 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002126 audit_log_format(audit_buf,
2127 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002128 NIPQUAD(x->props.saddr.a4),
2129 NIPQUAD(x->id.daddr.a4));
2130 break;
2131 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002132 audit_log_format(audit_buf,
2133 " src=" NIP6_FMT " dst=" NIP6_FMT,
2134 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2135 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002136 break;
2137 }
Paul Moore68277ac2007-12-20 20:49:33 -08002138
2139 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002140}
2141
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002142static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2143 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002144{
2145 struct iphdr *iph4;
2146 struct ipv6hdr *iph6;
2147
2148 switch (family) {
2149 case AF_INET:
2150 iph4 = ip_hdr(skb);
2151 audit_log_format(audit_buf,
2152 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2153 NIPQUAD(iph4->saddr),
2154 NIPQUAD(iph4->daddr));
2155 break;
2156 case AF_INET6:
2157 iph6 = ipv6_hdr(skb);
2158 audit_log_format(audit_buf,
2159 " src=" NIP6_FMT " dst=" NIP6_FMT
YOSHIFUJI Hideaki5e2c4332008-04-26 22:24:10 -07002160 " flowlbl=0x%x%02x%02x",
Paul Mooreafeb14b2007-12-21 14:58:11 -08002161 NIP6(iph6->saddr),
2162 NIP6(iph6->daddr),
2163 iph6->flow_lbl[0] & 0x0f,
2164 iph6->flow_lbl[1],
2165 iph6->flow_lbl[2]);
2166 break;
2167 }
2168}
2169
Paul Moore68277ac2007-12-20 20:49:33 -08002170void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002171 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002172{
2173 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002174
Paul Mooreafeb14b2007-12-21 14:58:11 -08002175 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002176 if (audit_buf == NULL)
2177 return;
Eric Paris25323862008-04-18 10:09:25 -04002178 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002179 xfrm_audit_helper_sainfo(x, audit_buf);
2180 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002181 audit_log_end(audit_buf);
2182}
2183EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2184
Paul Moore68277ac2007-12-20 20:49:33 -08002185void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002186 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002187{
2188 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002189
Paul Mooreafeb14b2007-12-21 14:58:11 -08002190 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002191 if (audit_buf == NULL)
2192 return;
Eric Paris25323862008-04-18 10:09:25 -04002193 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002194 xfrm_audit_helper_sainfo(x, audit_buf);
2195 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002196 audit_log_end(audit_buf);
2197}
2198EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002199
2200void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2201 struct sk_buff *skb)
2202{
2203 struct audit_buffer *audit_buf;
2204 u32 spi;
2205
2206 audit_buf = xfrm_audit_start("SA-replay-overflow");
2207 if (audit_buf == NULL)
2208 return;
2209 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2210 /* don't record the sequence number because it's inherent in this kind
2211 * of audit message */
2212 spi = ntohl(x->id.spi);
2213 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2214 audit_log_end(audit_buf);
2215}
2216EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2217
2218static void xfrm_audit_state_replay(struct xfrm_state *x,
2219 struct sk_buff *skb, __be32 net_seq)
2220{
2221 struct audit_buffer *audit_buf;
2222 u32 spi;
2223
2224 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2225 if (audit_buf == NULL)
2226 return;
2227 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2228 spi = ntohl(x->id.spi);
2229 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2230 spi, spi, ntohl(net_seq));
2231 audit_log_end(audit_buf);
2232}
2233
2234void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2235{
2236 struct audit_buffer *audit_buf;
2237
2238 audit_buf = xfrm_audit_start("SA-notfound");
2239 if (audit_buf == NULL)
2240 return;
2241 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2242 audit_log_end(audit_buf);
2243}
2244EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2245
2246void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2247 __be32 net_spi, __be32 net_seq)
2248{
2249 struct audit_buffer *audit_buf;
2250 u32 spi;
2251
2252 audit_buf = xfrm_audit_start("SA-notfound");
2253 if (audit_buf == NULL)
2254 return;
2255 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2256 spi = ntohl(net_spi);
2257 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2258 spi, spi, ntohl(net_seq));
2259 audit_log_end(audit_buf);
2260}
2261EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2262
2263void xfrm_audit_state_icvfail(struct xfrm_state *x,
2264 struct sk_buff *skb, u8 proto)
2265{
2266 struct audit_buffer *audit_buf;
2267 __be32 net_spi;
2268 __be32 net_seq;
2269
2270 audit_buf = xfrm_audit_start("SA-icv-failure");
2271 if (audit_buf == NULL)
2272 return;
2273 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2274 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2275 u32 spi = ntohl(net_spi);
2276 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2277 spi, spi, ntohl(net_seq));
2278 }
2279 audit_log_end(audit_buf);
2280}
2281EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002282#endif /* CONFIG_AUDITSYSCALL */