blob: e25ff62ab2a62676e239cef57e9a288973d1ed25 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070027/* Each xfrm_state may be linked to two tables:
28
29 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070030 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 destination/tunnel endpoint. (output)
32 */
33
34static DEFINE_SPINLOCK(xfrm_state_lock);
35
David S. Millerf034b5d2006-08-24 03:08:07 -070036static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
David S. Miller9d4a7062006-08-24 03:18:09 -070037static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Herbert Xu17c2a422007-10-17 21:33:12 -070039static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
40static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
41
Paul Mooreafeb14b2007-12-21 14:58:11 -080042#ifdef CONFIG_AUDITSYSCALL
43static void xfrm_audit_state_replay(struct xfrm_state *x,
44 struct sk_buff *skb, __be32 net_seq);
45#else
46#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
47#endif /* CONFIG_AUDITSYSCALL */
48
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080049static inline unsigned int xfrm_dst_hash(struct net *net,
50 xfrm_address_t *daddr,
David S. Millerc1969f22006-08-24 04:00:03 -070051 xfrm_address_t *saddr,
52 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070053 unsigned short family)
54{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080055 return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070056}
57
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080058static inline unsigned int xfrm_src_hash(struct net *net,
59 xfrm_address_t *daddr,
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070060 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070061 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070062{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080063 return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070064}
65
David S. Miller2575b652006-08-24 03:26:44 -070066static inline unsigned int
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080067xfrm_spi_hash(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070068{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080069 return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070070}
71
David S. Millerf034b5d2006-08-24 03:08:07 -070072static void xfrm_hash_transfer(struct hlist_head *list,
73 struct hlist_head *ndsttable,
74 struct hlist_head *nsrctable,
75 struct hlist_head *nspitable,
76 unsigned int nhashmask)
77{
78 struct hlist_node *entry, *tmp;
79 struct xfrm_state *x;
80
81 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
82 unsigned int h;
83
David S. Millerc1969f22006-08-24 04:00:03 -070084 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
85 x->props.reqid, x->props.family,
86 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070087 hlist_add_head(&x->bydst, ndsttable+h);
88
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070089 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
90 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -070091 nhashmask);
92 hlist_add_head(&x->bysrc, nsrctable+h);
93
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -070094 if (x->id.spi) {
95 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
96 x->id.proto, x->props.family,
97 nhashmask);
98 hlist_add_head(&x->byspi, nspitable+h);
99 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700100 }
101}
102
Alexey Dobriyan63082732008-11-25 17:19:07 -0800103static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
David S. Millerf034b5d2006-08-24 03:08:07 -0700104{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800105 return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
David S. Millerf034b5d2006-08-24 03:08:07 -0700106}
107
108static DEFINE_MUTEX(hash_resize_mutex);
109
Alexey Dobriyan63082732008-11-25 17:19:07 -0800110static void xfrm_hash_resize(struct work_struct *work)
David S. Millerf034b5d2006-08-24 03:08:07 -0700111{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800112 struct net *net = container_of(work, struct net, xfrm.state_hash_work);
David S. Millerf034b5d2006-08-24 03:08:07 -0700113 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
114 unsigned long nsize, osize;
115 unsigned int nhashmask, ohashmask;
116 int i;
117
118 mutex_lock(&hash_resize_mutex);
119
Alexey Dobriyan63082732008-11-25 17:19:07 -0800120 nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
David S. Miller44e36b42006-08-24 04:50:50 -0700121 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700122 if (!ndst)
123 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700124 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700125 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700126 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700127 goto out_unlock;
128 }
David S. Miller44e36b42006-08-24 04:50:50 -0700129 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700130 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700131 xfrm_hash_free(ndst, nsize);
132 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700133 goto out_unlock;
134 }
135
136 spin_lock_bh(&xfrm_state_lock);
137
138 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
Alexey Dobriyan63082732008-11-25 17:19:07 -0800139 for (i = net->xfrm.state_hmask; i >= 0; i--)
140 xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
David S. Millerf034b5d2006-08-24 03:08:07 -0700141 nhashmask);
142
Alexey Dobriyan63082732008-11-25 17:19:07 -0800143 odst = net->xfrm.state_bydst;
144 osrc = net->xfrm.state_bysrc;
145 ospi = net->xfrm.state_byspi;
146 ohashmask = net->xfrm.state_hmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700147
Alexey Dobriyan63082732008-11-25 17:19:07 -0800148 net->xfrm.state_bydst = ndst;
149 net->xfrm.state_bysrc = nsrc;
150 net->xfrm.state_byspi = nspi;
151 net->xfrm.state_hmask = nhashmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700152
153 spin_unlock_bh(&xfrm_state_lock);
154
155 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700156 xfrm_hash_free(odst, osize);
157 xfrm_hash_free(osrc, osize);
158 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700159
160out_unlock:
161 mutex_unlock(&hash_resize_mutex);
162}
163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
165static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167static DEFINE_SPINLOCK(xfrm_state_gc_lock);
168
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800169int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800171int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800172void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700174static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
175{
176 struct xfrm_state_afinfo *afinfo;
177 if (unlikely(family >= NPROTO))
178 return NULL;
179 write_lock_bh(&xfrm_state_afinfo_lock);
180 afinfo = xfrm_state_afinfo[family];
181 if (unlikely(!afinfo))
182 write_unlock_bh(&xfrm_state_afinfo_lock);
183 return afinfo;
184}
185
186static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800187 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700188{
189 write_unlock_bh(&xfrm_state_afinfo_lock);
190}
191
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800192int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700193{
194 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800195 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700196 int err = 0;
197
198 if (unlikely(afinfo == NULL))
199 return -EAFNOSUPPORT;
200 typemap = afinfo->type_map;
201
202 if (likely(typemap[type->proto] == NULL))
203 typemap[type->proto] = type;
204 else
205 err = -EEXIST;
206 xfrm_state_unlock_afinfo(afinfo);
207 return err;
208}
209EXPORT_SYMBOL(xfrm_register_type);
210
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800211int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700212{
213 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800214 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700215 int err = 0;
216
217 if (unlikely(afinfo == NULL))
218 return -EAFNOSUPPORT;
219 typemap = afinfo->type_map;
220
221 if (unlikely(typemap[type->proto] != type))
222 err = -ENOENT;
223 else
224 typemap[type->proto] = NULL;
225 xfrm_state_unlock_afinfo(afinfo);
226 return err;
227}
228EXPORT_SYMBOL(xfrm_unregister_type);
229
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800230static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700231{
232 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800233 const struct xfrm_type **typemap;
234 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700235 int modload_attempted = 0;
236
237retry:
238 afinfo = xfrm_state_get_afinfo(family);
239 if (unlikely(afinfo == NULL))
240 return NULL;
241 typemap = afinfo->type_map;
242
243 type = typemap[proto];
244 if (unlikely(type && !try_module_get(type->owner)))
245 type = NULL;
246 if (!type && !modload_attempted) {
247 xfrm_state_put_afinfo(afinfo);
248 request_module("xfrm-type-%d-%d", family, proto);
249 modload_attempted = 1;
250 goto retry;
251 }
252
253 xfrm_state_put_afinfo(afinfo);
254 return type;
255}
256
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800257static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700258{
259 module_put(type->owner);
260}
261
262int xfrm_register_mode(struct xfrm_mode *mode, int family)
263{
264 struct xfrm_state_afinfo *afinfo;
265 struct xfrm_mode **modemap;
266 int err;
267
268 if (unlikely(mode->encap >= XFRM_MODE_MAX))
269 return -EINVAL;
270
271 afinfo = xfrm_state_lock_afinfo(family);
272 if (unlikely(afinfo == NULL))
273 return -EAFNOSUPPORT;
274
275 err = -EEXIST;
276 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700277 if (modemap[mode->encap])
278 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700279
Herbert Xu17c2a422007-10-17 21:33:12 -0700280 err = -ENOENT;
281 if (!try_module_get(afinfo->owner))
282 goto out;
283
284 mode->afinfo = afinfo;
285 modemap[mode->encap] = mode;
286 err = 0;
287
288out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700289 xfrm_state_unlock_afinfo(afinfo);
290 return err;
291}
292EXPORT_SYMBOL(xfrm_register_mode);
293
294int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
295{
296 struct xfrm_state_afinfo *afinfo;
297 struct xfrm_mode **modemap;
298 int err;
299
300 if (unlikely(mode->encap >= XFRM_MODE_MAX))
301 return -EINVAL;
302
303 afinfo = xfrm_state_lock_afinfo(family);
304 if (unlikely(afinfo == NULL))
305 return -EAFNOSUPPORT;
306
307 err = -ENOENT;
308 modemap = afinfo->mode_map;
309 if (likely(modemap[mode->encap] == mode)) {
310 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700311 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700312 err = 0;
313 }
314
315 xfrm_state_unlock_afinfo(afinfo);
316 return err;
317}
318EXPORT_SYMBOL(xfrm_unregister_mode);
319
320static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
321{
322 struct xfrm_state_afinfo *afinfo;
323 struct xfrm_mode *mode;
324 int modload_attempted = 0;
325
326 if (unlikely(encap >= XFRM_MODE_MAX))
327 return NULL;
328
329retry:
330 afinfo = xfrm_state_get_afinfo(family);
331 if (unlikely(afinfo == NULL))
332 return NULL;
333
334 mode = afinfo->mode_map[encap];
335 if (unlikely(mode && !try_module_get(mode->owner)))
336 mode = NULL;
337 if (!mode && !modload_attempted) {
338 xfrm_state_put_afinfo(afinfo);
339 request_module("xfrm-mode-%d-%d", family, encap);
340 modload_attempted = 1;
341 goto retry;
342 }
343
344 xfrm_state_put_afinfo(afinfo);
345 return mode;
346}
347
348static void xfrm_put_mode(struct xfrm_mode *mode)
349{
350 module_put(mode->owner);
351}
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353static void xfrm_state_gc_destroy(struct xfrm_state *x)
354{
David S. Millera47f0ce2006-08-24 03:54:22 -0700355 del_timer_sync(&x->timer);
356 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800357 kfree(x->aalg);
358 kfree(x->ealg);
359 kfree(x->calg);
360 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700361 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700362 if (x->inner_mode)
363 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700364 if (x->inner_mode_iaf)
365 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700366 if (x->outer_mode)
367 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if (x->type) {
369 x->type->destructor(x);
370 xfrm_put_type(x->type);
371 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800372 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 kfree(x);
374}
375
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800376static void xfrm_state_gc_task(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800378 struct net *net = container_of(work, struct net, xfrm.state_gc_work);
Herbert Xu12a169e2008-10-01 07:03:24 -0700379 struct xfrm_state *x;
380 struct hlist_node *entry, *tmp;
381 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800384 hlist_move_list(&net->xfrm.state_gc_list, &gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 spin_unlock_bh(&xfrm_state_gc_lock);
386
Herbert Xu12a169e2008-10-01 07:03:24 -0700387 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700389
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800390 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391}
392
393static inline unsigned long make_jiffies(long secs)
394{
395 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
396 return MAX_SCHEDULE_TIMEOUT-1;
397 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900398 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
401static void xfrm_timer_handler(unsigned long data)
402{
403 struct xfrm_state *x = (struct xfrm_state*)data;
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800404 struct net *net = xs_net(x);
James Morris9d729f72007-03-04 16:12:44 -0800405 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 long next = LONG_MAX;
407 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600408 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
410 spin_lock(&x->lock);
411 if (x->km.state == XFRM_STATE_DEAD)
412 goto out;
413 if (x->km.state == XFRM_STATE_EXPIRED)
414 goto expired;
415 if (x->lft.hard_add_expires_seconds) {
416 long tmo = x->lft.hard_add_expires_seconds +
417 x->curlft.add_time - now;
418 if (tmo <= 0)
419 goto expired;
420 if (tmo < next)
421 next = tmo;
422 }
423 if (x->lft.hard_use_expires_seconds) {
424 long tmo = x->lft.hard_use_expires_seconds +
425 (x->curlft.use_time ? : now) - now;
426 if (tmo <= 0)
427 goto expired;
428 if (tmo < next)
429 next = tmo;
430 }
431 if (x->km.dying)
432 goto resched;
433 if (x->lft.soft_add_expires_seconds) {
434 long tmo = x->lft.soft_add_expires_seconds +
435 x->curlft.add_time - now;
436 if (tmo <= 0)
437 warn = 1;
438 else if (tmo < next)
439 next = tmo;
440 }
441 if (x->lft.soft_use_expires_seconds) {
442 long tmo = x->lft.soft_use_expires_seconds +
443 (x->curlft.use_time ? : now) - now;
444 if (tmo <= 0)
445 warn = 1;
446 else if (tmo < next)
447 next = tmo;
448 }
449
Herbert Xu4666faa2005-06-18 22:43:22 -0700450 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800452 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700454 if (next != LONG_MAX)
455 mod_timer(&x->timer, jiffies + make_jiffies(next));
456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 goto out;
458
459expired:
460 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
461 x->km.state = XFRM_STATE_EXPIRED;
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800462 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 next = 2;
464 goto resched;
465 }
Joy Latten161a09e2006-11-27 13:11:54 -0600466
467 err = __xfrm_state_delete(x);
468 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800469 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Joy Lattenab5f5e82007-09-17 11:51:22 -0700471 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400472 audit_get_loginuid(current),
473 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600474
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475out:
476 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478
David S. Miller0ac84752006-03-20 19:18:23 -0800479static void xfrm_replay_timer_handler(unsigned long data);
480
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800481struct xfrm_state *xfrm_state_alloc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
483 struct xfrm_state *x;
484
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700485 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 if (x) {
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800488 write_pnet(&x->xs_net, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 atomic_set(&x->refcnt, 1);
490 atomic_set(&x->tunnel_users, 0);
Herbert Xu12a169e2008-10-01 07:03:24 -0700491 INIT_LIST_HEAD(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700492 INIT_HLIST_NODE(&x->bydst);
493 INIT_HLIST_NODE(&x->bysrc);
494 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800495 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
496 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
497 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800498 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 x->lft.soft_byte_limit = XFRM_INF;
500 x->lft.soft_packet_limit = XFRM_INF;
501 x->lft.hard_byte_limit = XFRM_INF;
502 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800503 x->replay_maxage = 0;
504 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700505 x->inner_mode = NULL;
506 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 spin_lock_init(&x->lock);
508 }
509 return x;
510}
511EXPORT_SYMBOL(xfrm_state_alloc);
512
513void __xfrm_state_destroy(struct xfrm_state *x)
514{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800515 struct net *net = xs_net(x);
516
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700517 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800520 hlist_add_head(&x->gclist, &net->xfrm.state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 spin_unlock_bh(&xfrm_state_gc_lock);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800522 schedule_work(&net->xfrm.state_gc_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523}
524EXPORT_SYMBOL(__xfrm_state_destroy);
525
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800526int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800528 struct net *net = xs_net(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700529 int err = -ESRCH;
530
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 if (x->km.state != XFRM_STATE_DEAD) {
532 x->km.state = XFRM_STATE_DEAD;
533 spin_lock(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -0700534 list_del(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700535 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700536 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700537 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700538 hlist_del(&x->byspi);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800539 net->xfrm.state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 /* All xfrm_state objects are created by xfrm_state_alloc.
543 * The xfrm_state_alloc call gives a reference, and that
544 * is what we are dropping here.
545 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800546 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700547 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700549
550 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800552EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700554int 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;
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700559 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700561
562 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563}
564EXPORT_SYMBOL(xfrm_state_delete);
565
Joy Latten4aa2e622007-06-04 19:05:57 -0400566#ifdef CONFIG_SECURITY_NETWORK_XFRM
567static inline int
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800568xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Joy Latten4aa2e622007-06-04 19:05:57 -0400570 int i, err = 0;
571
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800572 for (i = 0; i <= net->xfrm.state_hmask; i++) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400573 struct hlist_node *entry;
574 struct xfrm_state *x;
575
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800576 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400577 if (xfrm_id_proto_match(x->id.proto, proto) &&
578 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700579 xfrm_audit_state_delete(x, 0,
580 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400581 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700582 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400583 return err;
584 }
585 }
586 }
587
588 return err;
589}
590#else
591static inline int
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800592xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
Joy Latten4aa2e622007-06-04 19:05:57 -0400593{
594 return 0;
595}
596#endif
597
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800598int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
Joy Latten4aa2e622007-06-04 19:05:57 -0400599{
600 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800603 err = xfrm_state_flush_secctx_check(net, proto, audit_info);
Joy Latten4aa2e622007-06-04 19:05:57 -0400604 if (err)
605 goto out;
606
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800607 for (i = 0; i <= net->xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700608 struct hlist_node *entry;
609 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610restart:
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800611 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700613 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 xfrm_state_hold(x);
615 spin_unlock_bh(&xfrm_state_lock);
616
Joy Latten161a09e2006-11-27 13:11:54 -0600617 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700618 xfrm_audit_state_delete(x, err ? 0 : 1,
619 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400620 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700621 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 xfrm_state_put(x);
623
624 spin_lock_bh(&xfrm_state_lock);
625 goto restart;
626 }
627 }
628 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400629 err = 0;
630
631out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 spin_unlock_bh(&xfrm_state_lock);
Alexey Dobriyan0e602452008-11-25 17:30:18 -0800633 wake_up(&net->xfrm.km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400634 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636EXPORT_SYMBOL(xfrm_state_flush);
637
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700638void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700639{
640 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800641 si->sadcnt = init_net.xfrm.state_num;
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800642 si->sadhcnt = init_net.xfrm.state_hmask;
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700643 si->sadhmcnt = xfrm_state_hashmax;
644 spin_unlock_bh(&xfrm_state_lock);
645}
646EXPORT_SYMBOL(xfrm_sad_getinfo);
647
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648static int
649xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
650 struct xfrm_tmpl *tmpl,
651 xfrm_address_t *daddr, xfrm_address_t *saddr,
652 unsigned short family)
653{
654 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
655 if (!afinfo)
656 return -1;
657 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
658 xfrm_state_put_afinfo(afinfo);
659 return 0;
660}
661
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800662static struct xfrm_state *__xfrm_state_lookup(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700663{
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800664 unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700665 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700666 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700667
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800668 hlist_for_each_entry(x, entry, net->xfrm.state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700669 if (x->props.family != family ||
670 x->id.spi != spi ||
671 x->id.proto != proto)
672 continue;
673
674 switch (family) {
675 case AF_INET:
676 if (x->id.daddr.a4 != daddr->a4)
677 continue;
678 break;
679 case AF_INET6:
680 if (!ipv6_addr_equal((struct in6_addr *)daddr,
681 (struct in6_addr *)
682 x->id.daddr.a6))
683 continue;
684 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700685 }
David S. Milleredcd5822006-08-24 00:42:45 -0700686
687 xfrm_state_hold(x);
688 return x;
689 }
690
691 return NULL;
692}
693
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800694static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700695{
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800696 unsigned int h = xfrm_src_hash(net, daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700697 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700698 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700699
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800700 hlist_for_each_entry(x, entry, net->xfrm.state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700701 if (x->props.family != family ||
702 x->id.proto != proto)
703 continue;
704
705 switch (family) {
706 case AF_INET:
707 if (x->id.daddr.a4 != daddr->a4 ||
708 x->props.saddr.a4 != saddr->a4)
709 continue;
710 break;
711 case AF_INET6:
712 if (!ipv6_addr_equal((struct in6_addr *)daddr,
713 (struct in6_addr *)
714 x->id.daddr.a6) ||
715 !ipv6_addr_equal((struct in6_addr *)saddr,
716 (struct in6_addr *)
717 x->props.saddr.a6))
718 continue;
719 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700720 }
David S. Milleredcd5822006-08-24 00:42:45 -0700721
722 xfrm_state_hold(x);
723 return x;
724 }
725
726 return NULL;
727}
728
729static inline struct xfrm_state *
730__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
731{
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800732 struct net *net = xs_net(x);
733
David S. Milleredcd5822006-08-24 00:42:45 -0700734 if (use_spi)
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800735 return __xfrm_state_lookup(net, &x->id.daddr, x->id.spi,
David S. Milleredcd5822006-08-24 00:42:45 -0700736 x->id.proto, family);
737 else
Alexey Dobriyan221df1e2008-11-25 17:30:50 -0800738 return __xfrm_state_lookup_byaddr(net, &x->id.daddr,
David S. Milleredcd5822006-08-24 00:42:45 -0700739 &x->props.saddr,
740 x->id.proto, family);
741}
742
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800743static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700744{
745 if (have_hash_collision &&
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800746 (net->xfrm.state_hmask + 1) < xfrm_state_hashmax &&
747 net->xfrm.state_num > net->xfrm.state_hmask)
748 schedule_work(&net->xfrm.state_hash_work);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700749}
750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900752xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 struct flowi *fl, struct xfrm_tmpl *tmpl,
754 struct xfrm_policy *pol, int *err,
755 unsigned short family)
756{
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800757 struct net *net = xp_net(pol);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800758 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700759 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700760 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 int acquire_in_progress = 0;
762 int error = 0;
763 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900764
David S. Miller37b08e32008-09-02 20:14:15 -0700765 to_put = NULL;
766
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800768 h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, family);
769 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 if (x->props.family == family &&
771 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700772 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 xfrm_state_addr_check(x, daddr, saddr, family) &&
774 tmpl->mode == x->props.mode &&
775 tmpl->id.proto == x->id.proto &&
776 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
777 /* Resolution logic:
778 1. There is a valid state with matching selector.
779 Done.
780 2. Valid state with inappropriate selector. Skip.
781
782 Entering area of "sysdeps".
783
784 3. If state is not valid, selector is temporary,
785 it selects only session which triggered
786 previous resolution. Key manager will do
787 something to install a state with proper
788 selector.
789 */
790 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700791 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700792 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 continue;
794 if (!best ||
795 best->km.dying > x->km.dying ||
796 (best->km.dying == x->km.dying &&
797 best->curlft.add_time < x->curlft.add_time))
798 best = x;
799 } else if (x->km.state == XFRM_STATE_ACQ) {
800 acquire_in_progress = 1;
801 } else if (x->km.state == XFRM_STATE_ERROR ||
802 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700803 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700804 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 error = -ESRCH;
806 }
807 }
808 }
809
810 x = best;
811 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700812 if (tmpl->id.spi &&
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800813 (x0 = __xfrm_state_lookup(net, daddr, tmpl->id.spi,
David S. Milleredcd5822006-08-24 00:42:45 -0700814 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700815 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 error = -EEXIST;
817 goto out;
818 }
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800819 x = xfrm_state_alloc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 if (x == NULL) {
821 error = -ENOMEM;
822 goto out;
823 }
824 /* Initialize temporary selector matching only
825 * to current session. */
826 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
827
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700828 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
829 if (error) {
830 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700831 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700832 x = NULL;
833 goto out;
834 }
835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 if (km_query(x, tmpl, pol) == 0) {
837 x->km.state = XFRM_STATE_ACQ;
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800838 list_add(&x->km.all, &net->xfrm.state_all);
839 hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
840 h = xfrm_src_hash(net, daddr, saddr, family);
841 hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 if (x->id.spi) {
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800843 h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, family);
844 hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 }
Alexey Dobriyanb27aead2008-11-25 18:00:48 -0800846 x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
847 x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 add_timer(&x->timer);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800849 net->xfrm.state_num++;
850 xfrm_hash_grow_check(net, x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else {
852 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700853 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 x = NULL;
855 error = -ESRCH;
856 }
857 }
858out:
859 if (x)
860 xfrm_state_hold(x);
861 else
862 *err = acquire_in_progress ? -EAGAIN : error;
863 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700864 if (to_put)
865 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 return x;
867}
868
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700869struct xfrm_state *
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800870xfrm_stateonly_find(struct net *net,
871 xfrm_address_t *daddr, xfrm_address_t *saddr,
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700872 unsigned short family, u8 mode, u8 proto, u32 reqid)
873{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800874 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700875 struct xfrm_state *rx = NULL, *x = NULL;
876 struct hlist_node *entry;
877
878 spin_lock(&xfrm_state_lock);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800879 h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
880 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700881 if (x->props.family == family &&
882 x->props.reqid == reqid &&
883 !(x->props.flags & XFRM_STATE_WILDRECV) &&
884 xfrm_state_addr_check(x, daddr, saddr, family) &&
885 mode == x->props.mode &&
886 proto == x->id.proto &&
887 x->km.state == XFRM_STATE_VALID) {
888 rx = x;
889 break;
890 }
891 }
892
893 if (rx)
894 xfrm_state_hold(rx);
895 spin_unlock(&xfrm_state_lock);
896
897
898 return rx;
899}
900EXPORT_SYMBOL(xfrm_stateonly_find);
901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902static void __xfrm_state_insert(struct xfrm_state *x)
903{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800904 struct net *net = xs_net(x);
David S. Millera624c102006-08-24 03:24:33 -0700905 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
David S. Miller9d4a7062006-08-24 03:18:09 -0700907 x->genid = ++xfrm_state_genid;
908
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800909 list_add(&x->km.all, &net->xfrm.state_all);
Timo Teras4c563f72008-02-28 21:31:08 -0800910
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800911 h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
David S. Millerc1969f22006-08-24 04:00:03 -0700912 x->props.reqid, x->props.family);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800913 hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800915 h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
916 hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700918 if (x->id.spi) {
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800919 h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700920 x->props.family);
921
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800922 hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700923 }
924
David S. Millera47f0ce2006-08-24 03:54:22 -0700925 mod_timer(&x->timer, jiffies + HZ);
926 if (x->replay_maxage)
927 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800928
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800929 wake_up(&net->xfrm.km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700930
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800931 net->xfrm.state_num++;
David S. Millerf034b5d2006-08-24 03:08:07 -0700932
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800933 xfrm_hash_grow_check(net, x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934}
935
David S. Millerc7f5ea32006-08-24 03:29:04 -0700936/* xfrm_state_lock is held */
937static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
938{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800939 struct net *net = xs_net(xnew);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700940 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
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800946 h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
947 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
David S. Millerc7f5ea32006-08-24 03:29:04 -0700948 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 */
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800966static struct xfrm_state *__find_acq_core(struct net *net, unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
David S. Miller27708342006-08-24 00:13:10 -0700967{
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800968 unsigned int h = xfrm_dst_hash(net, 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
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -0800972 hlist_for_each_entry(x, entry, net->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
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001004 x = xfrm_state_alloc(net);
David S. Miller27708342006-08-24 00:13:10 -07001005 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;
Alexey Dobriyanb27aead2008-11-25 18:00:48 -08001035 x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001036 xfrm_state_hold(x);
Alexey Dobriyanb27aead2008-11-25 18:00:48 -08001037 x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001038 add_timer(&x->timer);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001039 list_add(&x->km.all, &net->xfrm.state_all);
1040 hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
1041 h = xfrm_src_hash(net, daddr, saddr, family);
1042 hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001043
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001044 net->xfrm.state_num++;
David S. Miller918049f2006-10-12 22:03:24 -07001045
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001046 xfrm_hash_grow_check(net, x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001047 }
1048
1049 return x;
1050}
1051
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001052static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
1054int xfrm_state_add(struct xfrm_state *x)
1055{
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001056 struct net *net = xs_net(x);
David S. Miller37b08e32008-09-02 20:14:15 -07001057 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 int family;
1059 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001060 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
1062 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
David S. Miller37b08e32008-09-02 20:14:15 -07001064 to_put = NULL;
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 spin_lock_bh(&xfrm_state_lock);
1067
David S. Milleredcd5822006-08-24 00:42:45 -07001068 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001070 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 x1 = NULL;
1072 err = -EEXIST;
1073 goto out;
1074 }
1075
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001076 if (use_spi && x->km.seq) {
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001077 x1 = __xfrm_find_acq_byseq(net, x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001078 if (x1 && ((x1->id.proto != x->id.proto) ||
1079 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001080 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 x1 = NULL;
1082 }
1083 }
1084
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001085 if (use_spi && !x1)
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001086 x1 = __find_acq_core(net, family, x->props.mode, x->props.reqid,
David S. Miller27708342006-08-24 00:13:10 -07001087 x->id.proto,
1088 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
David S. Millerc7f5ea32006-08-24 03:29:04 -07001090 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 __xfrm_state_insert(x);
1092 err = 0;
1093
1094out:
1095 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
1097 if (x1) {
1098 xfrm_state_delete(x1);
1099 xfrm_state_put(x1);
1100 }
1101
David S. Miller37b08e32008-09-02 20:14:15 -07001102 if (to_put)
1103 xfrm_state_put(to_put);
1104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 return err;
1106}
1107EXPORT_SYMBOL(xfrm_state_add);
1108
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001109#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001110static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001111{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001112 struct net *net = xs_net(orig);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001113 int err = -ENOMEM;
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001114 struct xfrm_state *x = xfrm_state_alloc(net);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001115 if (!x)
1116 goto error;
1117
1118 memcpy(&x->id, &orig->id, sizeof(x->id));
1119 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1120 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1121 x->props.mode = orig->props.mode;
1122 x->props.replay_window = orig->props.replay_window;
1123 x->props.reqid = orig->props.reqid;
1124 x->props.family = orig->props.family;
1125 x->props.saddr = orig->props.saddr;
1126
1127 if (orig->aalg) {
1128 x->aalg = xfrm_algo_clone(orig->aalg);
1129 if (!x->aalg)
1130 goto error;
1131 }
1132 x->props.aalgo = orig->props.aalgo;
1133
1134 if (orig->ealg) {
1135 x->ealg = xfrm_algo_clone(orig->ealg);
1136 if (!x->ealg)
1137 goto error;
1138 }
1139 x->props.ealgo = orig->props.ealgo;
1140
1141 if (orig->calg) {
1142 x->calg = xfrm_algo_clone(orig->calg);
1143 if (!x->calg)
1144 goto error;
1145 }
1146 x->props.calgo = orig->props.calgo;
1147
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001148 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001149 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1150 if (!x->encap)
1151 goto error;
1152 }
1153
1154 if (orig->coaddr) {
1155 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1156 GFP_KERNEL);
1157 if (!x->coaddr)
1158 goto error;
1159 }
1160
1161 err = xfrm_init_state(x);
1162 if (err)
1163 goto error;
1164
1165 x->props.flags = orig->props.flags;
1166
1167 x->curlft.add_time = orig->curlft.add_time;
1168 x->km.state = orig->km.state;
1169 x->km.seq = orig->km.seq;
1170
1171 return x;
1172
1173 error:
1174 if (errp)
1175 *errp = err;
1176 if (x) {
1177 kfree(x->aalg);
1178 kfree(x->ealg);
1179 kfree(x->calg);
1180 kfree(x->encap);
1181 kfree(x->coaddr);
1182 }
1183 kfree(x);
1184 return NULL;
1185}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001186
1187/* xfrm_state_lock is held */
1188struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1189{
1190 unsigned int h;
1191 struct xfrm_state *x;
1192 struct hlist_node *entry;
1193
1194 if (m->reqid) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001195 h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001196 m->reqid, m->old_family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001197 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001198 if (x->props.mode != m->mode ||
1199 x->id.proto != m->proto)
1200 continue;
1201 if (m->reqid && x->props.reqid != m->reqid)
1202 continue;
1203 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1204 m->old_family) ||
1205 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1206 m->old_family))
1207 continue;
1208 xfrm_state_hold(x);
1209 return x;
1210 }
1211 } else {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001212 h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001213 m->old_family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001214 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001215 if (x->props.mode != m->mode ||
1216 x->id.proto != m->proto)
1217 continue;
1218 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1219 m->old_family) ||
1220 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1221 m->old_family))
1222 continue;
1223 xfrm_state_hold(x);
1224 return x;
1225 }
1226 }
1227
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001228 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001229}
1230EXPORT_SYMBOL(xfrm_migrate_state_find);
1231
1232struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1233 struct xfrm_migrate *m)
1234{
1235 struct xfrm_state *xc;
1236 int err;
1237
1238 xc = xfrm_state_clone(x, &err);
1239 if (!xc)
1240 return NULL;
1241
1242 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1243 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1244
1245 /* add state */
1246 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1247 /* a care is needed when the destination address of the
1248 state is to be updated as it is a part of triplet */
1249 xfrm_state_insert(xc);
1250 } else {
1251 if ((err = xfrm_state_add(xc)) < 0)
1252 goto error;
1253 }
1254
1255 return xc;
1256error:
1257 kfree(xc);
1258 return NULL;
1259}
1260EXPORT_SYMBOL(xfrm_state_migrate);
1261#endif
1262
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263int xfrm_state_update(struct xfrm_state *x)
1264{
David S. Miller37b08e32008-09-02 20:14:15 -07001265 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001267 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
David S. Miller37b08e32008-09-02 20:14:15 -07001269 to_put = NULL;
1270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001272 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 err = -ESRCH;
1275 if (!x1)
1276 goto out;
1277
1278 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001279 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 err = -EEXIST;
1281 goto out;
1282 }
1283
1284 if (x1->km.state == XFRM_STATE_ACQ) {
1285 __xfrm_state_insert(x);
1286 x = NULL;
1287 }
1288 err = 0;
1289
1290out:
1291 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
David S. Miller37b08e32008-09-02 20:14:15 -07001293 if (to_put)
1294 xfrm_state_put(to_put);
1295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (err)
1297 return err;
1298
1299 if (!x) {
1300 xfrm_state_delete(x1);
1301 xfrm_state_put(x1);
1302 return 0;
1303 }
1304
1305 err = -EINVAL;
1306 spin_lock_bh(&x1->lock);
1307 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1308 if (x->encap && x1->encap)
1309 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001310 if (x->coaddr && x1->coaddr) {
1311 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1312 }
1313 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1314 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1316 x1->km.dying = 0;
1317
David S. Millera47f0ce2006-08-24 03:54:22 -07001318 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 if (x1->curlft.use_time)
1320 xfrm_state_check_expire(x1);
1321
1322 err = 0;
1323 }
1324 spin_unlock_bh(&x1->lock);
1325
1326 xfrm_state_put(x1);
1327
1328 return err;
1329}
1330EXPORT_SYMBOL(xfrm_state_update);
1331
1332int xfrm_state_check_expire(struct xfrm_state *x)
1333{
1334 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001335 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
1337 if (x->km.state != XFRM_STATE_VALID)
1338 return -EINVAL;
1339
1340 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1341 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001342 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001343 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 return -EINVAL;
1345 }
1346
1347 if (!x->km.dying &&
1348 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001349 x->curlft.packets >= x->lft.soft_packet_limit)) {
1350 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001351 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001352 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 return 0;
1354}
1355EXPORT_SYMBOL(xfrm_state_check_expire);
1356
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357struct xfrm_state *
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001358xfrm_state_lookup(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 unsigned short family)
1360{
1361 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
1363 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001364 x = __xfrm_state_lookup(net, daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 return x;
1367}
1368EXPORT_SYMBOL(xfrm_state_lookup);
1369
1370struct xfrm_state *
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001371xfrm_state_lookup_byaddr(struct net *net,
1372 xfrm_address_t *daddr, xfrm_address_t *saddr,
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001373 u8 proto, unsigned short family)
1374{
1375 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001376
1377 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001378 x = __xfrm_state_lookup_byaddr(net, daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001379 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001380 return x;
1381}
1382EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1383
1384struct xfrm_state *
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001385xfrm_find_acq(struct net *net, u8 mode, u32 reqid, u8 proto,
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001386 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 int create, unsigned short family)
1388{
1389 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
1391 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001392 x = __find_acq_core(net, family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001394
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 return x;
1396}
1397EXPORT_SYMBOL(xfrm_find_acq);
1398
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001399#ifdef CONFIG_XFRM_SUB_POLICY
1400int
1401xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1402 unsigned short family)
1403{
1404 int err = 0;
1405 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1406 if (!afinfo)
1407 return -EAFNOSUPPORT;
1408
1409 spin_lock_bh(&xfrm_state_lock);
1410 if (afinfo->tmpl_sort)
1411 err = afinfo->tmpl_sort(dst, src, n);
1412 spin_unlock_bh(&xfrm_state_lock);
1413 xfrm_state_put_afinfo(afinfo);
1414 return err;
1415}
1416EXPORT_SYMBOL(xfrm_tmpl_sort);
1417
1418int
1419xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1420 unsigned short family)
1421{
1422 int err = 0;
1423 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1424 if (!afinfo)
1425 return -EAFNOSUPPORT;
1426
1427 spin_lock_bh(&xfrm_state_lock);
1428 if (afinfo->state_sort)
1429 err = afinfo->state_sort(dst, src, n);
1430 spin_unlock_bh(&xfrm_state_lock);
1431 xfrm_state_put_afinfo(afinfo);
1432 return err;
1433}
1434EXPORT_SYMBOL(xfrm_state_sort);
1435#endif
1436
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437/* Silly enough, but I'm lazy to build resolution list */
1438
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001439static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440{
1441 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001443 for (i = 0; i <= net->xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001444 struct hlist_node *entry;
1445 struct xfrm_state *x;
1446
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001447 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
David S. Miller8f126e32006-08-24 02:45:07 -07001448 if (x->km.seq == seq &&
1449 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 xfrm_state_hold(x);
1451 return x;
1452 }
1453 }
1454 }
1455 return NULL;
1456}
1457
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001458struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459{
1460 struct xfrm_state *x;
1461
1462 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan5447c5e2008-11-25 17:31:51 -08001463 x = __xfrm_find_acq_byseq(net, seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 spin_unlock_bh(&xfrm_state_lock);
1465 return x;
1466}
1467EXPORT_SYMBOL(xfrm_find_acq_byseq);
1468
1469u32 xfrm_get_acqseq(void)
1470{
1471 u32 res;
1472 static u32 acqseq;
1473 static DEFINE_SPINLOCK(acqseq_lock);
1474
1475 spin_lock_bh(&acqseq_lock);
1476 res = (++acqseq ? : ++acqseq);
1477 spin_unlock_bh(&acqseq_lock);
1478 return res;
1479}
1480EXPORT_SYMBOL(xfrm_get_acqseq);
1481
Herbert Xu658b2192007-10-09 13:29:52 -07001482int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483{
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001484 struct net *net = xs_net(x);
David S. Millerf034b5d2006-08-24 03:08:07 -07001485 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001487 int err = -ENOENT;
1488 __be32 minspi = htonl(low);
1489 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
Herbert Xu658b2192007-10-09 13:29:52 -07001491 spin_lock_bh(&x->lock);
1492 if (x->km.state == XFRM_STATE_DEAD)
1493 goto unlock;
1494
1495 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001497 goto unlock;
1498
1499 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 if (minspi == maxspi) {
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001502 x0 = xfrm_state_lookup(net, &x->id.daddr, minspi, x->id.proto, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 if (x0) {
1504 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001505 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 }
1507 x->id.spi = minspi;
1508 } else {
1509 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001510 for (h=0; h<high-low+1; h++) {
1511 spi = low + net_random()%(high-low+1);
Alexey Dobriyan221df1e2008-11-25 17:30:50 -08001512 x0 = xfrm_state_lookup(net, &x->id.daddr, htonl(spi), x->id.proto, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 if (x0 == NULL) {
1514 x->id.spi = htonl(spi);
1515 break;
1516 }
1517 xfrm_state_put(x0);
1518 }
1519 }
1520 if (x->id.spi) {
1521 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan12604d82008-11-25 17:31:18 -08001522 h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
1523 hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001525
1526 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 }
Herbert Xu658b2192007-10-09 13:29:52 -07001528
1529unlock:
1530 spin_unlock_bh(&x->lock);
1531
1532 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533}
1534EXPORT_SYMBOL(xfrm_alloc_spi);
1535
Alexey Dobriyan284fa7d2008-11-25 17:32:14 -08001536int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
Timo Teras4c563f72008-02-28 21:31:08 -08001537 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 void *data)
1539{
Herbert Xu12a169e2008-10-01 07:03:24 -07001540 struct xfrm_state *state;
1541 struct xfrm_state_walk *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 int err = 0;
1543
Herbert Xu12a169e2008-10-01 07:03:24 -07001544 if (walk->seq != 0 && list_empty(&walk->all))
Timo Teras4c563f72008-02-28 21:31:08 -08001545 return 0;
1546
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 spin_lock_bh(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -07001548 if (list_empty(&walk->all))
Alexey Dobriyan284fa7d2008-11-25 17:32:14 -08001549 x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all);
Herbert Xu12a169e2008-10-01 07:03:24 -07001550 else
1551 x = list_entry(&walk->all, struct xfrm_state_walk, all);
Alexey Dobriyan284fa7d2008-11-25 17:32:14 -08001552 list_for_each_entry_from(x, &net->xfrm.state_all, all) {
Herbert Xu12a169e2008-10-01 07:03:24 -07001553 if (x->state == XFRM_STATE_DEAD)
Timo Teras4c563f72008-02-28 21:31:08 -08001554 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001555 state = container_of(x, struct xfrm_state, km);
1556 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
Timo Teras4c563f72008-02-28 21:31:08 -08001557 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001558 err = func(state, walk->seq, data);
1559 if (err) {
1560 list_move_tail(&walk->all, &x->all);
1561 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001563 walk->seq++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001565 if (walk->seq == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 err = -ENOENT;
1567 goto out;
1568 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001569 list_del_init(&walk->all);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570out:
1571 spin_unlock_bh(&xfrm_state_lock);
1572 return err;
1573}
1574EXPORT_SYMBOL(xfrm_state_walk);
1575
Herbert Xu5c182452008-09-22 19:48:19 -07001576void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1577{
Herbert Xu12a169e2008-10-01 07:03:24 -07001578 INIT_LIST_HEAD(&walk->all);
Herbert Xu5c182452008-09-22 19:48:19 -07001579 walk->proto = proto;
Herbert Xu12a169e2008-10-01 07:03:24 -07001580 walk->state = XFRM_STATE_DEAD;
1581 walk->seq = 0;
Herbert Xu5c182452008-09-22 19:48:19 -07001582}
1583EXPORT_SYMBOL(xfrm_state_walk_init);
1584
Herbert Xuabb81c42008-09-09 19:58:29 -07001585void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1586{
Herbert Xu12a169e2008-10-01 07:03:24 -07001587 if (list_empty(&walk->all))
Herbert Xu5c182452008-09-22 19:48:19 -07001588 return;
Herbert Xu5c182452008-09-22 19:48:19 -07001589
Herbert Xu12a169e2008-10-01 07:03:24 -07001590 spin_lock_bh(&xfrm_state_lock);
1591 list_del(&walk->all);
1592 spin_lock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001593}
1594EXPORT_SYMBOL(xfrm_state_walk_done);
1595
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001596
1597void xfrm_replay_notify(struct xfrm_state *x, int event)
1598{
1599 struct km_event c;
1600 /* we send notify messages in case
1601 * 1. we updated on of the sequence numbers, and the seqno difference
1602 * is at least x->replay_maxdiff, in this case we also update the
1603 * timeout of our timer function
1604 * 2. if x->replay_maxage has elapsed since last update,
1605 * and there were changes
1606 *
1607 * The state structure must be locked!
1608 */
1609
1610 switch (event) {
1611 case XFRM_REPLAY_UPDATE:
1612 if (x->replay_maxdiff &&
1613 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001614 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1615 if (x->xflags & XFRM_TIME_DEFER)
1616 event = XFRM_REPLAY_TIMEOUT;
1617 else
1618 return;
1619 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001620
1621 break;
1622
1623 case XFRM_REPLAY_TIMEOUT:
1624 if ((x->replay.seq == x->preplay.seq) &&
1625 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001626 (x->replay.oseq == x->preplay.oseq)) {
1627 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001628 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001629 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001630
1631 break;
1632 }
1633
1634 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1635 c.event = XFRM_MSG_NEWAE;
1636 c.data.aevent = event;
1637 km_state_notify(x, &c);
1638
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001639 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001640 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001641 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001642}
1643
1644static void xfrm_replay_timer_handler(unsigned long data)
1645{
1646 struct xfrm_state *x = (struct xfrm_state*)data;
1647
1648 spin_lock(&x->lock);
1649
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001650 if (x->km.state == XFRM_STATE_VALID) {
Alexey Dobriyana6483b72008-11-25 17:38:20 -08001651 if (xfrm_aevent_is_on(xs_net(x)))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001652 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1653 else
1654 x->xflags |= XFRM_TIME_DEFER;
1655 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001656
1657 spin_unlock(&x->lock);
1658}
1659
Paul Mooreafeb14b2007-12-21 14:58:11 -08001660int xfrm_replay_check(struct xfrm_state *x,
1661 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
1663 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001664 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
1666 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001667 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 if (likely(seq > x->replay.seq))
1670 return 0;
1671
1672 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001673 if (diff >= min_t(unsigned int, x->props.replay_window,
1674 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001676 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 }
1678
1679 if (x->replay.bitmap & (1U << diff)) {
1680 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001681 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 }
1683 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001684
1685err:
1686 xfrm_audit_state_replay(x, skb, net_seq);
1687 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
Al Viro61f46272006-09-27 18:48:33 -07001690void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691{
1692 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001693 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
1695 if (seq > x->replay.seq) {
1696 diff = seq - x->replay.seq;
1697 if (diff < x->props.replay_window)
1698 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1699 else
1700 x->replay.bitmap = 1;
1701 x->replay.seq = seq;
1702 } else {
1703 diff = x->replay.seq - seq;
1704 x->replay.bitmap |= (1U << diff);
1705 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001706
Alexey Dobriyana6483b72008-11-25 17:38:20 -08001707 if (xfrm_aevent_is_on(xs_net(x)))
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001708 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710
Denis Chengdf018122007-12-07 00:51:11 -08001711static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712static DEFINE_RWLOCK(xfrm_km_lock);
1713
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001714void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715{
1716 struct xfrm_mgr *km;
1717
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001718 read_lock(&xfrm_km_lock);
1719 list_for_each_entry(km, &xfrm_km_list, list)
1720 if (km->notify_policy)
1721 km->notify_policy(xp, dir, c);
1722 read_unlock(&xfrm_km_lock);
1723}
1724
1725void km_state_notify(struct xfrm_state *x, struct km_event *c)
1726{
1727 struct xfrm_mgr *km;
1728 read_lock(&xfrm_km_lock);
1729 list_for_each_entry(km, &xfrm_km_list, list)
1730 if (km->notify)
1731 km->notify(x, c);
1732 read_unlock(&xfrm_km_lock);
1733}
1734
1735EXPORT_SYMBOL(km_policy_notify);
1736EXPORT_SYMBOL(km_state_notify);
1737
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001738void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001739{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001740 struct net *net = xs_net(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001741 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)
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001749 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750}
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{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001792 struct net *net = xp_net(pol);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001793 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
Herbert Xubf088672005-06-18 22:44:00 -07001795 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001796 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001797 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001798 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
1800 if (hard)
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001801 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802}
David S. Millera70fcb02006-03-20 19:18:52 -08001803EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001805#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001806int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001807 struct xfrm_migrate *m, int num_migrate,
1808 struct xfrm_kmaddress *k)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001809{
1810 int err = -EINVAL;
1811 int ret;
1812 struct xfrm_mgr *km;
1813
1814 read_lock(&xfrm_km_lock);
1815 list_for_each_entry(km, &xfrm_km_list, list) {
1816 if (km->migrate) {
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001817 ret = km->migrate(sel, dir, type, m, num_migrate, k);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001818 if (!ret)
1819 err = ret;
1820 }
1821 }
1822 read_unlock(&xfrm_km_lock);
1823 return err;
1824}
1825EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001826#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001827
Alexey Dobriyandb983c12008-11-25 17:51:01 -08001828int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001829{
1830 int err = -EINVAL;
1831 int ret;
1832 struct xfrm_mgr *km;
1833
1834 read_lock(&xfrm_km_lock);
1835 list_for_each_entry(km, &xfrm_km_list, list) {
1836 if (km->report) {
Alexey Dobriyandb983c12008-11-25 17:51:01 -08001837 ret = km->report(net, proto, sel, addr);
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001838 if (!ret)
1839 err = ret;
1840 }
1841 }
1842 read_unlock(&xfrm_km_lock);
1843 return err;
1844}
1845EXPORT_SYMBOL(km_report);
1846
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1848{
1849 int err;
1850 u8 *data;
1851 struct xfrm_mgr *km;
1852 struct xfrm_policy *pol = NULL;
1853
1854 if (optlen <= 0 || optlen > PAGE_SIZE)
1855 return -EMSGSIZE;
1856
1857 data = kmalloc(optlen, GFP_KERNEL);
1858 if (!data)
1859 return -ENOMEM;
1860
1861 err = -EFAULT;
1862 if (copy_from_user(data, optval, optlen))
1863 goto out;
1864
1865 err = -EINVAL;
1866 read_lock(&xfrm_km_lock);
1867 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001868 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 optlen, &err);
1870 if (err >= 0)
1871 break;
1872 }
1873 read_unlock(&xfrm_km_lock);
1874
1875 if (err >= 0) {
1876 xfrm_sk_policy_insert(sk, err, pol);
1877 xfrm_pol_put(pol);
1878 err = 0;
1879 }
1880
1881out:
1882 kfree(data);
1883 return err;
1884}
1885EXPORT_SYMBOL(xfrm_user_policy);
1886
1887int xfrm_register_km(struct xfrm_mgr *km)
1888{
1889 write_lock_bh(&xfrm_km_lock);
1890 list_add_tail(&km->list, &xfrm_km_list);
1891 write_unlock_bh(&xfrm_km_lock);
1892 return 0;
1893}
1894EXPORT_SYMBOL(xfrm_register_km);
1895
1896int xfrm_unregister_km(struct xfrm_mgr *km)
1897{
1898 write_lock_bh(&xfrm_km_lock);
1899 list_del(&km->list);
1900 write_unlock_bh(&xfrm_km_lock);
1901 return 0;
1902}
1903EXPORT_SYMBOL(xfrm_unregister_km);
1904
1905int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1906{
1907 int err = 0;
1908 if (unlikely(afinfo == NULL))
1909 return -EINVAL;
1910 if (unlikely(afinfo->family >= NPROTO))
1911 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001912 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1914 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001915 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001917 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 return err;
1919}
1920EXPORT_SYMBOL(xfrm_state_register_afinfo);
1921
1922int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1923{
1924 int err = 0;
1925 if (unlikely(afinfo == NULL))
1926 return -EINVAL;
1927 if (unlikely(afinfo->family >= NPROTO))
1928 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001929 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1931 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1932 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001933 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001936 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 return err;
1938}
1939EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1940
Herbert Xu17c2a422007-10-17 21:33:12 -07001941static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942{
1943 struct xfrm_state_afinfo *afinfo;
1944 if (unlikely(family >= NPROTO))
1945 return NULL;
1946 read_lock(&xfrm_state_afinfo_lock);
1947 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001948 if (unlikely(!afinfo))
1949 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 return afinfo;
1951}
1952
Herbert Xu17c2a422007-10-17 21:33:12 -07001953static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001954 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Herbert Xu546be242006-05-27 23:03:58 -07001956 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957}
1958
1959/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1960void xfrm_state_delete_tunnel(struct xfrm_state *x)
1961{
1962 if (x->tunnel) {
1963 struct xfrm_state *t = x->tunnel;
1964
1965 if (atomic_read(&t->tunnel_users) == 2)
1966 xfrm_state_delete(t);
1967 atomic_dec(&t->tunnel_users);
1968 xfrm_state_put(t);
1969 x->tunnel = NULL;
1970 }
1971}
1972EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1973
1974int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1975{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001976 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
Patrick McHardyc5c25232007-04-09 11:47:18 -07001978 spin_lock_bh(&x->lock);
1979 if (x->km.state == XFRM_STATE_VALID &&
1980 x->type && x->type->get_mtu)
1981 res = x->type->get_mtu(x, mtu);
1982 else
Patrick McHardy28121612007-06-18 22:30:15 -07001983 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001984 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 return res;
1986}
1987
Herbert Xu72cb6962005-06-20 13:18:08 -07001988int xfrm_init_state(struct xfrm_state *x)
1989{
Herbert Xud094cd82005-06-20 13:19:41 -07001990 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001991 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001992 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001993 int err;
1994
Herbert Xud094cd82005-06-20 13:19:41 -07001995 err = -EAFNOSUPPORT;
1996 afinfo = xfrm_state_get_afinfo(family);
1997 if (!afinfo)
1998 goto error;
1999
2000 err = 0;
2001 if (afinfo->init_flags)
2002 err = afinfo->init_flags(x);
2003
2004 xfrm_state_put_afinfo(afinfo);
2005
2006 if (err)
2007 goto error;
2008
2009 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002010
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002011 if (x->sel.family != AF_UNSPEC) {
2012 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2013 if (inner_mode == NULL)
2014 goto error;
2015
2016 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2017 family != x->sel.family) {
2018 xfrm_put_mode(inner_mode);
2019 goto error;
2020 }
2021
2022 x->inner_mode = inner_mode;
2023 } else {
2024 struct xfrm_mode *inner_mode_iaf;
Martin Willid81d2282008-12-03 15:38:07 -08002025 int iafamily = AF_INET;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002026
Martin Willid81d2282008-12-03 15:38:07 -08002027 inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002028 if (inner_mode == NULL)
2029 goto error;
2030
2031 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2032 xfrm_put_mode(inner_mode);
2033 goto error;
2034 }
Martin Willid81d2282008-12-03 15:38:07 -08002035 x->inner_mode = inner_mode;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002036
Martin Willid81d2282008-12-03 15:38:07 -08002037 if (x->props.family == AF_INET)
2038 iafamily = AF_INET6;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002039
Martin Willid81d2282008-12-03 15:38:07 -08002040 inner_mode_iaf = xfrm_get_mode(x->props.mode, iafamily);
2041 if (inner_mode_iaf) {
2042 if (inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)
2043 x->inner_mode_iaf = inner_mode_iaf;
2044 else
2045 xfrm_put_mode(inner_mode_iaf);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002046 }
2047 }
Herbert Xu13996372007-10-17 21:35:51 -07002048
Herbert Xud094cd82005-06-20 13:19:41 -07002049 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002050 if (x->type == NULL)
2051 goto error;
2052
2053 err = x->type->init_state(x);
2054 if (err)
2055 goto error;
2056
Herbert Xu13996372007-10-17 21:35:51 -07002057 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2058 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002059 goto error;
2060
Herbert Xu72cb6962005-06-20 13:18:08 -07002061 x->km.state = XFRM_STATE_VALID;
2062
2063error:
2064 return err;
2065}
2066
2067EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002068
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002069int __net_init xfrm_state_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070{
David S. Millerf034b5d2006-08-24 03:08:07 -07002071 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002073 INIT_LIST_HEAD(&net->xfrm.state_all);
2074
David S. Millerf034b5d2006-08-24 03:08:07 -07002075 sz = sizeof(struct hlist_head) * 8;
2076
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002077 net->xfrm.state_bydst = xfrm_hash_alloc(sz);
2078 if (!net->xfrm.state_bydst)
2079 goto out_bydst;
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002080 net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2081 if (!net->xfrm.state_bysrc)
2082 goto out_bysrc;
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002083 net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2084 if (!net->xfrm.state_byspi)
2085 goto out_byspi;
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002086 net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
David S. Millerf034b5d2006-08-24 03:08:07 -07002087
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08002088 net->xfrm.state_num = 0;
Alexey Dobriyan63082732008-11-25 17:19:07 -08002089 INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -08002090 INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
Alexey Dobriyanc7837142008-11-25 17:20:36 -08002091 INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
Alexey Dobriyan50a30652008-11-25 17:21:01 -08002092 init_waitqueue_head(&net->xfrm.km_waitq);
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002093 return 0;
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002094
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002095out_byspi:
2096 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002097out_bysrc:
2098 xfrm_hash_free(net->xfrm.state_bydst, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002099out_bydst:
2100 return -ENOMEM;
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002101}
2102
2103void xfrm_state_fini(struct net *net)
2104{
Alexey Dobriyan7c2776e2008-11-25 17:57:44 -08002105 struct xfrm_audit audit_info;
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002106 unsigned int sz;
2107
Alexey Dobriyan7c2776e2008-11-25 17:57:44 -08002108 flush_work(&net->xfrm.state_hash_work);
2109 audit_info.loginuid = -1;
2110 audit_info.sessionid = -1;
2111 audit_info.secid = 0;
2112 xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
2113 flush_work(&net->xfrm.state_gc_work);
2114
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002115 WARN_ON(!list_empty(&net->xfrm.state_all));
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002116
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002117 sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002118 WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2119 xfrm_hash_free(net->xfrm.state_byspi, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002120 WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2121 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002122 WARN_ON(!hlist_empty(net->xfrm.state_bydst));
2123 xfrm_hash_free(net->xfrm.state_bydst, sz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124}
2125
Joy Lattenab5f5e82007-09-17 11:51:22 -07002126#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002127static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2128 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002129{
Paul Moore68277ac2007-12-20 20:49:33 -08002130 struct xfrm_sec_ctx *ctx = x->security;
2131 u32 spi = ntohl(x->id.spi);
2132
2133 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002134 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002135 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002136
2137 switch(x->props.family) {
2138 case AF_INET:
Harvey Harrison21454aa2008-10-31 00:54:56 -07002139 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2140 &x->props.saddr.a4, &x->id.daddr.a4);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002141 break;
2142 case AF_INET6:
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002143 audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002144 x->props.saddr.a6, x->id.daddr.a6);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002145 break;
2146 }
Paul Moore68277ac2007-12-20 20:49:33 -08002147
2148 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002149}
2150
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002151static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2152 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002153{
2154 struct iphdr *iph4;
2155 struct ipv6hdr *iph6;
2156
2157 switch (family) {
2158 case AF_INET:
2159 iph4 = ip_hdr(skb);
Harvey Harrison21454aa2008-10-31 00:54:56 -07002160 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2161 &iph4->saddr, &iph4->daddr);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002162 break;
2163 case AF_INET6:
2164 iph6 = ipv6_hdr(skb);
2165 audit_log_format(audit_buf,
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002166 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002167 &iph6->saddr,&iph6->daddr,
Paul Mooreafeb14b2007-12-21 14:58:11 -08002168 iph6->flow_lbl[0] & 0x0f,
2169 iph6->flow_lbl[1],
2170 iph6->flow_lbl[2]);
2171 break;
2172 }
2173}
2174
Paul Moore68277ac2007-12-20 20:49:33 -08002175void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002176 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002177{
2178 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002179
Paul Mooreafeb14b2007-12-21 14:58:11 -08002180 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002181 if (audit_buf == NULL)
2182 return;
Eric Paris25323862008-04-18 10:09:25 -04002183 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002184 xfrm_audit_helper_sainfo(x, audit_buf);
2185 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002186 audit_log_end(audit_buf);
2187}
2188EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2189
Paul Moore68277ac2007-12-20 20:49:33 -08002190void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002191 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002192{
2193 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002194
Paul Mooreafeb14b2007-12-21 14:58:11 -08002195 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002196 if (audit_buf == NULL)
2197 return;
Eric Paris25323862008-04-18 10:09:25 -04002198 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002199 xfrm_audit_helper_sainfo(x, audit_buf);
2200 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002201 audit_log_end(audit_buf);
2202}
2203EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002204
2205void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2206 struct sk_buff *skb)
2207{
2208 struct audit_buffer *audit_buf;
2209 u32 spi;
2210
2211 audit_buf = xfrm_audit_start("SA-replay-overflow");
2212 if (audit_buf == NULL)
2213 return;
2214 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2215 /* don't record the sequence number because it's inherent in this kind
2216 * of audit message */
2217 spi = ntohl(x->id.spi);
2218 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2219 audit_log_end(audit_buf);
2220}
2221EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2222
2223static void xfrm_audit_state_replay(struct xfrm_state *x,
2224 struct sk_buff *skb, __be32 net_seq)
2225{
2226 struct audit_buffer *audit_buf;
2227 u32 spi;
2228
2229 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2230 if (audit_buf == NULL)
2231 return;
2232 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2233 spi = ntohl(x->id.spi);
2234 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2235 spi, spi, ntohl(net_seq));
2236 audit_log_end(audit_buf);
2237}
2238
2239void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2240{
2241 struct audit_buffer *audit_buf;
2242
2243 audit_buf = xfrm_audit_start("SA-notfound");
2244 if (audit_buf == NULL)
2245 return;
2246 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2247 audit_log_end(audit_buf);
2248}
2249EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2250
2251void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2252 __be32 net_spi, __be32 net_seq)
2253{
2254 struct audit_buffer *audit_buf;
2255 u32 spi;
2256
2257 audit_buf = xfrm_audit_start("SA-notfound");
2258 if (audit_buf == NULL)
2259 return;
2260 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2261 spi = ntohl(net_spi);
2262 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2263 spi, spi, ntohl(net_seq));
2264 audit_log_end(audit_buf);
2265}
2266EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2267
2268void xfrm_audit_state_icvfail(struct xfrm_state *x,
2269 struct sk_buff *skb, u8 proto)
2270{
2271 struct audit_buffer *audit_buf;
2272 __be32 net_spi;
2273 __be32 net_seq;
2274
2275 audit_buf = xfrm_audit_start("SA-icv-failure");
2276 if (audit_buf == NULL)
2277 return;
2278 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2279 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2280 u32 spi = ntohl(net_spi);
2281 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2282 spi, spi, ntohl(net_seq));
2283 }
2284 audit_log_end(audit_buf);
2285}
2286EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002287#endif /* CONFIG_AUDITSYSCALL */