blob: 0a8f09c3144c835fccbc1e3de4855b654c45a3b9 [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 Xu17c2a422007-10-17 21:33:12 -070062static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
63static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
64
Paul Mooreafeb14b2007-12-21 14:58:11 -080065#ifdef CONFIG_AUDITSYSCALL
66static void xfrm_audit_state_replay(struct xfrm_state *x,
67 struct sk_buff *skb, __be32 net_seq);
68#else
69#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
70#endif /* CONFIG_AUDITSYSCALL */
71
David S. Millerc1969f22006-08-24 04:00:03 -070072static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
73 xfrm_address_t *saddr,
74 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070075 unsigned short family)
76{
David S. Millerc1969f22006-08-24 04:00:03 -070077 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070078}
79
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070080static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
81 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070082 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070083{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070084 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070085}
86
David S. Miller2575b652006-08-24 03:26:44 -070087static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070088xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070089{
David S. Millerc1969f22006-08-24 04:00:03 -070090 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070091}
92
David S. Millerf034b5d2006-08-24 03:08:07 -070093static void xfrm_hash_transfer(struct hlist_head *list,
94 struct hlist_head *ndsttable,
95 struct hlist_head *nsrctable,
96 struct hlist_head *nspitable,
97 unsigned int nhashmask)
98{
99 struct hlist_node *entry, *tmp;
100 struct xfrm_state *x;
101
102 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
103 unsigned int h;
104
David S. Millerc1969f22006-08-24 04:00:03 -0700105 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
106 x->props.reqid, x->props.family,
107 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700108 hlist_add_head(&x->bydst, ndsttable+h);
109
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700110 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
111 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700112 nhashmask);
113 hlist_add_head(&x->bysrc, nsrctable+h);
114
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700115 if (x->id.spi) {
116 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
117 x->id.proto, x->props.family,
118 nhashmask);
119 hlist_add_head(&x->byspi, nspitable+h);
120 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700121 }
122}
123
124static unsigned long xfrm_hash_new_size(void)
125{
126 return ((xfrm_state_hmask + 1) << 1) *
127 sizeof(struct hlist_head);
128}
129
130static DEFINE_MUTEX(hash_resize_mutex);
131
David Howellsc4028952006-11-22 14:57:56 +0000132static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700133{
134 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
135 unsigned long nsize, osize;
136 unsigned int nhashmask, ohashmask;
137 int i;
138
139 mutex_lock(&hash_resize_mutex);
140
141 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700142 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700143 if (!ndst)
144 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700145 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700146 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700147 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700148 goto out_unlock;
149 }
David S. Miller44e36b42006-08-24 04:50:50 -0700150 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700151 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700152 xfrm_hash_free(ndst, nsize);
153 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700154 goto out_unlock;
155 }
156
157 spin_lock_bh(&xfrm_state_lock);
158
159 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
160 for (i = xfrm_state_hmask; i >= 0; i--)
161 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
162 nhashmask);
163
164 odst = xfrm_state_bydst;
165 osrc = xfrm_state_bysrc;
166 ospi = xfrm_state_byspi;
167 ohashmask = xfrm_state_hmask;
168
169 xfrm_state_bydst = ndst;
170 xfrm_state_bysrc = nsrc;
171 xfrm_state_byspi = nspi;
172 xfrm_state_hmask = nhashmask;
173
174 spin_unlock_bh(&xfrm_state_lock);
175
176 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700177 xfrm_hash_free(odst, osize);
178 xfrm_hash_free(osrc, osize);
179 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700180
181out_unlock:
182 mutex_unlock(&hash_resize_mutex);
183}
184
David Howellsc4028952006-11-22 14:57:56 +0000185static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187DECLARE_WAIT_QUEUE_HEAD(km_waitq);
188EXPORT_SYMBOL(km_waitq);
189
190static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
191static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
192
193static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700194static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195static DEFINE_SPINLOCK(xfrm_state_gc_lock);
196
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800197int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800199int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800200void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700202static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
203{
204 struct xfrm_state_afinfo *afinfo;
205 if (unlikely(family >= NPROTO))
206 return NULL;
207 write_lock_bh(&xfrm_state_afinfo_lock);
208 afinfo = xfrm_state_afinfo[family];
209 if (unlikely(!afinfo))
210 write_unlock_bh(&xfrm_state_afinfo_lock);
211 return afinfo;
212}
213
214static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800215 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700216{
217 write_unlock_bh(&xfrm_state_afinfo_lock);
218}
219
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800220int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700221{
222 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800223 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700224 int err = 0;
225
226 if (unlikely(afinfo == NULL))
227 return -EAFNOSUPPORT;
228 typemap = afinfo->type_map;
229
230 if (likely(typemap[type->proto] == NULL))
231 typemap[type->proto] = type;
232 else
233 err = -EEXIST;
234 xfrm_state_unlock_afinfo(afinfo);
235 return err;
236}
237EXPORT_SYMBOL(xfrm_register_type);
238
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800239int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700240{
241 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800242 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700243 int err = 0;
244
245 if (unlikely(afinfo == NULL))
246 return -EAFNOSUPPORT;
247 typemap = afinfo->type_map;
248
249 if (unlikely(typemap[type->proto] != type))
250 err = -ENOENT;
251 else
252 typemap[type->proto] = NULL;
253 xfrm_state_unlock_afinfo(afinfo);
254 return err;
255}
256EXPORT_SYMBOL(xfrm_unregister_type);
257
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800258static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700259{
260 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800261 const struct xfrm_type **typemap;
262 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700263 int modload_attempted = 0;
264
265retry:
266 afinfo = xfrm_state_get_afinfo(family);
267 if (unlikely(afinfo == NULL))
268 return NULL;
269 typemap = afinfo->type_map;
270
271 type = typemap[proto];
272 if (unlikely(type && !try_module_get(type->owner)))
273 type = NULL;
274 if (!type && !modload_attempted) {
275 xfrm_state_put_afinfo(afinfo);
276 request_module("xfrm-type-%d-%d", family, proto);
277 modload_attempted = 1;
278 goto retry;
279 }
280
281 xfrm_state_put_afinfo(afinfo);
282 return type;
283}
284
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800285static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700286{
287 module_put(type->owner);
288}
289
290int xfrm_register_mode(struct xfrm_mode *mode, int family)
291{
292 struct xfrm_state_afinfo *afinfo;
293 struct xfrm_mode **modemap;
294 int err;
295
296 if (unlikely(mode->encap >= XFRM_MODE_MAX))
297 return -EINVAL;
298
299 afinfo = xfrm_state_lock_afinfo(family);
300 if (unlikely(afinfo == NULL))
301 return -EAFNOSUPPORT;
302
303 err = -EEXIST;
304 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700305 if (modemap[mode->encap])
306 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700307
Herbert Xu17c2a422007-10-17 21:33:12 -0700308 err = -ENOENT;
309 if (!try_module_get(afinfo->owner))
310 goto out;
311
312 mode->afinfo = afinfo;
313 modemap[mode->encap] = mode;
314 err = 0;
315
316out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700317 xfrm_state_unlock_afinfo(afinfo);
318 return err;
319}
320EXPORT_SYMBOL(xfrm_register_mode);
321
322int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
323{
324 struct xfrm_state_afinfo *afinfo;
325 struct xfrm_mode **modemap;
326 int err;
327
328 if (unlikely(mode->encap >= XFRM_MODE_MAX))
329 return -EINVAL;
330
331 afinfo = xfrm_state_lock_afinfo(family);
332 if (unlikely(afinfo == NULL))
333 return -EAFNOSUPPORT;
334
335 err = -ENOENT;
336 modemap = afinfo->mode_map;
337 if (likely(modemap[mode->encap] == mode)) {
338 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700339 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700340 err = 0;
341 }
342
343 xfrm_state_unlock_afinfo(afinfo);
344 return err;
345}
346EXPORT_SYMBOL(xfrm_unregister_mode);
347
348static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
349{
350 struct xfrm_state_afinfo *afinfo;
351 struct xfrm_mode *mode;
352 int modload_attempted = 0;
353
354 if (unlikely(encap >= XFRM_MODE_MAX))
355 return NULL;
356
357retry:
358 afinfo = xfrm_state_get_afinfo(family);
359 if (unlikely(afinfo == NULL))
360 return NULL;
361
362 mode = afinfo->mode_map[encap];
363 if (unlikely(mode && !try_module_get(mode->owner)))
364 mode = NULL;
365 if (!mode && !modload_attempted) {
366 xfrm_state_put_afinfo(afinfo);
367 request_module("xfrm-mode-%d-%d", family, encap);
368 modload_attempted = 1;
369 goto retry;
370 }
371
372 xfrm_state_put_afinfo(afinfo);
373 return mode;
374}
375
376static void xfrm_put_mode(struct xfrm_mode *mode)
377{
378 module_put(mode->owner);
379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381static void xfrm_state_gc_destroy(struct xfrm_state *x)
382{
David S. Millera47f0ce2006-08-24 03:54:22 -0700383 del_timer_sync(&x->timer);
384 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800385 kfree(x->aalg);
386 kfree(x->ealg);
387 kfree(x->calg);
388 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700389 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700390 if (x->inner_mode)
391 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700392 if (x->inner_mode_iaf)
393 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700394 if (x->outer_mode)
395 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 if (x->type) {
397 x->type->destructor(x);
398 xfrm_put_type(x->type);
399 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800400 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 kfree(x);
402}
403
David Howellsc4028952006-11-22 14:57:56 +0000404static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
406 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700407 struct hlist_node *entry, *tmp;
408 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700411 gc_list.first = xfrm_state_gc_list.first;
412 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 spin_unlock_bh(&xfrm_state_gc_lock);
414
David S. Miller8f126e32006-08-24 02:45:07 -0700415 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 wake_up(&km_waitq);
419}
420
421static inline unsigned long make_jiffies(long secs)
422{
423 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
424 return MAX_SCHEDULE_TIMEOUT-1;
425 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900426 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427}
428
429static void xfrm_timer_handler(unsigned long data)
430{
431 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800432 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 long next = LONG_MAX;
434 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600435 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 spin_lock(&x->lock);
438 if (x->km.state == XFRM_STATE_DEAD)
439 goto out;
440 if (x->km.state == XFRM_STATE_EXPIRED)
441 goto expired;
442 if (x->lft.hard_add_expires_seconds) {
443 long tmo = x->lft.hard_add_expires_seconds +
444 x->curlft.add_time - now;
445 if (tmo <= 0)
446 goto expired;
447 if (tmo < next)
448 next = tmo;
449 }
450 if (x->lft.hard_use_expires_seconds) {
451 long tmo = x->lft.hard_use_expires_seconds +
452 (x->curlft.use_time ? : now) - now;
453 if (tmo <= 0)
454 goto expired;
455 if (tmo < next)
456 next = tmo;
457 }
458 if (x->km.dying)
459 goto resched;
460 if (x->lft.soft_add_expires_seconds) {
461 long tmo = x->lft.soft_add_expires_seconds +
462 x->curlft.add_time - now;
463 if (tmo <= 0)
464 warn = 1;
465 else if (tmo < next)
466 next = tmo;
467 }
468 if (x->lft.soft_use_expires_seconds) {
469 long tmo = x->lft.soft_use_expires_seconds +
470 (x->curlft.use_time ? : now) - now;
471 if (tmo <= 0)
472 warn = 1;
473 else if (tmo < next)
474 next = tmo;
475 }
476
Herbert Xu4666faa2005-06-18 22:43:22 -0700477 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800479 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700481 if (next != LONG_MAX)
482 mod_timer(&x->timer, jiffies + make_jiffies(next));
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 goto out;
485
486expired:
487 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
488 x->km.state = XFRM_STATE_EXPIRED;
489 wake_up(&km_waitq);
490 next = 2;
491 goto resched;
492 }
Joy Latten161a09e2006-11-27 13:11:54 -0600493
494 err = __xfrm_state_delete(x);
495 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800496 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Joy Lattenab5f5e82007-09-17 11:51:22 -0700498 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400499 audit_get_loginuid(current),
500 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600501
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502out:
503 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504}
505
David S. Miller0ac84752006-03-20 19:18:23 -0800506static void xfrm_replay_timer_handler(unsigned long data);
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508struct xfrm_state *xfrm_state_alloc(void)
509{
510 struct xfrm_state *x;
511
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700512 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
514 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 atomic_set(&x->refcnt, 1);
516 atomic_set(&x->tunnel_users, 0);
Timo Teras4c563f72008-02-28 21:31:08 -0800517 INIT_LIST_HEAD(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700518 INIT_HLIST_NODE(&x->bydst);
519 INIT_HLIST_NODE(&x->bysrc);
520 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800521 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
522 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
523 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800524 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 x->lft.soft_byte_limit = XFRM_INF;
526 x->lft.soft_packet_limit = XFRM_INF;
527 x->lft.hard_byte_limit = XFRM_INF;
528 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800529 x->replay_maxage = 0;
530 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700531 x->inner_mode = NULL;
532 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 spin_lock_init(&x->lock);
534 }
535 return x;
536}
537EXPORT_SYMBOL(xfrm_state_alloc);
538
539void __xfrm_state_destroy(struct xfrm_state *x)
540{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700541 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Timo Teras4c563f72008-02-28 21:31:08 -0800543 spin_lock_bh(&xfrm_state_lock);
544 list_del(&x->all);
545 spin_unlock_bh(&xfrm_state_lock);
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700548 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 spin_unlock_bh(&xfrm_state_gc_lock);
550 schedule_work(&xfrm_state_gc_work);
551}
552EXPORT_SYMBOL(__xfrm_state_destroy);
553
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800554int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700556 int err = -ESRCH;
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if (x->km.state != XFRM_STATE_DEAD) {
559 x->km.state = XFRM_STATE_DEAD;
560 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700561 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700562 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700563 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700564 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700565 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 /* All xfrm_state objects are created by xfrm_state_alloc.
569 * The xfrm_state_alloc call gives a reference, and that
570 * is what we are dropping here.
571 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800572 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700573 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700575
576 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800578EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700580int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700582 int err;
583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700585 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700587
588 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590EXPORT_SYMBOL(xfrm_state_delete);
591
Joy Latten4aa2e622007-06-04 19:05:57 -0400592#ifdef CONFIG_SECURITY_NETWORK_XFRM
593static inline int
594xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595{
Joy Latten4aa2e622007-06-04 19:05:57 -0400596 int i, err = 0;
597
598 for (i = 0; i <= xfrm_state_hmask; i++) {
599 struct hlist_node *entry;
600 struct xfrm_state *x;
601
602 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
603 if (xfrm_id_proto_match(x->id.proto, proto) &&
604 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700605 xfrm_audit_state_delete(x, 0,
606 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400607 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700608 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400609 return err;
610 }
611 }
612 }
613
614 return err;
615}
616#else
617static inline int
618xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
619{
620 return 0;
621}
622#endif
623
624int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
625{
626 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400629 err = xfrm_state_flush_secctx_check(proto, audit_info);
630 if (err)
631 goto out;
632
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700633 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700634 struct hlist_node *entry;
635 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700637 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700639 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 xfrm_state_hold(x);
641 spin_unlock_bh(&xfrm_state_lock);
642
Joy Latten161a09e2006-11-27 13:11:54 -0600643 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700644 xfrm_audit_state_delete(x, err ? 0 : 1,
645 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400646 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700647 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 xfrm_state_put(x);
649
650 spin_lock_bh(&xfrm_state_lock);
651 goto restart;
652 }
653 }
654 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400655 err = 0;
656
657out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 spin_unlock_bh(&xfrm_state_lock);
659 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400660 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661}
662EXPORT_SYMBOL(xfrm_state_flush);
663
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700664void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700665{
666 spin_lock_bh(&xfrm_state_lock);
667 si->sadcnt = xfrm_state_num;
668 si->sadhcnt = xfrm_state_hmask;
669 si->sadhmcnt = xfrm_state_hashmax;
670 spin_unlock_bh(&xfrm_state_lock);
671}
672EXPORT_SYMBOL(xfrm_sad_getinfo);
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674static int
675xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
676 struct xfrm_tmpl *tmpl,
677 xfrm_address_t *daddr, xfrm_address_t *saddr,
678 unsigned short family)
679{
680 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
681 if (!afinfo)
682 return -1;
683 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
684 xfrm_state_put_afinfo(afinfo);
685 return 0;
686}
687
Al Viroa94cfd12006-09-27 18:47:24 -0700688static 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 -0700689{
690 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
691 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700692 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700693
David S. Miller8f126e32006-08-24 02:45:07 -0700694 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700695 if (x->props.family != family ||
696 x->id.spi != spi ||
697 x->id.proto != proto)
698 continue;
699
700 switch (family) {
701 case AF_INET:
702 if (x->id.daddr.a4 != daddr->a4)
703 continue;
704 break;
705 case AF_INET6:
706 if (!ipv6_addr_equal((struct in6_addr *)daddr,
707 (struct in6_addr *)
708 x->id.daddr.a6))
709 continue;
710 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700711 }
David S. Milleredcd5822006-08-24 00:42:45 -0700712
713 xfrm_state_hold(x);
714 return x;
715 }
716
717 return NULL;
718}
719
720static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
721{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700722 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700723 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700724 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700725
David S. Miller8f126e32006-08-24 02:45:07 -0700726 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700727 if (x->props.family != family ||
728 x->id.proto != proto)
729 continue;
730
731 switch (family) {
732 case AF_INET:
733 if (x->id.daddr.a4 != daddr->a4 ||
734 x->props.saddr.a4 != saddr->a4)
735 continue;
736 break;
737 case AF_INET6:
738 if (!ipv6_addr_equal((struct in6_addr *)daddr,
739 (struct in6_addr *)
740 x->id.daddr.a6) ||
741 !ipv6_addr_equal((struct in6_addr *)saddr,
742 (struct in6_addr *)
743 x->props.saddr.a6))
744 continue;
745 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700746 }
David S. Milleredcd5822006-08-24 00:42:45 -0700747
748 xfrm_state_hold(x);
749 return x;
750 }
751
752 return NULL;
753}
754
755static inline struct xfrm_state *
756__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
757{
758 if (use_spi)
759 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
760 x->id.proto, family);
761 else
762 return __xfrm_state_lookup_byaddr(&x->id.daddr,
763 &x->props.saddr,
764 x->id.proto, family);
765}
766
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700767static void xfrm_hash_grow_check(int have_hash_collision)
768{
769 if (have_hash_collision &&
770 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
771 xfrm_state_num > xfrm_state_hmask)
772 schedule_work(&xfrm_hash_work);
773}
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900776xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 struct flowi *fl, struct xfrm_tmpl *tmpl,
778 struct xfrm_policy *pol, int *err,
779 unsigned short family)
780{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800781 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700782 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700783 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 int acquire_in_progress = 0;
785 int error = 0;
786 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900787
David S. Miller37b08e32008-09-02 20:14:15 -0700788 to_put = NULL;
789
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800791 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700792 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 if (x->props.family == family &&
794 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700795 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 xfrm_state_addr_check(x, daddr, saddr, family) &&
797 tmpl->mode == x->props.mode &&
798 tmpl->id.proto == x->id.proto &&
799 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
800 /* Resolution logic:
801 1. There is a valid state with matching selector.
802 Done.
803 2. Valid state with inappropriate selector. Skip.
804
805 Entering area of "sysdeps".
806
807 3. If state is not valid, selector is temporary,
808 it selects only session which triggered
809 previous resolution. Key manager will do
810 something to install a state with proper
811 selector.
812 */
813 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700814 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700815 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 continue;
817 if (!best ||
818 best->km.dying > x->km.dying ||
819 (best->km.dying == x->km.dying &&
820 best->curlft.add_time < x->curlft.add_time))
821 best = x;
822 } else if (x->km.state == XFRM_STATE_ACQ) {
823 acquire_in_progress = 1;
824 } else if (x->km.state == XFRM_STATE_ERROR ||
825 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700826 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700827 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 error = -ESRCH;
829 }
830 }
831 }
832
833 x = best;
834 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700835 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700836 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
837 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700838 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 error = -EEXIST;
840 goto out;
841 }
842 x = xfrm_state_alloc();
843 if (x == NULL) {
844 error = -ENOMEM;
845 goto out;
846 }
847 /* Initialize temporary selector matching only
848 * to current session. */
849 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
850
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700851 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
852 if (error) {
853 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700854 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700855 x = NULL;
856 goto out;
857 }
858
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 if (km_query(x, tmpl, pol) == 0) {
860 x->km.state = XFRM_STATE_ACQ;
Herbert Xu225f4002008-09-09 05:23:37 -0700861 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -0700862 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700863 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700864 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 if (x->id.spi) {
866 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700867 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 }
David S. Miller01e67d02007-05-25 00:41:38 -0700869 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
870 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700872 xfrm_state_num++;
873 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 } else {
875 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700876 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 x = NULL;
878 error = -ESRCH;
879 }
880 }
881out:
882 if (x)
883 xfrm_state_hold(x);
884 else
885 *err = acquire_in_progress ? -EAGAIN : error;
886 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700887 if (to_put)
888 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 return x;
890}
891
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700892struct xfrm_state *
893xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
894 unsigned short family, u8 mode, u8 proto, u32 reqid)
895{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800896 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700897 struct xfrm_state *rx = NULL, *x = NULL;
898 struct hlist_node *entry;
899
900 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800901 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700902 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
903 if (x->props.family == family &&
904 x->props.reqid == reqid &&
905 !(x->props.flags & XFRM_STATE_WILDRECV) &&
906 xfrm_state_addr_check(x, daddr, saddr, family) &&
907 mode == x->props.mode &&
908 proto == x->id.proto &&
909 x->km.state == XFRM_STATE_VALID) {
910 rx = x;
911 break;
912 }
913 }
914
915 if (rx)
916 xfrm_state_hold(rx);
917 spin_unlock(&xfrm_state_lock);
918
919
920 return rx;
921}
922EXPORT_SYMBOL(xfrm_stateonly_find);
923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924static void __xfrm_state_insert(struct xfrm_state *x)
925{
David S. Millera624c102006-08-24 03:24:33 -0700926 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
David S. Miller9d4a7062006-08-24 03:18:09 -0700928 x->genid = ++xfrm_state_genid;
929
Timo Teras4c563f72008-02-28 21:31:08 -0800930 list_add_tail(&x->all, &xfrm_state_all);
931
David S. Millerc1969f22006-08-24 04:00:03 -0700932 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
933 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700934 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700936 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700937 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700939 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700940 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
941 x->props.family);
942
David S. Miller8f126e32006-08-24 02:45:07 -0700943 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700944 }
945
David S. Millera47f0ce2006-08-24 03:54:22 -0700946 mod_timer(&x->timer, jiffies + HZ);
947 if (x->replay_maxage)
948 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700951
952 xfrm_state_num++;
953
David S. Miller918049f2006-10-12 22:03:24 -0700954 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955}
956
David S. Millerc7f5ea32006-08-24 03:29:04 -0700957/* xfrm_state_lock is held */
958static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
959{
960 unsigned short family = xnew->props.family;
961 u32 reqid = xnew->props.reqid;
962 struct xfrm_state *x;
963 struct hlist_node *entry;
964 unsigned int h;
965
David S. Millerc1969f22006-08-24 04:00:03 -0700966 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700967 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
968 if (x->props.family == family &&
969 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700970 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
971 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700972 x->genid = xfrm_state_genid;
973 }
974}
975
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976void xfrm_state_insert(struct xfrm_state *x)
977{
978 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700979 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 __xfrm_state_insert(x);
981 spin_unlock_bh(&xfrm_state_lock);
982}
983EXPORT_SYMBOL(xfrm_state_insert);
984
David S. Miller27708342006-08-24 00:13:10 -0700985/* xfrm_state_lock is held */
986static 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)
987{
David S. Millerc1969f22006-08-24 04:00:03 -0700988 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700989 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700990 struct xfrm_state *x;
991
David S. Miller8f126e32006-08-24 02:45:07 -0700992 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700993 if (x->props.reqid != reqid ||
994 x->props.mode != mode ||
995 x->props.family != family ||
996 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700997 x->id.spi != 0 ||
998 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700999 continue;
1000
1001 switch (family) {
1002 case AF_INET:
1003 if (x->id.daddr.a4 != daddr->a4 ||
1004 x->props.saddr.a4 != saddr->a4)
1005 continue;
1006 break;
1007 case AF_INET6:
1008 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
1009 (struct in6_addr *)daddr) ||
1010 !ipv6_addr_equal((struct in6_addr *)
1011 x->props.saddr.a6,
1012 (struct in6_addr *)saddr))
1013 continue;
1014 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001015 }
David S. Miller27708342006-08-24 00:13:10 -07001016
1017 xfrm_state_hold(x);
1018 return x;
1019 }
1020
1021 if (!create)
1022 return NULL;
1023
1024 x = xfrm_state_alloc();
1025 if (likely(x)) {
1026 switch (family) {
1027 case AF_INET:
1028 x->sel.daddr.a4 = daddr->a4;
1029 x->sel.saddr.a4 = saddr->a4;
1030 x->sel.prefixlen_d = 32;
1031 x->sel.prefixlen_s = 32;
1032 x->props.saddr.a4 = saddr->a4;
1033 x->id.daddr.a4 = daddr->a4;
1034 break;
1035
1036 case AF_INET6:
1037 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1038 (struct in6_addr *)daddr);
1039 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1040 (struct in6_addr *)saddr);
1041 x->sel.prefixlen_d = 128;
1042 x->sel.prefixlen_s = 128;
1043 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1044 (struct in6_addr *)saddr);
1045 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1046 (struct in6_addr *)daddr);
1047 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001048 }
David S. Miller27708342006-08-24 00:13:10 -07001049
1050 x->km.state = XFRM_STATE_ACQ;
1051 x->id.proto = proto;
1052 x->props.family = family;
1053 x->props.mode = mode;
1054 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001055 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001056 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001057 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001058 add_timer(&x->timer);
Herbert Xu225f4002008-09-09 05:23:37 -07001059 list_add_tail(&x->all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -07001060 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001061 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001062 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001063
1064 xfrm_state_num++;
1065
1066 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001067 }
1068
1069 return x;
1070}
1071
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1073
1074int xfrm_state_add(struct xfrm_state *x)
1075{
David S. Miller37b08e32008-09-02 20:14:15 -07001076 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 int family;
1078 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001079 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
1081 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
David S. Miller37b08e32008-09-02 20:14:15 -07001083 to_put = NULL;
1084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 spin_lock_bh(&xfrm_state_lock);
1086
David S. Milleredcd5822006-08-24 00:42:45 -07001087 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001089 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 x1 = NULL;
1091 err = -EEXIST;
1092 goto out;
1093 }
1094
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001095 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001097 if (x1 && ((x1->id.proto != x->id.proto) ||
1098 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001099 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 x1 = NULL;
1101 }
1102 }
1103
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001104 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001105 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1106 x->id.proto,
1107 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
David S. Millerc7f5ea32006-08-24 03:29:04 -07001109 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 __xfrm_state_insert(x);
1111 err = 0;
1112
1113out:
1114 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 if (x1) {
1117 xfrm_state_delete(x1);
1118 xfrm_state_put(x1);
1119 }
1120
David S. Miller37b08e32008-09-02 20:14:15 -07001121 if (to_put)
1122 xfrm_state_put(to_put);
1123
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 return err;
1125}
1126EXPORT_SYMBOL(xfrm_state_add);
1127
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001128#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001129static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001130{
1131 int err = -ENOMEM;
1132 struct xfrm_state *x = xfrm_state_alloc();
1133 if (!x)
1134 goto error;
1135
1136 memcpy(&x->id, &orig->id, sizeof(x->id));
1137 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1138 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1139 x->props.mode = orig->props.mode;
1140 x->props.replay_window = orig->props.replay_window;
1141 x->props.reqid = orig->props.reqid;
1142 x->props.family = orig->props.family;
1143 x->props.saddr = orig->props.saddr;
1144
1145 if (orig->aalg) {
1146 x->aalg = xfrm_algo_clone(orig->aalg);
1147 if (!x->aalg)
1148 goto error;
1149 }
1150 x->props.aalgo = orig->props.aalgo;
1151
1152 if (orig->ealg) {
1153 x->ealg = xfrm_algo_clone(orig->ealg);
1154 if (!x->ealg)
1155 goto error;
1156 }
1157 x->props.ealgo = orig->props.ealgo;
1158
1159 if (orig->calg) {
1160 x->calg = xfrm_algo_clone(orig->calg);
1161 if (!x->calg)
1162 goto error;
1163 }
1164 x->props.calgo = orig->props.calgo;
1165
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001166 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001167 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1168 if (!x->encap)
1169 goto error;
1170 }
1171
1172 if (orig->coaddr) {
1173 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1174 GFP_KERNEL);
1175 if (!x->coaddr)
1176 goto error;
1177 }
1178
1179 err = xfrm_init_state(x);
1180 if (err)
1181 goto error;
1182
1183 x->props.flags = orig->props.flags;
1184
1185 x->curlft.add_time = orig->curlft.add_time;
1186 x->km.state = orig->km.state;
1187 x->km.seq = orig->km.seq;
1188
1189 return x;
1190
1191 error:
1192 if (errp)
1193 *errp = err;
1194 if (x) {
1195 kfree(x->aalg);
1196 kfree(x->ealg);
1197 kfree(x->calg);
1198 kfree(x->encap);
1199 kfree(x->coaddr);
1200 }
1201 kfree(x);
1202 return NULL;
1203}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001204
1205/* xfrm_state_lock is held */
1206struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1207{
1208 unsigned int h;
1209 struct xfrm_state *x;
1210 struct hlist_node *entry;
1211
1212 if (m->reqid) {
1213 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1214 m->reqid, m->old_family);
1215 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1216 if (x->props.mode != m->mode ||
1217 x->id.proto != m->proto)
1218 continue;
1219 if (m->reqid && x->props.reqid != m->reqid)
1220 continue;
1221 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1222 m->old_family) ||
1223 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1224 m->old_family))
1225 continue;
1226 xfrm_state_hold(x);
1227 return x;
1228 }
1229 } else {
1230 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1231 m->old_family);
1232 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1233 if (x->props.mode != m->mode ||
1234 x->id.proto != m->proto)
1235 continue;
1236 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1237 m->old_family) ||
1238 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1239 m->old_family))
1240 continue;
1241 xfrm_state_hold(x);
1242 return x;
1243 }
1244 }
1245
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001246 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001247}
1248EXPORT_SYMBOL(xfrm_migrate_state_find);
1249
1250struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1251 struct xfrm_migrate *m)
1252{
1253 struct xfrm_state *xc;
1254 int err;
1255
1256 xc = xfrm_state_clone(x, &err);
1257 if (!xc)
1258 return NULL;
1259
1260 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1261 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1262
1263 /* add state */
1264 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1265 /* a care is needed when the destination address of the
1266 state is to be updated as it is a part of triplet */
1267 xfrm_state_insert(xc);
1268 } else {
1269 if ((err = xfrm_state_add(xc)) < 0)
1270 goto error;
1271 }
1272
1273 return xc;
1274error:
1275 kfree(xc);
1276 return NULL;
1277}
1278EXPORT_SYMBOL(xfrm_state_migrate);
1279#endif
1280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281int xfrm_state_update(struct xfrm_state *x)
1282{
David S. Miller37b08e32008-09-02 20:14:15 -07001283 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001285 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
David S. Miller37b08e32008-09-02 20:14:15 -07001287 to_put = NULL;
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001290 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
1292 err = -ESRCH;
1293 if (!x1)
1294 goto out;
1295
1296 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001297 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 err = -EEXIST;
1299 goto out;
1300 }
1301
1302 if (x1->km.state == XFRM_STATE_ACQ) {
1303 __xfrm_state_insert(x);
1304 x = NULL;
1305 }
1306 err = 0;
1307
1308out:
1309 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
David S. Miller37b08e32008-09-02 20:14:15 -07001311 if (to_put)
1312 xfrm_state_put(to_put);
1313
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 if (err)
1315 return err;
1316
1317 if (!x) {
1318 xfrm_state_delete(x1);
1319 xfrm_state_put(x1);
1320 return 0;
1321 }
1322
1323 err = -EINVAL;
1324 spin_lock_bh(&x1->lock);
1325 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1326 if (x->encap && x1->encap)
1327 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001328 if (x->coaddr && x1->coaddr) {
1329 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1330 }
1331 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1332 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1334 x1->km.dying = 0;
1335
David S. Millera47f0ce2006-08-24 03:54:22 -07001336 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 if (x1->curlft.use_time)
1338 xfrm_state_check_expire(x1);
1339
1340 err = 0;
1341 }
1342 spin_unlock_bh(&x1->lock);
1343
1344 xfrm_state_put(x1);
1345
1346 return err;
1347}
1348EXPORT_SYMBOL(xfrm_state_update);
1349
1350int xfrm_state_check_expire(struct xfrm_state *x)
1351{
1352 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001353 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
1355 if (x->km.state != XFRM_STATE_VALID)
1356 return -EINVAL;
1357
1358 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1359 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001360 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001361 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 return -EINVAL;
1363 }
1364
1365 if (!x->km.dying &&
1366 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001367 x->curlft.packets >= x->lft.soft_packet_limit)) {
1368 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001369 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001370 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return 0;
1372}
1373EXPORT_SYMBOL(xfrm_state_check_expire);
1374
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001376xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 unsigned short family)
1378{
1379 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001382 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 return x;
1385}
1386EXPORT_SYMBOL(xfrm_state_lookup);
1387
1388struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001389xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1390 u8 proto, unsigned short family)
1391{
1392 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001393
1394 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001395 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001396 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001397 return x;
1398}
1399EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1400
1401struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001402xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1403 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 int create, unsigned short family)
1405{
1406 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001409 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001411
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 return x;
1413}
1414EXPORT_SYMBOL(xfrm_find_acq);
1415
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001416#ifdef CONFIG_XFRM_SUB_POLICY
1417int
1418xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1419 unsigned short family)
1420{
1421 int err = 0;
1422 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1423 if (!afinfo)
1424 return -EAFNOSUPPORT;
1425
1426 spin_lock_bh(&xfrm_state_lock);
1427 if (afinfo->tmpl_sort)
1428 err = afinfo->tmpl_sort(dst, src, n);
1429 spin_unlock_bh(&xfrm_state_lock);
1430 xfrm_state_put_afinfo(afinfo);
1431 return err;
1432}
1433EXPORT_SYMBOL(xfrm_tmpl_sort);
1434
1435int
1436xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1437 unsigned short family)
1438{
1439 int err = 0;
1440 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1441 if (!afinfo)
1442 return -EAFNOSUPPORT;
1443
1444 spin_lock_bh(&xfrm_state_lock);
1445 if (afinfo->state_sort)
1446 err = afinfo->state_sort(dst, src, n);
1447 spin_unlock_bh(&xfrm_state_lock);
1448 xfrm_state_put_afinfo(afinfo);
1449 return err;
1450}
1451EXPORT_SYMBOL(xfrm_state_sort);
1452#endif
1453
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454/* Silly enough, but I'm lazy to build resolution list */
1455
1456static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1457{
1458 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
David S. Millerf034b5d2006-08-24 03:08:07 -07001460 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001461 struct hlist_node *entry;
1462 struct xfrm_state *x;
1463
1464 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1465 if (x->km.seq == seq &&
1466 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 xfrm_state_hold(x);
1468 return x;
1469 }
1470 }
1471 }
1472 return NULL;
1473}
1474
1475struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1476{
1477 struct xfrm_state *x;
1478
1479 spin_lock_bh(&xfrm_state_lock);
1480 x = __xfrm_find_acq_byseq(seq);
1481 spin_unlock_bh(&xfrm_state_lock);
1482 return x;
1483}
1484EXPORT_SYMBOL(xfrm_find_acq_byseq);
1485
1486u32 xfrm_get_acqseq(void)
1487{
1488 u32 res;
1489 static u32 acqseq;
1490 static DEFINE_SPINLOCK(acqseq_lock);
1491
1492 spin_lock_bh(&acqseq_lock);
1493 res = (++acqseq ? : ++acqseq);
1494 spin_unlock_bh(&acqseq_lock);
1495 return res;
1496}
1497EXPORT_SYMBOL(xfrm_get_acqseq);
1498
Herbert Xu658b2192007-10-09 13:29:52 -07001499int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500{
David S. Millerf034b5d2006-08-24 03:08:07 -07001501 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001503 int err = -ENOENT;
1504 __be32 minspi = htonl(low);
1505 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
Herbert Xu658b2192007-10-09 13:29:52 -07001507 spin_lock_bh(&x->lock);
1508 if (x->km.state == XFRM_STATE_DEAD)
1509 goto unlock;
1510
1511 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001513 goto unlock;
1514
1515 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
1517 if (minspi == maxspi) {
1518 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1519 if (x0) {
1520 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001521 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 }
1523 x->id.spi = minspi;
1524 } else {
1525 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001526 for (h=0; h<high-low+1; h++) {
1527 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1529 if (x0 == NULL) {
1530 x->id.spi = htonl(spi);
1531 break;
1532 }
1533 xfrm_state_put(x0);
1534 }
1535 }
1536 if (x->id.spi) {
1537 spin_lock_bh(&xfrm_state_lock);
1538 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001539 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001541
1542 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 }
Herbert Xu658b2192007-10-09 13:29:52 -07001544
1545unlock:
1546 spin_unlock_bh(&x->lock);
1547
1548 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549}
1550EXPORT_SYMBOL(xfrm_alloc_spi);
1551
Timo Teras4c563f72008-02-28 21:31:08 -08001552int xfrm_state_walk(struct xfrm_state_walk *walk,
1553 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 void *data)
1555{
Timo Teras4c563f72008-02-28 21:31:08 -08001556 struct xfrm_state *old, *x, *last = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 int err = 0;
1558
Timo Teras4c563f72008-02-28 21:31:08 -08001559 if (walk->state == NULL && walk->count != 0)
1560 return 0;
1561
1562 old = x = walk->state;
1563 walk->state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 spin_lock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001565 if (x == NULL)
1566 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1567 list_for_each_entry_from(x, &xfrm_state_all, all) {
1568 if (x->km.state == XFRM_STATE_DEAD)
1569 continue;
1570 if (!xfrm_id_proto_match(x->id.proto, walk->proto))
1571 continue;
1572 if (last) {
1573 err = func(last, walk->count, data);
1574 if (err) {
1575 xfrm_state_hold(last);
1576 walk->state = last;
1577 goto out;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001578 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 }
Timo Teras4c563f72008-02-28 21:31:08 -08001580 last = x;
1581 walk->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 }
Timo Teras4c563f72008-02-28 21:31:08 -08001583 if (walk->count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 err = -ENOENT;
1585 goto out;
1586 }
Timo Teras4c563f72008-02-28 21:31:08 -08001587 if (last)
1588 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589out:
1590 spin_unlock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001591 if (old != NULL)
1592 xfrm_state_put(old);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 return err;
1594}
1595EXPORT_SYMBOL(xfrm_state_walk);
1596
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001597
1598void xfrm_replay_notify(struct xfrm_state *x, int event)
1599{
1600 struct km_event c;
1601 /* we send notify messages in case
1602 * 1. we updated on of the sequence numbers, and the seqno difference
1603 * is at least x->replay_maxdiff, in this case we also update the
1604 * timeout of our timer function
1605 * 2. if x->replay_maxage has elapsed since last update,
1606 * and there were changes
1607 *
1608 * The state structure must be locked!
1609 */
1610
1611 switch (event) {
1612 case XFRM_REPLAY_UPDATE:
1613 if (x->replay_maxdiff &&
1614 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001615 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1616 if (x->xflags & XFRM_TIME_DEFER)
1617 event = XFRM_REPLAY_TIMEOUT;
1618 else
1619 return;
1620 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001621
1622 break;
1623
1624 case XFRM_REPLAY_TIMEOUT:
1625 if ((x->replay.seq == x->preplay.seq) &&
1626 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001627 (x->replay.oseq == x->preplay.oseq)) {
1628 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001629 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001630 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001631
1632 break;
1633 }
1634
1635 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1636 c.event = XFRM_MSG_NEWAE;
1637 c.data.aevent = event;
1638 km_state_notify(x, &c);
1639
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001640 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001641 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001642 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001643}
1644
1645static void xfrm_replay_timer_handler(unsigned long data)
1646{
1647 struct xfrm_state *x = (struct xfrm_state*)data;
1648
1649 spin_lock(&x->lock);
1650
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001651 if (x->km.state == XFRM_STATE_VALID) {
1652 if (xfrm_aevent_is_on())
1653 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1654 else
1655 x->xflags |= XFRM_TIME_DEFER;
1656 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001657
1658 spin_unlock(&x->lock);
1659}
1660
Paul Mooreafeb14b2007-12-21 14:58:11 -08001661int xfrm_replay_check(struct xfrm_state *x,
1662 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
1664 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001665 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
1667 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001668 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 if (likely(seq > x->replay.seq))
1671 return 0;
1672
1673 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001674 if (diff >= min_t(unsigned int, x->props.replay_window,
1675 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001677 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 }
1679
1680 if (x->replay.bitmap & (1U << diff)) {
1681 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001682 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 }
1684 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001685
1686err:
1687 xfrm_audit_state_replay(x, skb, net_seq);
1688 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Al Viro61f46272006-09-27 18:48:33 -07001691void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692{
1693 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001694 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 if (seq > x->replay.seq) {
1697 diff = seq - x->replay.seq;
1698 if (diff < x->props.replay_window)
1699 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1700 else
1701 x->replay.bitmap = 1;
1702 x->replay.seq = seq;
1703 } else {
1704 diff = x->replay.seq - seq;
1705 x->replay.bitmap |= (1U << diff);
1706 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001707
1708 if (xfrm_aevent_is_on())
1709 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Denis Chengdf018122007-12-07 00:51:11 -08001712static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713static DEFINE_RWLOCK(xfrm_km_lock);
1714
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001715void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716{
1717 struct xfrm_mgr *km;
1718
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001719 read_lock(&xfrm_km_lock);
1720 list_for_each_entry(km, &xfrm_km_list, list)
1721 if (km->notify_policy)
1722 km->notify_policy(xp, dir, c);
1723 read_unlock(&xfrm_km_lock);
1724}
1725
1726void km_state_notify(struct xfrm_state *x, struct km_event *c)
1727{
1728 struct xfrm_mgr *km;
1729 read_lock(&xfrm_km_lock);
1730 list_for_each_entry(km, &xfrm_km_list, list)
1731 if (km->notify)
1732 km->notify(x, c);
1733 read_unlock(&xfrm_km_lock);
1734}
1735
1736EXPORT_SYMBOL(km_policy_notify);
1737EXPORT_SYMBOL(km_state_notify);
1738
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001739void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001740{
1741 struct km_event c;
1742
Herbert Xubf088672005-06-18 22:44:00 -07001743 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001744 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001745 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001746 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
1748 if (hard)
1749 wake_up(&km_waitq);
1750}
1751
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001752EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001753/*
1754 * We send to all registered managers regardless of failure
1755 * We are happy with one success
1756*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001757int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001759 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 struct xfrm_mgr *km;
1761
1762 read_lock(&xfrm_km_lock);
1763 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001764 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1765 if (!acqret)
1766 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 }
1768 read_unlock(&xfrm_km_lock);
1769 return err;
1770}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001771EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
Al Viro5d36b182006-11-08 00:24:06 -08001773int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774{
1775 int err = -EINVAL;
1776 struct xfrm_mgr *km;
1777
1778 read_lock(&xfrm_km_lock);
1779 list_for_each_entry(km, &xfrm_km_list, list) {
1780 if (km->new_mapping)
1781 err = km->new_mapping(x, ipaddr, sport);
1782 if (!err)
1783 break;
1784 }
1785 read_unlock(&xfrm_km_lock);
1786 return err;
1787}
1788EXPORT_SYMBOL(km_new_mapping);
1789
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001790void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001792 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
Herbert Xubf088672005-06-18 22:44:00 -07001794 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001795 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001796 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001797 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
1799 if (hard)
1800 wake_up(&km_waitq);
1801}
David S. Millera70fcb02006-03-20 19:18:52 -08001802EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001804#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001805int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1806 struct xfrm_migrate *m, int num_migrate)
1807{
1808 int err = -EINVAL;
1809 int ret;
1810 struct xfrm_mgr *km;
1811
1812 read_lock(&xfrm_km_lock);
1813 list_for_each_entry(km, &xfrm_km_list, list) {
1814 if (km->migrate) {
1815 ret = km->migrate(sel, dir, type, m, num_migrate);
1816 if (!ret)
1817 err = ret;
1818 }
1819 }
1820 read_unlock(&xfrm_km_lock);
1821 return err;
1822}
1823EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001824#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001825
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001826int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1827{
1828 int err = -EINVAL;
1829 int ret;
1830 struct xfrm_mgr *km;
1831
1832 read_lock(&xfrm_km_lock);
1833 list_for_each_entry(km, &xfrm_km_list, list) {
1834 if (km->report) {
1835 ret = km->report(proto, sel, addr);
1836 if (!ret)
1837 err = ret;
1838 }
1839 }
1840 read_unlock(&xfrm_km_lock);
1841 return err;
1842}
1843EXPORT_SYMBOL(km_report);
1844
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1846{
1847 int err;
1848 u8 *data;
1849 struct xfrm_mgr *km;
1850 struct xfrm_policy *pol = NULL;
1851
1852 if (optlen <= 0 || optlen > PAGE_SIZE)
1853 return -EMSGSIZE;
1854
1855 data = kmalloc(optlen, GFP_KERNEL);
1856 if (!data)
1857 return -ENOMEM;
1858
1859 err = -EFAULT;
1860 if (copy_from_user(data, optval, optlen))
1861 goto out;
1862
1863 err = -EINVAL;
1864 read_lock(&xfrm_km_lock);
1865 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001866 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 optlen, &err);
1868 if (err >= 0)
1869 break;
1870 }
1871 read_unlock(&xfrm_km_lock);
1872
1873 if (err >= 0) {
1874 xfrm_sk_policy_insert(sk, err, pol);
1875 xfrm_pol_put(pol);
1876 err = 0;
1877 }
1878
1879out:
1880 kfree(data);
1881 return err;
1882}
1883EXPORT_SYMBOL(xfrm_user_policy);
1884
1885int xfrm_register_km(struct xfrm_mgr *km)
1886{
1887 write_lock_bh(&xfrm_km_lock);
1888 list_add_tail(&km->list, &xfrm_km_list);
1889 write_unlock_bh(&xfrm_km_lock);
1890 return 0;
1891}
1892EXPORT_SYMBOL(xfrm_register_km);
1893
1894int xfrm_unregister_km(struct xfrm_mgr *km)
1895{
1896 write_lock_bh(&xfrm_km_lock);
1897 list_del(&km->list);
1898 write_unlock_bh(&xfrm_km_lock);
1899 return 0;
1900}
1901EXPORT_SYMBOL(xfrm_unregister_km);
1902
1903int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1904{
1905 int err = 0;
1906 if (unlikely(afinfo == NULL))
1907 return -EINVAL;
1908 if (unlikely(afinfo->family >= NPROTO))
1909 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001910 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1912 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001913 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001915 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 return err;
1917}
1918EXPORT_SYMBOL(xfrm_state_register_afinfo);
1919
1920int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1921{
1922 int err = 0;
1923 if (unlikely(afinfo == NULL))
1924 return -EINVAL;
1925 if (unlikely(afinfo->family >= NPROTO))
1926 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001927 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1929 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1930 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001931 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001934 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 return err;
1936}
1937EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1938
Herbert Xu17c2a422007-10-17 21:33:12 -07001939static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940{
1941 struct xfrm_state_afinfo *afinfo;
1942 if (unlikely(family >= NPROTO))
1943 return NULL;
1944 read_lock(&xfrm_state_afinfo_lock);
1945 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001946 if (unlikely(!afinfo))
1947 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 return afinfo;
1949}
1950
Herbert Xu17c2a422007-10-17 21:33:12 -07001951static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001952 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953{
Herbert Xu546be242006-05-27 23:03:58 -07001954 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955}
1956
1957/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1958void xfrm_state_delete_tunnel(struct xfrm_state *x)
1959{
1960 if (x->tunnel) {
1961 struct xfrm_state *t = x->tunnel;
1962
1963 if (atomic_read(&t->tunnel_users) == 2)
1964 xfrm_state_delete(t);
1965 atomic_dec(&t->tunnel_users);
1966 xfrm_state_put(t);
1967 x->tunnel = NULL;
1968 }
1969}
1970EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1971
1972int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1973{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001974 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975
Patrick McHardyc5c25232007-04-09 11:47:18 -07001976 spin_lock_bh(&x->lock);
1977 if (x->km.state == XFRM_STATE_VALID &&
1978 x->type && x->type->get_mtu)
1979 res = x->type->get_mtu(x, mtu);
1980 else
Patrick McHardy28121612007-06-18 22:30:15 -07001981 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001982 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 return res;
1984}
1985
Herbert Xu72cb6962005-06-20 13:18:08 -07001986int xfrm_init_state(struct xfrm_state *x)
1987{
Herbert Xud094cd82005-06-20 13:19:41 -07001988 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001989 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001990 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001991 int err;
1992
Herbert Xud094cd82005-06-20 13:19:41 -07001993 err = -EAFNOSUPPORT;
1994 afinfo = xfrm_state_get_afinfo(family);
1995 if (!afinfo)
1996 goto error;
1997
1998 err = 0;
1999 if (afinfo->init_flags)
2000 err = afinfo->init_flags(x);
2001
2002 xfrm_state_put_afinfo(afinfo);
2003
2004 if (err)
2005 goto error;
2006
2007 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002008
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002009 if (x->sel.family != AF_UNSPEC) {
2010 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2011 if (inner_mode == NULL)
2012 goto error;
2013
2014 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2015 family != x->sel.family) {
2016 xfrm_put_mode(inner_mode);
2017 goto error;
2018 }
2019
2020 x->inner_mode = inner_mode;
2021 } else {
2022 struct xfrm_mode *inner_mode_iaf;
2023
2024 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2025 if (inner_mode == NULL)
2026 goto error;
2027
2028 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2029 xfrm_put_mode(inner_mode);
2030 goto error;
2031 }
2032
2033 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2034 if (inner_mode_iaf == NULL)
2035 goto error;
2036
2037 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2038 xfrm_put_mode(inner_mode_iaf);
2039 goto error;
2040 }
2041
2042 if (x->props.family == AF_INET) {
2043 x->inner_mode = inner_mode;
2044 x->inner_mode_iaf = inner_mode_iaf;
2045 } else {
2046 x->inner_mode = inner_mode_iaf;
2047 x->inner_mode_iaf = inner_mode;
2048 }
2049 }
Herbert Xu13996372007-10-17 21:35:51 -07002050
Herbert Xud094cd82005-06-20 13:19:41 -07002051 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002052 if (x->type == NULL)
2053 goto error;
2054
2055 err = x->type->init_state(x);
2056 if (err)
2057 goto error;
2058
Herbert Xu13996372007-10-17 21:35:51 -07002059 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2060 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002061 goto error;
2062
Herbert Xu72cb6962005-06-20 13:18:08 -07002063 x->km.state = XFRM_STATE_VALID;
2064
2065error:
2066 return err;
2067}
2068
2069EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071void __init xfrm_state_init(void)
2072{
David S. Millerf034b5d2006-08-24 03:08:07 -07002073 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
David S. Millerf034b5d2006-08-24 03:08:07 -07002075 sz = sizeof(struct hlist_head) * 8;
2076
David S. Miller44e36b42006-08-24 04:50:50 -07002077 xfrm_state_bydst = xfrm_hash_alloc(sz);
2078 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2079 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002080 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2081 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2082 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2083
David Howellsc4028952006-11-22 14:57:56 +00002084 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085}
2086
Joy Lattenab5f5e82007-09-17 11:51:22 -07002087#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002088static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2089 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002090{
Paul Moore68277ac2007-12-20 20:49:33 -08002091 struct xfrm_sec_ctx *ctx = x->security;
2092 u32 spi = ntohl(x->id.spi);
2093
2094 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002095 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002096 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002097
2098 switch(x->props.family) {
2099 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002100 audit_log_format(audit_buf,
2101 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002102 NIPQUAD(x->props.saddr.a4),
2103 NIPQUAD(x->id.daddr.a4));
2104 break;
2105 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002106 audit_log_format(audit_buf,
2107 " src=" NIP6_FMT " dst=" NIP6_FMT,
2108 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2109 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002110 break;
2111 }
Paul Moore68277ac2007-12-20 20:49:33 -08002112
2113 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002114}
2115
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002116static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2117 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002118{
2119 struct iphdr *iph4;
2120 struct ipv6hdr *iph6;
2121
2122 switch (family) {
2123 case AF_INET:
2124 iph4 = ip_hdr(skb);
2125 audit_log_format(audit_buf,
2126 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2127 NIPQUAD(iph4->saddr),
2128 NIPQUAD(iph4->daddr));
2129 break;
2130 case AF_INET6:
2131 iph6 = ipv6_hdr(skb);
2132 audit_log_format(audit_buf,
2133 " src=" NIP6_FMT " dst=" NIP6_FMT
YOSHIFUJI Hideaki5e2c4332008-04-26 22:24:10 -07002134 " flowlbl=0x%x%02x%02x",
Paul Mooreafeb14b2007-12-21 14:58:11 -08002135 NIP6(iph6->saddr),
2136 NIP6(iph6->daddr),
2137 iph6->flow_lbl[0] & 0x0f,
2138 iph6->flow_lbl[1],
2139 iph6->flow_lbl[2]);
2140 break;
2141 }
2142}
2143
Paul Moore68277ac2007-12-20 20:49:33 -08002144void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002145 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002146{
2147 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002148
Paul Mooreafeb14b2007-12-21 14:58:11 -08002149 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002150 if (audit_buf == NULL)
2151 return;
Eric Paris25323862008-04-18 10:09:25 -04002152 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002153 xfrm_audit_helper_sainfo(x, audit_buf);
2154 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002155 audit_log_end(audit_buf);
2156}
2157EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2158
Paul Moore68277ac2007-12-20 20:49:33 -08002159void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002160 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002161{
2162 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002163
Paul Mooreafeb14b2007-12-21 14:58:11 -08002164 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002165 if (audit_buf == NULL)
2166 return;
Eric Paris25323862008-04-18 10:09:25 -04002167 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002168 xfrm_audit_helper_sainfo(x, audit_buf);
2169 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002170 audit_log_end(audit_buf);
2171}
2172EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002173
2174void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2175 struct sk_buff *skb)
2176{
2177 struct audit_buffer *audit_buf;
2178 u32 spi;
2179
2180 audit_buf = xfrm_audit_start("SA-replay-overflow");
2181 if (audit_buf == NULL)
2182 return;
2183 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2184 /* don't record the sequence number because it's inherent in this kind
2185 * of audit message */
2186 spi = ntohl(x->id.spi);
2187 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2188 audit_log_end(audit_buf);
2189}
2190EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2191
2192static void xfrm_audit_state_replay(struct xfrm_state *x,
2193 struct sk_buff *skb, __be32 net_seq)
2194{
2195 struct audit_buffer *audit_buf;
2196 u32 spi;
2197
2198 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2199 if (audit_buf == NULL)
2200 return;
2201 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2202 spi = ntohl(x->id.spi);
2203 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2204 spi, spi, ntohl(net_seq));
2205 audit_log_end(audit_buf);
2206}
2207
2208void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2209{
2210 struct audit_buffer *audit_buf;
2211
2212 audit_buf = xfrm_audit_start("SA-notfound");
2213 if (audit_buf == NULL)
2214 return;
2215 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2216 audit_log_end(audit_buf);
2217}
2218EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2219
2220void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2221 __be32 net_spi, __be32 net_seq)
2222{
2223 struct audit_buffer *audit_buf;
2224 u32 spi;
2225
2226 audit_buf = xfrm_audit_start("SA-notfound");
2227 if (audit_buf == NULL)
2228 return;
2229 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2230 spi = ntohl(net_spi);
2231 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2232 spi, spi, ntohl(net_seq));
2233 audit_log_end(audit_buf);
2234}
2235EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2236
2237void xfrm_audit_state_icvfail(struct xfrm_state *x,
2238 struct sk_buff *skb, u8 proto)
2239{
2240 struct audit_buffer *audit_buf;
2241 __be32 net_spi;
2242 __be32 net_seq;
2243
2244 audit_buf = xfrm_audit_start("SA-icv-failure");
2245 if (audit_buf == NULL)
2246 return;
2247 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2248 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2249 u32 spi = ntohl(net_spi);
2250 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2251 spi, spi, ntohl(net_seq));
2252 }
2253 audit_log_end(audit_buf);
2254}
2255EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002256#endif /* CONFIG_AUDITSYSCALL */