blob: f3f635d4ee66a6cd5fd1f5d93302f035ecedea99 [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;
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800415 struct net *net = xs_net(x);
James Morris9d729f72007-03-04 16:12:44 -0800416 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 long next = LONG_MAX;
418 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600419 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421 spin_lock(&x->lock);
422 if (x->km.state == XFRM_STATE_DEAD)
423 goto out;
424 if (x->km.state == XFRM_STATE_EXPIRED)
425 goto expired;
426 if (x->lft.hard_add_expires_seconds) {
427 long tmo = x->lft.hard_add_expires_seconds +
428 x->curlft.add_time - now;
429 if (tmo <= 0)
430 goto expired;
431 if (tmo < next)
432 next = tmo;
433 }
434 if (x->lft.hard_use_expires_seconds) {
435 long tmo = x->lft.hard_use_expires_seconds +
436 (x->curlft.use_time ? : now) - now;
437 if (tmo <= 0)
438 goto expired;
439 if (tmo < next)
440 next = tmo;
441 }
442 if (x->km.dying)
443 goto resched;
444 if (x->lft.soft_add_expires_seconds) {
445 long tmo = x->lft.soft_add_expires_seconds +
446 x->curlft.add_time - now;
447 if (tmo <= 0)
448 warn = 1;
449 else if (tmo < next)
450 next = tmo;
451 }
452 if (x->lft.soft_use_expires_seconds) {
453 long tmo = x->lft.soft_use_expires_seconds +
454 (x->curlft.use_time ? : now) - now;
455 if (tmo <= 0)
456 warn = 1;
457 else if (tmo < next)
458 next = tmo;
459 }
460
Herbert Xu4666faa2005-06-18 22:43:22 -0700461 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800463 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700465 if (next != LONG_MAX)
466 mod_timer(&x->timer, jiffies + make_jiffies(next));
467
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 goto out;
469
470expired:
471 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
472 x->km.state = XFRM_STATE_EXPIRED;
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800473 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 next = 2;
475 goto resched;
476 }
Joy Latten161a09e2006-11-27 13:11:54 -0600477
478 err = __xfrm_state_delete(x);
479 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800480 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Joy Lattenab5f5e82007-09-17 11:51:22 -0700482 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400483 audit_get_loginuid(current),
484 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486out:
487 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
David S. Miller0ac84752006-03-20 19:18:23 -0800490static void xfrm_replay_timer_handler(unsigned long data);
491
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800492struct xfrm_state *xfrm_state_alloc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
494 struct xfrm_state *x;
495
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700496 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
498 if (x) {
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800499 write_pnet(&x->xs_net, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 atomic_set(&x->refcnt, 1);
501 atomic_set(&x->tunnel_users, 0);
Herbert Xu12a169e2008-10-01 07:03:24 -0700502 INIT_LIST_HEAD(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700503 INIT_HLIST_NODE(&x->bydst);
504 INIT_HLIST_NODE(&x->bysrc);
505 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800506 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
507 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
508 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800509 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 x->lft.soft_byte_limit = XFRM_INF;
511 x->lft.soft_packet_limit = XFRM_INF;
512 x->lft.hard_byte_limit = XFRM_INF;
513 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800514 x->replay_maxage = 0;
515 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700516 x->inner_mode = NULL;
517 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 spin_lock_init(&x->lock);
519 }
520 return x;
521}
522EXPORT_SYMBOL(xfrm_state_alloc);
523
524void __xfrm_state_destroy(struct xfrm_state *x)
525{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800526 struct net *net = xs_net(x);
527
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700528 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
530 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800531 hlist_add_head(&x->gclist, &net->xfrm.state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 spin_unlock_bh(&xfrm_state_gc_lock);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800533 schedule_work(&net->xfrm.state_gc_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534}
535EXPORT_SYMBOL(__xfrm_state_destroy);
536
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800537int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800539 struct net *net = xs_net(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700540 int err = -ESRCH;
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 if (x->km.state != XFRM_STATE_DEAD) {
543 x->km.state = XFRM_STATE_DEAD;
544 spin_lock(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -0700545 list_del(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700546 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700547 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700548 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700549 hlist_del(&x->byspi);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800550 net->xfrm.state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 /* All xfrm_state objects are created by xfrm_state_alloc.
554 * The xfrm_state_alloc call gives a reference, and that
555 * is what we are dropping here.
556 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800557 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700558 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700560
561 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800563EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700565int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700567 int err;
568
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700570 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700572
573 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574}
575EXPORT_SYMBOL(xfrm_state_delete);
576
Joy Latten4aa2e622007-06-04 19:05:57 -0400577#ifdef CONFIG_SECURITY_NETWORK_XFRM
578static inline int
579xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
Joy Latten4aa2e622007-06-04 19:05:57 -0400581 int i, err = 0;
582
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800583 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400584 struct hlist_node *entry;
585 struct xfrm_state *x;
586
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800587 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400588 if (xfrm_id_proto_match(x->id.proto, proto) &&
589 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700590 xfrm_audit_state_delete(x, 0,
591 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400592 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700593 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400594 return err;
595 }
596 }
597 }
598
599 return err;
600}
601#else
602static inline int
603xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
604{
605 return 0;
606}
607#endif
608
609int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
610{
611 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
613 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400614 err = xfrm_state_flush_secctx_check(proto, audit_info);
615 if (err)
616 goto out;
617
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800618 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700619 struct hlist_node *entry;
620 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621restart:
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800622 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700624 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 xfrm_state_hold(x);
626 spin_unlock_bh(&xfrm_state_lock);
627
Joy Latten161a09e2006-11-27 13:11:54 -0600628 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700629 xfrm_audit_state_delete(x, err ? 0 : 1,
630 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400631 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700632 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 xfrm_state_put(x);
634
635 spin_lock_bh(&xfrm_state_lock);
636 goto restart;
637 }
638 }
639 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400640 err = 0;
641
642out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 spin_unlock_bh(&xfrm_state_lock);
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800644 wake_up(&init_net.xfrm.km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400645 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646}
647EXPORT_SYMBOL(xfrm_state_flush);
648
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700649void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700650{
651 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800652 si->sadcnt = init_net.xfrm.state_num;
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800653 si->sadhcnt = init_net.xfrm.state_hmask;
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700654 si->sadhmcnt = xfrm_state_hashmax;
655 spin_unlock_bh(&xfrm_state_lock);
656}
657EXPORT_SYMBOL(xfrm_sad_getinfo);
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659static int
660xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
661 struct xfrm_tmpl *tmpl,
662 xfrm_address_t *daddr, xfrm_address_t *saddr,
663 unsigned short family)
664{
665 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
666 if (!afinfo)
667 return -1;
668 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
669 xfrm_state_put_afinfo(afinfo);
670 return 0;
671}
672
Al Viroa94cfd12006-09-27 18:47:24 -0700673static 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 -0700674{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800675 unsigned int h = xfrm_spi_hash(&init_net, daddr, spi, proto, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700676 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700677 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700678
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800679 hlist_for_each_entry(x, entry, init_net.xfrm.state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700680 if (x->props.family != family ||
681 x->id.spi != spi ||
682 x->id.proto != proto)
683 continue;
684
685 switch (family) {
686 case AF_INET:
687 if (x->id.daddr.a4 != daddr->a4)
688 continue;
689 break;
690 case AF_INET6:
691 if (!ipv6_addr_equal((struct in6_addr *)daddr,
692 (struct in6_addr *)
693 x->id.daddr.a6))
694 continue;
695 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700696 }
David S. Milleredcd5822006-08-24 00:42:45 -0700697
698 xfrm_state_hold(x);
699 return x;
700 }
701
702 return NULL;
703}
704
705static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
706{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800707 unsigned int h = xfrm_src_hash(&init_net, daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700708 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700709 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700710
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800711 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700712 if (x->props.family != family ||
713 x->id.proto != proto)
714 continue;
715
716 switch (family) {
717 case AF_INET:
718 if (x->id.daddr.a4 != daddr->a4 ||
719 x->props.saddr.a4 != saddr->a4)
720 continue;
721 break;
722 case AF_INET6:
723 if (!ipv6_addr_equal((struct in6_addr *)daddr,
724 (struct in6_addr *)
725 x->id.daddr.a6) ||
726 !ipv6_addr_equal((struct in6_addr *)saddr,
727 (struct in6_addr *)
728 x->props.saddr.a6))
729 continue;
730 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700731 }
David S. Milleredcd5822006-08-24 00:42:45 -0700732
733 xfrm_state_hold(x);
734 return x;
735 }
736
737 return NULL;
738}
739
740static inline struct xfrm_state *
741__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
742{
743 if (use_spi)
744 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
745 x->id.proto, family);
746 else
747 return __xfrm_state_lookup_byaddr(&x->id.daddr,
748 &x->props.saddr,
749 x->id.proto, family);
750}
751
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800752static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700753{
754 if (have_hash_collision &&
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800755 (net->xfrm.state_hmask + 1) < xfrm_state_hashmax &&
756 net->xfrm.state_num > net->xfrm.state_hmask)
757 schedule_work(&net->xfrm.state_hash_work);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700758}
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900761xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 struct flowi *fl, struct xfrm_tmpl *tmpl,
763 struct xfrm_policy *pol, int *err,
764 unsigned short family)
765{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800766 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700767 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700768 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 int acquire_in_progress = 0;
770 int error = 0;
771 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900772
David S. Miller37b08e32008-09-02 20:14:15 -0700773 to_put = NULL;
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800776 h = xfrm_dst_hash(&init_net, daddr, saddr, tmpl->reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800777 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 if (x->props.family == family &&
779 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700780 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 xfrm_state_addr_check(x, daddr, saddr, family) &&
782 tmpl->mode == x->props.mode &&
783 tmpl->id.proto == x->id.proto &&
784 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
785 /* Resolution logic:
786 1. There is a valid state with matching selector.
787 Done.
788 2. Valid state with inappropriate selector. Skip.
789
790 Entering area of "sysdeps".
791
792 3. If state is not valid, selector is temporary,
793 it selects only session which triggered
794 previous resolution. Key manager will do
795 something to install a state with proper
796 selector.
797 */
798 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700799 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700800 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 continue;
802 if (!best ||
803 best->km.dying > x->km.dying ||
804 (best->km.dying == x->km.dying &&
805 best->curlft.add_time < x->curlft.add_time))
806 best = x;
807 } else if (x->km.state == XFRM_STATE_ACQ) {
808 acquire_in_progress = 1;
809 } else if (x->km.state == XFRM_STATE_ERROR ||
810 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700811 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700812 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 error = -ESRCH;
814 }
815 }
816 }
817
818 x = best;
819 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700820 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700821 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
822 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700823 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 error = -EEXIST;
825 goto out;
826 }
Alexey Dobriyan673c09b2008-11-25 17:15:16 -0800827 x = xfrm_state_alloc(&init_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if (x == NULL) {
829 error = -ENOMEM;
830 goto out;
831 }
832 /* Initialize temporary selector matching only
833 * to current session. */
834 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
835
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700836 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
837 if (error) {
838 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700839 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700840 x = NULL;
841 goto out;
842 }
843
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 if (km_query(x, tmpl, pol) == 0) {
845 x->km.state = XFRM_STATE_ACQ;
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -0800846 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800847 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800848 h = xfrm_src_hash(&init_net, daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800849 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (x->id.spi) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800851 h = xfrm_spi_hash(&init_net, &x->id.daddr, x->id.spi, x->id.proto, family);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800852 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 }
David S. Miller01e67d02007-05-25 00:41:38 -0700854 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
855 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 add_timer(&x->timer);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800857 init_net.xfrm.state_num++;
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800858 xfrm_hash_grow_check(&init_net, x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 } else {
860 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700861 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 x = NULL;
863 error = -ESRCH;
864 }
865 }
866out:
867 if (x)
868 xfrm_state_hold(x);
869 else
870 *err = acquire_in_progress ? -EAGAIN : error;
871 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700872 if (to_put)
873 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 return x;
875}
876
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700877struct xfrm_state *
878xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
879 unsigned short family, u8 mode, u8 proto, u32 reqid)
880{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800881 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700882 struct xfrm_state *rx = NULL, *x = NULL;
883 struct hlist_node *entry;
884
885 spin_lock(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800886 h = xfrm_dst_hash(&init_net, daddr, saddr, reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800887 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700888 if (x->props.family == family &&
889 x->props.reqid == reqid &&
890 !(x->props.flags & XFRM_STATE_WILDRECV) &&
891 xfrm_state_addr_check(x, daddr, saddr, family) &&
892 mode == x->props.mode &&
893 proto == x->id.proto &&
894 x->km.state == XFRM_STATE_VALID) {
895 rx = x;
896 break;
897 }
898 }
899
900 if (rx)
901 xfrm_state_hold(rx);
902 spin_unlock(&xfrm_state_lock);
903
904
905 return rx;
906}
907EXPORT_SYMBOL(xfrm_stateonly_find);
908
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909static void __xfrm_state_insert(struct xfrm_state *x)
910{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800911 struct net *net = xs_net(x);
David S. Millera624c102006-08-24 03:24:33 -0700912 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
David S. Miller9d4a7062006-08-24 03:18:09 -0700914 x->genid = ++xfrm_state_genid;
915
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800916 list_add(&x->km.all, &net->xfrm.state_all);
Timo Teras4c563f72008-02-28 21:31:08 -0800917
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800918 h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
David S. Millerc1969f22006-08-24 04:00:03 -0700919 x->props.reqid, x->props.family);
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800920 hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800922 h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
923 hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700925 if (x->id.spi) {
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800926 h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700927 x->props.family);
928
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800929 hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700930 }
931
David S. Millera47f0ce2006-08-24 03:54:22 -0700932 mod_timer(&x->timer, jiffies + HZ);
933 if (x->replay_maxage)
934 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800935
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800936 wake_up(&net->xfrm.km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700937
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800938 net->xfrm.state_num++;
David S. Millerf034b5d2006-08-24 03:08:07 -0700939
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800940 xfrm_hash_grow_check(net, x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941}
942
David S. Millerc7f5ea32006-08-24 03:29:04 -0700943/* xfrm_state_lock is held */
944static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
945{
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800946 struct net *net = xs_net(xnew);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700947 unsigned short family = xnew->props.family;
948 u32 reqid = xnew->props.reqid;
949 struct xfrm_state *x;
950 struct hlist_node *entry;
951 unsigned int h;
952
Alexey Dobriyan98806f72008-11-25 17:29:47 -0800953 h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
954 hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
David S. Millerc7f5ea32006-08-24 03:29:04 -0700955 if (x->props.family == family &&
956 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700957 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
958 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700959 x->genid = xfrm_state_genid;
960 }
961}
962
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963void xfrm_state_insert(struct xfrm_state *x)
964{
965 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700966 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 __xfrm_state_insert(x);
968 spin_unlock_bh(&xfrm_state_lock);
969}
970EXPORT_SYMBOL(xfrm_state_insert);
971
David S. Miller27708342006-08-24 00:13:10 -0700972/* xfrm_state_lock is held */
973static 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)
974{
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -0800975 unsigned int h = xfrm_dst_hash(&init_net, daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700976 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700977 struct xfrm_state *x;
978
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800979 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700980 if (x->props.reqid != reqid ||
981 x->props.mode != mode ||
982 x->props.family != family ||
983 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700984 x->id.spi != 0 ||
985 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700986 continue;
987
988 switch (family) {
989 case AF_INET:
990 if (x->id.daddr.a4 != daddr->a4 ||
991 x->props.saddr.a4 != saddr->a4)
992 continue;
993 break;
994 case AF_INET6:
995 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
996 (struct in6_addr *)daddr) ||
997 !ipv6_addr_equal((struct in6_addr *)
998 x->props.saddr.a6,
999 (struct in6_addr *)saddr))
1000 continue;
1001 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001002 }
David S. Miller27708342006-08-24 00:13:10 -07001003
1004 xfrm_state_hold(x);
1005 return x;
1006 }
1007
1008 if (!create)
1009 return NULL;
1010
Alexey Dobriyan673c09b2008-11-25 17:15:16 -08001011 x = xfrm_state_alloc(&init_net);
David S. Miller27708342006-08-24 00:13:10 -07001012 if (likely(x)) {
1013 switch (family) {
1014 case AF_INET:
1015 x->sel.daddr.a4 = daddr->a4;
1016 x->sel.saddr.a4 = saddr->a4;
1017 x->sel.prefixlen_d = 32;
1018 x->sel.prefixlen_s = 32;
1019 x->props.saddr.a4 = saddr->a4;
1020 x->id.daddr.a4 = daddr->a4;
1021 break;
1022
1023 case AF_INET6:
1024 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1025 (struct in6_addr *)daddr);
1026 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1027 (struct in6_addr *)saddr);
1028 x->sel.prefixlen_d = 128;
1029 x->sel.prefixlen_s = 128;
1030 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1031 (struct in6_addr *)saddr);
1032 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1033 (struct in6_addr *)daddr);
1034 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001035 }
David S. Miller27708342006-08-24 00:13:10 -07001036
1037 x->km.state = XFRM_STATE_ACQ;
1038 x->id.proto = proto;
1039 x->props.family = family;
1040 x->props.mode = mode;
1041 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001042 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001043 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001044 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001045 add_timer(&x->timer);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001046 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001047 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001048 h = xfrm_src_hash(&init_net, daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001049 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001050
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08001051 init_net.xfrm.state_num++;
David S. Miller918049f2006-10-12 22:03:24 -07001052
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001053 xfrm_hash_grow_check(&init_net, x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001054 }
1055
1056 return x;
1057}
1058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1060
1061int xfrm_state_add(struct xfrm_state *x)
1062{
David S. Miller37b08e32008-09-02 20:14:15 -07001063 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 int family;
1065 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001066 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
David S. Miller37b08e32008-09-02 20:14:15 -07001070 to_put = NULL;
1071
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 spin_lock_bh(&xfrm_state_lock);
1073
David S. Milleredcd5822006-08-24 00:42:45 -07001074 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001076 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 x1 = NULL;
1078 err = -EEXIST;
1079 goto out;
1080 }
1081
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001082 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001084 if (x1 && ((x1->id.proto != x->id.proto) ||
1085 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001086 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 x1 = NULL;
1088 }
1089 }
1090
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001091 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001092 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1093 x->id.proto,
1094 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
David S. Millerc7f5ea32006-08-24 03:29:04 -07001096 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 __xfrm_state_insert(x);
1098 err = 0;
1099
1100out:
1101 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
1103 if (x1) {
1104 xfrm_state_delete(x1);
1105 xfrm_state_put(x1);
1106 }
1107
David S. Miller37b08e32008-09-02 20:14:15 -07001108 if (to_put)
1109 xfrm_state_put(to_put);
1110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 return err;
1112}
1113EXPORT_SYMBOL(xfrm_state_add);
1114
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001115#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001116static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001117{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001118 struct net *net = xs_net(orig);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001119 int err = -ENOMEM;
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001120 struct xfrm_state *x = xfrm_state_alloc(net);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001121 if (!x)
1122 goto error;
1123
1124 memcpy(&x->id, &orig->id, sizeof(x->id));
1125 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1126 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1127 x->props.mode = orig->props.mode;
1128 x->props.replay_window = orig->props.replay_window;
1129 x->props.reqid = orig->props.reqid;
1130 x->props.family = orig->props.family;
1131 x->props.saddr = orig->props.saddr;
1132
1133 if (orig->aalg) {
1134 x->aalg = xfrm_algo_clone(orig->aalg);
1135 if (!x->aalg)
1136 goto error;
1137 }
1138 x->props.aalgo = orig->props.aalgo;
1139
1140 if (orig->ealg) {
1141 x->ealg = xfrm_algo_clone(orig->ealg);
1142 if (!x->ealg)
1143 goto error;
1144 }
1145 x->props.ealgo = orig->props.ealgo;
1146
1147 if (orig->calg) {
1148 x->calg = xfrm_algo_clone(orig->calg);
1149 if (!x->calg)
1150 goto error;
1151 }
1152 x->props.calgo = orig->props.calgo;
1153
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001154 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001155 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1156 if (!x->encap)
1157 goto error;
1158 }
1159
1160 if (orig->coaddr) {
1161 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1162 GFP_KERNEL);
1163 if (!x->coaddr)
1164 goto error;
1165 }
1166
1167 err = xfrm_init_state(x);
1168 if (err)
1169 goto error;
1170
1171 x->props.flags = orig->props.flags;
1172
1173 x->curlft.add_time = orig->curlft.add_time;
1174 x->km.state = orig->km.state;
1175 x->km.seq = orig->km.seq;
1176
1177 return x;
1178
1179 error:
1180 if (errp)
1181 *errp = err;
1182 if (x) {
1183 kfree(x->aalg);
1184 kfree(x->ealg);
1185 kfree(x->calg);
1186 kfree(x->encap);
1187 kfree(x->coaddr);
1188 }
1189 kfree(x);
1190 return NULL;
1191}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001192
1193/* xfrm_state_lock is held */
1194struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1195{
1196 unsigned int h;
1197 struct xfrm_state *x;
1198 struct hlist_node *entry;
1199
1200 if (m->reqid) {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001201 h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001202 m->reqid, m->old_family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001203 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001204 if (x->props.mode != m->mode ||
1205 x->id.proto != m->proto)
1206 continue;
1207 if (m->reqid && x->props.reqid != m->reqid)
1208 continue;
1209 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1210 m->old_family) ||
1211 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1212 m->old_family))
1213 continue;
1214 xfrm_state_hold(x);
1215 return x;
1216 }
1217 } else {
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001218 h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr,
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001219 m->old_family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001220 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001221 if (x->props.mode != m->mode ||
1222 x->id.proto != m->proto)
1223 continue;
1224 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1225 m->old_family) ||
1226 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1227 m->old_family))
1228 continue;
1229 xfrm_state_hold(x);
1230 return x;
1231 }
1232 }
1233
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001234 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001235}
1236EXPORT_SYMBOL(xfrm_migrate_state_find);
1237
1238struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1239 struct xfrm_migrate *m)
1240{
1241 struct xfrm_state *xc;
1242 int err;
1243
1244 xc = xfrm_state_clone(x, &err);
1245 if (!xc)
1246 return NULL;
1247
1248 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1249 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1250
1251 /* add state */
1252 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1253 /* a care is needed when the destination address of the
1254 state is to be updated as it is a part of triplet */
1255 xfrm_state_insert(xc);
1256 } else {
1257 if ((err = xfrm_state_add(xc)) < 0)
1258 goto error;
1259 }
1260
1261 return xc;
1262error:
1263 kfree(xc);
1264 return NULL;
1265}
1266EXPORT_SYMBOL(xfrm_state_migrate);
1267#endif
1268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269int xfrm_state_update(struct xfrm_state *x)
1270{
David S. Miller37b08e32008-09-02 20:14:15 -07001271 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001273 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
David S. Miller37b08e32008-09-02 20:14:15 -07001275 to_put = NULL;
1276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001278 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
1280 err = -ESRCH;
1281 if (!x1)
1282 goto out;
1283
1284 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001285 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 err = -EEXIST;
1287 goto out;
1288 }
1289
1290 if (x1->km.state == XFRM_STATE_ACQ) {
1291 __xfrm_state_insert(x);
1292 x = NULL;
1293 }
1294 err = 0;
1295
1296out:
1297 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
David S. Miller37b08e32008-09-02 20:14:15 -07001299 if (to_put)
1300 xfrm_state_put(to_put);
1301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (err)
1303 return err;
1304
1305 if (!x) {
1306 xfrm_state_delete(x1);
1307 xfrm_state_put(x1);
1308 return 0;
1309 }
1310
1311 err = -EINVAL;
1312 spin_lock_bh(&x1->lock);
1313 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1314 if (x->encap && x1->encap)
1315 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001316 if (x->coaddr && x1->coaddr) {
1317 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1318 }
1319 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1320 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1322 x1->km.dying = 0;
1323
David S. Millera47f0ce2006-08-24 03:54:22 -07001324 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 if (x1->curlft.use_time)
1326 xfrm_state_check_expire(x1);
1327
1328 err = 0;
1329 }
1330 spin_unlock_bh(&x1->lock);
1331
1332 xfrm_state_put(x1);
1333
1334 return err;
1335}
1336EXPORT_SYMBOL(xfrm_state_update);
1337
1338int xfrm_state_check_expire(struct xfrm_state *x)
1339{
1340 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001341 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
1343 if (x->km.state != XFRM_STATE_VALID)
1344 return -EINVAL;
1345
1346 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1347 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001348 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001349 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 return -EINVAL;
1351 }
1352
1353 if (!x->km.dying &&
1354 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001355 x->curlft.packets >= x->lft.soft_packet_limit)) {
1356 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001357 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 return 0;
1360}
1361EXPORT_SYMBOL(xfrm_state_check_expire);
1362
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001364xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 unsigned short family)
1366{
1367 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
1369 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001370 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 return x;
1373}
1374EXPORT_SYMBOL(xfrm_state_lookup);
1375
1376struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001377xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1378 u8 proto, unsigned short family)
1379{
1380 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001381
1382 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001383 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001384 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001385 return x;
1386}
1387EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1388
1389struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001390xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1391 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 int create, unsigned short family)
1393{
1394 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
1396 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001397 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001399
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 return x;
1401}
1402EXPORT_SYMBOL(xfrm_find_acq);
1403
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001404#ifdef CONFIG_XFRM_SUB_POLICY
1405int
1406xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1407 unsigned short family)
1408{
1409 int err = 0;
1410 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1411 if (!afinfo)
1412 return -EAFNOSUPPORT;
1413
1414 spin_lock_bh(&xfrm_state_lock);
1415 if (afinfo->tmpl_sort)
1416 err = afinfo->tmpl_sort(dst, src, n);
1417 spin_unlock_bh(&xfrm_state_lock);
1418 xfrm_state_put_afinfo(afinfo);
1419 return err;
1420}
1421EXPORT_SYMBOL(xfrm_tmpl_sort);
1422
1423int
1424xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1425 unsigned short family)
1426{
1427 int err = 0;
1428 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1429 if (!afinfo)
1430 return -EAFNOSUPPORT;
1431
1432 spin_lock_bh(&xfrm_state_lock);
1433 if (afinfo->state_sort)
1434 err = afinfo->state_sort(dst, src, n);
1435 spin_unlock_bh(&xfrm_state_lock);
1436 xfrm_state_put_afinfo(afinfo);
1437 return err;
1438}
1439EXPORT_SYMBOL(xfrm_state_sort);
1440#endif
1441
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442/* Silly enough, but I'm lazy to build resolution list */
1443
1444static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1445{
1446 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
Alexey Dobriyan529983e2008-11-25 17:18:12 -08001448 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001449 struct hlist_node *entry;
1450 struct xfrm_state *x;
1451
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001452 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
David S. Miller8f126e32006-08-24 02:45:07 -07001453 if (x->km.seq == seq &&
1454 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 xfrm_state_hold(x);
1456 return x;
1457 }
1458 }
1459 }
1460 return NULL;
1461}
1462
1463struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1464{
1465 struct xfrm_state *x;
1466
1467 spin_lock_bh(&xfrm_state_lock);
1468 x = __xfrm_find_acq_byseq(seq);
1469 spin_unlock_bh(&xfrm_state_lock);
1470 return x;
1471}
1472EXPORT_SYMBOL(xfrm_find_acq_byseq);
1473
1474u32 xfrm_get_acqseq(void)
1475{
1476 u32 res;
1477 static u32 acqseq;
1478 static DEFINE_SPINLOCK(acqseq_lock);
1479
1480 spin_lock_bh(&acqseq_lock);
1481 res = (++acqseq ? : ++acqseq);
1482 spin_unlock_bh(&acqseq_lock);
1483 return res;
1484}
1485EXPORT_SYMBOL(xfrm_get_acqseq);
1486
Herbert Xu658b2192007-10-09 13:29:52 -07001487int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488{
David S. Millerf034b5d2006-08-24 03:08:07 -07001489 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001491 int err = -ENOENT;
1492 __be32 minspi = htonl(low);
1493 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Herbert Xu658b2192007-10-09 13:29:52 -07001495 spin_lock_bh(&x->lock);
1496 if (x->km.state == XFRM_STATE_DEAD)
1497 goto unlock;
1498
1499 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001501 goto unlock;
1502
1503 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 if (minspi == maxspi) {
1506 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1507 if (x0) {
1508 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001509 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 }
1511 x->id.spi = minspi;
1512 } else {
1513 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001514 for (h=0; h<high-low+1; h++) {
1515 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1517 if (x0 == NULL) {
1518 x->id.spi = htonl(spi);
1519 break;
1520 }
1521 xfrm_state_put(x0);
1522 }
1523 }
1524 if (x->id.spi) {
1525 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan64d0cd02008-11-25 17:29:21 -08001526 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 -08001527 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001529
1530 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 }
Herbert Xu658b2192007-10-09 13:29:52 -07001532
1533unlock:
1534 spin_unlock_bh(&x->lock);
1535
1536 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537}
1538EXPORT_SYMBOL(xfrm_alloc_spi);
1539
Timo Teras4c563f72008-02-28 21:31:08 -08001540int xfrm_state_walk(struct xfrm_state_walk *walk,
1541 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 void *data)
1543{
Herbert Xu12a169e2008-10-01 07:03:24 -07001544 struct xfrm_state *state;
1545 struct xfrm_state_walk *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 int err = 0;
1547
Herbert Xu12a169e2008-10-01 07:03:24 -07001548 if (walk->seq != 0 && list_empty(&walk->all))
Timo Teras4c563f72008-02-28 21:31:08 -08001549 return 0;
1550
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 spin_lock_bh(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -07001552 if (list_empty(&walk->all))
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001553 x = list_first_entry(&init_net.xfrm.state_all, struct xfrm_state_walk, all);
Herbert Xu12a169e2008-10-01 07:03:24 -07001554 else
1555 x = list_entry(&walk->all, struct xfrm_state_walk, all);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001556 list_for_each_entry_from(x, &init_net.xfrm.state_all, all) {
Herbert Xu12a169e2008-10-01 07:03:24 -07001557 if (x->state == XFRM_STATE_DEAD)
Timo Teras4c563f72008-02-28 21:31:08 -08001558 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001559 state = container_of(x, struct xfrm_state, km);
1560 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
Timo Teras4c563f72008-02-28 21:31:08 -08001561 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001562 err = func(state, walk->seq, data);
1563 if (err) {
1564 list_move_tail(&walk->all, &x->all);
1565 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001567 walk->seq++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001569 if (walk->seq == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 err = -ENOENT;
1571 goto out;
1572 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001573 list_del_init(&walk->all);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574out:
1575 spin_unlock_bh(&xfrm_state_lock);
1576 return err;
1577}
1578EXPORT_SYMBOL(xfrm_state_walk);
1579
Herbert Xu5c182452008-09-22 19:48:19 -07001580void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1581{
Herbert Xu12a169e2008-10-01 07:03:24 -07001582 INIT_LIST_HEAD(&walk->all);
Herbert Xu5c182452008-09-22 19:48:19 -07001583 walk->proto = proto;
Herbert Xu12a169e2008-10-01 07:03:24 -07001584 walk->state = XFRM_STATE_DEAD;
1585 walk->seq = 0;
Herbert Xu5c182452008-09-22 19:48:19 -07001586}
1587EXPORT_SYMBOL(xfrm_state_walk_init);
1588
Herbert Xuabb81c42008-09-09 19:58:29 -07001589void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1590{
Herbert Xu12a169e2008-10-01 07:03:24 -07001591 if (list_empty(&walk->all))
Herbert Xu5c182452008-09-22 19:48:19 -07001592 return;
Herbert Xu5c182452008-09-22 19:48:19 -07001593
Herbert Xu12a169e2008-10-01 07:03:24 -07001594 spin_lock_bh(&xfrm_state_lock);
1595 list_del(&walk->all);
1596 spin_lock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001597}
1598EXPORT_SYMBOL(xfrm_state_walk_done);
1599
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001600
1601void xfrm_replay_notify(struct xfrm_state *x, int event)
1602{
1603 struct km_event c;
1604 /* we send notify messages in case
1605 * 1. we updated on of the sequence numbers, and the seqno difference
1606 * is at least x->replay_maxdiff, in this case we also update the
1607 * timeout of our timer function
1608 * 2. if x->replay_maxage has elapsed since last update,
1609 * and there were changes
1610 *
1611 * The state structure must be locked!
1612 */
1613
1614 switch (event) {
1615 case XFRM_REPLAY_UPDATE:
1616 if (x->replay_maxdiff &&
1617 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001618 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1619 if (x->xflags & XFRM_TIME_DEFER)
1620 event = XFRM_REPLAY_TIMEOUT;
1621 else
1622 return;
1623 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001624
1625 break;
1626
1627 case XFRM_REPLAY_TIMEOUT:
1628 if ((x->replay.seq == x->preplay.seq) &&
1629 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001630 (x->replay.oseq == x->preplay.oseq)) {
1631 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001632 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001633 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001634
1635 break;
1636 }
1637
1638 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1639 c.event = XFRM_MSG_NEWAE;
1640 c.data.aevent = event;
1641 km_state_notify(x, &c);
1642
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001643 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001644 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001645 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001646}
1647
1648static void xfrm_replay_timer_handler(unsigned long data)
1649{
1650 struct xfrm_state *x = (struct xfrm_state*)data;
1651
1652 spin_lock(&x->lock);
1653
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001654 if (x->km.state == XFRM_STATE_VALID) {
1655 if (xfrm_aevent_is_on())
1656 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1657 else
1658 x->xflags |= XFRM_TIME_DEFER;
1659 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001660
1661 spin_unlock(&x->lock);
1662}
1663
Paul Mooreafeb14b2007-12-21 14:58:11 -08001664int xfrm_replay_check(struct xfrm_state *x,
1665 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666{
1667 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001668 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001671 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
1673 if (likely(seq > x->replay.seq))
1674 return 0;
1675
1676 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001677 if (diff >= min_t(unsigned int, x->props.replay_window,
1678 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001680 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 }
1682
1683 if (x->replay.bitmap & (1U << diff)) {
1684 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001685 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 }
1687 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001688
1689err:
1690 xfrm_audit_state_replay(x, skb, net_seq);
1691 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
Al Viro61f46272006-09-27 18:48:33 -07001694void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695{
1696 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001697 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
1699 if (seq > x->replay.seq) {
1700 diff = seq - x->replay.seq;
1701 if (diff < x->props.replay_window)
1702 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1703 else
1704 x->replay.bitmap = 1;
1705 x->replay.seq = seq;
1706 } else {
1707 diff = x->replay.seq - seq;
1708 x->replay.bitmap |= (1U << diff);
1709 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001710
1711 if (xfrm_aevent_is_on())
1712 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
Denis Chengdf018122007-12-07 00:51:11 -08001715static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716static DEFINE_RWLOCK(xfrm_km_lock);
1717
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001718void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
1720 struct xfrm_mgr *km;
1721
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001722 read_lock(&xfrm_km_lock);
1723 list_for_each_entry(km, &xfrm_km_list, list)
1724 if (km->notify_policy)
1725 km->notify_policy(xp, dir, c);
1726 read_unlock(&xfrm_km_lock);
1727}
1728
1729void km_state_notify(struct xfrm_state *x, struct km_event *c)
1730{
1731 struct xfrm_mgr *km;
1732 read_lock(&xfrm_km_lock);
1733 list_for_each_entry(km, &xfrm_km_list, list)
1734 if (km->notify)
1735 km->notify(x, c);
1736 read_unlock(&xfrm_km_lock);
1737}
1738
1739EXPORT_SYMBOL(km_policy_notify);
1740EXPORT_SYMBOL(km_state_notify);
1741
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001742void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001743{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001744 struct net *net = xs_net(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001745 struct km_event c;
1746
Herbert Xubf088672005-06-18 22:44:00 -07001747 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001748 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001749 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001750 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
1752 if (hard)
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001753 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754}
1755
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001756EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001757/*
1758 * We send to all registered managers regardless of failure
1759 * We are happy with one success
1760*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001761int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001763 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 struct xfrm_mgr *km;
1765
1766 read_lock(&xfrm_km_lock);
1767 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001768 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1769 if (!acqret)
1770 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 }
1772 read_unlock(&xfrm_km_lock);
1773 return err;
1774}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001775EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
Al Viro5d36b182006-11-08 00:24:06 -08001777int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778{
1779 int err = -EINVAL;
1780 struct xfrm_mgr *km;
1781
1782 read_lock(&xfrm_km_lock);
1783 list_for_each_entry(km, &xfrm_km_list, list) {
1784 if (km->new_mapping)
1785 err = km->new_mapping(x, ipaddr, sport);
1786 if (!err)
1787 break;
1788 }
1789 read_unlock(&xfrm_km_lock);
1790 return err;
1791}
1792EXPORT_SYMBOL(km_new_mapping);
1793
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001794void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795{
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001796 struct net *net = xp_net(pol);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001797 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Herbert Xubf088672005-06-18 22:44:00 -07001799 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001800 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001801 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001802 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
1804 if (hard)
Alexey Dobriyan98806f72008-11-25 17:29:47 -08001805 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806}
David S. Millera70fcb02006-03-20 19:18:52 -08001807EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001809#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001810int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001811 struct xfrm_migrate *m, int num_migrate,
1812 struct xfrm_kmaddress *k)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001813{
1814 int err = -EINVAL;
1815 int ret;
1816 struct xfrm_mgr *km;
1817
1818 read_lock(&xfrm_km_lock);
1819 list_for_each_entry(km, &xfrm_km_list, list) {
1820 if (km->migrate) {
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001821 ret = km->migrate(sel, dir, type, m, num_migrate, k);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001822 if (!ret)
1823 err = ret;
1824 }
1825 }
1826 read_unlock(&xfrm_km_lock);
1827 return err;
1828}
1829EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001830#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001831
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001832int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1833{
1834 int err = -EINVAL;
1835 int ret;
1836 struct xfrm_mgr *km;
1837
1838 read_lock(&xfrm_km_lock);
1839 list_for_each_entry(km, &xfrm_km_list, list) {
1840 if (km->report) {
1841 ret = km->report(proto, sel, addr);
1842 if (!ret)
1843 err = ret;
1844 }
1845 }
1846 read_unlock(&xfrm_km_lock);
1847 return err;
1848}
1849EXPORT_SYMBOL(km_report);
1850
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1852{
1853 int err;
1854 u8 *data;
1855 struct xfrm_mgr *km;
1856 struct xfrm_policy *pol = NULL;
1857
1858 if (optlen <= 0 || optlen > PAGE_SIZE)
1859 return -EMSGSIZE;
1860
1861 data = kmalloc(optlen, GFP_KERNEL);
1862 if (!data)
1863 return -ENOMEM;
1864
1865 err = -EFAULT;
1866 if (copy_from_user(data, optval, optlen))
1867 goto out;
1868
1869 err = -EINVAL;
1870 read_lock(&xfrm_km_lock);
1871 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001872 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 optlen, &err);
1874 if (err >= 0)
1875 break;
1876 }
1877 read_unlock(&xfrm_km_lock);
1878
1879 if (err >= 0) {
1880 xfrm_sk_policy_insert(sk, err, pol);
1881 xfrm_pol_put(pol);
1882 err = 0;
1883 }
1884
1885out:
1886 kfree(data);
1887 return err;
1888}
1889EXPORT_SYMBOL(xfrm_user_policy);
1890
1891int xfrm_register_km(struct xfrm_mgr *km)
1892{
1893 write_lock_bh(&xfrm_km_lock);
1894 list_add_tail(&km->list, &xfrm_km_list);
1895 write_unlock_bh(&xfrm_km_lock);
1896 return 0;
1897}
1898EXPORT_SYMBOL(xfrm_register_km);
1899
1900int xfrm_unregister_km(struct xfrm_mgr *km)
1901{
1902 write_lock_bh(&xfrm_km_lock);
1903 list_del(&km->list);
1904 write_unlock_bh(&xfrm_km_lock);
1905 return 0;
1906}
1907EXPORT_SYMBOL(xfrm_unregister_km);
1908
1909int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1910{
1911 int err = 0;
1912 if (unlikely(afinfo == NULL))
1913 return -EINVAL;
1914 if (unlikely(afinfo->family >= NPROTO))
1915 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001916 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1918 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001919 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001921 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 return err;
1923}
1924EXPORT_SYMBOL(xfrm_state_register_afinfo);
1925
1926int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1927{
1928 int err = 0;
1929 if (unlikely(afinfo == NULL))
1930 return -EINVAL;
1931 if (unlikely(afinfo->family >= NPROTO))
1932 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001933 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1935 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1936 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001937 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001940 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 return err;
1942}
1943EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1944
Herbert Xu17c2a422007-10-17 21:33:12 -07001945static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946{
1947 struct xfrm_state_afinfo *afinfo;
1948 if (unlikely(family >= NPROTO))
1949 return NULL;
1950 read_lock(&xfrm_state_afinfo_lock);
1951 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001952 if (unlikely(!afinfo))
1953 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 return afinfo;
1955}
1956
Herbert Xu17c2a422007-10-17 21:33:12 -07001957static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001958 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959{
Herbert Xu546be242006-05-27 23:03:58 -07001960 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961}
1962
1963/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1964void xfrm_state_delete_tunnel(struct xfrm_state *x)
1965{
1966 if (x->tunnel) {
1967 struct xfrm_state *t = x->tunnel;
1968
1969 if (atomic_read(&t->tunnel_users) == 2)
1970 xfrm_state_delete(t);
1971 atomic_dec(&t->tunnel_users);
1972 xfrm_state_put(t);
1973 x->tunnel = NULL;
1974 }
1975}
1976EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1977
1978int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1979{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001980 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981
Patrick McHardyc5c25232007-04-09 11:47:18 -07001982 spin_lock_bh(&x->lock);
1983 if (x->km.state == XFRM_STATE_VALID &&
1984 x->type && x->type->get_mtu)
1985 res = x->type->get_mtu(x, mtu);
1986 else
Patrick McHardy28121612007-06-18 22:30:15 -07001987 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001988 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 return res;
1990}
1991
Herbert Xu72cb6962005-06-20 13:18:08 -07001992int xfrm_init_state(struct xfrm_state *x)
1993{
Herbert Xud094cd82005-06-20 13:19:41 -07001994 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001995 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001996 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001997 int err;
1998
Herbert Xud094cd82005-06-20 13:19:41 -07001999 err = -EAFNOSUPPORT;
2000 afinfo = xfrm_state_get_afinfo(family);
2001 if (!afinfo)
2002 goto error;
2003
2004 err = 0;
2005 if (afinfo->init_flags)
2006 err = afinfo->init_flags(x);
2007
2008 xfrm_state_put_afinfo(afinfo);
2009
2010 if (err)
2011 goto error;
2012
2013 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002014
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002015 if (x->sel.family != AF_UNSPEC) {
2016 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2017 if (inner_mode == NULL)
2018 goto error;
2019
2020 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2021 family != x->sel.family) {
2022 xfrm_put_mode(inner_mode);
2023 goto error;
2024 }
2025
2026 x->inner_mode = inner_mode;
2027 } else {
2028 struct xfrm_mode *inner_mode_iaf;
2029
2030 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2031 if (inner_mode == NULL)
2032 goto error;
2033
2034 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2035 xfrm_put_mode(inner_mode);
2036 goto error;
2037 }
2038
2039 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2040 if (inner_mode_iaf == NULL)
2041 goto error;
2042
2043 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2044 xfrm_put_mode(inner_mode_iaf);
2045 goto error;
2046 }
2047
2048 if (x->props.family == AF_INET) {
2049 x->inner_mode = inner_mode;
2050 x->inner_mode_iaf = inner_mode_iaf;
2051 } else {
2052 x->inner_mode = inner_mode_iaf;
2053 x->inner_mode_iaf = inner_mode;
2054 }
2055 }
Herbert Xu13996372007-10-17 21:35:51 -07002056
Herbert Xud094cd82005-06-20 13:19:41 -07002057 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002058 if (x->type == NULL)
2059 goto error;
2060
2061 err = x->type->init_state(x);
2062 if (err)
2063 goto error;
2064
Herbert Xu13996372007-10-17 21:35:51 -07002065 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2066 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002067 goto error;
2068
Herbert Xu72cb6962005-06-20 13:18:08 -07002069 x->km.state = XFRM_STATE_VALID;
2070
2071error:
2072 return err;
2073}
2074
2075EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002076
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002077int __net_init xfrm_state_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078{
David S. Millerf034b5d2006-08-24 03:08:07 -07002079 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002081 INIT_LIST_HEAD(&net->xfrm.state_all);
2082
David S. Millerf034b5d2006-08-24 03:08:07 -07002083 sz = sizeof(struct hlist_head) * 8;
2084
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002085 net->xfrm.state_bydst = xfrm_hash_alloc(sz);
2086 if (!net->xfrm.state_bydst)
2087 goto out_bydst;
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002088 net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2089 if (!net->xfrm.state_bysrc)
2090 goto out_bysrc;
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002091 net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2092 if (!net->xfrm.state_byspi)
2093 goto out_byspi;
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002094 net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
David S. Millerf034b5d2006-08-24 03:08:07 -07002095
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08002096 net->xfrm.state_num = 0;
Alexey Dobriyan63082732008-11-25 17:19:07 -08002097 INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -08002098 INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
Alexey Dobriyanc7837142008-11-25 17:20:36 -08002099 INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
Alexey Dobriyan50a30652008-11-25 17:21:01 -08002100 init_waitqueue_head(&net->xfrm.km_waitq);
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002101 return 0;
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002102
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002103out_byspi:
2104 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002105out_bysrc:
2106 xfrm_hash_free(net->xfrm.state_bydst, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002107out_bydst:
2108 return -ENOMEM;
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002109}
2110
2111void xfrm_state_fini(struct net *net)
2112{
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002113 unsigned int sz;
2114
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002115 WARN_ON(!list_empty(&net->xfrm.state_all));
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002116
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002117 sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002118 WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2119 xfrm_hash_free(net->xfrm.state_byspi, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002120 WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2121 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002122 WARN_ON(!hlist_empty(net->xfrm.state_bydst));
2123 xfrm_hash_free(net->xfrm.state_bydst, sz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124}
2125
Joy Lattenab5f5e82007-09-17 11:51:22 -07002126#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002127static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2128 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002129{
Paul Moore68277ac2007-12-20 20:49:33 -08002130 struct xfrm_sec_ctx *ctx = x->security;
2131 u32 spi = ntohl(x->id.spi);
2132
2133 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002134 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002135 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002136
2137 switch(x->props.family) {
2138 case AF_INET:
Harvey Harrison21454aa2008-10-31 00:54:56 -07002139 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2140 &x->props.saddr.a4, &x->id.daddr.a4);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002141 break;
2142 case AF_INET6:
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002143 audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002144 x->props.saddr.a6, x->id.daddr.a6);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002145 break;
2146 }
Paul Moore68277ac2007-12-20 20:49:33 -08002147
2148 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002149}
2150
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002151static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2152 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002153{
2154 struct iphdr *iph4;
2155 struct ipv6hdr *iph6;
2156
2157 switch (family) {
2158 case AF_INET:
2159 iph4 = ip_hdr(skb);
Harvey Harrison21454aa2008-10-31 00:54:56 -07002160 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2161 &iph4->saddr, &iph4->daddr);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002162 break;
2163 case AF_INET6:
2164 iph6 = ipv6_hdr(skb);
2165 audit_log_format(audit_buf,
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002166 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002167 &iph6->saddr,&iph6->daddr,
Paul Mooreafeb14b2007-12-21 14:58:11 -08002168 iph6->flow_lbl[0] & 0x0f,
2169 iph6->flow_lbl[1],
2170 iph6->flow_lbl[2]);
2171 break;
2172 }
2173}
2174
Paul Moore68277ac2007-12-20 20:49:33 -08002175void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002176 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002177{
2178 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002179
Paul Mooreafeb14b2007-12-21 14:58:11 -08002180 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002181 if (audit_buf == NULL)
2182 return;
Eric Paris25323862008-04-18 10:09:25 -04002183 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002184 xfrm_audit_helper_sainfo(x, audit_buf);
2185 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002186 audit_log_end(audit_buf);
2187}
2188EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2189
Paul Moore68277ac2007-12-20 20:49:33 -08002190void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002191 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002192{
2193 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002194
Paul Mooreafeb14b2007-12-21 14:58:11 -08002195 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002196 if (audit_buf == NULL)
2197 return;
Eric Paris25323862008-04-18 10:09:25 -04002198 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002199 xfrm_audit_helper_sainfo(x, audit_buf);
2200 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002201 audit_log_end(audit_buf);
2202}
2203EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002204
2205void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2206 struct sk_buff *skb)
2207{
2208 struct audit_buffer *audit_buf;
2209 u32 spi;
2210
2211 audit_buf = xfrm_audit_start("SA-replay-overflow");
2212 if (audit_buf == NULL)
2213 return;
2214 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2215 /* don't record the sequence number because it's inherent in this kind
2216 * of audit message */
2217 spi = ntohl(x->id.spi);
2218 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2219 audit_log_end(audit_buf);
2220}
2221EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2222
2223static void xfrm_audit_state_replay(struct xfrm_state *x,
2224 struct sk_buff *skb, __be32 net_seq)
2225{
2226 struct audit_buffer *audit_buf;
2227 u32 spi;
2228
2229 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2230 if (audit_buf == NULL)
2231 return;
2232 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2233 spi = ntohl(x->id.spi);
2234 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2235 spi, spi, ntohl(net_seq));
2236 audit_log_end(audit_buf);
2237}
2238
2239void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2240{
2241 struct audit_buffer *audit_buf;
2242
2243 audit_buf = xfrm_audit_start("SA-notfound");
2244 if (audit_buf == NULL)
2245 return;
2246 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2247 audit_log_end(audit_buf);
2248}
2249EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2250
2251void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2252 __be32 net_spi, __be32 net_seq)
2253{
2254 struct audit_buffer *audit_buf;
2255 u32 spi;
2256
2257 audit_buf = xfrm_audit_start("SA-notfound");
2258 if (audit_buf == NULL)
2259 return;
2260 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2261 spi = ntohl(net_spi);
2262 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2263 spi, spi, ntohl(net_seq));
2264 audit_log_end(audit_buf);
2265}
2266EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2267
2268void xfrm_audit_state_icvfail(struct xfrm_state *x,
2269 struct sk_buff *skb, u8 proto)
2270{
2271 struct audit_buffer *audit_buf;
2272 __be32 net_spi;
2273 __be32 net_seq;
2274
2275 audit_buf = xfrm_audit_start("SA-icv-failure");
2276 if (audit_buf == NULL)
2277 return;
2278 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2279 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2280 u32 spi = ntohl(net_spi);
2281 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2282 spi, spi, ntohl(net_seq));
2283 }
2284 audit_log_end(audit_buf);
2285}
2286EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002287#endif /* CONFIG_AUDITSYSCALL */