blob: 7ba65e82941c4f7794f6ada15a418d60606dbba4 [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 */
David S. Millerf034b5d2006-08-24 03:08:07 -070053static struct hlist_head *xfrm_state_bydst __read_mostly;
54static struct hlist_head *xfrm_state_bysrc __read_mostly;
55static struct hlist_head *xfrm_state_byspi __read_mostly;
56static unsigned int xfrm_state_hmask __read_mostly;
57static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
58static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070059static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Herbert Xu17c2a422007-10-17 21:33:12 -070061static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
62static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
63
Paul Mooreafeb14b2007-12-21 14:58:11 -080064#ifdef CONFIG_AUDITSYSCALL
65static void xfrm_audit_state_replay(struct xfrm_state *x,
66 struct sk_buff *skb, __be32 net_seq);
67#else
68#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
69#endif /* CONFIG_AUDITSYSCALL */
70
David S. Millerc1969f22006-08-24 04:00:03 -070071static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
72 xfrm_address_t *saddr,
73 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070074 unsigned short family)
75{
David S. Millerc1969f22006-08-24 04:00:03 -070076 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070077}
78
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070079static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
80 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070081 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070082{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070083 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070084}
85
David S. Miller2575b652006-08-24 03:26:44 -070086static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070087xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070088{
David S. Millerc1969f22006-08-24 04:00:03 -070089 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070090}
91
David S. Millerf034b5d2006-08-24 03:08:07 -070092static void xfrm_hash_transfer(struct hlist_head *list,
93 struct hlist_head *ndsttable,
94 struct hlist_head *nsrctable,
95 struct hlist_head *nspitable,
96 unsigned int nhashmask)
97{
98 struct hlist_node *entry, *tmp;
99 struct xfrm_state *x;
100
101 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
102 unsigned int h;
103
David S. Millerc1969f22006-08-24 04:00:03 -0700104 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
105 x->props.reqid, x->props.family,
106 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700107 hlist_add_head(&x->bydst, ndsttable+h);
108
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700109 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
110 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700111 nhashmask);
112 hlist_add_head(&x->bysrc, nsrctable+h);
113
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700114 if (x->id.spi) {
115 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
116 x->id.proto, x->props.family,
117 nhashmask);
118 hlist_add_head(&x->byspi, nspitable+h);
119 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700120 }
121}
122
123static unsigned long xfrm_hash_new_size(void)
124{
125 return ((xfrm_state_hmask + 1) << 1) *
126 sizeof(struct hlist_head);
127}
128
129static DEFINE_MUTEX(hash_resize_mutex);
130
David Howellsc4028952006-11-22 14:57:56 +0000131static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700132{
133 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
134 unsigned long nsize, osize;
135 unsigned int nhashmask, ohashmask;
136 int i;
137
138 mutex_lock(&hash_resize_mutex);
139
140 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700141 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 if (!ndst)
143 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700144 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700145 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700146 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700147 goto out_unlock;
148 }
David S. Miller44e36b42006-08-24 04:50:50 -0700149 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700150 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700151 xfrm_hash_free(ndst, nsize);
152 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700153 goto out_unlock;
154 }
155
156 spin_lock_bh(&xfrm_state_lock);
157
158 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
159 for (i = xfrm_state_hmask; i >= 0; i--)
160 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
161 nhashmask);
162
163 odst = xfrm_state_bydst;
164 osrc = xfrm_state_bysrc;
165 ospi = xfrm_state_byspi;
166 ohashmask = xfrm_state_hmask;
167
168 xfrm_state_bydst = ndst;
169 xfrm_state_bysrc = nsrc;
170 xfrm_state_byspi = nspi;
171 xfrm_state_hmask = nhashmask;
172
173 spin_unlock_bh(&xfrm_state_lock);
174
175 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700176 xfrm_hash_free(odst, osize);
177 xfrm_hash_free(osrc, osize);
178 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700179
180out_unlock:
181 mutex_unlock(&hash_resize_mutex);
182}
183
David Howellsc4028952006-11-22 14:57:56 +0000184static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186DECLARE_WAIT_QUEUE_HEAD(km_waitq);
187EXPORT_SYMBOL(km_waitq);
188
189static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
190static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
191
192static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700193static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194static DEFINE_SPINLOCK(xfrm_state_gc_lock);
195
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800196int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800198int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800199void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700201static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
202{
203 struct xfrm_state_afinfo *afinfo;
204 if (unlikely(family >= NPROTO))
205 return NULL;
206 write_lock_bh(&xfrm_state_afinfo_lock);
207 afinfo = xfrm_state_afinfo[family];
208 if (unlikely(!afinfo))
209 write_unlock_bh(&xfrm_state_afinfo_lock);
210 return afinfo;
211}
212
213static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800214 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700215{
216 write_unlock_bh(&xfrm_state_afinfo_lock);
217}
218
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800219int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700220{
221 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800222 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700223 int err = 0;
224
225 if (unlikely(afinfo == NULL))
226 return -EAFNOSUPPORT;
227 typemap = afinfo->type_map;
228
229 if (likely(typemap[type->proto] == NULL))
230 typemap[type->proto] = type;
231 else
232 err = -EEXIST;
233 xfrm_state_unlock_afinfo(afinfo);
234 return err;
235}
236EXPORT_SYMBOL(xfrm_register_type);
237
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800238int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700239{
240 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800241 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700242 int err = 0;
243
244 if (unlikely(afinfo == NULL))
245 return -EAFNOSUPPORT;
246 typemap = afinfo->type_map;
247
248 if (unlikely(typemap[type->proto] != type))
249 err = -ENOENT;
250 else
251 typemap[type->proto] = NULL;
252 xfrm_state_unlock_afinfo(afinfo);
253 return err;
254}
255EXPORT_SYMBOL(xfrm_unregister_type);
256
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800257static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700258{
259 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800260 const struct xfrm_type **typemap;
261 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700262 int modload_attempted = 0;
263
264retry:
265 afinfo = xfrm_state_get_afinfo(family);
266 if (unlikely(afinfo == NULL))
267 return NULL;
268 typemap = afinfo->type_map;
269
270 type = typemap[proto];
271 if (unlikely(type && !try_module_get(type->owner)))
272 type = NULL;
273 if (!type && !modload_attempted) {
274 xfrm_state_put_afinfo(afinfo);
275 request_module("xfrm-type-%d-%d", family, proto);
276 modload_attempted = 1;
277 goto retry;
278 }
279
280 xfrm_state_put_afinfo(afinfo);
281 return type;
282}
283
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800284static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700285{
286 module_put(type->owner);
287}
288
289int xfrm_register_mode(struct xfrm_mode *mode, int family)
290{
291 struct xfrm_state_afinfo *afinfo;
292 struct xfrm_mode **modemap;
293 int err;
294
295 if (unlikely(mode->encap >= XFRM_MODE_MAX))
296 return -EINVAL;
297
298 afinfo = xfrm_state_lock_afinfo(family);
299 if (unlikely(afinfo == NULL))
300 return -EAFNOSUPPORT;
301
302 err = -EEXIST;
303 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700304 if (modemap[mode->encap])
305 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700306
Herbert Xu17c2a422007-10-17 21:33:12 -0700307 err = -ENOENT;
308 if (!try_module_get(afinfo->owner))
309 goto out;
310
311 mode->afinfo = afinfo;
312 modemap[mode->encap] = mode;
313 err = 0;
314
315out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700316 xfrm_state_unlock_afinfo(afinfo);
317 return err;
318}
319EXPORT_SYMBOL(xfrm_register_mode);
320
321int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
322{
323 struct xfrm_state_afinfo *afinfo;
324 struct xfrm_mode **modemap;
325 int err;
326
327 if (unlikely(mode->encap >= XFRM_MODE_MAX))
328 return -EINVAL;
329
330 afinfo = xfrm_state_lock_afinfo(family);
331 if (unlikely(afinfo == NULL))
332 return -EAFNOSUPPORT;
333
334 err = -ENOENT;
335 modemap = afinfo->mode_map;
336 if (likely(modemap[mode->encap] == mode)) {
337 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700338 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700339 err = 0;
340 }
341
342 xfrm_state_unlock_afinfo(afinfo);
343 return err;
344}
345EXPORT_SYMBOL(xfrm_unregister_mode);
346
347static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
348{
349 struct xfrm_state_afinfo *afinfo;
350 struct xfrm_mode *mode;
351 int modload_attempted = 0;
352
353 if (unlikely(encap >= XFRM_MODE_MAX))
354 return NULL;
355
356retry:
357 afinfo = xfrm_state_get_afinfo(family);
358 if (unlikely(afinfo == NULL))
359 return NULL;
360
361 mode = afinfo->mode_map[encap];
362 if (unlikely(mode && !try_module_get(mode->owner)))
363 mode = NULL;
364 if (!mode && !modload_attempted) {
365 xfrm_state_put_afinfo(afinfo);
366 request_module("xfrm-mode-%d-%d", family, encap);
367 modload_attempted = 1;
368 goto retry;
369 }
370
371 xfrm_state_put_afinfo(afinfo);
372 return mode;
373}
374
375static void xfrm_put_mode(struct xfrm_mode *mode)
376{
377 module_put(mode->owner);
378}
379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380static void xfrm_state_gc_destroy(struct xfrm_state *x)
381{
David S. Millera47f0ce2006-08-24 03:54:22 -0700382 del_timer_sync(&x->timer);
383 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800384 kfree(x->aalg);
385 kfree(x->ealg);
386 kfree(x->calg);
387 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700388 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700389 if (x->inner_mode)
390 xfrm_put_mode(x->inner_mode);
391 if (x->outer_mode)
392 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if (x->type) {
394 x->type->destructor(x);
395 xfrm_put_type(x->type);
396 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800397 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 kfree(x);
399}
400
David Howellsc4028952006-11-22 14:57:56 +0000401static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
403 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700404 struct hlist_node *entry, *tmp;
405 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700408 gc_list.first = xfrm_state_gc_list.first;
409 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 spin_unlock_bh(&xfrm_state_gc_lock);
411
David S. Miller8f126e32006-08-24 02:45:07 -0700412 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 wake_up(&km_waitq);
416}
417
418static inline unsigned long make_jiffies(long secs)
419{
420 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
421 return MAX_SCHEDULE_TIMEOUT-1;
422 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900423 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424}
425
426static void xfrm_timer_handler(unsigned long data)
427{
428 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800429 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 long next = LONG_MAX;
431 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600432 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434 spin_lock(&x->lock);
435 if (x->km.state == XFRM_STATE_DEAD)
436 goto out;
437 if (x->km.state == XFRM_STATE_EXPIRED)
438 goto expired;
439 if (x->lft.hard_add_expires_seconds) {
440 long tmo = x->lft.hard_add_expires_seconds +
441 x->curlft.add_time - now;
442 if (tmo <= 0)
443 goto expired;
444 if (tmo < next)
445 next = tmo;
446 }
447 if (x->lft.hard_use_expires_seconds) {
448 long tmo = x->lft.hard_use_expires_seconds +
449 (x->curlft.use_time ? : now) - now;
450 if (tmo <= 0)
451 goto expired;
452 if (tmo < next)
453 next = tmo;
454 }
455 if (x->km.dying)
456 goto resched;
457 if (x->lft.soft_add_expires_seconds) {
458 long tmo = x->lft.soft_add_expires_seconds +
459 x->curlft.add_time - now;
460 if (tmo <= 0)
461 warn = 1;
462 else if (tmo < next)
463 next = tmo;
464 }
465 if (x->lft.soft_use_expires_seconds) {
466 long tmo = x->lft.soft_use_expires_seconds +
467 (x->curlft.use_time ? : now) - now;
468 if (tmo <= 0)
469 warn = 1;
470 else if (tmo < next)
471 next = tmo;
472 }
473
Herbert Xu4666faa2005-06-18 22:43:22 -0700474 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800476 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700478 if (next != LONG_MAX)
479 mod_timer(&x->timer, jiffies + make_jiffies(next));
480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 goto out;
482
483expired:
484 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
485 x->km.state = XFRM_STATE_EXPIRED;
486 wake_up(&km_waitq);
487 next = 2;
488 goto resched;
489 }
Joy Latten161a09e2006-11-27 13:11:54 -0600490
491 err = __xfrm_state_delete(x);
492 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800493 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
Joy Lattenab5f5e82007-09-17 11:51:22 -0700495 xfrm_audit_state_delete(x, err ? 0 : 1,
Al Viro0c11b942008-01-10 04:20:52 -0500496 audit_get_loginuid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498out:
499 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
David S. Miller0ac84752006-03-20 19:18:23 -0800502static void xfrm_replay_timer_handler(unsigned long data);
503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504struct xfrm_state *xfrm_state_alloc(void)
505{
506 struct xfrm_state *x;
507
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700508 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 atomic_set(&x->refcnt, 1);
512 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700513 INIT_HLIST_NODE(&x->bydst);
514 INIT_HLIST_NODE(&x->bysrc);
515 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800516 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
517 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
518 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800519 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 x->lft.soft_byte_limit = XFRM_INF;
521 x->lft.soft_packet_limit = XFRM_INF;
522 x->lft.hard_byte_limit = XFRM_INF;
523 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800524 x->replay_maxage = 0;
525 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 spin_lock_init(&x->lock);
527 }
528 return x;
529}
530EXPORT_SYMBOL(xfrm_state_alloc);
531
532void __xfrm_state_destroy(struct xfrm_state *x)
533{
534 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
535
536 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700537 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 spin_unlock_bh(&xfrm_state_gc_lock);
539 schedule_work(&xfrm_state_gc_work);
540}
541EXPORT_SYMBOL(__xfrm_state_destroy);
542
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800543int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700545 int err = -ESRCH;
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (x->km.state != XFRM_STATE_DEAD) {
548 x->km.state = XFRM_STATE_DEAD;
549 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700550 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700551 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700552 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700553 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700554 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 /* All xfrm_state objects are created by xfrm_state_alloc.
558 * The xfrm_state_alloc call gives a reference, and that
559 * is what we are dropping here.
560 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800561 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700562 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700564
565 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800567EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700569int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700571 int err;
572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700574 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700576
577 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579EXPORT_SYMBOL(xfrm_state_delete);
580
Joy Latten4aa2e622007-06-04 19:05:57 -0400581#ifdef CONFIG_SECURITY_NETWORK_XFRM
582static inline int
583xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584{
Joy Latten4aa2e622007-06-04 19:05:57 -0400585 int i, err = 0;
586
587 for (i = 0; i <= xfrm_state_hmask; i++) {
588 struct hlist_node *entry;
589 struct xfrm_state *x;
590
591 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
592 if (xfrm_id_proto_match(x->id.proto, proto) &&
593 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700594 xfrm_audit_state_delete(x, 0,
595 audit_info->loginuid,
596 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400597 return err;
598 }
599 }
600 }
601
602 return err;
603}
604#else
605static inline int
606xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
607{
608 return 0;
609}
610#endif
611
612int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
613{
614 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400617 err = xfrm_state_flush_secctx_check(proto, audit_info);
618 if (err)
619 goto out;
620
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700621 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700622 struct hlist_node *entry;
623 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700625 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700627 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 xfrm_state_hold(x);
629 spin_unlock_bh(&xfrm_state_lock);
630
Joy Latten161a09e2006-11-27 13:11:54 -0600631 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700632 xfrm_audit_state_delete(x, err ? 0 : 1,
633 audit_info->loginuid,
634 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 xfrm_state_put(x);
636
637 spin_lock_bh(&xfrm_state_lock);
638 goto restart;
639 }
640 }
641 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400642 err = 0;
643
644out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 spin_unlock_bh(&xfrm_state_lock);
646 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400647 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648}
649EXPORT_SYMBOL(xfrm_state_flush);
650
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700651void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700652{
653 spin_lock_bh(&xfrm_state_lock);
654 si->sadcnt = xfrm_state_num;
655 si->sadhcnt = xfrm_state_hmask;
656 si->sadhmcnt = xfrm_state_hashmax;
657 spin_unlock_bh(&xfrm_state_lock);
658}
659EXPORT_SYMBOL(xfrm_sad_getinfo);
660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661static int
662xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
663 struct xfrm_tmpl *tmpl,
664 xfrm_address_t *daddr, xfrm_address_t *saddr,
665 unsigned short family)
666{
667 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
668 if (!afinfo)
669 return -1;
670 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
671 xfrm_state_put_afinfo(afinfo);
672 return 0;
673}
674
Al Viroa94cfd12006-09-27 18:47:24 -0700675static 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 -0700676{
677 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
678 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700679 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700680
David S. Miller8f126e32006-08-24 02:45:07 -0700681 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700682 if (x->props.family != family ||
683 x->id.spi != spi ||
684 x->id.proto != proto)
685 continue;
686
687 switch (family) {
688 case AF_INET:
689 if (x->id.daddr.a4 != daddr->a4)
690 continue;
691 break;
692 case AF_INET6:
693 if (!ipv6_addr_equal((struct in6_addr *)daddr,
694 (struct in6_addr *)
695 x->id.daddr.a6))
696 continue;
697 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700698 }
David S. Milleredcd5822006-08-24 00:42:45 -0700699
700 xfrm_state_hold(x);
701 return x;
702 }
703
704 return NULL;
705}
706
707static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
708{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700709 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700710 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700711 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700712
David S. Miller8f126e32006-08-24 02:45:07 -0700713 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700714 if (x->props.family != family ||
715 x->id.proto != proto)
716 continue;
717
718 switch (family) {
719 case AF_INET:
720 if (x->id.daddr.a4 != daddr->a4 ||
721 x->props.saddr.a4 != saddr->a4)
722 continue;
723 break;
724 case AF_INET6:
725 if (!ipv6_addr_equal((struct in6_addr *)daddr,
726 (struct in6_addr *)
727 x->id.daddr.a6) ||
728 !ipv6_addr_equal((struct in6_addr *)saddr,
729 (struct in6_addr *)
730 x->props.saddr.a6))
731 continue;
732 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700733 }
David S. Milleredcd5822006-08-24 00:42:45 -0700734
735 xfrm_state_hold(x);
736 return x;
737 }
738
739 return NULL;
740}
741
742static inline struct xfrm_state *
743__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
744{
745 if (use_spi)
746 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
747 x->id.proto, family);
748 else
749 return __xfrm_state_lookup_byaddr(&x->id.daddr,
750 &x->props.saddr,
751 x->id.proto, family);
752}
753
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700754static void xfrm_hash_grow_check(int have_hash_collision)
755{
756 if (have_hash_collision &&
757 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
758 xfrm_state_num > xfrm_state_hmask)
759 schedule_work(&xfrm_hash_work);
760}
761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900763xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 struct flowi *fl, struct xfrm_tmpl *tmpl,
765 struct xfrm_policy *pol, int *err,
766 unsigned short family)
767{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800768 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700769 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 struct xfrm_state *x, *x0;
771 int acquire_in_progress = 0;
772 int error = 0;
773 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800776 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700777 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 if (x->props.family == family &&
779 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700780 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 xfrm_state_addr_check(x, daddr, saddr, family) &&
782 tmpl->mode == x->props.mode &&
783 tmpl->id.proto == x->id.proto &&
784 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
785 /* Resolution logic:
786 1. There is a valid state with matching selector.
787 Done.
788 2. Valid state with inappropriate selector. Skip.
789
790 Entering area of "sysdeps".
791
792 3. If state is not valid, selector is temporary,
793 it selects only session which triggered
794 previous resolution. Key manager will do
795 something to install a state with proper
796 selector.
797 */
798 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700799 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700800 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 continue;
802 if (!best ||
803 best->km.dying > x->km.dying ||
804 (best->km.dying == x->km.dying &&
805 best->curlft.add_time < x->curlft.add_time))
806 best = x;
807 } else if (x->km.state == XFRM_STATE_ACQ) {
808 acquire_in_progress = 1;
809 } else if (x->km.state == XFRM_STATE_ERROR ||
810 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700811 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700812 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 error = -ESRCH;
814 }
815 }
816 }
817
818 x = best;
819 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700820 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700821 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
822 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 xfrm_state_put(x0);
824 error = -EEXIST;
825 goto out;
826 }
827 x = xfrm_state_alloc();
828 if (x == NULL) {
829 error = -ENOMEM;
830 goto out;
831 }
832 /* Initialize temporary selector matching only
833 * to current session. */
834 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
835
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700836 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
837 if (error) {
838 x->km.state = XFRM_STATE_DEAD;
839 xfrm_state_put(x);
840 x = NULL;
841 goto out;
842 }
843
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 if (km_query(x, tmpl, pol) == 0) {
845 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700846 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700847 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700848 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 if (x->id.spi) {
850 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700851 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 }
David S. Miller01e67d02007-05-25 00:41:38 -0700853 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
854 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700856 xfrm_state_num++;
857 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 } else {
859 x->km.state = XFRM_STATE_DEAD;
860 xfrm_state_put(x);
861 x = NULL;
862 error = -ESRCH;
863 }
864 }
865out:
866 if (x)
867 xfrm_state_hold(x);
868 else
869 *err = acquire_in_progress ? -EAGAIN : error;
870 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 return x;
872}
873
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700874struct xfrm_state *
875xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
876 unsigned short family, u8 mode, u8 proto, u32 reqid)
877{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800878 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700879 struct xfrm_state *rx = NULL, *x = NULL;
880 struct hlist_node *entry;
881
882 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800883 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700884 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
885 if (x->props.family == family &&
886 x->props.reqid == reqid &&
887 !(x->props.flags & XFRM_STATE_WILDRECV) &&
888 xfrm_state_addr_check(x, daddr, saddr, family) &&
889 mode == x->props.mode &&
890 proto == x->id.proto &&
891 x->km.state == XFRM_STATE_VALID) {
892 rx = x;
893 break;
894 }
895 }
896
897 if (rx)
898 xfrm_state_hold(rx);
899 spin_unlock(&xfrm_state_lock);
900
901
902 return rx;
903}
904EXPORT_SYMBOL(xfrm_stateonly_find);
905
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906static void __xfrm_state_insert(struct xfrm_state *x)
907{
David S. Millera624c102006-08-24 03:24:33 -0700908 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
David S. Miller9d4a7062006-08-24 03:18:09 -0700910 x->genid = ++xfrm_state_genid;
911
David S. Millerc1969f22006-08-24 04:00:03 -0700912 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
913 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700914 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700916 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700917 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700919 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700920 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
921 x->props.family);
922
David S. Miller8f126e32006-08-24 02:45:07 -0700923 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700924 }
925
David S. Millera47f0ce2006-08-24 03:54:22 -0700926 mod_timer(&x->timer, jiffies + HZ);
927 if (x->replay_maxage)
928 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700931
932 xfrm_state_num++;
933
David S. Miller918049f2006-10-12 22:03:24 -0700934 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935}
936
David S. Millerc7f5ea32006-08-24 03:29:04 -0700937/* xfrm_state_lock is held */
938static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
939{
940 unsigned short family = xnew->props.family;
941 u32 reqid = xnew->props.reqid;
942 struct xfrm_state *x;
943 struct hlist_node *entry;
944 unsigned int h;
945
David S. Millerc1969f22006-08-24 04:00:03 -0700946 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700947 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
948 if (x->props.family == family &&
949 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700950 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
951 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700952 x->genid = xfrm_state_genid;
953 }
954}
955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956void xfrm_state_insert(struct xfrm_state *x)
957{
958 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700959 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 __xfrm_state_insert(x);
961 spin_unlock_bh(&xfrm_state_lock);
962}
963EXPORT_SYMBOL(xfrm_state_insert);
964
David S. Miller27708342006-08-24 00:13:10 -0700965/* xfrm_state_lock is held */
966static 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)
967{
David S. Millerc1969f22006-08-24 04:00:03 -0700968 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700969 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700970 struct xfrm_state *x;
971
David S. Miller8f126e32006-08-24 02:45:07 -0700972 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700973 if (x->props.reqid != reqid ||
974 x->props.mode != mode ||
975 x->props.family != family ||
976 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700977 x->id.spi != 0 ||
978 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700979 continue;
980
981 switch (family) {
982 case AF_INET:
983 if (x->id.daddr.a4 != daddr->a4 ||
984 x->props.saddr.a4 != saddr->a4)
985 continue;
986 break;
987 case AF_INET6:
988 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
989 (struct in6_addr *)daddr) ||
990 !ipv6_addr_equal((struct in6_addr *)
991 x->props.saddr.a6,
992 (struct in6_addr *)saddr))
993 continue;
994 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700995 }
David S. Miller27708342006-08-24 00:13:10 -0700996
997 xfrm_state_hold(x);
998 return x;
999 }
1000
1001 if (!create)
1002 return NULL;
1003
1004 x = xfrm_state_alloc();
1005 if (likely(x)) {
1006 switch (family) {
1007 case AF_INET:
1008 x->sel.daddr.a4 = daddr->a4;
1009 x->sel.saddr.a4 = saddr->a4;
1010 x->sel.prefixlen_d = 32;
1011 x->sel.prefixlen_s = 32;
1012 x->props.saddr.a4 = saddr->a4;
1013 x->id.daddr.a4 = daddr->a4;
1014 break;
1015
1016 case AF_INET6:
1017 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1018 (struct in6_addr *)daddr);
1019 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1020 (struct in6_addr *)saddr);
1021 x->sel.prefixlen_d = 128;
1022 x->sel.prefixlen_s = 128;
1023 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1024 (struct in6_addr *)saddr);
1025 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1026 (struct in6_addr *)daddr);
1027 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001028 }
David S. Miller27708342006-08-24 00:13:10 -07001029
1030 x->km.state = XFRM_STATE_ACQ;
1031 x->id.proto = proto;
1032 x->props.family = family;
1033 x->props.mode = mode;
1034 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001035 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001036 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001037 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001038 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001039 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001040 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001041 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001042
1043 xfrm_state_num++;
1044
1045 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001046 }
1047
1048 return x;
1049}
1050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1052
1053int xfrm_state_add(struct xfrm_state *x)
1054{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 struct xfrm_state *x1;
1056 int family;
1057 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001058 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
1060 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
1062 spin_lock_bh(&xfrm_state_lock);
1063
David S. Milleredcd5822006-08-24 00:42:45 -07001064 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 if (x1) {
1066 xfrm_state_put(x1);
1067 x1 = NULL;
1068 err = -EEXIST;
1069 goto out;
1070 }
1071
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001072 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001074 if (x1 && ((x1->id.proto != x->id.proto) ||
1075 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 xfrm_state_put(x1);
1077 x1 = NULL;
1078 }
1079 }
1080
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001081 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001082 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1083 x->id.proto,
1084 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
David S. Millerc7f5ea32006-08-24 03:29:04 -07001086 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 __xfrm_state_insert(x);
1088 err = 0;
1089
1090out:
1091 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
1093 if (x1) {
1094 xfrm_state_delete(x1);
1095 xfrm_state_put(x1);
1096 }
1097
1098 return err;
1099}
1100EXPORT_SYMBOL(xfrm_state_add);
1101
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001102#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001103static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001104{
1105 int err = -ENOMEM;
1106 struct xfrm_state *x = xfrm_state_alloc();
1107 if (!x)
1108 goto error;
1109
1110 memcpy(&x->id, &orig->id, sizeof(x->id));
1111 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1112 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1113 x->props.mode = orig->props.mode;
1114 x->props.replay_window = orig->props.replay_window;
1115 x->props.reqid = orig->props.reqid;
1116 x->props.family = orig->props.family;
1117 x->props.saddr = orig->props.saddr;
1118
1119 if (orig->aalg) {
1120 x->aalg = xfrm_algo_clone(orig->aalg);
1121 if (!x->aalg)
1122 goto error;
1123 }
1124 x->props.aalgo = orig->props.aalgo;
1125
1126 if (orig->ealg) {
1127 x->ealg = xfrm_algo_clone(orig->ealg);
1128 if (!x->ealg)
1129 goto error;
1130 }
1131 x->props.ealgo = orig->props.ealgo;
1132
1133 if (orig->calg) {
1134 x->calg = xfrm_algo_clone(orig->calg);
1135 if (!x->calg)
1136 goto error;
1137 }
1138 x->props.calgo = orig->props.calgo;
1139
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001140 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001141 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1142 if (!x->encap)
1143 goto error;
1144 }
1145
1146 if (orig->coaddr) {
1147 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1148 GFP_KERNEL);
1149 if (!x->coaddr)
1150 goto error;
1151 }
1152
1153 err = xfrm_init_state(x);
1154 if (err)
1155 goto error;
1156
1157 x->props.flags = orig->props.flags;
1158
1159 x->curlft.add_time = orig->curlft.add_time;
1160 x->km.state = orig->km.state;
1161 x->km.seq = orig->km.seq;
1162
1163 return x;
1164
1165 error:
1166 if (errp)
1167 *errp = err;
1168 if (x) {
1169 kfree(x->aalg);
1170 kfree(x->ealg);
1171 kfree(x->calg);
1172 kfree(x->encap);
1173 kfree(x->coaddr);
1174 }
1175 kfree(x);
1176 return NULL;
1177}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001178
1179/* xfrm_state_lock is held */
1180struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1181{
1182 unsigned int h;
1183 struct xfrm_state *x;
1184 struct hlist_node *entry;
1185
1186 if (m->reqid) {
1187 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1188 m->reqid, m->old_family);
1189 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1190 if (x->props.mode != m->mode ||
1191 x->id.proto != m->proto)
1192 continue;
1193 if (m->reqid && x->props.reqid != m->reqid)
1194 continue;
1195 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1196 m->old_family) ||
1197 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1198 m->old_family))
1199 continue;
1200 xfrm_state_hold(x);
1201 return x;
1202 }
1203 } else {
1204 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1205 m->old_family);
1206 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1207 if (x->props.mode != m->mode ||
1208 x->id.proto != m->proto)
1209 continue;
1210 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1211 m->old_family) ||
1212 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1213 m->old_family))
1214 continue;
1215 xfrm_state_hold(x);
1216 return x;
1217 }
1218 }
1219
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001220 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001221}
1222EXPORT_SYMBOL(xfrm_migrate_state_find);
1223
1224struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1225 struct xfrm_migrate *m)
1226{
1227 struct xfrm_state *xc;
1228 int err;
1229
1230 xc = xfrm_state_clone(x, &err);
1231 if (!xc)
1232 return NULL;
1233
1234 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1235 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1236
1237 /* add state */
1238 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1239 /* a care is needed when the destination address of the
1240 state is to be updated as it is a part of triplet */
1241 xfrm_state_insert(xc);
1242 } else {
1243 if ((err = xfrm_state_add(xc)) < 0)
1244 goto error;
1245 }
1246
1247 return xc;
1248error:
1249 kfree(xc);
1250 return NULL;
1251}
1252EXPORT_SYMBOL(xfrm_state_migrate);
1253#endif
1254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255int xfrm_state_update(struct xfrm_state *x)
1256{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 struct xfrm_state *x1;
1258 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001259 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001262 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 err = -ESRCH;
1265 if (!x1)
1266 goto out;
1267
1268 if (xfrm_state_kern(x1)) {
1269 xfrm_state_put(x1);
1270 err = -EEXIST;
1271 goto out;
1272 }
1273
1274 if (x1->km.state == XFRM_STATE_ACQ) {
1275 __xfrm_state_insert(x);
1276 x = NULL;
1277 }
1278 err = 0;
1279
1280out:
1281 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
1283 if (err)
1284 return err;
1285
1286 if (!x) {
1287 xfrm_state_delete(x1);
1288 xfrm_state_put(x1);
1289 return 0;
1290 }
1291
1292 err = -EINVAL;
1293 spin_lock_bh(&x1->lock);
1294 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1295 if (x->encap && x1->encap)
1296 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001297 if (x->coaddr && x1->coaddr) {
1298 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1299 }
1300 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1301 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1303 x1->km.dying = 0;
1304
David S. Millera47f0ce2006-08-24 03:54:22 -07001305 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (x1->curlft.use_time)
1307 xfrm_state_check_expire(x1);
1308
1309 err = 0;
1310 }
1311 spin_unlock_bh(&x1->lock);
1312
1313 xfrm_state_put(x1);
1314
1315 return err;
1316}
1317EXPORT_SYMBOL(xfrm_state_update);
1318
1319int xfrm_state_check_expire(struct xfrm_state *x)
1320{
1321 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001322 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 if (x->km.state != XFRM_STATE_VALID)
1325 return -EINVAL;
1326
1327 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1328 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001329 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001330 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return -EINVAL;
1332 }
1333
1334 if (!x->km.dying &&
1335 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001336 x->curlft.packets >= x->lft.soft_packet_limit)) {
1337 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001338 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001339 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 return 0;
1341}
1342EXPORT_SYMBOL(xfrm_state_check_expire);
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001345xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 unsigned short family)
1347{
1348 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
1350 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001351 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 return x;
1354}
1355EXPORT_SYMBOL(xfrm_state_lookup);
1356
1357struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001358xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1359 u8 proto, unsigned short family)
1360{
1361 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001362
1363 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001364 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001365 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001366 return x;
1367}
1368EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1369
1370struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001371xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1372 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 int create, unsigned short family)
1374{
1375 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
1377 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001378 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 return x;
1382}
1383EXPORT_SYMBOL(xfrm_find_acq);
1384
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001385#ifdef CONFIG_XFRM_SUB_POLICY
1386int
1387xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1388 unsigned short family)
1389{
1390 int err = 0;
1391 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1392 if (!afinfo)
1393 return -EAFNOSUPPORT;
1394
1395 spin_lock_bh(&xfrm_state_lock);
1396 if (afinfo->tmpl_sort)
1397 err = afinfo->tmpl_sort(dst, src, n);
1398 spin_unlock_bh(&xfrm_state_lock);
1399 xfrm_state_put_afinfo(afinfo);
1400 return err;
1401}
1402EXPORT_SYMBOL(xfrm_tmpl_sort);
1403
1404int
1405xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1406 unsigned short family)
1407{
1408 int err = 0;
1409 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1410 if (!afinfo)
1411 return -EAFNOSUPPORT;
1412
1413 spin_lock_bh(&xfrm_state_lock);
1414 if (afinfo->state_sort)
1415 err = afinfo->state_sort(dst, src, n);
1416 spin_unlock_bh(&xfrm_state_lock);
1417 xfrm_state_put_afinfo(afinfo);
1418 return err;
1419}
1420EXPORT_SYMBOL(xfrm_state_sort);
1421#endif
1422
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423/* Silly enough, but I'm lazy to build resolution list */
1424
1425static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1426{
1427 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
David S. Millerf034b5d2006-08-24 03:08:07 -07001429 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001430 struct hlist_node *entry;
1431 struct xfrm_state *x;
1432
1433 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1434 if (x->km.seq == seq &&
1435 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 xfrm_state_hold(x);
1437 return x;
1438 }
1439 }
1440 }
1441 return NULL;
1442}
1443
1444struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1445{
1446 struct xfrm_state *x;
1447
1448 spin_lock_bh(&xfrm_state_lock);
1449 x = __xfrm_find_acq_byseq(seq);
1450 spin_unlock_bh(&xfrm_state_lock);
1451 return x;
1452}
1453EXPORT_SYMBOL(xfrm_find_acq_byseq);
1454
1455u32 xfrm_get_acqseq(void)
1456{
1457 u32 res;
1458 static u32 acqseq;
1459 static DEFINE_SPINLOCK(acqseq_lock);
1460
1461 spin_lock_bh(&acqseq_lock);
1462 res = (++acqseq ? : ++acqseq);
1463 spin_unlock_bh(&acqseq_lock);
1464 return res;
1465}
1466EXPORT_SYMBOL(xfrm_get_acqseq);
1467
Herbert Xu658b2192007-10-09 13:29:52 -07001468int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
David S. Millerf034b5d2006-08-24 03:08:07 -07001470 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001472 int err = -ENOENT;
1473 __be32 minspi = htonl(low);
1474 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
Herbert Xu658b2192007-10-09 13:29:52 -07001476 spin_lock_bh(&x->lock);
1477 if (x->km.state == XFRM_STATE_DEAD)
1478 goto unlock;
1479
1480 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001482 goto unlock;
1483
1484 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
1486 if (minspi == maxspi) {
1487 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1488 if (x0) {
1489 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001490 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 }
1492 x->id.spi = minspi;
1493 } else {
1494 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001495 for (h=0; h<high-low+1; h++) {
1496 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1498 if (x0 == NULL) {
1499 x->id.spi = htonl(spi);
1500 break;
1501 }
1502 xfrm_state_put(x0);
1503 }
1504 }
1505 if (x->id.spi) {
1506 spin_lock_bh(&xfrm_state_lock);
1507 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001508 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001510
1511 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 }
Herbert Xu658b2192007-10-09 13:29:52 -07001513
1514unlock:
1515 spin_unlock_bh(&x->lock);
1516
1517 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518}
1519EXPORT_SYMBOL(xfrm_alloc_spi);
1520
1521int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1522 void *data)
1523{
1524 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001525 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001526 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 int count = 0;
1528 int err = 0;
1529
1530 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001531 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001532 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001533 if (!xfrm_id_proto_match(x->id.proto, proto))
1534 continue;
1535 if (last) {
1536 err = func(last, count, data);
1537 if (err)
1538 goto out;
1539 }
1540 last = x;
1541 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 }
1543 }
1544 if (count == 0) {
1545 err = -ENOENT;
1546 goto out;
1547 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001548 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549out:
1550 spin_unlock_bh(&xfrm_state_lock);
1551 return err;
1552}
1553EXPORT_SYMBOL(xfrm_state_walk);
1554
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001555
1556void xfrm_replay_notify(struct xfrm_state *x, int event)
1557{
1558 struct km_event c;
1559 /* we send notify messages in case
1560 * 1. we updated on of the sequence numbers, and the seqno difference
1561 * is at least x->replay_maxdiff, in this case we also update the
1562 * timeout of our timer function
1563 * 2. if x->replay_maxage has elapsed since last update,
1564 * and there were changes
1565 *
1566 * The state structure must be locked!
1567 */
1568
1569 switch (event) {
1570 case XFRM_REPLAY_UPDATE:
1571 if (x->replay_maxdiff &&
1572 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001573 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1574 if (x->xflags & XFRM_TIME_DEFER)
1575 event = XFRM_REPLAY_TIMEOUT;
1576 else
1577 return;
1578 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001579
1580 break;
1581
1582 case XFRM_REPLAY_TIMEOUT:
1583 if ((x->replay.seq == x->preplay.seq) &&
1584 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001585 (x->replay.oseq == x->preplay.oseq)) {
1586 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001587 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001588 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001589
1590 break;
1591 }
1592
1593 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1594 c.event = XFRM_MSG_NEWAE;
1595 c.data.aevent = event;
1596 km_state_notify(x, &c);
1597
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001598 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001599 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001600 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001601}
1602
1603static void xfrm_replay_timer_handler(unsigned long data)
1604{
1605 struct xfrm_state *x = (struct xfrm_state*)data;
1606
1607 spin_lock(&x->lock);
1608
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001609 if (x->km.state == XFRM_STATE_VALID) {
1610 if (xfrm_aevent_is_on())
1611 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1612 else
1613 x->xflags |= XFRM_TIME_DEFER;
1614 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001615
1616 spin_unlock(&x->lock);
1617}
1618
Paul Mooreafeb14b2007-12-21 14:58:11 -08001619int xfrm_replay_check(struct xfrm_state *x,
1620 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621{
1622 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001623 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
1625 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001626 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
1628 if (likely(seq > x->replay.seq))
1629 return 0;
1630
1631 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001632 if (diff >= min_t(unsigned int, x->props.replay_window,
1633 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001635 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 }
1637
1638 if (x->replay.bitmap & (1U << diff)) {
1639 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001640 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 }
1642 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001643
1644err:
1645 xfrm_audit_state_replay(x, skb, net_seq);
1646 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Al Viro61f46272006-09-27 18:48:33 -07001649void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650{
1651 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001652 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 if (seq > x->replay.seq) {
1655 diff = seq - x->replay.seq;
1656 if (diff < x->props.replay_window)
1657 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1658 else
1659 x->replay.bitmap = 1;
1660 x->replay.seq = seq;
1661 } else {
1662 diff = x->replay.seq - seq;
1663 x->replay.bitmap |= (1U << diff);
1664 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001665
1666 if (xfrm_aevent_is_on())
1667 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
Denis Chengdf018122007-12-07 00:51:11 -08001670static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671static DEFINE_RWLOCK(xfrm_km_lock);
1672
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001673void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674{
1675 struct xfrm_mgr *km;
1676
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001677 read_lock(&xfrm_km_lock);
1678 list_for_each_entry(km, &xfrm_km_list, list)
1679 if (km->notify_policy)
1680 km->notify_policy(xp, dir, c);
1681 read_unlock(&xfrm_km_lock);
1682}
1683
1684void km_state_notify(struct xfrm_state *x, struct km_event *c)
1685{
1686 struct xfrm_mgr *km;
1687 read_lock(&xfrm_km_lock);
1688 list_for_each_entry(km, &xfrm_km_list, list)
1689 if (km->notify)
1690 km->notify(x, c);
1691 read_unlock(&xfrm_km_lock);
1692}
1693
1694EXPORT_SYMBOL(km_policy_notify);
1695EXPORT_SYMBOL(km_state_notify);
1696
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001697void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001698{
1699 struct km_event c;
1700
Herbert Xubf088672005-06-18 22:44:00 -07001701 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001702 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001703 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001704 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
1706 if (hard)
1707 wake_up(&km_waitq);
1708}
1709
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001710EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001711/*
1712 * We send to all registered managers regardless of failure
1713 * We are happy with one success
1714*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001715int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001717 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 struct xfrm_mgr *km;
1719
1720 read_lock(&xfrm_km_lock);
1721 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001722 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1723 if (!acqret)
1724 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 }
1726 read_unlock(&xfrm_km_lock);
1727 return err;
1728}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001729EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
Al Viro5d36b182006-11-08 00:24:06 -08001731int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732{
1733 int err = -EINVAL;
1734 struct xfrm_mgr *km;
1735
1736 read_lock(&xfrm_km_lock);
1737 list_for_each_entry(km, &xfrm_km_list, list) {
1738 if (km->new_mapping)
1739 err = km->new_mapping(x, ipaddr, sport);
1740 if (!err)
1741 break;
1742 }
1743 read_unlock(&xfrm_km_lock);
1744 return err;
1745}
1746EXPORT_SYMBOL(km_new_mapping);
1747
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001748void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001750 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
Herbert Xubf088672005-06-18 22:44:00 -07001752 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001753 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001754 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001755 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756
1757 if (hard)
1758 wake_up(&km_waitq);
1759}
David S. Millera70fcb02006-03-20 19:18:52 -08001760EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001762#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001763int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1764 struct xfrm_migrate *m, int num_migrate)
1765{
1766 int err = -EINVAL;
1767 int ret;
1768 struct xfrm_mgr *km;
1769
1770 read_lock(&xfrm_km_lock);
1771 list_for_each_entry(km, &xfrm_km_list, list) {
1772 if (km->migrate) {
1773 ret = km->migrate(sel, dir, type, m, num_migrate);
1774 if (!ret)
1775 err = ret;
1776 }
1777 }
1778 read_unlock(&xfrm_km_lock);
1779 return err;
1780}
1781EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001782#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001783
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001784int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1785{
1786 int err = -EINVAL;
1787 int ret;
1788 struct xfrm_mgr *km;
1789
1790 read_lock(&xfrm_km_lock);
1791 list_for_each_entry(km, &xfrm_km_list, list) {
1792 if (km->report) {
1793 ret = km->report(proto, sel, addr);
1794 if (!ret)
1795 err = ret;
1796 }
1797 }
1798 read_unlock(&xfrm_km_lock);
1799 return err;
1800}
1801EXPORT_SYMBOL(km_report);
1802
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1804{
1805 int err;
1806 u8 *data;
1807 struct xfrm_mgr *km;
1808 struct xfrm_policy *pol = NULL;
1809
1810 if (optlen <= 0 || optlen > PAGE_SIZE)
1811 return -EMSGSIZE;
1812
1813 data = kmalloc(optlen, GFP_KERNEL);
1814 if (!data)
1815 return -ENOMEM;
1816
1817 err = -EFAULT;
1818 if (copy_from_user(data, optval, optlen))
1819 goto out;
1820
1821 err = -EINVAL;
1822 read_lock(&xfrm_km_lock);
1823 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001824 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 optlen, &err);
1826 if (err >= 0)
1827 break;
1828 }
1829 read_unlock(&xfrm_km_lock);
1830
1831 if (err >= 0) {
1832 xfrm_sk_policy_insert(sk, err, pol);
1833 xfrm_pol_put(pol);
1834 err = 0;
1835 }
1836
1837out:
1838 kfree(data);
1839 return err;
1840}
1841EXPORT_SYMBOL(xfrm_user_policy);
1842
1843int xfrm_register_km(struct xfrm_mgr *km)
1844{
1845 write_lock_bh(&xfrm_km_lock);
1846 list_add_tail(&km->list, &xfrm_km_list);
1847 write_unlock_bh(&xfrm_km_lock);
1848 return 0;
1849}
1850EXPORT_SYMBOL(xfrm_register_km);
1851
1852int xfrm_unregister_km(struct xfrm_mgr *km)
1853{
1854 write_lock_bh(&xfrm_km_lock);
1855 list_del(&km->list);
1856 write_unlock_bh(&xfrm_km_lock);
1857 return 0;
1858}
1859EXPORT_SYMBOL(xfrm_unregister_km);
1860
1861int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1862{
1863 int err = 0;
1864 if (unlikely(afinfo == NULL))
1865 return -EINVAL;
1866 if (unlikely(afinfo->family >= NPROTO))
1867 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001868 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1870 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001871 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001873 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 return err;
1875}
1876EXPORT_SYMBOL(xfrm_state_register_afinfo);
1877
1878int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1879{
1880 int err = 0;
1881 if (unlikely(afinfo == NULL))
1882 return -EINVAL;
1883 if (unlikely(afinfo->family >= NPROTO))
1884 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001885 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1887 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1888 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001889 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001892 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 return err;
1894}
1895EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1896
Herbert Xu17c2a422007-10-17 21:33:12 -07001897static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898{
1899 struct xfrm_state_afinfo *afinfo;
1900 if (unlikely(family >= NPROTO))
1901 return NULL;
1902 read_lock(&xfrm_state_afinfo_lock);
1903 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001904 if (unlikely(!afinfo))
1905 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 return afinfo;
1907}
1908
Herbert Xu17c2a422007-10-17 21:33:12 -07001909static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001910 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
Herbert Xu546be242006-05-27 23:03:58 -07001912 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913}
1914
1915/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1916void xfrm_state_delete_tunnel(struct xfrm_state *x)
1917{
1918 if (x->tunnel) {
1919 struct xfrm_state *t = x->tunnel;
1920
1921 if (atomic_read(&t->tunnel_users) == 2)
1922 xfrm_state_delete(t);
1923 atomic_dec(&t->tunnel_users);
1924 xfrm_state_put(t);
1925 x->tunnel = NULL;
1926 }
1927}
1928EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1929
1930int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1931{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001932 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Patrick McHardyc5c25232007-04-09 11:47:18 -07001934 spin_lock_bh(&x->lock);
1935 if (x->km.state == XFRM_STATE_VALID &&
1936 x->type && x->type->get_mtu)
1937 res = x->type->get_mtu(x, mtu);
1938 else
Patrick McHardy28121612007-06-18 22:30:15 -07001939 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001940 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 return res;
1942}
1943
Herbert Xu72cb6962005-06-20 13:18:08 -07001944int xfrm_init_state(struct xfrm_state *x)
1945{
Herbert Xud094cd82005-06-20 13:19:41 -07001946 struct xfrm_state_afinfo *afinfo;
1947 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001948 int err;
1949
Herbert Xud094cd82005-06-20 13:19:41 -07001950 err = -EAFNOSUPPORT;
1951 afinfo = xfrm_state_get_afinfo(family);
1952 if (!afinfo)
1953 goto error;
1954
1955 err = 0;
1956 if (afinfo->init_flags)
1957 err = afinfo->init_flags(x);
1958
1959 xfrm_state_put_afinfo(afinfo);
1960
1961 if (err)
1962 goto error;
1963
1964 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001965 x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1966 if (x->inner_mode == NULL)
1967 goto error;
1968
1969 if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1970 family != x->sel.family)
1971 goto error;
1972
Herbert Xud094cd82005-06-20 13:19:41 -07001973 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001974 if (x->type == NULL)
1975 goto error;
1976
1977 err = x->type->init_state(x);
1978 if (err)
1979 goto error;
1980
Herbert Xu13996372007-10-17 21:35:51 -07001981 x->outer_mode = xfrm_get_mode(x->props.mode, family);
1982 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07001983 goto error;
1984
Herbert Xu72cb6962005-06-20 13:18:08 -07001985 x->km.state = XFRM_STATE_VALID;
1986
1987error:
1988 return err;
1989}
1990
1991EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001992
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993void __init xfrm_state_init(void)
1994{
David S. Millerf034b5d2006-08-24 03:08:07 -07001995 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996
David S. Millerf034b5d2006-08-24 03:08:07 -07001997 sz = sizeof(struct hlist_head) * 8;
1998
David S. Miller44e36b42006-08-24 04:50:50 -07001999 xfrm_state_bydst = xfrm_hash_alloc(sz);
2000 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2001 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002002 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2003 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2004 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2005
David Howellsc4028952006-11-22 14:57:56 +00002006 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007}
2008
Joy Lattenab5f5e82007-09-17 11:51:22 -07002009#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002010static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2011 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002012{
Paul Moore68277ac2007-12-20 20:49:33 -08002013 struct xfrm_sec_ctx *ctx = x->security;
2014 u32 spi = ntohl(x->id.spi);
2015
2016 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002017 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002018 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002019
2020 switch(x->props.family) {
2021 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002022 audit_log_format(audit_buf,
2023 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002024 NIPQUAD(x->props.saddr.a4),
2025 NIPQUAD(x->id.daddr.a4));
2026 break;
2027 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002028 audit_log_format(audit_buf,
2029 " src=" NIP6_FMT " dst=" NIP6_FMT,
2030 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2031 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002032 break;
2033 }
Paul Moore68277ac2007-12-20 20:49:33 -08002034
2035 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002036}
2037
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002038static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2039 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002040{
2041 struct iphdr *iph4;
2042 struct ipv6hdr *iph6;
2043
2044 switch (family) {
2045 case AF_INET:
2046 iph4 = ip_hdr(skb);
2047 audit_log_format(audit_buf,
2048 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2049 NIPQUAD(iph4->saddr),
2050 NIPQUAD(iph4->daddr));
2051 break;
2052 case AF_INET6:
2053 iph6 = ipv6_hdr(skb);
2054 audit_log_format(audit_buf,
2055 " src=" NIP6_FMT " dst=" NIP6_FMT
2056 " flowlbl=0x%x%x%x",
2057 NIP6(iph6->saddr),
2058 NIP6(iph6->daddr),
2059 iph6->flow_lbl[0] & 0x0f,
2060 iph6->flow_lbl[1],
2061 iph6->flow_lbl[2]);
2062 break;
2063 }
2064}
2065
Paul Moore68277ac2007-12-20 20:49:33 -08002066void xfrm_audit_state_add(struct xfrm_state *x, int result,
2067 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002068{
2069 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002070
Paul Mooreafeb14b2007-12-21 14:58:11 -08002071 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002072 if (audit_buf == NULL)
2073 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002074 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2075 xfrm_audit_helper_sainfo(x, audit_buf);
2076 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002077 audit_log_end(audit_buf);
2078}
2079EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2080
Paul Moore68277ac2007-12-20 20:49:33 -08002081void xfrm_audit_state_delete(struct xfrm_state *x, int result,
2082 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002083{
2084 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002085
Paul Mooreafeb14b2007-12-21 14:58:11 -08002086 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002087 if (audit_buf == NULL)
2088 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002089 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2090 xfrm_audit_helper_sainfo(x, audit_buf);
2091 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002092 audit_log_end(audit_buf);
2093}
2094EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002095
2096void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2097 struct sk_buff *skb)
2098{
2099 struct audit_buffer *audit_buf;
2100 u32 spi;
2101
2102 audit_buf = xfrm_audit_start("SA-replay-overflow");
2103 if (audit_buf == NULL)
2104 return;
2105 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2106 /* don't record the sequence number because it's inherent in this kind
2107 * of audit message */
2108 spi = ntohl(x->id.spi);
2109 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2110 audit_log_end(audit_buf);
2111}
2112EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2113
2114static void xfrm_audit_state_replay(struct xfrm_state *x,
2115 struct sk_buff *skb, __be32 net_seq)
2116{
2117 struct audit_buffer *audit_buf;
2118 u32 spi;
2119
2120 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2121 if (audit_buf == NULL)
2122 return;
2123 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2124 spi = ntohl(x->id.spi);
2125 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2126 spi, spi, ntohl(net_seq));
2127 audit_log_end(audit_buf);
2128}
2129
2130void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2131{
2132 struct audit_buffer *audit_buf;
2133
2134 audit_buf = xfrm_audit_start("SA-notfound");
2135 if (audit_buf == NULL)
2136 return;
2137 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2138 audit_log_end(audit_buf);
2139}
2140EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2141
2142void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2143 __be32 net_spi, __be32 net_seq)
2144{
2145 struct audit_buffer *audit_buf;
2146 u32 spi;
2147
2148 audit_buf = xfrm_audit_start("SA-notfound");
2149 if (audit_buf == NULL)
2150 return;
2151 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2152 spi = ntohl(net_spi);
2153 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2154 spi, spi, ntohl(net_seq));
2155 audit_log_end(audit_buf);
2156}
2157EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2158
2159void xfrm_audit_state_icvfail(struct xfrm_state *x,
2160 struct sk_buff *skb, u8 proto)
2161{
2162 struct audit_buffer *audit_buf;
2163 __be32 net_spi;
2164 __be32 net_seq;
2165
2166 audit_buf = xfrm_audit_start("SA-icv-failure");
2167 if (audit_buf == NULL)
2168 return;
2169 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2170 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2171 u32 spi = ntohl(net_spi);
2172 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2173 spi, spi, ntohl(net_seq));
2174 }
2175 audit_log_end(audit_buf);
2176}
2177EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002178#endif /* CONFIG_AUDITSYSCALL */