blob: 24bd89e76236917d81175952ff87d83e7b58aa50 [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
David S. Millerc1969f22006-08-24 04:00:03 -070060static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
61 xfrm_address_t *saddr,
62 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070063 unsigned short family)
64{
Alexey Dobriyan529983e2008-11-25 17:18:12 -080065 return __xfrm_dst_hash(daddr, saddr, reqid, family, init_net.xfrm.state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070066}
67
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070068static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
69 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070070 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070071{
Alexey Dobriyan529983e2008-11-25 17:18:12 -080072 return __xfrm_src_hash(daddr, saddr, family, init_net.xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070073}
74
David S. Miller2575b652006-08-24 03:26:44 -070075static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070076xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070077{
Alexey Dobriyan529983e2008-11-25 17:18:12 -080078 return __xfrm_spi_hash(daddr, spi, proto, family, init_net.xfrm.state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070079}
80
David S. Millerf034b5d2006-08-24 03:08:07 -070081static void xfrm_hash_transfer(struct hlist_head *list,
82 struct hlist_head *ndsttable,
83 struct hlist_head *nsrctable,
84 struct hlist_head *nspitable,
85 unsigned int nhashmask)
86{
87 struct hlist_node *entry, *tmp;
88 struct xfrm_state *x;
89
90 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
91 unsigned int h;
92
David S. Millerc1969f22006-08-24 04:00:03 -070093 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
94 x->props.reqid, x->props.family,
95 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070096 hlist_add_head(&x->bydst, ndsttable+h);
97
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070098 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
99 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700100 nhashmask);
101 hlist_add_head(&x->bysrc, nsrctable+h);
102
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700103 if (x->id.spi) {
104 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
105 x->id.proto, x->props.family,
106 nhashmask);
107 hlist_add_head(&x->byspi, nspitable+h);
108 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700109 }
110}
111
Alexey Dobriyan63082732008-11-25 17:19:07 -0800112static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
David S. Millerf034b5d2006-08-24 03:08:07 -0700113{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800114 return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
David S. Millerf034b5d2006-08-24 03:08:07 -0700115}
116
117static DEFINE_MUTEX(hash_resize_mutex);
118
Alexey Dobriyan63082732008-11-25 17:19:07 -0800119static void xfrm_hash_resize(struct work_struct *work)
David S. Millerf034b5d2006-08-24 03:08:07 -0700120{
Alexey Dobriyan63082732008-11-25 17:19:07 -0800121 struct net *net = container_of(work, struct net, xfrm.state_hash_work);
David S. Millerf034b5d2006-08-24 03:08:07 -0700122 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
123 unsigned long nsize, osize;
124 unsigned int nhashmask, ohashmask;
125 int i;
126
127 mutex_lock(&hash_resize_mutex);
128
Alexey Dobriyan63082732008-11-25 17:19:07 -0800129 nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
David S. Miller44e36b42006-08-24 04:50:50 -0700130 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700131 if (!ndst)
132 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700133 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700134 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700135 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700136 goto out_unlock;
137 }
David S. Miller44e36b42006-08-24 04:50:50 -0700138 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700139 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700140 xfrm_hash_free(ndst, nsize);
141 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 goto out_unlock;
143 }
144
145 spin_lock_bh(&xfrm_state_lock);
146
147 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
Alexey Dobriyan63082732008-11-25 17:19:07 -0800148 for (i = net->xfrm.state_hmask; i >= 0; i--)
149 xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
David S. Millerf034b5d2006-08-24 03:08:07 -0700150 nhashmask);
151
Alexey Dobriyan63082732008-11-25 17:19:07 -0800152 odst = net->xfrm.state_bydst;
153 osrc = net->xfrm.state_bysrc;
154 ospi = net->xfrm.state_byspi;
155 ohashmask = net->xfrm.state_hmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700156
Alexey Dobriyan63082732008-11-25 17:19:07 -0800157 net->xfrm.state_bydst = ndst;
158 net->xfrm.state_bysrc = nsrc;
159 net->xfrm.state_byspi = nspi;
160 net->xfrm.state_hmask = nhashmask;
David S. Millerf034b5d2006-08-24 03:08:07 -0700161
162 spin_unlock_bh(&xfrm_state_lock);
163
164 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700165 xfrm_hash_free(odst, osize);
166 xfrm_hash_free(osrc, osize);
167 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700168
169out_unlock:
170 mutex_unlock(&hash_resize_mutex);
171}
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
174static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176static DEFINE_SPINLOCK(xfrm_state_gc_lock);
177
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800178int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800180int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800181void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700183static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
184{
185 struct xfrm_state_afinfo *afinfo;
186 if (unlikely(family >= NPROTO))
187 return NULL;
188 write_lock_bh(&xfrm_state_afinfo_lock);
189 afinfo = xfrm_state_afinfo[family];
190 if (unlikely(!afinfo))
191 write_unlock_bh(&xfrm_state_afinfo_lock);
192 return afinfo;
193}
194
195static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800196 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700197{
198 write_unlock_bh(&xfrm_state_afinfo_lock);
199}
200
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800201int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700202{
203 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800204 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700205 int err = 0;
206
207 if (unlikely(afinfo == NULL))
208 return -EAFNOSUPPORT;
209 typemap = afinfo->type_map;
210
211 if (likely(typemap[type->proto] == NULL))
212 typemap[type->proto] = type;
213 else
214 err = -EEXIST;
215 xfrm_state_unlock_afinfo(afinfo);
216 return err;
217}
218EXPORT_SYMBOL(xfrm_register_type);
219
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800220int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700221{
222 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800223 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700224 int err = 0;
225
226 if (unlikely(afinfo == NULL))
227 return -EAFNOSUPPORT;
228 typemap = afinfo->type_map;
229
230 if (unlikely(typemap[type->proto] != type))
231 err = -ENOENT;
232 else
233 typemap[type->proto] = NULL;
234 xfrm_state_unlock_afinfo(afinfo);
235 return err;
236}
237EXPORT_SYMBOL(xfrm_unregister_type);
238
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800239static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700240{
241 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800242 const struct xfrm_type **typemap;
243 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700244 int modload_attempted = 0;
245
246retry:
247 afinfo = xfrm_state_get_afinfo(family);
248 if (unlikely(afinfo == NULL))
249 return NULL;
250 typemap = afinfo->type_map;
251
252 type = typemap[proto];
253 if (unlikely(type && !try_module_get(type->owner)))
254 type = NULL;
255 if (!type && !modload_attempted) {
256 xfrm_state_put_afinfo(afinfo);
257 request_module("xfrm-type-%d-%d", family, proto);
258 modload_attempted = 1;
259 goto retry;
260 }
261
262 xfrm_state_put_afinfo(afinfo);
263 return type;
264}
265
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800266static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700267{
268 module_put(type->owner);
269}
270
271int xfrm_register_mode(struct xfrm_mode *mode, int family)
272{
273 struct xfrm_state_afinfo *afinfo;
274 struct xfrm_mode **modemap;
275 int err;
276
277 if (unlikely(mode->encap >= XFRM_MODE_MAX))
278 return -EINVAL;
279
280 afinfo = xfrm_state_lock_afinfo(family);
281 if (unlikely(afinfo == NULL))
282 return -EAFNOSUPPORT;
283
284 err = -EEXIST;
285 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700286 if (modemap[mode->encap])
287 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700288
Herbert Xu17c2a422007-10-17 21:33:12 -0700289 err = -ENOENT;
290 if (!try_module_get(afinfo->owner))
291 goto out;
292
293 mode->afinfo = afinfo;
294 modemap[mode->encap] = mode;
295 err = 0;
296
297out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700298 xfrm_state_unlock_afinfo(afinfo);
299 return err;
300}
301EXPORT_SYMBOL(xfrm_register_mode);
302
303int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
304{
305 struct xfrm_state_afinfo *afinfo;
306 struct xfrm_mode **modemap;
307 int err;
308
309 if (unlikely(mode->encap >= XFRM_MODE_MAX))
310 return -EINVAL;
311
312 afinfo = xfrm_state_lock_afinfo(family);
313 if (unlikely(afinfo == NULL))
314 return -EAFNOSUPPORT;
315
316 err = -ENOENT;
317 modemap = afinfo->mode_map;
318 if (likely(modemap[mode->encap] == mode)) {
319 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700320 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700321 err = 0;
322 }
323
324 xfrm_state_unlock_afinfo(afinfo);
325 return err;
326}
327EXPORT_SYMBOL(xfrm_unregister_mode);
328
329static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
330{
331 struct xfrm_state_afinfo *afinfo;
332 struct xfrm_mode *mode;
333 int modload_attempted = 0;
334
335 if (unlikely(encap >= XFRM_MODE_MAX))
336 return NULL;
337
338retry:
339 afinfo = xfrm_state_get_afinfo(family);
340 if (unlikely(afinfo == NULL))
341 return NULL;
342
343 mode = afinfo->mode_map[encap];
344 if (unlikely(mode && !try_module_get(mode->owner)))
345 mode = NULL;
346 if (!mode && !modload_attempted) {
347 xfrm_state_put_afinfo(afinfo);
348 request_module("xfrm-mode-%d-%d", family, encap);
349 modload_attempted = 1;
350 goto retry;
351 }
352
353 xfrm_state_put_afinfo(afinfo);
354 return mode;
355}
356
357static void xfrm_put_mode(struct xfrm_mode *mode)
358{
359 module_put(mode->owner);
360}
361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362static void xfrm_state_gc_destroy(struct xfrm_state *x)
363{
David S. Millera47f0ce2006-08-24 03:54:22 -0700364 del_timer_sync(&x->timer);
365 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800366 kfree(x->aalg);
367 kfree(x->ealg);
368 kfree(x->calg);
369 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700370 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700371 if (x->inner_mode)
372 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700373 if (x->inner_mode_iaf)
374 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700375 if (x->outer_mode)
376 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (x->type) {
378 x->type->destructor(x);
379 xfrm_put_type(x->type);
380 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800381 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 kfree(x);
383}
384
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800385static void xfrm_state_gc_task(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800387 struct net *net = container_of(work, struct net, xfrm.state_gc_work);
Herbert Xu12a169e2008-10-01 07:03:24 -0700388 struct xfrm_state *x;
389 struct hlist_node *entry, *tmp;
390 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800393 hlist_move_list(&net->xfrm.state_gc_list, &gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 spin_unlock_bh(&xfrm_state_gc_lock);
395
Herbert Xu12a169e2008-10-01 07:03:24 -0700396 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700398
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800399 wake_up(&net->xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
402static inline unsigned long make_jiffies(long secs)
403{
404 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
405 return MAX_SCHEDULE_TIMEOUT-1;
406 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900407 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408}
409
410static void xfrm_timer_handler(unsigned long data)
411{
412 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800413 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 long next = LONG_MAX;
415 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600416 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 spin_lock(&x->lock);
419 if (x->km.state == XFRM_STATE_DEAD)
420 goto out;
421 if (x->km.state == XFRM_STATE_EXPIRED)
422 goto expired;
423 if (x->lft.hard_add_expires_seconds) {
424 long tmo = x->lft.hard_add_expires_seconds +
425 x->curlft.add_time - now;
426 if (tmo <= 0)
427 goto expired;
428 if (tmo < next)
429 next = tmo;
430 }
431 if (x->lft.hard_use_expires_seconds) {
432 long tmo = x->lft.hard_use_expires_seconds +
433 (x->curlft.use_time ? : now) - now;
434 if (tmo <= 0)
435 goto expired;
436 if (tmo < next)
437 next = tmo;
438 }
439 if (x->km.dying)
440 goto resched;
441 if (x->lft.soft_add_expires_seconds) {
442 long tmo = x->lft.soft_add_expires_seconds +
443 x->curlft.add_time - now;
444 if (tmo <= 0)
445 warn = 1;
446 else if (tmo < next)
447 next = tmo;
448 }
449 if (x->lft.soft_use_expires_seconds) {
450 long tmo = x->lft.soft_use_expires_seconds +
451 (x->curlft.use_time ? : now) - now;
452 if (tmo <= 0)
453 warn = 1;
454 else if (tmo < next)
455 next = tmo;
456 }
457
Herbert Xu4666faa2005-06-18 22:43:22 -0700458 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (warn)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800460 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700462 if (next != LONG_MAX)
463 mod_timer(&x->timer, jiffies + make_jiffies(next));
464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 goto out;
466
467expired:
468 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
469 x->km.state = XFRM_STATE_EXPIRED;
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800470 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 next = 2;
472 goto resched;
473 }
Joy Latten161a09e2006-11-27 13:11:54 -0600474
475 err = __xfrm_state_delete(x);
476 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800477 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Joy Lattenab5f5e82007-09-17 11:51:22 -0700479 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400480 audit_get_loginuid(current),
481 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483out:
484 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}
486
David S. Miller0ac84752006-03-20 19:18:23 -0800487static void xfrm_replay_timer_handler(unsigned long data);
488
Alexey Dobriyan673c09be2008-11-25 17:15:16 -0800489struct xfrm_state *xfrm_state_alloc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
491 struct xfrm_state *x;
492
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700493 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
495 if (x) {
Alexey Dobriyan673c09be2008-11-25 17:15:16 -0800496 write_pnet(&x->xs_net, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 atomic_set(&x->refcnt, 1);
498 atomic_set(&x->tunnel_users, 0);
Herbert Xu12a169e2008-10-01 07:03:24 -0700499 INIT_LIST_HEAD(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700500 INIT_HLIST_NODE(&x->bydst);
501 INIT_HLIST_NODE(&x->bysrc);
502 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800503 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
504 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
505 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800506 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 x->lft.soft_byte_limit = XFRM_INF;
508 x->lft.soft_packet_limit = XFRM_INF;
509 x->lft.hard_byte_limit = XFRM_INF;
510 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800511 x->replay_maxage = 0;
512 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700513 x->inner_mode = NULL;
514 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 spin_lock_init(&x->lock);
516 }
517 return x;
518}
519EXPORT_SYMBOL(xfrm_state_alloc);
520
521void __xfrm_state_destroy(struct xfrm_state *x)
522{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700523 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
525 spin_lock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -0800526 hlist_add_head(&x->gclist, &init_net.xfrm.state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 spin_unlock_bh(&xfrm_state_gc_lock);
Alexey Dobriyanc7837142008-11-25 17:20:36 -0800528 schedule_work(&init_net.xfrm.state_gc_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529}
530EXPORT_SYMBOL(__xfrm_state_destroy);
531
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800532int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700534 int err = -ESRCH;
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (x->km.state != XFRM_STATE_DEAD) {
537 x->km.state = XFRM_STATE_DEAD;
538 spin_lock(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -0700539 list_del(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700540 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700541 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700542 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700543 hlist_del(&x->byspi);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800544 init_net.xfrm.state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 /* All xfrm_state objects are created by xfrm_state_alloc.
548 * The xfrm_state_alloc call gives a reference, and that
549 * is what we are dropping here.
550 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800551 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700552 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700554
555 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556}
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800557EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700559int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700561 int err;
562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700564 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700566
567 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569EXPORT_SYMBOL(xfrm_state_delete);
570
Joy Latten4aa2e622007-06-04 19:05:57 -0400571#ifdef CONFIG_SECURITY_NETWORK_XFRM
572static inline int
573xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
Joy Latten4aa2e622007-06-04 19:05:57 -0400575 int i, err = 0;
576
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800577 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400578 struct hlist_node *entry;
579 struct xfrm_state *x;
580
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800581 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Joy Latten4aa2e622007-06-04 19:05:57 -0400582 if (xfrm_id_proto_match(x->id.proto, proto) &&
583 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700584 xfrm_audit_state_delete(x, 0,
585 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400586 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700587 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400588 return err;
589 }
590 }
591 }
592
593 return err;
594}
595#else
596static inline int
597xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
598{
599 return 0;
600}
601#endif
602
603int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
604{
605 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400608 err = xfrm_state_flush_secctx_check(proto, audit_info);
609 if (err)
610 goto out;
611
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800612 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700613 struct hlist_node *entry;
614 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615restart:
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800616 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700618 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 xfrm_state_hold(x);
620 spin_unlock_bh(&xfrm_state_lock);
621
Joy Latten161a09e2006-11-27 13:11:54 -0600622 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700623 xfrm_audit_state_delete(x, err ? 0 : 1,
624 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400625 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700626 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 xfrm_state_put(x);
628
629 spin_lock_bh(&xfrm_state_lock);
630 goto restart;
631 }
632 }
633 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400634 err = 0;
635
636out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 spin_unlock_bh(&xfrm_state_lock);
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800638 wake_up(&init_net.xfrm.km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400639 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640}
641EXPORT_SYMBOL(xfrm_state_flush);
642
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700643void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700644{
645 spin_lock_bh(&xfrm_state_lock);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800646 si->sadcnt = init_net.xfrm.state_num;
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800647 si->sadhcnt = init_net.xfrm.state_hmask;
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700648 si->sadhmcnt = xfrm_state_hashmax;
649 spin_unlock_bh(&xfrm_state_lock);
650}
651EXPORT_SYMBOL(xfrm_sad_getinfo);
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653static int
654xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
655 struct xfrm_tmpl *tmpl,
656 xfrm_address_t *daddr, xfrm_address_t *saddr,
657 unsigned short family)
658{
659 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
660 if (!afinfo)
661 return -1;
662 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
663 xfrm_state_put_afinfo(afinfo);
664 return 0;
665}
666
Al Viroa94cfd12006-09-27 18:47:24 -0700667static 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 -0700668{
669 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
670 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700671 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700672
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800673 hlist_for_each_entry(x, entry, init_net.xfrm.state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700674 if (x->props.family != family ||
675 x->id.spi != spi ||
676 x->id.proto != proto)
677 continue;
678
679 switch (family) {
680 case AF_INET:
681 if (x->id.daddr.a4 != daddr->a4)
682 continue;
683 break;
684 case AF_INET6:
685 if (!ipv6_addr_equal((struct in6_addr *)daddr,
686 (struct in6_addr *)
687 x->id.daddr.a6))
688 continue;
689 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700690 }
David S. Milleredcd5822006-08-24 00:42:45 -0700691
692 xfrm_state_hold(x);
693 return x;
694 }
695
696 return NULL;
697}
698
699static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
700{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700701 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700702 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700703 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700704
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800705 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700706 if (x->props.family != family ||
707 x->id.proto != proto)
708 continue;
709
710 switch (family) {
711 case AF_INET:
712 if (x->id.daddr.a4 != daddr->a4 ||
713 x->props.saddr.a4 != saddr->a4)
714 continue;
715 break;
716 case AF_INET6:
717 if (!ipv6_addr_equal((struct in6_addr *)daddr,
718 (struct in6_addr *)
719 x->id.daddr.a6) ||
720 !ipv6_addr_equal((struct in6_addr *)saddr,
721 (struct in6_addr *)
722 x->props.saddr.a6))
723 continue;
724 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700725 }
David S. Milleredcd5822006-08-24 00:42:45 -0700726
727 xfrm_state_hold(x);
728 return x;
729 }
730
731 return NULL;
732}
733
734static inline struct xfrm_state *
735__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
736{
737 if (use_spi)
738 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
739 x->id.proto, family);
740 else
741 return __xfrm_state_lookup_byaddr(&x->id.daddr,
742 &x->props.saddr,
743 x->id.proto, family);
744}
745
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700746static void xfrm_hash_grow_check(int have_hash_collision)
747{
748 if (have_hash_collision &&
Alexey Dobriyan529983e2008-11-25 17:18:12 -0800749 (init_net.xfrm.state_hmask + 1) < xfrm_state_hashmax &&
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800750 init_net.xfrm.state_num > init_net.xfrm.state_hmask)
Alexey Dobriyan63082732008-11-25 17:19:07 -0800751 schedule_work(&init_net.xfrm.state_hash_work);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700752}
753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900755xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 struct flowi *fl, struct xfrm_tmpl *tmpl,
757 struct xfrm_policy *pol, int *err,
758 unsigned short family)
759{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800760 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700761 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700762 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 int acquire_in_progress = 0;
764 int error = 0;
765 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900766
David S. Miller37b08e32008-09-02 20:14:15 -0700767 to_put = NULL;
768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800770 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800771 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 if (x->props.family == family &&
773 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700774 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 xfrm_state_addr_check(x, daddr, saddr, family) &&
776 tmpl->mode == x->props.mode &&
777 tmpl->id.proto == x->id.proto &&
778 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
779 /* Resolution logic:
780 1. There is a valid state with matching selector.
781 Done.
782 2. Valid state with inappropriate selector. Skip.
783
784 Entering area of "sysdeps".
785
786 3. If state is not valid, selector is temporary,
787 it selects only session which triggered
788 previous resolution. Key manager will do
789 something to install a state with proper
790 selector.
791 */
792 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700793 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700794 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 continue;
796 if (!best ||
797 best->km.dying > x->km.dying ||
798 (best->km.dying == x->km.dying &&
799 best->curlft.add_time < x->curlft.add_time))
800 best = x;
801 } else if (x->km.state == XFRM_STATE_ACQ) {
802 acquire_in_progress = 1;
803 } else if (x->km.state == XFRM_STATE_ERROR ||
804 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700805 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700806 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 error = -ESRCH;
808 }
809 }
810 }
811
812 x = best;
813 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700814 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700815 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
816 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700817 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 error = -EEXIST;
819 goto out;
820 }
Alexey Dobriyan673c09be2008-11-25 17:15:16 -0800821 x = xfrm_state_alloc(&init_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 if (x == NULL) {
823 error = -ENOMEM;
824 goto out;
825 }
826 /* Initialize temporary selector matching only
827 * to current session. */
828 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
829
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700830 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
831 if (error) {
832 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700833 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700834 x = NULL;
835 goto out;
836 }
837
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 if (km_query(x, tmpl, pol) == 0) {
839 x->km.state = XFRM_STATE_ACQ;
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -0800840 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800841 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700842 h = xfrm_src_hash(daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800843 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 if (x->id.spi) {
845 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800846 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 }
David S. Miller01e67d02007-05-25 00:41:38 -0700848 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
849 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 add_timer(&x->timer);
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800851 init_net.xfrm.state_num++;
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700852 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 } else {
854 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700855 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 x = NULL;
857 error = -ESRCH;
858 }
859 }
860out:
861 if (x)
862 xfrm_state_hold(x);
863 else
864 *err = acquire_in_progress ? -EAGAIN : error;
865 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700866 if (to_put)
867 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 return x;
869}
870
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700871struct xfrm_state *
872xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
873 unsigned short family, u8 mode, u8 proto, u32 reqid)
874{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800875 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700876 struct xfrm_state *rx = NULL, *x = NULL;
877 struct hlist_node *entry;
878
879 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800880 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800881 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700882 if (x->props.family == family &&
883 x->props.reqid == reqid &&
884 !(x->props.flags & XFRM_STATE_WILDRECV) &&
885 xfrm_state_addr_check(x, daddr, saddr, family) &&
886 mode == x->props.mode &&
887 proto == x->id.proto &&
888 x->km.state == XFRM_STATE_VALID) {
889 rx = x;
890 break;
891 }
892 }
893
894 if (rx)
895 xfrm_state_hold(rx);
896 spin_unlock(&xfrm_state_lock);
897
898
899 return rx;
900}
901EXPORT_SYMBOL(xfrm_stateonly_find);
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903static void __xfrm_state_insert(struct xfrm_state *x)
904{
David S. Millera624c102006-08-24 03:24:33 -0700905 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
David S. Miller9d4a7062006-08-24 03:18:09 -0700907 x->genid = ++xfrm_state_genid;
908
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -0800909 list_add(&x->km.all, &init_net.xfrm.state_all);
Timo Teras4c563f72008-02-28 21:31:08 -0800910
David S. Millerc1969f22006-08-24 04:00:03 -0700911 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
912 x->props.reqid, x->props.family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800913 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700915 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -0800916 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700918 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700919 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
920 x->props.family);
921
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -0800922 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700923 }
924
David S. Millera47f0ce2006-08-24 03:54:22 -0700925 mod_timer(&x->timer, jiffies + HZ);
926 if (x->replay_maxage)
927 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800928
Alexey Dobriyan50a30652008-11-25 17:21:01 -0800929 wake_up(&init_net.xfrm.km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700930
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -0800931 init_net.xfrm.state_num++;
David S. Millerf034b5d2006-08-24 03:08:07 -0700932
David S. Miller918049f2006-10-12 22:03:24 -0700933 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934}
935
David S. Millerc7f5ea32006-08-24 03:29:04 -0700936/* xfrm_state_lock is held */
937static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
938{
939 unsigned short family = xnew->props.family;
940 u32 reqid = xnew->props.reqid;
941 struct xfrm_state *x;
942 struct hlist_node *entry;
943 unsigned int h;
944
David S. Millerc1969f22006-08-24 04:00:03 -0700945 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800946 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
David S. Millerc7f5ea32006-08-24 03:29:04 -0700947 if (x->props.family == family &&
948 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700949 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
950 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700951 x->genid = xfrm_state_genid;
952 }
953}
954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955void xfrm_state_insert(struct xfrm_state *x)
956{
957 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700958 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 __xfrm_state_insert(x);
960 spin_unlock_bh(&xfrm_state_lock);
961}
962EXPORT_SYMBOL(xfrm_state_insert);
963
David S. Miller27708342006-08-24 00:13:10 -0700964/* xfrm_state_lock is held */
965static 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)
966{
David S. Millerc1969f22006-08-24 04:00:03 -0700967 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700968 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700969 struct xfrm_state *x;
970
Alexey Dobriyan73d189d2008-11-25 17:16:58 -0800971 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700972 if (x->props.reqid != reqid ||
973 x->props.mode != mode ||
974 x->props.family != family ||
975 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700976 x->id.spi != 0 ||
977 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700978 continue;
979
980 switch (family) {
981 case AF_INET:
982 if (x->id.daddr.a4 != daddr->a4 ||
983 x->props.saddr.a4 != saddr->a4)
984 continue;
985 break;
986 case AF_INET6:
987 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
988 (struct in6_addr *)daddr) ||
989 !ipv6_addr_equal((struct in6_addr *)
990 x->props.saddr.a6,
991 (struct in6_addr *)saddr))
992 continue;
993 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700994 }
David S. Miller27708342006-08-24 00:13:10 -0700995
996 xfrm_state_hold(x);
997 return x;
998 }
999
1000 if (!create)
1001 return NULL;
1002
Alexey Dobriyan673c09be2008-11-25 17:15:16 -08001003 x = xfrm_state_alloc(&init_net);
David S. Miller27708342006-08-24 00:13:10 -07001004 if (likely(x)) {
1005 switch (family) {
1006 case AF_INET:
1007 x->sel.daddr.a4 = daddr->a4;
1008 x->sel.saddr.a4 = saddr->a4;
1009 x->sel.prefixlen_d = 32;
1010 x->sel.prefixlen_s = 32;
1011 x->props.saddr.a4 = saddr->a4;
1012 x->id.daddr.a4 = daddr->a4;
1013 break;
1014
1015 case AF_INET6:
1016 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1017 (struct in6_addr *)daddr);
1018 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1019 (struct in6_addr *)saddr);
1020 x->sel.prefixlen_d = 128;
1021 x->sel.prefixlen_s = 128;
1022 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1023 (struct in6_addr *)saddr);
1024 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1025 (struct in6_addr *)daddr);
1026 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001027 }
David S. Miller27708342006-08-24 00:13:10 -07001028
1029 x->km.state = XFRM_STATE_ACQ;
1030 x->id.proto = proto;
1031 x->props.family = family;
1032 x->props.mode = mode;
1033 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001034 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001035 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001036 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001037 add_timer(&x->timer);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001038 list_add(&x->km.all, &init_net.xfrm.state_all);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001039 hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001040 h = xfrm_src_hash(daddr, saddr, family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001041 hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001042
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08001043 init_net.xfrm.state_num++;
David S. Miller918049f2006-10-12 22:03:24 -07001044
1045 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001046 }
1047
1048 return x;
1049}
1050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1052
1053int xfrm_state_add(struct xfrm_state *x)
1054{
David S. Miller37b08e32008-09-02 20:14:15 -07001055 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 int family;
1057 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001058 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
1060 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
David S. Miller37b08e32008-09-02 20:14:15 -07001062 to_put = NULL;
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 spin_lock_bh(&xfrm_state_lock);
1065
David S. Milleredcd5822006-08-24 00:42:45 -07001066 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001068 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 x1 = NULL;
1070 err = -EEXIST;
1071 goto out;
1072 }
1073
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001074 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001076 if (x1 && ((x1->id.proto != x->id.proto) ||
1077 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001078 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 x1 = NULL;
1080 }
1081 }
1082
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001083 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001084 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1085 x->id.proto,
1086 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
David S. Millerc7f5ea32006-08-24 03:29:04 -07001088 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 __xfrm_state_insert(x);
1090 err = 0;
1091
1092out:
1093 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094
1095 if (x1) {
1096 xfrm_state_delete(x1);
1097 xfrm_state_put(x1);
1098 }
1099
David S. Miller37b08e32008-09-02 20:14:15 -07001100 if (to_put)
1101 xfrm_state_put(to_put);
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 return err;
1104}
1105EXPORT_SYMBOL(xfrm_state_add);
1106
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001107#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001108static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001109{
1110 int err = -ENOMEM;
Alexey Dobriyan673c09be2008-11-25 17:15:16 -08001111 struct xfrm_state *x = xfrm_state_alloc(&init_net);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001112 if (!x)
1113 goto error;
1114
1115 memcpy(&x->id, &orig->id, sizeof(x->id));
1116 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1117 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1118 x->props.mode = orig->props.mode;
1119 x->props.replay_window = orig->props.replay_window;
1120 x->props.reqid = orig->props.reqid;
1121 x->props.family = orig->props.family;
1122 x->props.saddr = orig->props.saddr;
1123
1124 if (orig->aalg) {
1125 x->aalg = xfrm_algo_clone(orig->aalg);
1126 if (!x->aalg)
1127 goto error;
1128 }
1129 x->props.aalgo = orig->props.aalgo;
1130
1131 if (orig->ealg) {
1132 x->ealg = xfrm_algo_clone(orig->ealg);
1133 if (!x->ealg)
1134 goto error;
1135 }
1136 x->props.ealgo = orig->props.ealgo;
1137
1138 if (orig->calg) {
1139 x->calg = xfrm_algo_clone(orig->calg);
1140 if (!x->calg)
1141 goto error;
1142 }
1143 x->props.calgo = orig->props.calgo;
1144
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001145 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001146 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1147 if (!x->encap)
1148 goto error;
1149 }
1150
1151 if (orig->coaddr) {
1152 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1153 GFP_KERNEL);
1154 if (!x->coaddr)
1155 goto error;
1156 }
1157
1158 err = xfrm_init_state(x);
1159 if (err)
1160 goto error;
1161
1162 x->props.flags = orig->props.flags;
1163
1164 x->curlft.add_time = orig->curlft.add_time;
1165 x->km.state = orig->km.state;
1166 x->km.seq = orig->km.seq;
1167
1168 return x;
1169
1170 error:
1171 if (errp)
1172 *errp = err;
1173 if (x) {
1174 kfree(x->aalg);
1175 kfree(x->ealg);
1176 kfree(x->calg);
1177 kfree(x->encap);
1178 kfree(x->coaddr);
1179 }
1180 kfree(x);
1181 return NULL;
1182}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001183
1184/* xfrm_state_lock is held */
1185struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1186{
1187 unsigned int h;
1188 struct xfrm_state *x;
1189 struct hlist_node *entry;
1190
1191 if (m->reqid) {
1192 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1193 m->reqid, m->old_family);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001194 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001195 if (x->props.mode != m->mode ||
1196 x->id.proto != m->proto)
1197 continue;
1198 if (m->reqid && x->props.reqid != m->reqid)
1199 continue;
1200 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1201 m->old_family) ||
1202 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1203 m->old_family))
1204 continue;
1205 xfrm_state_hold(x);
1206 return x;
1207 }
1208 } else {
1209 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1210 m->old_family);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08001211 hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001212 if (x->props.mode != m->mode ||
1213 x->id.proto != m->proto)
1214 continue;
1215 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1216 m->old_family) ||
1217 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1218 m->old_family))
1219 continue;
1220 xfrm_state_hold(x);
1221 return x;
1222 }
1223 }
1224
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001225 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001226}
1227EXPORT_SYMBOL(xfrm_migrate_state_find);
1228
1229struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1230 struct xfrm_migrate *m)
1231{
1232 struct xfrm_state *xc;
1233 int err;
1234
1235 xc = xfrm_state_clone(x, &err);
1236 if (!xc)
1237 return NULL;
1238
1239 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1240 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1241
1242 /* add state */
1243 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1244 /* a care is needed when the destination address of the
1245 state is to be updated as it is a part of triplet */
1246 xfrm_state_insert(xc);
1247 } else {
1248 if ((err = xfrm_state_add(xc)) < 0)
1249 goto error;
1250 }
1251
1252 return xc;
1253error:
1254 kfree(xc);
1255 return NULL;
1256}
1257EXPORT_SYMBOL(xfrm_state_migrate);
1258#endif
1259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260int xfrm_state_update(struct xfrm_state *x)
1261{
David S. Miller37b08e32008-09-02 20:14:15 -07001262 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001264 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
David S. Miller37b08e32008-09-02 20:14:15 -07001266 to_put = NULL;
1267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001269 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
1271 err = -ESRCH;
1272 if (!x1)
1273 goto out;
1274
1275 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001276 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 err = -EEXIST;
1278 goto out;
1279 }
1280
1281 if (x1->km.state == XFRM_STATE_ACQ) {
1282 __xfrm_state_insert(x);
1283 x = NULL;
1284 }
1285 err = 0;
1286
1287out:
1288 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
David S. Miller37b08e32008-09-02 20:14:15 -07001290 if (to_put)
1291 xfrm_state_put(to_put);
1292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 if (err)
1294 return err;
1295
1296 if (!x) {
1297 xfrm_state_delete(x1);
1298 xfrm_state_put(x1);
1299 return 0;
1300 }
1301
1302 err = -EINVAL;
1303 spin_lock_bh(&x1->lock);
1304 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1305 if (x->encap && x1->encap)
1306 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001307 if (x->coaddr && x1->coaddr) {
1308 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1309 }
1310 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1311 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1313 x1->km.dying = 0;
1314
David S. Millera47f0ce2006-08-24 03:54:22 -07001315 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 if (x1->curlft.use_time)
1317 xfrm_state_check_expire(x1);
1318
1319 err = 0;
1320 }
1321 spin_unlock_bh(&x1->lock);
1322
1323 xfrm_state_put(x1);
1324
1325 return err;
1326}
1327EXPORT_SYMBOL(xfrm_state_update);
1328
1329int xfrm_state_check_expire(struct xfrm_state *x)
1330{
1331 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001332 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333
1334 if (x->km.state != XFRM_STATE_VALID)
1335 return -EINVAL;
1336
1337 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1338 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001339 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001340 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 return -EINVAL;
1342 }
1343
1344 if (!x->km.dying &&
1345 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001346 x->curlft.packets >= x->lft.soft_packet_limit)) {
1347 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001348 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001349 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 return 0;
1351}
1352EXPORT_SYMBOL(xfrm_state_check_expire);
1353
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001355xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 unsigned short family)
1357{
1358 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
1360 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001361 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return x;
1364}
1365EXPORT_SYMBOL(xfrm_state_lookup);
1366
1367struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001368xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1369 u8 proto, unsigned short family)
1370{
1371 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001372
1373 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001374 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001375 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001376 return x;
1377}
1378EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1379
1380struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001381xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1382 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 int create, unsigned short family)
1384{
1385 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
1387 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001388 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001390
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 return x;
1392}
1393EXPORT_SYMBOL(xfrm_find_acq);
1394
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001395#ifdef CONFIG_XFRM_SUB_POLICY
1396int
1397xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1398 unsigned short family)
1399{
1400 int err = 0;
1401 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1402 if (!afinfo)
1403 return -EAFNOSUPPORT;
1404
1405 spin_lock_bh(&xfrm_state_lock);
1406 if (afinfo->tmpl_sort)
1407 err = afinfo->tmpl_sort(dst, src, n);
1408 spin_unlock_bh(&xfrm_state_lock);
1409 xfrm_state_put_afinfo(afinfo);
1410 return err;
1411}
1412EXPORT_SYMBOL(xfrm_tmpl_sort);
1413
1414int
1415xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1416 unsigned short family)
1417{
1418 int err = 0;
1419 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1420 if (!afinfo)
1421 return -EAFNOSUPPORT;
1422
1423 spin_lock_bh(&xfrm_state_lock);
1424 if (afinfo->state_sort)
1425 err = afinfo->state_sort(dst, src, n);
1426 spin_unlock_bh(&xfrm_state_lock);
1427 xfrm_state_put_afinfo(afinfo);
1428 return err;
1429}
1430EXPORT_SYMBOL(xfrm_state_sort);
1431#endif
1432
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433/* Silly enough, but I'm lazy to build resolution list */
1434
1435static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1436{
1437 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
Alexey Dobriyan529983e2008-11-25 17:18:12 -08001439 for (i = 0; i <= init_net.xfrm.state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001440 struct hlist_node *entry;
1441 struct xfrm_state *x;
1442
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08001443 hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
David S. Miller8f126e32006-08-24 02:45:07 -07001444 if (x->km.seq == seq &&
1445 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 xfrm_state_hold(x);
1447 return x;
1448 }
1449 }
1450 }
1451 return NULL;
1452}
1453
1454struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1455{
1456 struct xfrm_state *x;
1457
1458 spin_lock_bh(&xfrm_state_lock);
1459 x = __xfrm_find_acq_byseq(seq);
1460 spin_unlock_bh(&xfrm_state_lock);
1461 return x;
1462}
1463EXPORT_SYMBOL(xfrm_find_acq_byseq);
1464
1465u32 xfrm_get_acqseq(void)
1466{
1467 u32 res;
1468 static u32 acqseq;
1469 static DEFINE_SPINLOCK(acqseq_lock);
1470
1471 spin_lock_bh(&acqseq_lock);
1472 res = (++acqseq ? : ++acqseq);
1473 spin_unlock_bh(&acqseq_lock);
1474 return res;
1475}
1476EXPORT_SYMBOL(xfrm_get_acqseq);
1477
Herbert Xu658b2192007-10-09 13:29:52 -07001478int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479{
David S. Millerf034b5d2006-08-24 03:08:07 -07001480 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001482 int err = -ENOENT;
1483 __be32 minspi = htonl(low);
1484 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
Herbert Xu658b2192007-10-09 13:29:52 -07001486 spin_lock_bh(&x->lock);
1487 if (x->km.state == XFRM_STATE_DEAD)
1488 goto unlock;
1489
1490 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001492 goto unlock;
1493
1494 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495
1496 if (minspi == maxspi) {
1497 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1498 if (x0) {
1499 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001500 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 }
1502 x->id.spi = minspi;
1503 } else {
1504 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001505 for (h=0; h<high-low+1; h++) {
1506 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1508 if (x0 == NULL) {
1509 x->id.spi = htonl(spi);
1510 break;
1511 }
1512 xfrm_state_put(x0);
1513 }
1514 }
1515 if (x->id.spi) {
1516 spin_lock_bh(&xfrm_state_lock);
1517 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08001518 hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001520
1521 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 }
Herbert Xu658b2192007-10-09 13:29:52 -07001523
1524unlock:
1525 spin_unlock_bh(&x->lock);
1526
1527 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528}
1529EXPORT_SYMBOL(xfrm_alloc_spi);
1530
Timo Teras4c563f72008-02-28 21:31:08 -08001531int xfrm_state_walk(struct xfrm_state_walk *walk,
1532 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 void *data)
1534{
Herbert Xu12a169e2008-10-01 07:03:24 -07001535 struct xfrm_state *state;
1536 struct xfrm_state_walk *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 int err = 0;
1538
Herbert Xu12a169e2008-10-01 07:03:24 -07001539 if (walk->seq != 0 && list_empty(&walk->all))
Timo Teras4c563f72008-02-28 21:31:08 -08001540 return 0;
1541
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 spin_lock_bh(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -07001543 if (list_empty(&walk->all))
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001544 x = list_first_entry(&init_net.xfrm.state_all, struct xfrm_state_walk, all);
Herbert Xu12a169e2008-10-01 07:03:24 -07001545 else
1546 x = list_entry(&walk->all, struct xfrm_state_walk, all);
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08001547 list_for_each_entry_from(x, &init_net.xfrm.state_all, all) {
Herbert Xu12a169e2008-10-01 07:03:24 -07001548 if (x->state == XFRM_STATE_DEAD)
Timo Teras4c563f72008-02-28 21:31:08 -08001549 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001550 state = container_of(x, struct xfrm_state, km);
1551 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
Timo Teras4c563f72008-02-28 21:31:08 -08001552 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001553 err = func(state, walk->seq, data);
1554 if (err) {
1555 list_move_tail(&walk->all, &x->all);
1556 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001558 walk->seq++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001560 if (walk->seq == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 err = -ENOENT;
1562 goto out;
1563 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001564 list_del_init(&walk->all);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565out:
1566 spin_unlock_bh(&xfrm_state_lock);
1567 return err;
1568}
1569EXPORT_SYMBOL(xfrm_state_walk);
1570
Herbert Xu5c182452008-09-22 19:48:19 -07001571void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1572{
Herbert Xu12a169e2008-10-01 07:03:24 -07001573 INIT_LIST_HEAD(&walk->all);
Herbert Xu5c182452008-09-22 19:48:19 -07001574 walk->proto = proto;
Herbert Xu12a169e2008-10-01 07:03:24 -07001575 walk->state = XFRM_STATE_DEAD;
1576 walk->seq = 0;
Herbert Xu5c182452008-09-22 19:48:19 -07001577}
1578EXPORT_SYMBOL(xfrm_state_walk_init);
1579
Herbert Xuabb81c42008-09-09 19:58:29 -07001580void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1581{
Herbert Xu12a169e2008-10-01 07:03:24 -07001582 if (list_empty(&walk->all))
Herbert Xu5c182452008-09-22 19:48:19 -07001583 return;
Herbert Xu5c182452008-09-22 19:48:19 -07001584
Herbert Xu12a169e2008-10-01 07:03:24 -07001585 spin_lock_bh(&xfrm_state_lock);
1586 list_del(&walk->all);
1587 spin_lock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001588}
1589EXPORT_SYMBOL(xfrm_state_walk_done);
1590
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001591
1592void xfrm_replay_notify(struct xfrm_state *x, int event)
1593{
1594 struct km_event c;
1595 /* we send notify messages in case
1596 * 1. we updated on of the sequence numbers, and the seqno difference
1597 * is at least x->replay_maxdiff, in this case we also update the
1598 * timeout of our timer function
1599 * 2. if x->replay_maxage has elapsed since last update,
1600 * and there were changes
1601 *
1602 * The state structure must be locked!
1603 */
1604
1605 switch (event) {
1606 case XFRM_REPLAY_UPDATE:
1607 if (x->replay_maxdiff &&
1608 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001609 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1610 if (x->xflags & XFRM_TIME_DEFER)
1611 event = XFRM_REPLAY_TIMEOUT;
1612 else
1613 return;
1614 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001615
1616 break;
1617
1618 case XFRM_REPLAY_TIMEOUT:
1619 if ((x->replay.seq == x->preplay.seq) &&
1620 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001621 (x->replay.oseq == x->preplay.oseq)) {
1622 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001623 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001624 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001625
1626 break;
1627 }
1628
1629 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1630 c.event = XFRM_MSG_NEWAE;
1631 c.data.aevent = event;
1632 km_state_notify(x, &c);
1633
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001634 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001635 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001636 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001637}
1638
1639static void xfrm_replay_timer_handler(unsigned long data)
1640{
1641 struct xfrm_state *x = (struct xfrm_state*)data;
1642
1643 spin_lock(&x->lock);
1644
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001645 if (x->km.state == XFRM_STATE_VALID) {
1646 if (xfrm_aevent_is_on())
1647 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1648 else
1649 x->xflags |= XFRM_TIME_DEFER;
1650 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001651
1652 spin_unlock(&x->lock);
1653}
1654
Paul Mooreafeb14b2007-12-21 14:58:11 -08001655int xfrm_replay_check(struct xfrm_state *x,
1656 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657{
1658 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001659 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
1661 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001662 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
1664 if (likely(seq > x->replay.seq))
1665 return 0;
1666
1667 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001668 if (diff >= min_t(unsigned int, x->props.replay_window,
1669 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001671 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 }
1673
1674 if (x->replay.bitmap & (1U << diff)) {
1675 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001676 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 }
1678 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001679
1680err:
1681 xfrm_audit_state_replay(x, skb, net_seq);
1682 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Al Viro61f46272006-09-27 18:48:33 -07001685void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686{
1687 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001688 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
1690 if (seq > x->replay.seq) {
1691 diff = seq - x->replay.seq;
1692 if (diff < x->props.replay_window)
1693 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1694 else
1695 x->replay.bitmap = 1;
1696 x->replay.seq = seq;
1697 } else {
1698 diff = x->replay.seq - seq;
1699 x->replay.bitmap |= (1U << diff);
1700 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001701
1702 if (xfrm_aevent_is_on())
1703 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Denis Chengdf018122007-12-07 00:51:11 -08001706static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707static DEFINE_RWLOCK(xfrm_km_lock);
1708
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001709void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710{
1711 struct xfrm_mgr *km;
1712
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001713 read_lock(&xfrm_km_lock);
1714 list_for_each_entry(km, &xfrm_km_list, list)
1715 if (km->notify_policy)
1716 km->notify_policy(xp, dir, c);
1717 read_unlock(&xfrm_km_lock);
1718}
1719
1720void km_state_notify(struct xfrm_state *x, struct km_event *c)
1721{
1722 struct xfrm_mgr *km;
1723 read_lock(&xfrm_km_lock);
1724 list_for_each_entry(km, &xfrm_km_list, list)
1725 if (km->notify)
1726 km->notify(x, c);
1727 read_unlock(&xfrm_km_lock);
1728}
1729
1730EXPORT_SYMBOL(km_policy_notify);
1731EXPORT_SYMBOL(km_state_notify);
1732
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001733void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001734{
1735 struct km_event c;
1736
Herbert Xubf08867f92005-06-18 22:44:00 -07001737 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001738 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001739 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001740 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742 if (hard)
Alexey Dobriyan50a30652008-11-25 17:21:01 -08001743 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744}
1745
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001746EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001747/*
1748 * We send to all registered managers regardless of failure
1749 * We are happy with one success
1750*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001751int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001753 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 struct xfrm_mgr *km;
1755
1756 read_lock(&xfrm_km_lock);
1757 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001758 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1759 if (!acqret)
1760 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 }
1762 read_unlock(&xfrm_km_lock);
1763 return err;
1764}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001765EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766
Al Viro5d36b182006-11-08 00:24:06 -08001767int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
1769 int err = -EINVAL;
1770 struct xfrm_mgr *km;
1771
1772 read_lock(&xfrm_km_lock);
1773 list_for_each_entry(km, &xfrm_km_list, list) {
1774 if (km->new_mapping)
1775 err = km->new_mapping(x, ipaddr, sport);
1776 if (!err)
1777 break;
1778 }
1779 read_unlock(&xfrm_km_lock);
1780 return err;
1781}
1782EXPORT_SYMBOL(km_new_mapping);
1783
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001784void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001786 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
Herbert Xubf08867f92005-06-18 22:44:00 -07001788 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001789 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001790 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001791 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 if (hard)
Alexey Dobriyan50a30652008-11-25 17:21:01 -08001794 wake_up(&init_net.xfrm.km_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795}
David S. Millera70fcb02006-03-20 19:18:52 -08001796EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001798#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001799int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001800 struct xfrm_migrate *m, int num_migrate,
1801 struct xfrm_kmaddress *k)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001802{
1803 int err = -EINVAL;
1804 int ret;
1805 struct xfrm_mgr *km;
1806
1807 read_lock(&xfrm_km_lock);
1808 list_for_each_entry(km, &xfrm_km_list, list) {
1809 if (km->migrate) {
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001810 ret = km->migrate(sel, dir, type, m, num_migrate, k);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001811 if (!ret)
1812 err = ret;
1813 }
1814 }
1815 read_unlock(&xfrm_km_lock);
1816 return err;
1817}
1818EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001819#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001820
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001821int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1822{
1823 int err = -EINVAL;
1824 int ret;
1825 struct xfrm_mgr *km;
1826
1827 read_lock(&xfrm_km_lock);
1828 list_for_each_entry(km, &xfrm_km_list, list) {
1829 if (km->report) {
1830 ret = km->report(proto, sel, addr);
1831 if (!ret)
1832 err = ret;
1833 }
1834 }
1835 read_unlock(&xfrm_km_lock);
1836 return err;
1837}
1838EXPORT_SYMBOL(km_report);
1839
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1841{
1842 int err;
1843 u8 *data;
1844 struct xfrm_mgr *km;
1845 struct xfrm_policy *pol = NULL;
1846
1847 if (optlen <= 0 || optlen > PAGE_SIZE)
1848 return -EMSGSIZE;
1849
1850 data = kmalloc(optlen, GFP_KERNEL);
1851 if (!data)
1852 return -ENOMEM;
1853
1854 err = -EFAULT;
1855 if (copy_from_user(data, optval, optlen))
1856 goto out;
1857
1858 err = -EINVAL;
1859 read_lock(&xfrm_km_lock);
1860 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001861 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 optlen, &err);
1863 if (err >= 0)
1864 break;
1865 }
1866 read_unlock(&xfrm_km_lock);
1867
1868 if (err >= 0) {
1869 xfrm_sk_policy_insert(sk, err, pol);
1870 xfrm_pol_put(pol);
1871 err = 0;
1872 }
1873
1874out:
1875 kfree(data);
1876 return err;
1877}
1878EXPORT_SYMBOL(xfrm_user_policy);
1879
1880int xfrm_register_km(struct xfrm_mgr *km)
1881{
1882 write_lock_bh(&xfrm_km_lock);
1883 list_add_tail(&km->list, &xfrm_km_list);
1884 write_unlock_bh(&xfrm_km_lock);
1885 return 0;
1886}
1887EXPORT_SYMBOL(xfrm_register_km);
1888
1889int xfrm_unregister_km(struct xfrm_mgr *km)
1890{
1891 write_lock_bh(&xfrm_km_lock);
1892 list_del(&km->list);
1893 write_unlock_bh(&xfrm_km_lock);
1894 return 0;
1895}
1896EXPORT_SYMBOL(xfrm_unregister_km);
1897
1898int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1899{
1900 int err = 0;
1901 if (unlikely(afinfo == NULL))
1902 return -EINVAL;
1903 if (unlikely(afinfo->family >= NPROTO))
1904 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001905 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1907 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001908 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001910 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 return err;
1912}
1913EXPORT_SYMBOL(xfrm_state_register_afinfo);
1914
1915int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1916{
1917 int err = 0;
1918 if (unlikely(afinfo == NULL))
1919 return -EINVAL;
1920 if (unlikely(afinfo->family >= NPROTO))
1921 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001922 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1924 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1925 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001926 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001929 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 return err;
1931}
1932EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1933
Herbert Xu17c2a422007-10-17 21:33:12 -07001934static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935{
1936 struct xfrm_state_afinfo *afinfo;
1937 if (unlikely(family >= NPROTO))
1938 return NULL;
1939 read_lock(&xfrm_state_afinfo_lock);
1940 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001941 if (unlikely(!afinfo))
1942 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 return afinfo;
1944}
1945
Herbert Xu17c2a422007-10-17 21:33:12 -07001946static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001947 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Herbert Xu546be242006-05-27 23:03:58 -07001949 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950}
1951
1952/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1953void xfrm_state_delete_tunnel(struct xfrm_state *x)
1954{
1955 if (x->tunnel) {
1956 struct xfrm_state *t = x->tunnel;
1957
1958 if (atomic_read(&t->tunnel_users) == 2)
1959 xfrm_state_delete(t);
1960 atomic_dec(&t->tunnel_users);
1961 xfrm_state_put(t);
1962 x->tunnel = NULL;
1963 }
1964}
1965EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1966
1967int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1968{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001969 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970
Patrick McHardyc5c25232007-04-09 11:47:18 -07001971 spin_lock_bh(&x->lock);
1972 if (x->km.state == XFRM_STATE_VALID &&
1973 x->type && x->type->get_mtu)
1974 res = x->type->get_mtu(x, mtu);
1975 else
Patrick McHardy28121612007-06-18 22:30:15 -07001976 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001977 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 return res;
1979}
1980
Herbert Xu72cb6962005-06-20 13:18:08 -07001981int xfrm_init_state(struct xfrm_state *x)
1982{
Herbert Xud094cd82005-06-20 13:19:41 -07001983 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001984 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001985 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001986 int err;
1987
Herbert Xud094cd82005-06-20 13:19:41 -07001988 err = -EAFNOSUPPORT;
1989 afinfo = xfrm_state_get_afinfo(family);
1990 if (!afinfo)
1991 goto error;
1992
1993 err = 0;
1994 if (afinfo->init_flags)
1995 err = afinfo->init_flags(x);
1996
1997 xfrm_state_put_afinfo(afinfo);
1998
1999 if (err)
2000 goto error;
2001
2002 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002003
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002004 if (x->sel.family != AF_UNSPEC) {
2005 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2006 if (inner_mode == NULL)
2007 goto error;
2008
2009 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2010 family != x->sel.family) {
2011 xfrm_put_mode(inner_mode);
2012 goto error;
2013 }
2014
2015 x->inner_mode = inner_mode;
2016 } else {
2017 struct xfrm_mode *inner_mode_iaf;
2018
2019 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2020 if (inner_mode == NULL)
2021 goto error;
2022
2023 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2024 xfrm_put_mode(inner_mode);
2025 goto error;
2026 }
2027
2028 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2029 if (inner_mode_iaf == NULL)
2030 goto error;
2031
2032 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2033 xfrm_put_mode(inner_mode_iaf);
2034 goto error;
2035 }
2036
2037 if (x->props.family == AF_INET) {
2038 x->inner_mode = inner_mode;
2039 x->inner_mode_iaf = inner_mode_iaf;
2040 } else {
2041 x->inner_mode = inner_mode_iaf;
2042 x->inner_mode_iaf = inner_mode;
2043 }
2044 }
Herbert Xu13996372007-10-17 21:35:51 -07002045
Herbert Xud094cd82005-06-20 13:19:41 -07002046 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002047 if (x->type == NULL)
2048 goto error;
2049
2050 err = x->type->init_state(x);
2051 if (err)
2052 goto error;
2053
Herbert Xu13996372007-10-17 21:35:51 -07002054 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2055 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002056 goto error;
2057
Herbert Xu72cb6962005-06-20 13:18:08 -07002058 x->km.state = XFRM_STATE_VALID;
2059
2060error:
2061 return err;
2062}
2063
2064EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002065
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002066int __net_init xfrm_state_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
David S. Millerf034b5d2006-08-24 03:08:07 -07002068 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002070 INIT_LIST_HEAD(&net->xfrm.state_all);
2071
David S. Millerf034b5d2006-08-24 03:08:07 -07002072 sz = sizeof(struct hlist_head) * 8;
2073
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002074 net->xfrm.state_bydst = xfrm_hash_alloc(sz);
2075 if (!net->xfrm.state_bydst)
2076 goto out_bydst;
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002077 net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2078 if (!net->xfrm.state_bysrc)
2079 goto out_bysrc;
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002080 net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2081 if (!net->xfrm.state_byspi)
2082 goto out_byspi;
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002083 net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
David S. Millerf034b5d2006-08-24 03:08:07 -07002084
Alexey Dobriyan0bf7c5b2008-11-25 17:18:39 -08002085 net->xfrm.state_num = 0;
Alexey Dobriyan63082732008-11-25 17:19:07 -08002086 INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
Alexey Dobriyanb8a0ae22008-11-25 17:20:11 -08002087 INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
Alexey Dobriyanc7837142008-11-25 17:20:36 -08002088 INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
Alexey Dobriyan50a30652008-11-25 17:21:01 -08002089 init_waitqueue_head(&net->xfrm.km_waitq);
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002090 return 0;
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002091
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002092out_byspi:
2093 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002094out_bysrc:
2095 xfrm_hash_free(net->xfrm.state_bydst, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002096out_bydst:
2097 return -ENOMEM;
Alexey Dobriyand62ddc22008-11-25 17:14:31 -08002098}
2099
2100void xfrm_state_fini(struct net *net)
2101{
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002102 unsigned int sz;
2103
Alexey Dobriyan9d4139c2008-11-25 17:16:11 -08002104 WARN_ON(!list_empty(&net->xfrm.state_all));
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002105
Alexey Dobriyan529983e2008-11-25 17:18:12 -08002106 sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
Alexey Dobriyanb754a4f2008-11-25 17:17:47 -08002107 WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2108 xfrm_hash_free(net->xfrm.state_byspi, sz);
Alexey Dobriyand320bbb2008-11-25 17:17:24 -08002109 WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2110 xfrm_hash_free(net->xfrm.state_bysrc, sz);
Alexey Dobriyan73d189d2008-11-25 17:16:58 -08002111 WARN_ON(!hlist_empty(net->xfrm.state_bydst));
2112 xfrm_hash_free(net->xfrm.state_bydst, sz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113}
2114
Joy Lattenab5f5e82007-09-17 11:51:22 -07002115#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002116static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2117 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002118{
Paul Moore68277ac2007-12-20 20:49:33 -08002119 struct xfrm_sec_ctx *ctx = x->security;
2120 u32 spi = ntohl(x->id.spi);
2121
2122 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002123 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002124 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002125
2126 switch(x->props.family) {
2127 case AF_INET:
Harvey Harrison21454aa2008-10-31 00:54:56 -07002128 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2129 &x->props.saddr.a4, &x->id.daddr.a4);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002130 break;
2131 case AF_INET6:
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002132 audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002133 x->props.saddr.a6, x->id.daddr.a6);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002134 break;
2135 }
Paul Moore68277ac2007-12-20 20:49:33 -08002136
2137 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002138}
2139
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002140static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2141 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002142{
2143 struct iphdr *iph4;
2144 struct ipv6hdr *iph6;
2145
2146 switch (family) {
2147 case AF_INET:
2148 iph4 = ip_hdr(skb);
Harvey Harrison21454aa2008-10-31 00:54:56 -07002149 audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
2150 &iph4->saddr, &iph4->daddr);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002151 break;
2152 case AF_INET6:
2153 iph6 = ipv6_hdr(skb);
2154 audit_log_format(audit_buf,
Harvey Harrison5b095d9892008-10-29 12:52:50 -07002155 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
Harvey Harrisonfdb46ee2008-10-28 16:10:17 -07002156 &iph6->saddr,&iph6->daddr,
Paul Mooreafeb14b2007-12-21 14:58:11 -08002157 iph6->flow_lbl[0] & 0x0f,
2158 iph6->flow_lbl[1],
2159 iph6->flow_lbl[2]);
2160 break;
2161 }
2162}
2163
Paul Moore68277ac2007-12-20 20:49:33 -08002164void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002165 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002166{
2167 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002168
Paul Mooreafeb14b2007-12-21 14:58:11 -08002169 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002170 if (audit_buf == NULL)
2171 return;
Eric Paris25323862008-04-18 10:09:25 -04002172 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002173 xfrm_audit_helper_sainfo(x, audit_buf);
2174 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002175 audit_log_end(audit_buf);
2176}
2177EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2178
Paul Moore68277ac2007-12-20 20:49:33 -08002179void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002180 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002181{
2182 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002183
Paul Mooreafeb14b2007-12-21 14:58:11 -08002184 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002185 if (audit_buf == NULL)
2186 return;
Eric Paris25323862008-04-18 10:09:25 -04002187 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002188 xfrm_audit_helper_sainfo(x, audit_buf);
2189 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002190 audit_log_end(audit_buf);
2191}
2192EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002193
2194void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2195 struct sk_buff *skb)
2196{
2197 struct audit_buffer *audit_buf;
2198 u32 spi;
2199
2200 audit_buf = xfrm_audit_start("SA-replay-overflow");
2201 if (audit_buf == NULL)
2202 return;
2203 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2204 /* don't record the sequence number because it's inherent in this kind
2205 * of audit message */
2206 spi = ntohl(x->id.spi);
2207 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2208 audit_log_end(audit_buf);
2209}
2210EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2211
2212static void xfrm_audit_state_replay(struct xfrm_state *x,
2213 struct sk_buff *skb, __be32 net_seq)
2214{
2215 struct audit_buffer *audit_buf;
2216 u32 spi;
2217
2218 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2219 if (audit_buf == NULL)
2220 return;
2221 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2222 spi = ntohl(x->id.spi);
2223 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2224 spi, spi, ntohl(net_seq));
2225 audit_log_end(audit_buf);
2226}
2227
2228void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2229{
2230 struct audit_buffer *audit_buf;
2231
2232 audit_buf = xfrm_audit_start("SA-notfound");
2233 if (audit_buf == NULL)
2234 return;
2235 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2236 audit_log_end(audit_buf);
2237}
2238EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2239
2240void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2241 __be32 net_spi, __be32 net_seq)
2242{
2243 struct audit_buffer *audit_buf;
2244 u32 spi;
2245
2246 audit_buf = xfrm_audit_start("SA-notfound");
2247 if (audit_buf == NULL)
2248 return;
2249 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2250 spi = ntohl(net_spi);
2251 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2252 spi, spi, ntohl(net_seq));
2253 audit_log_end(audit_buf);
2254}
2255EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2256
2257void xfrm_audit_state_icvfail(struct xfrm_state *x,
2258 struct sk_buff *skb, u8 proto)
2259{
2260 struct audit_buffer *audit_buf;
2261 __be32 net_spi;
2262 __be32 net_seq;
2263
2264 audit_buf = xfrm_audit_start("SA-icv-failure");
2265 if (audit_buf == NULL)
2266 return;
2267 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2268 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2269 u32 spi = ntohl(net_spi);
2270 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2271 spi, spi, ntohl(net_seq));
2272 }
2273 audit_log_end(audit_buf);
2274}
2275EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002276#endif /* CONFIG_AUDITSYSCALL */