blob: 72b2956627d6c8949653e0927eda8c0d248e8045 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * xfrm_state.c
3 *
4 * Changes:
5 * Mitsuru KANDA @USAGI
6 * Kazunori MIYAZAWA @USAGI
7 * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8 * IPv6 support
9 * YOSHIFUJI Hideaki @USAGI
10 * Split up af-specific functions
11 * Derek Atkins <derek@ihtfp.com>
12 * Add UDP Encapsulation
Trent Jaegerdf718372005-12-13 23:12:27 -080013 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 */
15
16#include <linux/workqueue.h>
17#include <net/xfrm.h>
18#include <linux/pfkeyv2.h>
19#include <linux/ipsec.h>
20#include <linux/module.h>
David S. Millerf034b5d2006-08-24 03:08:07 -070021#include <linux/cache.h>
Paul Moore68277ac2007-12-20 20:49:33 -080022#include <linux/audit.h>
Jesper Juhlb5890d82007-08-10 15:20:21 -070023#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
David S. Miller44e36b42006-08-24 04:50:50 -070025#include "xfrm_hash.h"
26
David S. Milleree857a72006-03-20 19:18:37 -080027struct sock *xfrm_nl;
28EXPORT_SYMBOL(xfrm_nl);
29
David S. Miller01e67d02007-05-25 00:41:38 -070030u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080031EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32
David S. Miller01e67d02007-05-25 00:41:38 -070033u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080034EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35
David S. Miller01e67d02007-05-25 00:41:38 -070036u32 sysctl_xfrm_acq_expires __read_mostly = 30;
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038/* Each xfrm_state may be linked to two tables:
39
40 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070041 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 destination/tunnel endpoint. (output)
43 */
44
45static DEFINE_SPINLOCK(xfrm_state_lock);
46
David S. Millerf034b5d2006-08-24 03:08:07 -070047static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
David S. Miller9d4a7062006-08-24 03:18:09 -070048static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Herbert Xu17c2a422007-10-17 21:33:12 -070050static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
51static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
52
Paul Mooreafeb14b2007-12-21 14:58:11 -080053#ifdef CONFIG_AUDITSYSCALL
54static void xfrm_audit_state_replay(struct xfrm_state *x,
55 struct sk_buff *skb, __be32 net_seq);
56#else
57#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
58#endif /* CONFIG_AUDITSYSCALL */
59
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080060static inline unsigned int xfrm_dst_hash(struct net *net,
61 xfrm_address_t *daddr,
David S. Millerc1969f22006-08-24 04:00:03 -070062 xfrm_address_t *saddr,
63 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070064 unsigned short family)
65{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080066 return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070067}
68
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080069static inline unsigned int xfrm_src_hash(struct net *net,
70 xfrm_address_t *daddr,
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070071 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070072 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070073{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080074 return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070075}
76
David S. Miller2575b652006-08-24 03:26:44 -070077static inline unsigned int
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080078xfrm_spi_hash(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070079{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -080080 return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070081}
82
David S. Millerf034b5d2006-08-24 03:08:07 -070083static void xfrm_hash_transfer(struct hlist_head *list,
84 struct hlist_head *ndsttable,
85 struct hlist_head *nsrctable,
86 struct hlist_head *nspitable,
87 unsigned int nhashmask)
88{
89 struct hlist_node *entry, *tmp;
90 struct xfrm_state *x;
91
92 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
93 unsigned int h;
94
David S. Millerc1969f22006-08-24 04:00:03 -070095 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
96 x->props.reqid, x->props.family,
97 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070098 hlist_add_head(&x->bydst, ndsttable+h);
99
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700100 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
101 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700102 nhashmask);
103 hlist_add_head(&x->bysrc, nsrctable+h);
104
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700105 if (x->id.spi) {
106 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
107 x->id.proto, x->props.family,
108 nhashmask);
109 hlist_add_head(&x->byspi, nspitable+h);
110 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700111 }
112}
113
Alexey Dobriyan63082732008-11-25 17:19:07 -0800114static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
David S. Millerf034b5d2006-08-24 03:08:07 -0700115{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800116 return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
David S. Millerf034b5d2006-08-24 03:08:07 -0700117}
118
119static DEFINE_MUTEX(hash_resize_mutex);
120
Alexey Dobriyan63082732008-11-25 17:19:07 -0800121static void xfrm_hash_resize(struct work_struct *work)
David S. Millerf034b5d2006-08-24 03:08:07 -0700122{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800123 struct net *net = container_of(work, struct net, xfrm.state_hash_work);
David S. Millerf034b5d2006-08-24 03:08:07 -0700124 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
125 unsigned long nsize, osize;
126 unsigned int nhashmask, ohashmask;
127 int i;
128
129 mutex_lock(&hash_resize_mutex);
130
Alexey Dobriyan63082732008-11-25 17:19:07 -0800131 nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
David S. Miller44e36b42006-08-24 04:50:50 -0700132 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700133 if (!ndst)
134 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700135 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700136 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700137 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700138 goto out_unlock;
139 }
David S. Miller44e36b42006-08-24 04:50:50 -0700140 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700141 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700142 xfrm_hash_free(ndst, nsize);
143 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700144 goto out_unlock;
145 }
146
147 spin_lock_bh(&xfrm_state_lock);
148
149 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
Alexey Dobriyan63082732008-11-25 17:19:07 -0800150 for (i = net->xfrm.state_hmask; i >= 0; i--)
151 xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
David S. Millerf034b5d2006-08-24 03:08:07 -0700152 nhashmask);
153
Alexey Dobriyan63082732008-11-25 17:19:07 -0800154 odst = net->xfrm.state_bydst;
155 osrc = net->xfrm.state_bysrc;
156 ospi = net->xfrm.state_byspi;
157 ohashmask = net->xfrm.state_hmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700158
Alexey Dobriyan63082732008-11-25 17:19:07 -0800159 net->xfrm.state_bydst = ndst;
160 net->xfrm.state_bysrc = nsrc;
161 net->xfrm.state_byspi = nspi;
162 net->xfrm.state_hmask = nhashmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700163
164 spin_unlock_bh(&xfrm_state_lock);
165
166 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700167 xfrm_hash_free(odst, osize);
168 xfrm_hash_free(osrc, osize);
169 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700170
171out_unlock:
172 mutex_unlock(&hash_resize_mutex);
173}
174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
176static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178static DEFINE_SPINLOCK(xfrm_state_gc_lock);
179
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800180int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800182int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800183void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700185static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
186{
187 struct xfrm_state_afinfo *afinfo;
188 if (unlikely(family >= NPROTO))
189 return NULL;
190 write_lock_bh(&xfrm_state_afinfo_lock);
191 afinfo = xfrm_state_afinfo[family];
192 if (unlikely(!afinfo))
193 write_unlock_bh(&xfrm_state_afinfo_lock);
194 return afinfo;
195}
196
197static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800198 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700199{
200 write_unlock_bh(&xfrm_state_afinfo_lock);
201}
202
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800203int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700204{
205 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800206 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700207 int err = 0;
208
209 if (unlikely(afinfo == NULL))
210 return -EAFNOSUPPORT;
211 typemap = afinfo->type_map;
212
213 if (likely(typemap[type->proto] == NULL))
214 typemap[type->proto] = type;
215 else
216 err = -EEXIST;
217 xfrm_state_unlock_afinfo(afinfo);
218 return err;
219}
220EXPORT_SYMBOL(xfrm_register_type);
221
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800222int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700223{
224 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800225 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700226 int err = 0;
227
228 if (unlikely(afinfo == NULL))
229 return -EAFNOSUPPORT;
230 typemap = afinfo->type_map;
231
232 if (unlikely(typemap[type->proto] != type))
233 err = -ENOENT;
234 else
235 typemap[type->proto] = NULL;
236 xfrm_state_unlock_afinfo(afinfo);
237 return err;
238}
239EXPORT_SYMBOL(xfrm_unregister_type);
240
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800241static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700242{
243 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800244 const struct xfrm_type **typemap;
245 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700246 int modload_attempted = 0;
247
248retry:
249 afinfo = xfrm_state_get_afinfo(family);
250 if (unlikely(afinfo == NULL))
251 return NULL;
252 typemap = afinfo->type_map;
253
254 type = typemap[proto];
255 if (unlikely(type && !try_module_get(type->owner)))
256 type = NULL;
257 if (!type && !modload_attempted) {
258 xfrm_state_put_afinfo(afinfo);
259 request_module("xfrm-type-%d-%d", family, proto);
260 modload_attempted = 1;
261 goto retry;
262 }
263
264 xfrm_state_put_afinfo(afinfo);
265 return type;
266}
267
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800268static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700269{
270 module_put(type->owner);
271}
272
273int xfrm_register_mode(struct xfrm_mode *mode, int family)
274{
275 struct xfrm_state_afinfo *afinfo;
276 struct xfrm_mode **modemap;
277 int err;
278
279 if (unlikely(mode->encap >= XFRM_MODE_MAX))
280 return -EINVAL;
281
282 afinfo = xfrm_state_lock_afinfo(family);
283 if (unlikely(afinfo == NULL))
284 return -EAFNOSUPPORT;
285
286 err = -EEXIST;
287 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700288 if (modemap[mode->encap])
289 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700290
Herbert Xu17c2a422007-10-17 21:33:12 -0700291 err = -ENOENT;
292 if (!try_module_get(afinfo->owner))
293 goto out;
294
295 mode->afinfo = afinfo;
296 modemap[mode->encap] = mode;
297 err = 0;
298
299out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700300 xfrm_state_unlock_afinfo(afinfo);
301 return err;
302}
303EXPORT_SYMBOL(xfrm_register_mode);
304
305int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
306{
307 struct xfrm_state_afinfo *afinfo;
308 struct xfrm_mode **modemap;
309 int err;
310
311 if (unlikely(mode->encap >= XFRM_MODE_MAX))
312 return -EINVAL;
313
314 afinfo = xfrm_state_lock_afinfo(family);
315 if (unlikely(afinfo == NULL))
316 return -EAFNOSUPPORT;
317
318 err = -ENOENT;
319 modemap = afinfo->mode_map;
320 if (likely(modemap[mode->encap] == mode)) {
321 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700322 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700323 err = 0;
324 }
325
326 xfrm_state_unlock_afinfo(afinfo);
327 return err;
328}
329EXPORT_SYMBOL(xfrm_unregister_mode);
330
331static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
332{
333 struct xfrm_state_afinfo *afinfo;
334 struct xfrm_mode *mode;
335 int modload_attempted = 0;
336
337 if (unlikely(encap >= XFRM_MODE_MAX))
338 return NULL;
339
340retry:
341 afinfo = xfrm_state_get_afinfo(family);
342 if (unlikely(afinfo == NULL))
343 return NULL;
344
345 mode = afinfo->mode_map[encap];
346 if (unlikely(mode && !try_module_get(mode->owner)))
347 mode = NULL;
348 if (!mode && !modload_attempted) {
349 xfrm_state_put_afinfo(afinfo);
350 request_module("xfrm-mode-%d-%d", family, encap);
351 modload_attempted = 1;
352 goto retry;
353 }
354
355 xfrm_state_put_afinfo(afinfo);
356 return mode;
357}
358
359static void xfrm_put_mode(struct xfrm_mode *mode)
360{
361 module_put(mode->owner);
362}
363
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364static void xfrm_state_gc_destroy(struct xfrm_state *x)
365{
David S. Millera47f0ce2006-08-24 03:54:22 -0700366 del_timer_sync(&x->timer);
367 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800368 kfree(x->aalg);
369 kfree(x->ealg);
370 kfree(x->calg);
371 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700372 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700373 if (x->inner_mode)
374 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700375 if (x->inner_mode_iaf)
376 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700377 if (x->outer_mode)
378 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 if (x->type) {
380 x->type->destructor(x);
381 xfrm_put_type(x->type);
382 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800383 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 kfree(x);
385}
386
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800387static void xfrm_state_gc_task(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800389 struct net *net = container_of(work, struct net, xfrm.state_gc_work);
Herbert Xu12a169e2008-10-01 07:03:24 -0700390 struct xfrm_state *x;
391 struct hlist_node *entry, *tmp;
392 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800395 hlist_move_list(&net->xfrm.state_gc_list, &gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 spin_unlock_bh(&xfrm_state_gc_lock);
397
Herbert Xu12a169e2008-10-01 07:03:24 -0700398 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700400
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800401 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402}
403
404static inline unsigned long make_jiffies(long secs)
405{
406 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
407 return MAX_SCHEDULE_TIMEOUT-1;
408 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900409 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410}
411
412static void xfrm_timer_handler(unsigned long data)
413{
414 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800415 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 long next = LONG_MAX;
417 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600418 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 spin_lock(&x->lock);
421 if (x->km.state == XFRM_STATE_DEAD)
422 goto out;
423 if (x->km.state == XFRM_STATE_EXPIRED)
424 goto expired;
425 if (x->lft.hard_add_expires_seconds) {
426 long tmo = x->lft.hard_add_expires_seconds +
427 x->curlft.add_time - now;
428 if (tmo <= 0)
429 goto expired;
430 if (tmo < next)
431 next = tmo;
432 }
433 if (x->lft.hard_use_expires_seconds) {
434 long tmo = x->lft.hard_use_expires_seconds +
435 (x->curlft.use_time ? : now) - now;
436 if (tmo <= 0)
437 goto expired;
438 if (tmo < next)
439 next = tmo;
440 }
441 if (x->km.dying)
442 goto resched;
443 if (x->lft.soft_add_expires_seconds) {
444 long tmo = x->lft.soft_add_expires_seconds +
445 x->curlft.add_time - now;
446 if (tmo <= 0)
447 warn = 1;
448 else if (tmo < next)
449 next = tmo;
450 }
451 if (x->lft.soft_use_expires_seconds) {
452 long tmo = x->lft.soft_use_expires_seconds +
453 (x->curlft.use_time ? : now) - now;
454 if (tmo <= 0)
455 warn = 1;
456 else if (tmo < next)
457 next = tmo;
458 }
459
Herbert Xu4666faa2005-06-18 22:43:22 -0700460 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800462 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700464 if (next != LONG_MAX)
465 mod_timer(&x->timer, jiffies + make_jiffies(next));
466
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 goto out;
468
469expired:
470 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
471 x->km.state = XFRM_STATE_EXPIRED;
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800472 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 next = 2;
474 goto resched;
475 }
Joy Latten161a09e2006-11-27 13:11:54 -0600476
477 err = __xfrm_state_delete(x);
478 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800479 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Joy Lattenab5f5e82007-09-17 11:51:22 -0700481 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400482 audit_get_loginuid(current),
483 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485out:
486 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488
David S. Miller0ac84752006-03-20 19:18:23 -0800489static void xfrm_replay_timer_handler(unsigned long data);
490
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800491struct xfrm_state *xfrm_state_alloc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
493 struct xfrm_state *x;
494
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700495 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 if (x) {
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800498 write_pnet(&x->xs_net, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 atomic_set(&x->refcnt, 1);
500 atomic_set(&x->tunnel_users, 0);
Herbert Xu12a169e2008-10-01 07:03:24 -0700501 INIT_LIST_HEAD(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700502 INIT_HLIST_NODE(&x->bydst);
503 INIT_HLIST_NODE(&x->bysrc);
504 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800505 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
506 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
507 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800508 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 x->lft.soft_byte_limit = XFRM_INF;
510 x->lft.soft_packet_limit = XFRM_INF;
511 x->lft.hard_byte_limit = XFRM_INF;
512 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800513 x->replay_maxage = 0;
514 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700515 x->inner_mode = NULL;
516 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 spin_lock_init(&x->lock);
518 }
519 return x;
520}
521EXPORT_SYMBOL(xfrm_state_alloc);
522
523void __xfrm_state_destroy(struct xfrm_state *x)
524{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700525 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -0800528 hlist_add_head(&x->gclist, &init_net.xfrm.state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 spin_unlock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800530 schedule_work(&init_net.xfrm.state_gc_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531}
532EXPORT_SYMBOL(__xfrm_state_destroy);
533
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800534int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700536 int err = -ESRCH;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (x->km.state != XFRM_STATE_DEAD) {
539 x->km.state = XFRM_STATE_DEAD;
540 spin_lock(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -0700541 list_del(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700542 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700543 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700544 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700545 hlist_del(&x->byspi);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800546 init_net.xfrm.state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 /* All xfrm_state objects are created by xfrm_state_alloc.
550 * The xfrm_state_alloc call gives a reference, and that
551 * is what we are dropping here.
552 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800553 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700554 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700556
557 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800559EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700561int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700563 int err;
564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700566 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700568
569 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570}
571EXPORT_SYMBOL(xfrm_state_delete);
572
Joy Latten4aa2e622007-06-04 19:05:57 -0400573#ifdef CONFIG_SECURITY_NETWORK_XFRM
574static inline int
575xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
Joy Latten4aa2e622007-06-04 19:05:57 -0400577 int i, err = 0;
578
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800579 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400580 struct hlist_node *entry;
581 struct xfrm_state *x;
582
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800583 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400584 if (xfrm_id_proto_match(x->id.proto, proto) &&
585 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700586 xfrm_audit_state_delete(x, 0,
587 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400588 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700589 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400590 return err;
591 }
592 }
593 }
594
595 return err;
596}
597#else
598static inline int
599xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
600{
601 return 0;
602}
603#endif
604
605int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
606{
607 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400610 err = xfrm_state_flush_secctx_check(proto, audit_info);
611 if (err)
612 goto out;
613
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800614 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700615 struct hlist_node *entry;
616 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617restart:
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800618 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700620 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 xfrm_state_hold(x);
622 spin_unlock_bh(&xfrm_state_lock);
623
Joy Latten161a09e2006-11-27 13:11:54 -0600624 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700625 xfrm_audit_state_delete(x, err ? 0 : 1,
626 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400627 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700628 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 xfrm_state_put(x);
630
631 spin_lock_bh(&xfrm_state_lock);
632 goto restart;
633 }
634 }
635 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400636 err = 0;
637
638out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 spin_unlock_bh(&xfrm_state_lock);
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800640 wake_up(&init_net.xfrm.km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400641 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643EXPORT_SYMBOL(xfrm_state_flush);
644
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700645void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700646{
647 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800648 si->sadcnt = init_net.xfrm.state_num;
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800649 si->sadhcnt = init_net.xfrm.state_hmask;
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700650 si->sadhmcnt = xfrm_state_hashmax;
651 spin_unlock_bh(&xfrm_state_lock);
652}
653EXPORT_SYMBOL(xfrm_sad_getinfo);
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655static int
656xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
657 struct xfrm_tmpl *tmpl,
658 xfrm_address_t *daddr, xfrm_address_t *saddr,
659 unsigned short family)
660{
661 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
662 if (!afinfo)
663 return -1;
664 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
665 xfrm_state_put_afinfo(afinfo);
666 return 0;
667}
668
Al Viroa94cfd12006-09-27 18:47:24 -0700669static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700670{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800671 unsigned int h = xfrm_spi_hash(&init_net, daddr, spi, proto, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700672 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700673 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700674
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800675 hlist_for_each_entry(x, entry, init_net.xfrm.state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700676 if (x->props.family != family ||
677 x->id.spi != spi ||
678 x->id.proto != proto)
679 continue;
680
681 switch (family) {
682 case AF_INET:
683 if (x->id.daddr.a4 != daddr->a4)
684 continue;
685 break;
686 case AF_INET6:
687 if (!ipv6_addr_equal((struct in6_addr *)daddr,
688 (struct in6_addr *)
689 x->id.daddr.a6))
690 continue;
691 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700692 }
David S. Milleredcd5822006-08-24 00:42:45 -0700693
694 xfrm_state_hold(x);
695 return x;
696 }
697
698 return NULL;
699}
700
701static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
702{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800703 unsigned int h = xfrm_src_hash(&init_net, daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700704 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700705 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700706
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800707 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700708 if (x->props.family != family ||
709 x->id.proto != proto)
710 continue;
711
712 switch (family) {
713 case AF_INET:
714 if (x->id.daddr.a4 != daddr->a4 ||
715 x->props.saddr.a4 != saddr->a4)
716 continue;
717 break;
718 case AF_INET6:
719 if (!ipv6_addr_equal((struct in6_addr *)daddr,
720 (struct in6_addr *)
721 x->id.daddr.a6) ||
722 !ipv6_addr_equal((struct in6_addr *)saddr,
723 (struct in6_addr *)
724 x->props.saddr.a6))
725 continue;
726 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700727 }
David S. Milleredcd5822006-08-24 00:42:45 -0700728
729 xfrm_state_hold(x);
730 return x;
731 }
732
733 return NULL;
734}
735
736static inline struct xfrm_state *
737__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
738{
739 if (use_spi)
740 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
741 x->id.proto, family);
742 else
743 return __xfrm_state_lookup_byaddr(&x->id.daddr,
744 &x->props.saddr,
745 x->id.proto, family);
746}
747
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700748static void xfrm_hash_grow_check(int have_hash_collision)
749{
750 if (have_hash_collision &&
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800751 (init_net.xfrm.state_hmask + 1) < xfrm_state_hashmax &&
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800752 init_net.xfrm.state_num > init_net.xfrm.state_hmask)
Alexey Dobriyan63082732008-11-25 17:19:07 -0800753 schedule_work(&init_net.xfrm.state_hash_work);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700754}
755
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900757xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 struct flowi *fl, struct xfrm_tmpl *tmpl,
759 struct xfrm_policy *pol, int *err,
760 unsigned short family)
761{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800762 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700763 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700764 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 int acquire_in_progress = 0;
766 int error = 0;
767 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900768
David S. Miller37b08e32008-09-02 20:14:15 -0700769 to_put = NULL;
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800772 h = xfrm_dst_hash(&init_net, daddr, saddr, tmpl->reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800773 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 if (x->props.family == family &&
775 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700776 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 xfrm_state_addr_check(x, daddr, saddr, family) &&
778 tmpl->mode == x->props.mode &&
779 tmpl->id.proto == x->id.proto &&
780 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
781 /* Resolution logic:
782 1. There is a valid state with matching selector.
783 Done.
784 2. Valid state with inappropriate selector. Skip.
785
786 Entering area of "sysdeps".
787
788 3. If state is not valid, selector is temporary,
789 it selects only session which triggered
790 previous resolution. Key manager will do
791 something to install a state with proper
792 selector.
793 */
794 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700795 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700796 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 continue;
798 if (!best ||
799 best->km.dying > x->km.dying ||
800 (best->km.dying == x->km.dying &&
801 best->curlft.add_time < x->curlft.add_time))
802 best = x;
803 } else if (x->km.state == XFRM_STATE_ACQ) {
804 acquire_in_progress = 1;
805 } else if (x->km.state == XFRM_STATE_ERROR ||
806 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700807 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700808 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 error = -ESRCH;
810 }
811 }
812 }
813
814 x = best;
815 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700816 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700817 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
818 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700819 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 error = -EEXIST;
821 goto out;
822 }
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800823 x = xfrm_state_alloc(&init_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 if (x == NULL) {
825 error = -ENOMEM;
826 goto out;
827 }
828 /* Initialize temporary selector matching only
829 * to current session. */
830 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
831
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700832 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
833 if (error) {
834 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700835 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700836 x = NULL;
837 goto out;
838 }
839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 if (km_query(x, tmpl, pol) == 0) {
841 x->km.state = XFRM_STATE_ACQ;
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -0800842 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800843 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800844 h = xfrm_src_hash(&init_net, daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800845 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 if (x->id.spi) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800847 h = xfrm_spi_hash(&init_net, &x->id.daddr, x->id.spi, x->id.proto, family);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800848 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 }
David S. Miller01e67d02007-05-25 00:41:38 -0700850 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
851 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 add_timer(&x->timer);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800853 init_net.xfrm.state_num++;
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700854 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 } else {
856 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700857 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 x = NULL;
859 error = -ESRCH;
860 }
861 }
862out:
863 if (x)
864 xfrm_state_hold(x);
865 else
866 *err = acquire_in_progress ? -EAGAIN : error;
867 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700868 if (to_put)
869 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 return x;
871}
872
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700873struct xfrm_state *
874xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
875 unsigned short family, u8 mode, u8 proto, u32 reqid)
876{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800877 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700878 struct xfrm_state *rx = NULL, *x = NULL;
879 struct hlist_node *entry;
880
881 spin_lock(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800882 h = xfrm_dst_hash(&init_net, daddr, saddr, reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800883 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700884 if (x->props.family == family &&
885 x->props.reqid == reqid &&
886 !(x->props.flags & XFRM_STATE_WILDRECV) &&
887 xfrm_state_addr_check(x, daddr, saddr, family) &&
888 mode == x->props.mode &&
889 proto == x->id.proto &&
890 x->km.state == XFRM_STATE_VALID) {
891 rx = x;
892 break;
893 }
894 }
895
896 if (rx)
897 xfrm_state_hold(rx);
898 spin_unlock(&xfrm_state_lock);
899
900
901 return rx;
902}
903EXPORT_SYMBOL(xfrm_stateonly_find);
904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905static void __xfrm_state_insert(struct xfrm_state *x)
906{
David S. Millera624c102006-08-24 03:24:33 -0700907 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
David S. Miller9d4a7062006-08-24 03:18:09 -0700909 x->genid = ++xfrm_state_genid;
910
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -0800911 list_add(&x->km.all, &init_net.xfrm.state_all);
Timo Teras4c563f72008-02-28 21:31:08 -0800912
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800913 h = xfrm_dst_hash(&init_net, &x->id.daddr, &x->props.saddr,
David S. Millerc1969f22006-08-24 04:00:03 -0700914 x->props.reqid, x->props.family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800915 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800917 h = xfrm_src_hash(&init_net, &x->id.daddr, &x->props.saddr, x->props.family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800918 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700920 if (x->id.spi) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800921 h = xfrm_spi_hash(&init_net, &x->id.daddr, x->id.spi, x->id.proto,
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700922 x->props.family);
923
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800924 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700925 }
926
David S. Millera47f0ce2006-08-24 03:54:22 -0700927 mod_timer(&x->timer, jiffies + HZ);
928 if (x->replay_maxage)
929 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800930
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800931 wake_up(&init_net.xfrm.km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700932
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800933 init_net.xfrm.state_num++;
David S. Millerf034b5d2006-08-24 03:08:07 -0700934
David S. Miller918049f2006-10-12 22:03:24 -0700935 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936}
937
David S. Millerc7f5ea32006-08-24 03:29:04 -0700938/* xfrm_state_lock is held */
939static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
940{
941 unsigned short family = xnew->props.family;
942 u32 reqid = xnew->props.reqid;
943 struct xfrm_state *x;
944 struct hlist_node *entry;
945 unsigned int h;
946
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800947 h = xfrm_dst_hash(&init_net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800948 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
David S. Millerc7f5ea32006-08-24 03:29:04 -0700949 if (x->props.family == family &&
950 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700951 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
952 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700953 x->genid = xfrm_state_genid;
954 }
955}
956
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957void xfrm_state_insert(struct xfrm_state *x)
958{
959 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700960 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 __xfrm_state_insert(x);
962 spin_unlock_bh(&xfrm_state_lock);
963}
964EXPORT_SYMBOL(xfrm_state_insert);
965
David S. Miller27708342006-08-24 00:13:10 -0700966/* xfrm_state_lock is held */
967static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
968{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800969 unsigned int h = xfrm_dst_hash(&init_net, daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700970 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700971 struct xfrm_state *x;
972
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800973 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700974 if (x->props.reqid != reqid ||
975 x->props.mode != mode ||
976 x->props.family != family ||
977 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700978 x->id.spi != 0 ||
979 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700980 continue;
981
982 switch (family) {
983 case AF_INET:
984 if (x->id.daddr.a4 != daddr->a4 ||
985 x->props.saddr.a4 != saddr->a4)
986 continue;
987 break;
988 case AF_INET6:
989 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
990 (struct in6_addr *)daddr) ||
991 !ipv6_addr_equal((struct in6_addr *)
992 x->props.saddr.a6,
993 (struct in6_addr *)saddr))
994 continue;
995 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700996 }
David S. Miller27708342006-08-24 00:13:10 -0700997
998 xfrm_state_hold(x);
999 return x;
1000 }
1001
1002 if (!create)
1003 return NULL;
1004
Alexey Dobriyan673c09b2008-11-25 17:15:16 -08001005 x = xfrm_state_alloc(&init_net);
David S. Miller27708342006-08-24 00:13:10 -07001006 if (likely(x)) {
1007 switch (family) {
1008 case AF_INET:
1009 x->sel.daddr.a4 = daddr->a4;
1010 x->sel.saddr.a4 = saddr->a4;
1011 x->sel.prefixlen_d = 32;
1012 x->sel.prefixlen_s = 32;
1013 x->props.saddr.a4 = saddr->a4;
1014 x->id.daddr.a4 = daddr->a4;
1015 break;
1016
1017 case AF_INET6:
1018 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1019 (struct in6_addr *)daddr);
1020 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1021 (struct in6_addr *)saddr);
1022 x->sel.prefixlen_d = 128;
1023 x->sel.prefixlen_s = 128;
1024 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1025 (struct in6_addr *)saddr);
1026 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1027 (struct in6_addr *)daddr);
1028 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001029 }
David S. Miller27708342006-08-24 00:13:10 -07001030
1031 x->km.state = XFRM_STATE_ACQ;
1032 x->id.proto = proto;
1033 x->props.family = family;
1034 x->props.mode = mode;
1035 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001036 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001037 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001038 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001039 add_timer(&x->timer);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001040 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001041 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001042 h = xfrm_src_hash(&init_net, daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001043 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001044
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08001045 init_net.xfrm.state_num++;
David S. Miller918049f2006-10-12 22:03:24 -07001046
1047 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001048 }
1049
1050 return x;
1051}
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1054
1055int xfrm_state_add(struct xfrm_state *x)
1056{
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) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 x1 = __xfrm_find_acq_byseq(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)
David S. Miller27708342006-08-24 00:13:10 -07001086 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1087 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{
1112 int err = -ENOMEM;
Alexey Dobriyan673c09b2008-11-25 17:15:16 -08001113 struct xfrm_state *x = xfrm_state_alloc(&init_net);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001114 if (!x)
1115 goto error;
1116
1117 memcpy(&x->id, &orig->id, sizeof(x->id));
1118 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1119 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1120 x->props.mode = orig->props.mode;
1121 x->props.replay_window = orig->props.replay_window;
1122 x->props.reqid = orig->props.reqid;
1123 x->props.family = orig->props.family;
1124 x->props.saddr = orig->props.saddr;
1125
1126 if (orig->aalg) {
1127 x->aalg = xfrm_algo_clone(orig->aalg);
1128 if (!x->aalg)
1129 goto error;
1130 }
1131 x->props.aalgo = orig->props.aalgo;
1132
1133 if (orig->ealg) {
1134 x->ealg = xfrm_algo_clone(orig->ealg);
1135 if (!x->ealg)
1136 goto error;
1137 }
1138 x->props.ealgo = orig->props.ealgo;
1139
1140 if (orig->calg) {
1141 x->calg = xfrm_algo_clone(orig->calg);
1142 if (!x->calg)
1143 goto error;
1144 }
1145 x->props.calgo = orig->props.calgo;
1146
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001147 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001148 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1149 if (!x->encap)
1150 goto error;
1151 }
1152
1153 if (orig->coaddr) {
1154 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1155 GFP_KERNEL);
1156 if (!x->coaddr)
1157 goto error;
1158 }
1159
1160 err = xfrm_init_state(x);
1161 if (err)
1162 goto error;
1163
1164 x->props.flags = orig->props.flags;
1165
1166 x->curlft.add_time = orig->curlft.add_time;
1167 x->km.state = orig->km.state;
1168 x->km.seq = orig->km.seq;
1169
1170 return x;
1171
1172 error:
1173 if (errp)
1174 *errp = err;
1175 if (x) {
1176 kfree(x->aalg);
1177 kfree(x->ealg);
1178 kfree(x->calg);
1179 kfree(x->encap);
1180 kfree(x->coaddr);
1181 }
1182 kfree(x);
1183 return NULL;
1184}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001185
1186/* xfrm_state_lock is held */
1187struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1188{
1189 unsigned int h;
1190 struct xfrm_state *x;
1191 struct hlist_node *entry;
1192
1193 if (m->reqid) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001194 h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001195 m->reqid, m->old_family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001196 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001197 if (x->props.mode != m->mode ||
1198 x->id.proto != m->proto)
1199 continue;
1200 if (m->reqid && x->props.reqid != m->reqid)
1201 continue;
1202 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1203 m->old_family) ||
1204 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1205 m->old_family))
1206 continue;
1207 xfrm_state_hold(x);
1208 return x;
1209 }
1210 } else {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001211 h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001212 m->old_family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001213 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001214 if (x->props.mode != m->mode ||
1215 x->id.proto != m->proto)
1216 continue;
1217 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1218 m->old_family) ||
1219 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1220 m->old_family))
1221 continue;
1222 xfrm_state_hold(x);
1223 return x;
1224 }
1225 }
1226
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001227 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001228}
1229EXPORT_SYMBOL(xfrm_migrate_state_find);
1230
1231struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1232 struct xfrm_migrate *m)
1233{
1234 struct xfrm_state *xc;
1235 int err;
1236
1237 xc = xfrm_state_clone(x, &err);
1238 if (!xc)
1239 return NULL;
1240
1241 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1242 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1243
1244 /* add state */
1245 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1246 /* a care is needed when the destination address of the
1247 state is to be updated as it is a part of triplet */
1248 xfrm_state_insert(xc);
1249 } else {
1250 if ((err = xfrm_state_add(xc)) < 0)
1251 goto error;
1252 }
1253
1254 return xc;
1255error:
1256 kfree(xc);
1257 return NULL;
1258}
1259EXPORT_SYMBOL(xfrm_state_migrate);
1260#endif
1261
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262int xfrm_state_update(struct xfrm_state *x)
1263{
David S. Miller37b08e32008-09-02 20:14:15 -07001264 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001266 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267
David S. Miller37b08e32008-09-02 20:14:15 -07001268 to_put = NULL;
1269
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001271 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272
1273 err = -ESRCH;
1274 if (!x1)
1275 goto out;
1276
1277 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001278 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 err = -EEXIST;
1280 goto out;
1281 }
1282
1283 if (x1->km.state == XFRM_STATE_ACQ) {
1284 __xfrm_state_insert(x);
1285 x = NULL;
1286 }
1287 err = 0;
1288
1289out:
1290 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
David S. Miller37b08e32008-09-02 20:14:15 -07001292 if (to_put)
1293 xfrm_state_put(to_put);
1294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 if (err)
1296 return err;
1297
1298 if (!x) {
1299 xfrm_state_delete(x1);
1300 xfrm_state_put(x1);
1301 return 0;
1302 }
1303
1304 err = -EINVAL;
1305 spin_lock_bh(&x1->lock);
1306 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1307 if (x->encap && x1->encap)
1308 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001309 if (x->coaddr && x1->coaddr) {
1310 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1311 }
1312 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1313 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1315 x1->km.dying = 0;
1316
David S. Millera47f0ce2006-08-24 03:54:22 -07001317 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 if (x1->curlft.use_time)
1319 xfrm_state_check_expire(x1);
1320
1321 err = 0;
1322 }
1323 spin_unlock_bh(&x1->lock);
1324
1325 xfrm_state_put(x1);
1326
1327 return err;
1328}
1329EXPORT_SYMBOL(xfrm_state_update);
1330
1331int xfrm_state_check_expire(struct xfrm_state *x)
1332{
1333 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001334 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
1336 if (x->km.state != XFRM_STATE_VALID)
1337 return -EINVAL;
1338
1339 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1340 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001341 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001342 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 return -EINVAL;
1344 }
1345
1346 if (!x->km.dying &&
1347 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001348 x->curlft.packets >= x->lft.soft_packet_limit)) {
1349 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001350 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 return 0;
1353}
1354EXPORT_SYMBOL(xfrm_state_check_expire);
1355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001357xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 unsigned short family)
1359{
1360 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001363 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 return x;
1366}
1367EXPORT_SYMBOL(xfrm_state_lookup);
1368
1369struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001370xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1371 u8 proto, unsigned short family)
1372{
1373 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001374
1375 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001376 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001377 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001378 return x;
1379}
1380EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1381
1382struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001383xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1384 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 int create, unsigned short family)
1386{
1387 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001390 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001392
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return x;
1394}
1395EXPORT_SYMBOL(xfrm_find_acq);
1396
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001397#ifdef CONFIG_XFRM_SUB_POLICY
1398int
1399xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1400 unsigned short family)
1401{
1402 int err = 0;
1403 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1404 if (!afinfo)
1405 return -EAFNOSUPPORT;
1406
1407 spin_lock_bh(&xfrm_state_lock);
1408 if (afinfo->tmpl_sort)
1409 err = afinfo->tmpl_sort(dst, src, n);
1410 spin_unlock_bh(&xfrm_state_lock);
1411 xfrm_state_put_afinfo(afinfo);
1412 return err;
1413}
1414EXPORT_SYMBOL(xfrm_tmpl_sort);
1415
1416int
1417xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1418 unsigned short family)
1419{
1420 int err = 0;
1421 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1422 if (!afinfo)
1423 return -EAFNOSUPPORT;
1424
1425 spin_lock_bh(&xfrm_state_lock);
1426 if (afinfo->state_sort)
1427 err = afinfo->state_sort(dst, src, n);
1428 spin_unlock_bh(&xfrm_state_lock);
1429 xfrm_state_put_afinfo(afinfo);
1430 return err;
1431}
1432EXPORT_SYMBOL(xfrm_state_sort);
1433#endif
1434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435/* Silly enough, but I'm lazy to build resolution list */
1436
1437static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1438{
1439 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
Alexey Dobriyan529983e2008-11-25 17:18:12 -08001441 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001442 struct hlist_node *entry;
1443 struct xfrm_state *x;
1444
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001445 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
David S. Miller8f126e32006-08-24 02:45:07 -07001446 if (x->km.seq == seq &&
1447 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 xfrm_state_hold(x);
1449 return x;
1450 }
1451 }
1452 }
1453 return NULL;
1454}
1455
1456struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1457{
1458 struct xfrm_state *x;
1459
1460 spin_lock_bh(&xfrm_state_lock);
1461 x = __xfrm_find_acq_byseq(seq);
1462 spin_unlock_bh(&xfrm_state_lock);
1463 return x;
1464}
1465EXPORT_SYMBOL(xfrm_find_acq_byseq);
1466
1467u32 xfrm_get_acqseq(void)
1468{
1469 u32 res;
1470 static u32 acqseq;
1471 static DEFINE_SPINLOCK(acqseq_lock);
1472
1473 spin_lock_bh(&acqseq_lock);
1474 res = (++acqseq ? : ++acqseq);
1475 spin_unlock_bh(&acqseq_lock);
1476 return res;
1477}
1478EXPORT_SYMBOL(xfrm_get_acqseq);
1479
Herbert Xu658b2192007-10-09 13:29:52 -07001480int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481{
David S. Millerf034b5d2006-08-24 03:08:07 -07001482 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001484 int err = -ENOENT;
1485 __be32 minspi = htonl(low);
1486 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Herbert Xu658b2192007-10-09 13:29:52 -07001488 spin_lock_bh(&x->lock);
1489 if (x->km.state == XFRM_STATE_DEAD)
1490 goto unlock;
1491
1492 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001494 goto unlock;
1495
1496 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
1498 if (minspi == maxspi) {
1499 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1500 if (x0) {
1501 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001502 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 }
1504 x->id.spi = minspi;
1505 } else {
1506 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001507 for (h=0; h<high-low+1; h++) {
1508 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1510 if (x0 == NULL) {
1511 x->id.spi = htonl(spi);
1512 break;
1513 }
1514 xfrm_state_put(x0);
1515 }
1516 }
1517 if (x->id.spi) {
1518 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001519 h = xfrm_spi_hash(&init_net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08001520 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001522
1523 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 }
Herbert Xu658b2192007-10-09 13:29:52 -07001525
1526unlock:
1527 spin_unlock_bh(&x->lock);
1528
1529 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530}
1531EXPORT_SYMBOL(xfrm_alloc_spi);
1532
Timo Teras4c563f72008-02-28 21:31:08 -08001533int xfrm_state_walk(struct xfrm_state_walk *walk,
1534 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 void *data)
1536{
Herbert Xu12a169e2008-10-01 07:03:24 -07001537 struct xfrm_state *state;
1538 struct xfrm_state_walk *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 int err = 0;
1540
Herbert Xu12a169e2008-10-01 07:03:24 -07001541 if (walk->seq != 0 && list_empty(&walk->all))
Timo Teras4c563f72008-02-28 21:31:08 -08001542 return 0;
1543
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 spin_lock_bh(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -07001545 if (list_empty(&walk->all))
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001546 x = list_first_entry(&init_net.xfrm.state_all, struct xfrm_state_walk, all);
Herbert Xu12a169e2008-10-01 07:03:24 -07001547 else
1548 x = list_entry(&walk->all, struct xfrm_state_walk, all);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001549 list_for_each_entry_from(x, &init_net.xfrm.state_all, all) {
Herbert Xu12a169e2008-10-01 07:03:24 -07001550 if (x->state == XFRM_STATE_DEAD)
Timo Teras4c563f72008-02-28 21:31:08 -08001551 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001552 state = container_of(x, struct xfrm_state, km);
1553 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
Timo Teras4c563f72008-02-28 21:31:08 -08001554 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001555 err = func(state, walk->seq, data);
1556 if (err) {
1557 list_move_tail(&walk->all, &x->all);
1558 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001560 walk->seq++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001562 if (walk->seq == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 err = -ENOENT;
1564 goto out;
1565 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001566 list_del_init(&walk->all);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567out:
1568 spin_unlock_bh(&xfrm_state_lock);
1569 return err;
1570}
1571EXPORT_SYMBOL(xfrm_state_walk);
1572
Herbert Xu5c182452008-09-22 19:48:19 -07001573void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1574{
Herbert Xu12a169e2008-10-01 07:03:24 -07001575 INIT_LIST_HEAD(&walk->all);
Herbert Xu5c182452008-09-22 19:48:19 -07001576 walk->proto = proto;
Herbert Xu12a169e2008-10-01 07:03:24 -07001577 walk->state = XFRM_STATE_DEAD;
1578 walk->seq = 0;
Herbert Xu5c182452008-09-22 19:48:19 -07001579}
1580EXPORT_SYMBOL(xfrm_state_walk_init);
1581
Herbert Xuabb81c42008-09-09 19:58:29 -07001582void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1583{
Herbert Xu12a169e2008-10-01 07:03:24 -07001584 if (list_empty(&walk->all))
Herbert Xu5c182452008-09-22 19:48:19 -07001585 return;
Herbert Xu5c182452008-09-22 19:48:19 -07001586
Herbert Xu12a169e2008-10-01 07:03:24 -07001587 spin_lock_bh(&xfrm_state_lock);
1588 list_del(&walk->all);
1589 spin_lock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001590}
1591EXPORT_SYMBOL(xfrm_state_walk_done);
1592
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001593
1594void xfrm_replay_notify(struct xfrm_state *x, int event)
1595{
1596 struct km_event c;
1597 /* we send notify messages in case
1598 * 1. we updated on of the sequence numbers, and the seqno difference
1599 * is at least x->replay_maxdiff, in this case we also update the
1600 * timeout of our timer function
1601 * 2. if x->replay_maxage has elapsed since last update,
1602 * and there were changes
1603 *
1604 * The state structure must be locked!
1605 */
1606
1607 switch (event) {
1608 case XFRM_REPLAY_UPDATE:
1609 if (x->replay_maxdiff &&
1610 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001611 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1612 if (x->xflags & XFRM_TIME_DEFER)
1613 event = XFRM_REPLAY_TIMEOUT;
1614 else
1615 return;
1616 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001617
1618 break;
1619
1620 case XFRM_REPLAY_TIMEOUT:
1621 if ((x->replay.seq == x->preplay.seq) &&
1622 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001623 (x->replay.oseq == x->preplay.oseq)) {
1624 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001625 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001626 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001627
1628 break;
1629 }
1630
1631 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1632 c.event = XFRM_MSG_NEWAE;
1633 c.data.aevent = event;
1634 km_state_notify(x, &c);
1635
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001636 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001637 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001638 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001639}
1640
1641static void xfrm_replay_timer_handler(unsigned long data)
1642{
1643 struct xfrm_state *x = (struct xfrm_state*)data;
1644
1645 spin_lock(&x->lock);
1646
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001647 if (x->km.state == XFRM_STATE_VALID) {
1648 if (xfrm_aevent_is_on())
1649 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1650 else
1651 x->xflags |= XFRM_TIME_DEFER;
1652 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001653
1654 spin_unlock(&x->lock);
1655}
1656
Paul Mooreafeb14b2007-12-21 14:58:11 -08001657int xfrm_replay_check(struct xfrm_state *x,
1658 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659{
1660 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001661 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
1663 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001664 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
1666 if (likely(seq > x->replay.seq))
1667 return 0;
1668
1669 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001670 if (diff >= min_t(unsigned int, x->props.replay_window,
1671 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001673 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 }
1675
1676 if (x->replay.bitmap & (1U << diff)) {
1677 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001678 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 }
1680 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001681
1682err:
1683 xfrm_audit_state_replay(x, skb, net_seq);
1684 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Al Viro61f46272006-09-27 18:48:33 -07001687void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688{
1689 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001690 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
1692 if (seq > x->replay.seq) {
1693 diff = seq - x->replay.seq;
1694 if (diff < x->props.replay_window)
1695 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1696 else
1697 x->replay.bitmap = 1;
1698 x->replay.seq = seq;
1699 } else {
1700 diff = x->replay.seq - seq;
1701 x->replay.bitmap |= (1U << diff);
1702 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001703
1704 if (xfrm_aevent_is_on())
1705 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
Denis Chengdf018122007-12-07 00:51:11 -08001708static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709static DEFINE_RWLOCK(xfrm_km_lock);
1710
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001711void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
1713 struct xfrm_mgr *km;
1714
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001715 read_lock(&xfrm_km_lock);
1716 list_for_each_entry(km, &xfrm_km_list, list)
1717 if (km->notify_policy)
1718 km->notify_policy(xp, dir, c);
1719 read_unlock(&xfrm_km_lock);
1720}
1721
1722void km_state_notify(struct xfrm_state *x, struct km_event *c)
1723{
1724 struct xfrm_mgr *km;
1725 read_lock(&xfrm_km_lock);
1726 list_for_each_entry(km, &xfrm_km_list, list)
1727 if (km->notify)
1728 km->notify(x, c);
1729 read_unlock(&xfrm_km_lock);
1730}
1731
1732EXPORT_SYMBOL(km_policy_notify);
1733EXPORT_SYMBOL(km_state_notify);
1734
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001735void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001736{
1737 struct km_event c;
1738
Herbert Xubf088672005-06-18 22:44:00 -07001739 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001740 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001741 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001742 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
1744 if (hard)
Alexey Dobriyan50a30652008-11-25 17:21:01 -08001745 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746}
1747
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001748EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001749/*
1750 * We send to all registered managers regardless of failure
1751 * We are happy with one success
1752*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001753int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001755 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 struct xfrm_mgr *km;
1757
1758 read_lock(&xfrm_km_lock);
1759 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001760 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1761 if (!acqret)
1762 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 }
1764 read_unlock(&xfrm_km_lock);
1765 return err;
1766}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001767EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
Al Viro5d36b182006-11-08 00:24:06 -08001769int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
1771 int err = -EINVAL;
1772 struct xfrm_mgr *km;
1773
1774 read_lock(&xfrm_km_lock);
1775 list_for_each_entry(km, &xfrm_km_list, list) {
1776 if (km->new_mapping)
1777 err = km->new_mapping(x, ipaddr, sport);
1778 if (!err)
1779 break;
1780 }
1781 read_unlock(&xfrm_km_lock);
1782 return err;
1783}
1784EXPORT_SYMBOL(km_new_mapping);
1785
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001786void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001788 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789
Herbert Xubf088672005-06-18 22:44:00 -07001790 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001791 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001792 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001793 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
1795 if (hard)
Alexey Dobriyan50a30652008-11-25 17:21:01 -08001796 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797}
David S. Millera70fcb02006-03-20 19:18:52 -08001798EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001800#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001801int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001802 struct xfrm_migrate *m, int num_migrate,
1803 struct xfrm_kmaddress *k)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001804{
1805 int err = -EINVAL;
1806 int ret;
1807 struct xfrm_mgr *km;
1808
1809 read_lock(&xfrm_km_lock);
1810 list_for_each_entry(km, &xfrm_km_list, list) {
1811 if (km->migrate) {
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001812 ret = km->migrate(sel, dir, type, m, num_migrate, k);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001813 if (!ret)
1814 err = ret;
1815 }
1816 }
1817 read_unlock(&xfrm_km_lock);
1818 return err;
1819}
1820EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001821#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001822
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001823int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1824{
1825 int err = -EINVAL;
1826 int ret;
1827 struct xfrm_mgr *km;
1828
1829 read_lock(&xfrm_km_lock);
1830 list_for_each_entry(km, &xfrm_km_list, list) {
1831 if (km->report) {
1832 ret = km->report(proto, sel, addr);
1833 if (!ret)
1834 err = ret;
1835 }
1836 }
1837 read_unlock(&xfrm_km_lock);
1838 return err;
1839}
1840EXPORT_SYMBOL(km_report);
1841
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1843{
1844 int err;
1845 u8 *data;
1846 struct xfrm_mgr *km;
1847 struct xfrm_policy *pol = NULL;
1848
1849 if (optlen <= 0 || optlen > PAGE_SIZE)
1850 return -EMSGSIZE;
1851
1852 data = kmalloc(optlen, GFP_KERNEL);
1853 if (!data)
1854 return -ENOMEM;
1855
1856 err = -EFAULT;
1857 if (copy_from_user(data, optval, optlen))
1858 goto out;
1859
1860 err = -EINVAL;
1861 read_lock(&xfrm_km_lock);
1862 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001863 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 optlen, &err);
1865 if (err >= 0)
1866 break;
1867 }
1868 read_unlock(&xfrm_km_lock);
1869
1870 if (err >= 0) {
1871 xfrm_sk_policy_insert(sk, err, pol);
1872 xfrm_pol_put(pol);
1873 err = 0;
1874 }
1875
1876out:
1877 kfree(data);
1878 return err;
1879}
1880EXPORT_SYMBOL(xfrm_user_policy);
1881
1882int xfrm_register_km(struct xfrm_mgr *km)
1883{
1884 write_lock_bh(&xfrm_km_lock);
1885 list_add_tail(&km->list, &xfrm_km_list);
1886 write_unlock_bh(&xfrm_km_lock);
1887 return 0;
1888}
1889EXPORT_SYMBOL(xfrm_register_km);
1890
1891int xfrm_unregister_km(struct xfrm_mgr *km)
1892{
1893 write_lock_bh(&xfrm_km_lock);
1894 list_del(&km->list);
1895 write_unlock_bh(&xfrm_km_lock);
1896 return 0;
1897}
1898EXPORT_SYMBOL(xfrm_unregister_km);
1899
1900int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1901{
1902 int err = 0;
1903 if (unlikely(afinfo == NULL))
1904 return -EINVAL;
1905 if (unlikely(afinfo->family >= NPROTO))
1906 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001907 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1909 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001910 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001912 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 return err;
1914}
1915EXPORT_SYMBOL(xfrm_state_register_afinfo);
1916
1917int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1918{
1919 int err = 0;
1920 if (unlikely(afinfo == NULL))
1921 return -EINVAL;
1922 if (unlikely(afinfo->family >= NPROTO))
1923 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001924 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1926 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1927 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001928 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001931 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 return err;
1933}
1934EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1935
Herbert Xu17c2a422007-10-17 21:33:12 -07001936static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937{
1938 struct xfrm_state_afinfo *afinfo;
1939 if (unlikely(family >= NPROTO))
1940 return NULL;
1941 read_lock(&xfrm_state_afinfo_lock);
1942 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001943 if (unlikely(!afinfo))
1944 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 return afinfo;
1946}
1947
Herbert Xu17c2a422007-10-17 21:33:12 -07001948static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001949 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950{
Herbert Xu546be242006-05-27 23:03:58 -07001951 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952}
1953
1954/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1955void xfrm_state_delete_tunnel(struct xfrm_state *x)
1956{
1957 if (x->tunnel) {
1958 struct xfrm_state *t = x->tunnel;
1959
1960 if (atomic_read(&t->tunnel_users) == 2)
1961 xfrm_state_delete(t);
1962 atomic_dec(&t->tunnel_users);
1963 xfrm_state_put(t);
1964 x->tunnel = NULL;
1965 }
1966}
1967EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1968
1969int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1970{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001971 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Patrick McHardyc5c25232007-04-09 11:47:18 -07001973 spin_lock_bh(&x->lock);
1974 if (x->km.state == XFRM_STATE_VALID &&
1975 x->type && x->type->get_mtu)
1976 res = x->type->get_mtu(x, mtu);
1977 else
Patrick McHardy28121612007-06-18 22:30:15 -07001978 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001979 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 return res;
1981}
1982
Herbert Xu72cb6962005-06-20 13:18:08 -07001983int xfrm_init_state(struct xfrm_state *x)
1984{
Herbert Xud094cd82005-06-20 13:19:41 -07001985 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001986 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001987 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001988 int err;
1989
Herbert Xud094cd82005-06-20 13:19:41 -07001990 err = -EAFNOSUPPORT;
1991 afinfo = xfrm_state_get_afinfo(family);
1992 if (!afinfo)
1993 goto error;
1994
1995 err = 0;
1996 if (afinfo->init_flags)
1997 err = afinfo->init_flags(x);
1998
1999 xfrm_state_put_afinfo(afinfo);
2000
2001 if (err)
2002 goto error;
2003
2004 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002005
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002006 if (x->sel.family != AF_UNSPEC) {
2007 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2008 if (inner_mode == NULL)
2009 goto error;
2010
2011 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2012 family != x->sel.family) {
2013 xfrm_put_mode(inner_mode);
2014 goto error;
2015 }
2016
2017 x->inner_mode = inner_mode;
2018 } else {
2019 struct xfrm_mode *inner_mode_iaf;
2020
2021 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2022 if (inner_mode == NULL)
2023 goto error;
2024
2025 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2026 xfrm_put_mode(inner_mode);
2027 goto error;
2028 }
2029
2030 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2031 if (inner_mode_iaf == NULL)
2032 goto error;
2033
2034 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2035 xfrm_put_mode(inner_mode_iaf);
2036 goto error;
2037 }
2038
2039 if (x->props.family == AF_INET) {
2040 x->inner_mode = inner_mode;
2041 x->inner_mode_iaf = inner_mode_iaf;
2042 } else {
2043 x->inner_mode = inner_mode_iaf;
2044 x->inner_mode_iaf = inner_mode;
2045 }
2046 }
Herbert Xu13996372007-10-17 21:35:51 -07002047
Herbert Xud094cd82005-06-20 13:19:41 -07002048 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002049 if (x->type == NULL)
2050 goto error;
2051
2052 err = x->type->init_state(x);
2053 if (err)
2054 goto error;
2055
Herbert Xu13996372007-10-17 21:35:51 -07002056 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2057 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002058 goto error;
2059
Herbert Xu72cb6962005-06-20 13:18:08 -07002060 x->km.state = XFRM_STATE_VALID;
2061
2062error:
2063 return err;
2064}
2065
2066EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002067
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002068int __net_init xfrm_state_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069{
David S. Millerf034b5d2006-08-24 03:08:07 -07002070 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002072 INIT_LIST_HEAD(&net->xfrm.state_all);
2073
David S. Millerf034b5d2006-08-24 03:08:07 -07002074 sz = sizeof(struct hlist_head) * 8;
2075
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002076 net->xfrm.state_bydst = xfrm_hash_alloc(sz);
2077 if (!net->xfrm.state_bydst)
2078 goto out_bydst;
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002079 net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2080 if (!net->xfrm.state_bysrc)
2081 goto out_bysrc;
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002082 net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2083 if (!net->xfrm.state_byspi)
2084 goto out_byspi;
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002085 net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
David S. Millerf034b5d2006-08-24 03:08:07 -07002086
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08002087 net->xfrm.state_num = 0;
Alexey Dobriyan63082732008-11-25 17:19:07 -08002088 INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -08002089 INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
Alexey Dobriyanc7837142008-11-25 17:20:36 -08002090 INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
Alexey Dobriyan50a30652008-11-25 17:21:01 -08002091 init_waitqueue_head(&net->xfrm.km_waitq);
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002092 return 0;
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002093
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002094out_byspi:
2095 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002096out_bysrc:
2097 xfrm_hash_free(net->xfrm.state_bydst, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002098out_bydst:
2099 return -ENOMEM;
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002100}
2101
2102void xfrm_state_fini(struct net *net)
2103{
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002104 unsigned int sz;
2105
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002106 WARN_ON(!list_empty(&net->xfrm.state_all));
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002107
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002108 sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002109 WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2110 xfrm_hash_free(net->xfrm.state_byspi, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002111 WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2112 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002113 WARN_ON(!hlist_empty(net->xfrm.state_bydst));
2114 xfrm_hash_free(net->xfrm.state_bydst, sz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115}
2116
Joy Lattenab5f5e82007-09-17 11:51:22 -07002117#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002118static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2119 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002120{
Paul Moore68277ac2007-12-20 20:49:33 -08002121 struct xfrm_sec_ctx *ctx = x->security;
2122 u32 spi = ntohl(x->id.spi);
2123
2124 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002125 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002126 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002127
2128 switch(x->props.family) {
2129 case AF_INET:
Harvey Harrison21454aa2008-10-31 00:54:56 -07002130 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2131 &x->props.saddr.a4, &x->id.daddr.a4);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002132 break;
2133 case AF_INET6:
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002134 audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002135 x->props.saddr.a6, x->id.daddr.a6);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002136 break;
2137 }
Paul Moore68277ac2007-12-20 20:49:33 -08002138
2139 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002140}
2141
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002142static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2143 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002144{
2145 struct iphdr *iph4;
2146 struct ipv6hdr *iph6;
2147
2148 switch (family) {
2149 case AF_INET:
2150 iph4 = ip_hdr(skb);
Harvey Harrison21454aa2008-10-31 00:54:56 -07002151 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2152 &iph4->saddr, &iph4->daddr);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002153 break;
2154 case AF_INET6:
2155 iph6 = ipv6_hdr(skb);
2156 audit_log_format(audit_buf,
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002157 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002158 &iph6->saddr,&iph6->daddr,
Paul Mooreafeb14b2007-12-21 14:58:11 -08002159 iph6->flow_lbl[0] & 0x0f,
2160 iph6->flow_lbl[1],
2161 iph6->flow_lbl[2]);
2162 break;
2163 }
2164}
2165
Paul Moore68277ac2007-12-20 20:49:33 -08002166void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002167 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002168{
2169 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002170
Paul Mooreafeb14b2007-12-21 14:58:11 -08002171 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002172 if (audit_buf == NULL)
2173 return;
Eric Paris25323862008-04-18 10:09:25 -04002174 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002175 xfrm_audit_helper_sainfo(x, audit_buf);
2176 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002177 audit_log_end(audit_buf);
2178}
2179EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2180
Paul Moore68277ac2007-12-20 20:49:33 -08002181void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002182 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002183{
2184 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002185
Paul Mooreafeb14b2007-12-21 14:58:11 -08002186 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002187 if (audit_buf == NULL)
2188 return;
Eric Paris25323862008-04-18 10:09:25 -04002189 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002190 xfrm_audit_helper_sainfo(x, audit_buf);
2191 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002192 audit_log_end(audit_buf);
2193}
2194EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002195
2196void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2197 struct sk_buff *skb)
2198{
2199 struct audit_buffer *audit_buf;
2200 u32 spi;
2201
2202 audit_buf = xfrm_audit_start("SA-replay-overflow");
2203 if (audit_buf == NULL)
2204 return;
2205 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2206 /* don't record the sequence number because it's inherent in this kind
2207 * of audit message */
2208 spi = ntohl(x->id.spi);
2209 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2210 audit_log_end(audit_buf);
2211}
2212EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2213
2214static void xfrm_audit_state_replay(struct xfrm_state *x,
2215 struct sk_buff *skb, __be32 net_seq)
2216{
2217 struct audit_buffer *audit_buf;
2218 u32 spi;
2219
2220 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2221 if (audit_buf == NULL)
2222 return;
2223 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2224 spi = ntohl(x->id.spi);
2225 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2226 spi, spi, ntohl(net_seq));
2227 audit_log_end(audit_buf);
2228}
2229
2230void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2231{
2232 struct audit_buffer *audit_buf;
2233
2234 audit_buf = xfrm_audit_start("SA-notfound");
2235 if (audit_buf == NULL)
2236 return;
2237 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2238 audit_log_end(audit_buf);
2239}
2240EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2241
2242void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2243 __be32 net_spi, __be32 net_seq)
2244{
2245 struct audit_buffer *audit_buf;
2246 u32 spi;
2247
2248 audit_buf = xfrm_audit_start("SA-notfound");
2249 if (audit_buf == NULL)
2250 return;
2251 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2252 spi = ntohl(net_spi);
2253 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2254 spi, spi, ntohl(net_seq));
2255 audit_log_end(audit_buf);
2256}
2257EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2258
2259void xfrm_audit_state_icvfail(struct xfrm_state *x,
2260 struct sk_buff *skb, u8 proto)
2261{
2262 struct audit_buffer *audit_buf;
2263 __be32 net_spi;
2264 __be32 net_seq;
2265
2266 audit_buf = xfrm_audit_start("SA-icv-failure");
2267 if (audit_buf == NULL)
2268 return;
2269 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2270 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2271 u32 spi = ntohl(net_spi);
2272 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2273 spi, spi, ntohl(net_seq));
2274 }
2275 audit_log_end(audit_buf);
2276}
2277EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002278#endif /* CONFIG_AUDITSYSCALL */