blob: 4c6914ef7d92df5830baba92363108e502217c3f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * xfrm_state.c
3 *
4 * Changes:
5 * Mitsuru KANDA @USAGI
6 * Kazunori MIYAZAWA @USAGI
7 * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8 * IPv6 support
9 * YOSHIFUJI Hideaki @USAGI
10 * Split up af-specific functions
11 * Derek Atkins <derek@ihtfp.com>
12 * Add UDP Encapsulation
Trent Jaegerdf718372005-12-13 23:12:27 -080013 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 */
15
16#include <linux/workqueue.h>
17#include <net/xfrm.h>
18#include <linux/pfkeyv2.h>
19#include <linux/ipsec.h>
20#include <linux/module.h>
David S. Millerf034b5d2006-08-24 03:08:07 -070021#include <linux/cache.h>
Paul Moore68277ac2007-12-20 20:49:33 -080022#include <linux/audit.h>
Jesper Juhlb5890d82007-08-10 15:20:21 -070023#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
David S. Miller44e36b42006-08-24 04:50:50 -070025#include "xfrm_hash.h"
26
David S. Milleree857a72006-03-20 19:18:37 -080027struct sock *xfrm_nl;
28EXPORT_SYMBOL(xfrm_nl);
29
David S. Miller01e67d02007-05-25 00:41:38 -070030u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080031EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32
David S. Miller01e67d02007-05-25 00:41:38 -070033u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080034EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35
David S. Miller01e67d02007-05-25 00:41:38 -070036u32 sysctl_xfrm_acq_expires __read_mostly = 30;
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038/* Each xfrm_state may be linked to two tables:
39
40 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070041 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 destination/tunnel endpoint. (output)
43 */
44
45static DEFINE_SPINLOCK(xfrm_state_lock);
46
47/* Hash table to find appropriate SA towards given target (endpoint
48 * of tunnel or destination of transport mode) allowed by selector.
49 *
50 * Main use is finding SA after policy selected tunnel or transport mode.
51 * Also, it can be used by ah/esp icmp error handler to find offending SA.
52 */
Timo Teras4c563f72008-02-28 21:31:08 -080053static LIST_HEAD(xfrm_state_all);
David S. Millerf034b5d2006-08-24 03:08:07 -070054static struct hlist_head *xfrm_state_bydst __read_mostly;
55static struct hlist_head *xfrm_state_bysrc __read_mostly;
56static struct hlist_head *xfrm_state_byspi __read_mostly;
57static unsigned int xfrm_state_hmask __read_mostly;
58static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
59static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070060static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Herbert Xu17c2a422007-10-17 21:33:12 -070062static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
63static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
64
Paul Mooreafeb14b2007-12-21 14:58:11 -080065#ifdef CONFIG_AUDITSYSCALL
66static void xfrm_audit_state_replay(struct xfrm_state *x,
67 struct sk_buff *skb, __be32 net_seq);
68#else
69#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
70#endif /* CONFIG_AUDITSYSCALL */
71
David S. Millerc1969f22006-08-24 04:00:03 -070072static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
73 xfrm_address_t *saddr,
74 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070075 unsigned short family)
76{
David S. Millerc1969f22006-08-24 04:00:03 -070077 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070078}
79
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070080static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
81 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070082 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070083{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070084 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070085}
86
David S. Miller2575b652006-08-24 03:26:44 -070087static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070088xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070089{
David S. Millerc1969f22006-08-24 04:00:03 -070090 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070091}
92
David S. Millerf034b5d2006-08-24 03:08:07 -070093static void xfrm_hash_transfer(struct hlist_head *list,
94 struct hlist_head *ndsttable,
95 struct hlist_head *nsrctable,
96 struct hlist_head *nspitable,
97 unsigned int nhashmask)
98{
99 struct hlist_node *entry, *tmp;
100 struct xfrm_state *x;
101
102 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
103 unsigned int h;
104
David S. Millerc1969f22006-08-24 04:00:03 -0700105 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
106 x->props.reqid, x->props.family,
107 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700108 hlist_add_head(&x->bydst, ndsttable+h);
109
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700110 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
111 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700112 nhashmask);
113 hlist_add_head(&x->bysrc, nsrctable+h);
114
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700115 if (x->id.spi) {
116 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
117 x->id.proto, x->props.family,
118 nhashmask);
119 hlist_add_head(&x->byspi, nspitable+h);
120 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700121 }
122}
123
124static unsigned long xfrm_hash_new_size(void)
125{
126 return ((xfrm_state_hmask + 1) << 1) *
127 sizeof(struct hlist_head);
128}
129
130static DEFINE_MUTEX(hash_resize_mutex);
131
David Howellsc4028952006-11-22 14:57:56 +0000132static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700133{
134 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
135 unsigned long nsize, osize;
136 unsigned int nhashmask, ohashmask;
137 int i;
138
139 mutex_lock(&hash_resize_mutex);
140
141 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700142 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700143 if (!ndst)
144 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700145 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700146 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700147 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700148 goto out_unlock;
149 }
David S. Miller44e36b42006-08-24 04:50:50 -0700150 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700151 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700152 xfrm_hash_free(ndst, nsize);
153 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700154 goto out_unlock;
155 }
156
157 spin_lock_bh(&xfrm_state_lock);
158
159 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
160 for (i = xfrm_state_hmask; i >= 0; i--)
161 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
162 nhashmask);
163
164 odst = xfrm_state_bydst;
165 osrc = xfrm_state_bysrc;
166 ospi = xfrm_state_byspi;
167 ohashmask = xfrm_state_hmask;
168
169 xfrm_state_bydst = ndst;
170 xfrm_state_bysrc = nsrc;
171 xfrm_state_byspi = nspi;
172 xfrm_state_hmask = nhashmask;
173
174 spin_unlock_bh(&xfrm_state_lock);
175
176 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700177 xfrm_hash_free(odst, osize);
178 xfrm_hash_free(osrc, osize);
179 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700180
181out_unlock:
182 mutex_unlock(&hash_resize_mutex);
183}
184
David Howellsc4028952006-11-22 14:57:56 +0000185static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187DECLARE_WAIT_QUEUE_HEAD(km_waitq);
188EXPORT_SYMBOL(km_waitq);
189
190static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
191static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
192
193static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700194static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195static DEFINE_SPINLOCK(xfrm_state_gc_lock);
196
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800197int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800199int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800200void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700202static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
203{
204 struct xfrm_state_afinfo *afinfo;
205 if (unlikely(family >= NPROTO))
206 return NULL;
207 write_lock_bh(&xfrm_state_afinfo_lock);
208 afinfo = xfrm_state_afinfo[family];
209 if (unlikely(!afinfo))
210 write_unlock_bh(&xfrm_state_afinfo_lock);
211 return afinfo;
212}
213
214static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -0800215 __releases(xfrm_state_afinfo_lock)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700216{
217 write_unlock_bh(&xfrm_state_afinfo_lock);
218}
219
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800220int xfrm_register_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 (likely(typemap[type->proto] == NULL))
231 typemap[type->proto] = type;
232 else
233 err = -EEXIST;
234 xfrm_state_unlock_afinfo(afinfo);
235 return err;
236}
237EXPORT_SYMBOL(xfrm_register_type);
238
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800239int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700240{
241 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800242 const struct xfrm_type **typemap;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700243 int err = 0;
244
245 if (unlikely(afinfo == NULL))
246 return -EAFNOSUPPORT;
247 typemap = afinfo->type_map;
248
249 if (unlikely(typemap[type->proto] != type))
250 err = -ENOENT;
251 else
252 typemap[type->proto] = NULL;
253 xfrm_state_unlock_afinfo(afinfo);
254 return err;
255}
256EXPORT_SYMBOL(xfrm_unregister_type);
257
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800258static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700259{
260 struct xfrm_state_afinfo *afinfo;
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800261 const struct xfrm_type **typemap;
262 const struct xfrm_type *type;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700263 int modload_attempted = 0;
264
265retry:
266 afinfo = xfrm_state_get_afinfo(family);
267 if (unlikely(afinfo == NULL))
268 return NULL;
269 typemap = afinfo->type_map;
270
271 type = typemap[proto];
272 if (unlikely(type && !try_module_get(type->owner)))
273 type = NULL;
274 if (!type && !modload_attempted) {
275 xfrm_state_put_afinfo(afinfo);
276 request_module("xfrm-type-%d-%d", family, proto);
277 modload_attempted = 1;
278 goto retry;
279 }
280
281 xfrm_state_put_afinfo(afinfo);
282 return type;
283}
284
Eric Dumazet533cb5b2008-01-30 19:11:50 -0800285static void xfrm_put_type(const struct xfrm_type *type)
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700286{
287 module_put(type->owner);
288}
289
290int xfrm_register_mode(struct xfrm_mode *mode, int family)
291{
292 struct xfrm_state_afinfo *afinfo;
293 struct xfrm_mode **modemap;
294 int err;
295
296 if (unlikely(mode->encap >= XFRM_MODE_MAX))
297 return -EINVAL;
298
299 afinfo = xfrm_state_lock_afinfo(family);
300 if (unlikely(afinfo == NULL))
301 return -EAFNOSUPPORT;
302
303 err = -EEXIST;
304 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700305 if (modemap[mode->encap])
306 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700307
Herbert Xu17c2a422007-10-17 21:33:12 -0700308 err = -ENOENT;
309 if (!try_module_get(afinfo->owner))
310 goto out;
311
312 mode->afinfo = afinfo;
313 modemap[mode->encap] = mode;
314 err = 0;
315
316out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700317 xfrm_state_unlock_afinfo(afinfo);
318 return err;
319}
320EXPORT_SYMBOL(xfrm_register_mode);
321
322int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
323{
324 struct xfrm_state_afinfo *afinfo;
325 struct xfrm_mode **modemap;
326 int err;
327
328 if (unlikely(mode->encap >= XFRM_MODE_MAX))
329 return -EINVAL;
330
331 afinfo = xfrm_state_lock_afinfo(family);
332 if (unlikely(afinfo == NULL))
333 return -EAFNOSUPPORT;
334
335 err = -ENOENT;
336 modemap = afinfo->mode_map;
337 if (likely(modemap[mode->encap] == mode)) {
338 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700339 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700340 err = 0;
341 }
342
343 xfrm_state_unlock_afinfo(afinfo);
344 return err;
345}
346EXPORT_SYMBOL(xfrm_unregister_mode);
347
348static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
349{
350 struct xfrm_state_afinfo *afinfo;
351 struct xfrm_mode *mode;
352 int modload_attempted = 0;
353
354 if (unlikely(encap >= XFRM_MODE_MAX))
355 return NULL;
356
357retry:
358 afinfo = xfrm_state_get_afinfo(family);
359 if (unlikely(afinfo == NULL))
360 return NULL;
361
362 mode = afinfo->mode_map[encap];
363 if (unlikely(mode && !try_module_get(mode->owner)))
364 mode = NULL;
365 if (!mode && !modload_attempted) {
366 xfrm_state_put_afinfo(afinfo);
367 request_module("xfrm-mode-%d-%d", family, encap);
368 modload_attempted = 1;
369 goto retry;
370 }
371
372 xfrm_state_put_afinfo(afinfo);
373 return mode;
374}
375
376static void xfrm_put_mode(struct xfrm_mode *mode)
377{
378 module_put(mode->owner);
379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381static void xfrm_state_gc_destroy(struct xfrm_state *x)
382{
David S. Millera47f0ce2006-08-24 03:54:22 -0700383 del_timer_sync(&x->timer);
384 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800385 kfree(x->aalg);
386 kfree(x->ealg);
387 kfree(x->calg);
388 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700389 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700390 if (x->inner_mode)
391 xfrm_put_mode(x->inner_mode);
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700392 if (x->inner_mode_iaf)
393 xfrm_put_mode(x->inner_mode_iaf);
Herbert Xu13996372007-10-17 21:35:51 -0700394 if (x->outer_mode)
395 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 if (x->type) {
397 x->type->destructor(x);
398 xfrm_put_type(x->type);
399 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800400 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 kfree(x);
402}
403
David Howellsc4028952006-11-22 14:57:56 +0000404static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
406 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700407 struct hlist_node *entry, *tmp;
408 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700411 gc_list.first = xfrm_state_gc_list.first;
412 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 spin_unlock_bh(&xfrm_state_gc_lock);
414
David S. Miller8f126e32006-08-24 02:45:07 -0700415 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 wake_up(&km_waitq);
419}
420
421static inline unsigned long make_jiffies(long secs)
422{
423 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
424 return MAX_SCHEDULE_TIMEOUT-1;
425 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900426 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427}
428
429static void xfrm_timer_handler(unsigned long data)
430{
431 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800432 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 long next = LONG_MAX;
434 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600435 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 spin_lock(&x->lock);
438 if (x->km.state == XFRM_STATE_DEAD)
439 goto out;
440 if (x->km.state == XFRM_STATE_EXPIRED)
441 goto expired;
442 if (x->lft.hard_add_expires_seconds) {
443 long tmo = x->lft.hard_add_expires_seconds +
444 x->curlft.add_time - now;
445 if (tmo <= 0)
446 goto expired;
447 if (tmo < next)
448 next = tmo;
449 }
450 if (x->lft.hard_use_expires_seconds) {
451 long tmo = x->lft.hard_use_expires_seconds +
452 (x->curlft.use_time ? : now) - now;
453 if (tmo <= 0)
454 goto expired;
455 if (tmo < next)
456 next = tmo;
457 }
458 if (x->km.dying)
459 goto resched;
460 if (x->lft.soft_add_expires_seconds) {
461 long tmo = x->lft.soft_add_expires_seconds +
462 x->curlft.add_time - now;
463 if (tmo <= 0)
464 warn = 1;
465 else if (tmo < next)
466 next = tmo;
467 }
468 if (x->lft.soft_use_expires_seconds) {
469 long tmo = x->lft.soft_use_expires_seconds +
470 (x->curlft.use_time ? : now) - now;
471 if (tmo <= 0)
472 warn = 1;
473 else if (tmo < next)
474 next = tmo;
475 }
476
Herbert Xu4666faa2005-06-18 22:43:22 -0700477 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800479 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700481 if (next != LONG_MAX)
482 mod_timer(&x->timer, jiffies + make_jiffies(next));
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 goto out;
485
486expired:
487 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
488 x->km.state = XFRM_STATE_EXPIRED;
489 wake_up(&km_waitq);
490 next = 2;
491 goto resched;
492 }
Joy Latten161a09e2006-11-27 13:11:54 -0600493
494 err = __xfrm_state_delete(x);
495 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800496 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Joy Lattenab5f5e82007-09-17 11:51:22 -0700498 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400499 audit_get_loginuid(current),
500 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600501
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502out:
503 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504}
505
David S. Miller0ac84752006-03-20 19:18:23 -0800506static void xfrm_replay_timer_handler(unsigned long data);
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508struct xfrm_state *xfrm_state_alloc(void)
509{
510 struct xfrm_state *x;
511
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700512 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
514 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 atomic_set(&x->refcnt, 1);
516 atomic_set(&x->tunnel_users, 0);
Timo Teras4c563f72008-02-28 21:31:08 -0800517 INIT_LIST_HEAD(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700518 INIT_HLIST_NODE(&x->bydst);
519 INIT_HLIST_NODE(&x->bysrc);
520 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800521 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
522 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
523 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800524 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 x->lft.soft_byte_limit = XFRM_INF;
526 x->lft.soft_packet_limit = XFRM_INF;
527 x->lft.hard_byte_limit = XFRM_INF;
528 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800529 x->replay_maxage = 0;
530 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700531 x->inner_mode = NULL;
532 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 spin_lock_init(&x->lock);
534 }
535 return x;
536}
537EXPORT_SYMBOL(xfrm_state_alloc);
538
539void __xfrm_state_destroy(struct xfrm_state *x)
540{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700541 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Timo Teras4c563f72008-02-28 21:31:08 -0800543 spin_lock_bh(&xfrm_state_lock);
544 list_del(&x->all);
545 spin_unlock_bh(&xfrm_state_lock);
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700548 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 spin_unlock_bh(&xfrm_state_gc_lock);
550 schedule_work(&xfrm_state_gc_work);
551}
552EXPORT_SYMBOL(__xfrm_state_destroy);
553
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800554int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700556 int err = -ESRCH;
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if (x->km.state != XFRM_STATE_DEAD) {
559 x->km.state = XFRM_STATE_DEAD;
560 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700561 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700562 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700563 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700564 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700565 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 /* All xfrm_state objects are created by xfrm_state_alloc.
569 * The xfrm_state_alloc call gives a reference, and that
570 * is what we are dropping here.
571 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800572 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700573 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700575
576 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800578EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700580int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700582 int err;
583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700585 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700587
588 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590EXPORT_SYMBOL(xfrm_state_delete);
591
Joy Latten4aa2e622007-06-04 19:05:57 -0400592#ifdef CONFIG_SECURITY_NETWORK_XFRM
593static inline int
594xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595{
Joy Latten4aa2e622007-06-04 19:05:57 -0400596 int i, err = 0;
597
598 for (i = 0; i <= xfrm_state_hmask; i++) {
599 struct hlist_node *entry;
600 struct xfrm_state *x;
601
602 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
603 if (xfrm_id_proto_match(x->id.proto, proto) &&
604 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700605 xfrm_audit_state_delete(x, 0,
606 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400607 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700608 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400609 return err;
610 }
611 }
612 }
613
614 return err;
615}
616#else
617static inline int
618xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
619{
620 return 0;
621}
622#endif
623
624int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
625{
626 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400629 err = xfrm_state_flush_secctx_check(proto, audit_info);
630 if (err)
631 goto out;
632
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700633 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700634 struct hlist_node *entry;
635 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700637 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700639 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 xfrm_state_hold(x);
641 spin_unlock_bh(&xfrm_state_lock);
642
Joy Latten161a09e2006-11-27 13:11:54 -0600643 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700644 xfrm_audit_state_delete(x, err ? 0 : 1,
645 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400646 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700647 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 xfrm_state_put(x);
649
650 spin_lock_bh(&xfrm_state_lock);
651 goto restart;
652 }
653 }
654 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400655 err = 0;
656
657out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 spin_unlock_bh(&xfrm_state_lock);
659 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400660 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661}
662EXPORT_SYMBOL(xfrm_state_flush);
663
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700664void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700665{
666 spin_lock_bh(&xfrm_state_lock);
667 si->sadcnt = xfrm_state_num;
668 si->sadhcnt = xfrm_state_hmask;
669 si->sadhmcnt = xfrm_state_hashmax;
670 spin_unlock_bh(&xfrm_state_lock);
671}
672EXPORT_SYMBOL(xfrm_sad_getinfo);
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674static int
675xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
676 struct xfrm_tmpl *tmpl,
677 xfrm_address_t *daddr, xfrm_address_t *saddr,
678 unsigned short family)
679{
680 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
681 if (!afinfo)
682 return -1;
683 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
684 xfrm_state_put_afinfo(afinfo);
685 return 0;
686}
687
Al Viroa94cfd12006-09-27 18:47:24 -0700688static 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 -0700689{
690 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
691 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700692 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700693
David S. Miller8f126e32006-08-24 02:45:07 -0700694 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700695 if (x->props.family != family ||
696 x->id.spi != spi ||
697 x->id.proto != proto)
698 continue;
699
700 switch (family) {
701 case AF_INET:
702 if (x->id.daddr.a4 != daddr->a4)
703 continue;
704 break;
705 case AF_INET6:
706 if (!ipv6_addr_equal((struct in6_addr *)daddr,
707 (struct in6_addr *)
708 x->id.daddr.a6))
709 continue;
710 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700711 }
David S. Milleredcd5822006-08-24 00:42:45 -0700712
713 xfrm_state_hold(x);
714 return x;
715 }
716
717 return NULL;
718}
719
720static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
721{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700722 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700723 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700724 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700725
David S. Miller8f126e32006-08-24 02:45:07 -0700726 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700727 if (x->props.family != family ||
728 x->id.proto != proto)
729 continue;
730
731 switch (family) {
732 case AF_INET:
733 if (x->id.daddr.a4 != daddr->a4 ||
734 x->props.saddr.a4 != saddr->a4)
735 continue;
736 break;
737 case AF_INET6:
738 if (!ipv6_addr_equal((struct in6_addr *)daddr,
739 (struct in6_addr *)
740 x->id.daddr.a6) ||
741 !ipv6_addr_equal((struct in6_addr *)saddr,
742 (struct in6_addr *)
743 x->props.saddr.a6))
744 continue;
745 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700746 }
David S. Milleredcd5822006-08-24 00:42:45 -0700747
748 xfrm_state_hold(x);
749 return x;
750 }
751
752 return NULL;
753}
754
755static inline struct xfrm_state *
756__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
757{
758 if (use_spi)
759 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
760 x->id.proto, family);
761 else
762 return __xfrm_state_lookup_byaddr(&x->id.daddr,
763 &x->props.saddr,
764 x->id.proto, family);
765}
766
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700767static void xfrm_hash_grow_check(int have_hash_collision)
768{
769 if (have_hash_collision &&
770 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
771 xfrm_state_num > xfrm_state_hmask)
772 schedule_work(&xfrm_hash_work);
773}
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900776xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 struct flowi *fl, struct xfrm_tmpl *tmpl,
778 struct xfrm_policy *pol, int *err,
779 unsigned short family)
780{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800781 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700782 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 struct xfrm_state *x, *x0;
784 int acquire_in_progress = 0;
785 int error = 0;
786 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900787
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800789 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700790 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if (x->props.family == family &&
792 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700793 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 xfrm_state_addr_check(x, daddr, saddr, family) &&
795 tmpl->mode == x->props.mode &&
796 tmpl->id.proto == x->id.proto &&
797 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
798 /* Resolution logic:
799 1. There is a valid state with matching selector.
800 Done.
801 2. Valid state with inappropriate selector. Skip.
802
803 Entering area of "sysdeps".
804
805 3. If state is not valid, selector is temporary,
806 it selects only session which triggered
807 previous resolution. Key manager will do
808 something to install a state with proper
809 selector.
810 */
811 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700812 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700813 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 continue;
815 if (!best ||
816 best->km.dying > x->km.dying ||
817 (best->km.dying == x->km.dying &&
818 best->curlft.add_time < x->curlft.add_time))
819 best = x;
820 } else if (x->km.state == XFRM_STATE_ACQ) {
821 acquire_in_progress = 1;
822 } else if (x->km.state == XFRM_STATE_ERROR ||
823 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700824 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700825 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 error = -ESRCH;
827 }
828 }
829 }
830
831 x = best;
832 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700833 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700834 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
835 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 xfrm_state_put(x0);
837 error = -EEXIST;
838 goto out;
839 }
840 x = xfrm_state_alloc();
841 if (x == NULL) {
842 error = -ENOMEM;
843 goto out;
844 }
845 /* Initialize temporary selector matching only
846 * to current session. */
847 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
848
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700849 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
850 if (error) {
851 x->km.state = XFRM_STATE_DEAD;
852 xfrm_state_put(x);
853 x = NULL;
854 goto out;
855 }
856
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 if (km_query(x, tmpl, pol) == 0) {
858 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700859 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700860 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700861 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 if (x->id.spi) {
863 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700864 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 }
David S. Miller01e67d02007-05-25 00:41:38 -0700866 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
867 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700869 xfrm_state_num++;
870 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 } else {
872 x->km.state = XFRM_STATE_DEAD;
873 xfrm_state_put(x);
874 x = NULL;
875 error = -ESRCH;
876 }
877 }
878out:
879 if (x)
880 xfrm_state_hold(x);
881 else
882 *err = acquire_in_progress ? -EAGAIN : error;
883 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 return x;
885}
886
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700887struct xfrm_state *
888xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
889 unsigned short family, u8 mode, u8 proto, u32 reqid)
890{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800891 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700892 struct xfrm_state *rx = NULL, *x = NULL;
893 struct hlist_node *entry;
894
895 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800896 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700897 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
898 if (x->props.family == family &&
899 x->props.reqid == reqid &&
900 !(x->props.flags & XFRM_STATE_WILDRECV) &&
901 xfrm_state_addr_check(x, daddr, saddr, family) &&
902 mode == x->props.mode &&
903 proto == x->id.proto &&
904 x->km.state == XFRM_STATE_VALID) {
905 rx = x;
906 break;
907 }
908 }
909
910 if (rx)
911 xfrm_state_hold(rx);
912 spin_unlock(&xfrm_state_lock);
913
914
915 return rx;
916}
917EXPORT_SYMBOL(xfrm_stateonly_find);
918
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919static void __xfrm_state_insert(struct xfrm_state *x)
920{
David S. Millera624c102006-08-24 03:24:33 -0700921 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
David S. Miller9d4a7062006-08-24 03:18:09 -0700923 x->genid = ++xfrm_state_genid;
924
Timo Teras4c563f72008-02-28 21:31:08 -0800925 list_add_tail(&x->all, &xfrm_state_all);
926
David S. Millerc1969f22006-08-24 04:00:03 -0700927 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
928 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700929 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700931 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700932 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700934 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700935 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
936 x->props.family);
937
David S. Miller8f126e32006-08-24 02:45:07 -0700938 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700939 }
940
David S. Millera47f0ce2006-08-24 03:54:22 -0700941 mod_timer(&x->timer, jiffies + HZ);
942 if (x->replay_maxage)
943 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700946
947 xfrm_state_num++;
948
David S. Miller918049f2006-10-12 22:03:24 -0700949 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950}
951
David S. Millerc7f5ea32006-08-24 03:29:04 -0700952/* xfrm_state_lock is held */
953static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
954{
955 unsigned short family = xnew->props.family;
956 u32 reqid = xnew->props.reqid;
957 struct xfrm_state *x;
958 struct hlist_node *entry;
959 unsigned int h;
960
David S. Millerc1969f22006-08-24 04:00:03 -0700961 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700962 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
963 if (x->props.family == family &&
964 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700965 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
966 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700967 x->genid = xfrm_state_genid;
968 }
969}
970
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971void xfrm_state_insert(struct xfrm_state *x)
972{
973 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700974 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 __xfrm_state_insert(x);
976 spin_unlock_bh(&xfrm_state_lock);
977}
978EXPORT_SYMBOL(xfrm_state_insert);
979
David S. Miller27708342006-08-24 00:13:10 -0700980/* xfrm_state_lock is held */
981static 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)
982{
David S. Millerc1969f22006-08-24 04:00:03 -0700983 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700984 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700985 struct xfrm_state *x;
986
David S. Miller8f126e32006-08-24 02:45:07 -0700987 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700988 if (x->props.reqid != reqid ||
989 x->props.mode != mode ||
990 x->props.family != family ||
991 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700992 x->id.spi != 0 ||
993 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700994 continue;
995
996 switch (family) {
997 case AF_INET:
998 if (x->id.daddr.a4 != daddr->a4 ||
999 x->props.saddr.a4 != saddr->a4)
1000 continue;
1001 break;
1002 case AF_INET6:
1003 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
1004 (struct in6_addr *)daddr) ||
1005 !ipv6_addr_equal((struct in6_addr *)
1006 x->props.saddr.a6,
1007 (struct in6_addr *)saddr))
1008 continue;
1009 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001010 }
David S. Miller27708342006-08-24 00:13:10 -07001011
1012 xfrm_state_hold(x);
1013 return x;
1014 }
1015
1016 if (!create)
1017 return NULL;
1018
1019 x = xfrm_state_alloc();
1020 if (likely(x)) {
1021 switch (family) {
1022 case AF_INET:
1023 x->sel.daddr.a4 = daddr->a4;
1024 x->sel.saddr.a4 = saddr->a4;
1025 x->sel.prefixlen_d = 32;
1026 x->sel.prefixlen_s = 32;
1027 x->props.saddr.a4 = saddr->a4;
1028 x->id.daddr.a4 = daddr->a4;
1029 break;
1030
1031 case AF_INET6:
1032 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1033 (struct in6_addr *)daddr);
1034 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1035 (struct in6_addr *)saddr);
1036 x->sel.prefixlen_d = 128;
1037 x->sel.prefixlen_s = 128;
1038 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1039 (struct in6_addr *)saddr);
1040 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1041 (struct in6_addr *)daddr);
1042 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001043 }
David S. Miller27708342006-08-24 00:13:10 -07001044
1045 x->km.state = XFRM_STATE_ACQ;
1046 x->id.proto = proto;
1047 x->props.family = family;
1048 x->props.mode = mode;
1049 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001050 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001051 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001052 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001053 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001054 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001055 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001056 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001057
1058 xfrm_state_num++;
1059
1060 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001061 }
1062
1063 return x;
1064}
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1067
1068int xfrm_state_add(struct xfrm_state *x)
1069{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 struct xfrm_state *x1;
1071 int family;
1072 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001073 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 spin_lock_bh(&xfrm_state_lock);
1078
David S. Milleredcd5822006-08-24 00:42:45 -07001079 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 if (x1) {
1081 xfrm_state_put(x1);
1082 x1 = NULL;
1083 err = -EEXIST;
1084 goto out;
1085 }
1086
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001087 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001089 if (x1 && ((x1->id.proto != x->id.proto) ||
1090 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 xfrm_state_put(x1);
1092 x1 = NULL;
1093 }
1094 }
1095
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001096 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001097 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1098 x->id.proto,
1099 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
David S. Millerc7f5ea32006-08-24 03:29:04 -07001101 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 __xfrm_state_insert(x);
1103 err = 0;
1104
1105out:
1106 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107
1108 if (x1) {
1109 xfrm_state_delete(x1);
1110 xfrm_state_put(x1);
1111 }
1112
1113 return err;
1114}
1115EXPORT_SYMBOL(xfrm_state_add);
1116
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001117#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001118static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001119{
1120 int err = -ENOMEM;
1121 struct xfrm_state *x = xfrm_state_alloc();
1122 if (!x)
1123 goto error;
1124
1125 memcpy(&x->id, &orig->id, sizeof(x->id));
1126 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1127 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1128 x->props.mode = orig->props.mode;
1129 x->props.replay_window = orig->props.replay_window;
1130 x->props.reqid = orig->props.reqid;
1131 x->props.family = orig->props.family;
1132 x->props.saddr = orig->props.saddr;
1133
1134 if (orig->aalg) {
1135 x->aalg = xfrm_algo_clone(orig->aalg);
1136 if (!x->aalg)
1137 goto error;
1138 }
1139 x->props.aalgo = orig->props.aalgo;
1140
1141 if (orig->ealg) {
1142 x->ealg = xfrm_algo_clone(orig->ealg);
1143 if (!x->ealg)
1144 goto error;
1145 }
1146 x->props.ealgo = orig->props.ealgo;
1147
1148 if (orig->calg) {
1149 x->calg = xfrm_algo_clone(orig->calg);
1150 if (!x->calg)
1151 goto error;
1152 }
1153 x->props.calgo = orig->props.calgo;
1154
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001155 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001156 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1157 if (!x->encap)
1158 goto error;
1159 }
1160
1161 if (orig->coaddr) {
1162 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1163 GFP_KERNEL);
1164 if (!x->coaddr)
1165 goto error;
1166 }
1167
1168 err = xfrm_init_state(x);
1169 if (err)
1170 goto error;
1171
1172 x->props.flags = orig->props.flags;
1173
1174 x->curlft.add_time = orig->curlft.add_time;
1175 x->km.state = orig->km.state;
1176 x->km.seq = orig->km.seq;
1177
1178 return x;
1179
1180 error:
1181 if (errp)
1182 *errp = err;
1183 if (x) {
1184 kfree(x->aalg);
1185 kfree(x->ealg);
1186 kfree(x->calg);
1187 kfree(x->encap);
1188 kfree(x->coaddr);
1189 }
1190 kfree(x);
1191 return NULL;
1192}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001193
1194/* xfrm_state_lock is held */
1195struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1196{
1197 unsigned int h;
1198 struct xfrm_state *x;
1199 struct hlist_node *entry;
1200
1201 if (m->reqid) {
1202 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1203 m->reqid, m->old_family);
1204 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1205 if (x->props.mode != m->mode ||
1206 x->id.proto != m->proto)
1207 continue;
1208 if (m->reqid && x->props.reqid != m->reqid)
1209 continue;
1210 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1211 m->old_family) ||
1212 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1213 m->old_family))
1214 continue;
1215 xfrm_state_hold(x);
1216 return x;
1217 }
1218 } else {
1219 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1220 m->old_family);
1221 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1222 if (x->props.mode != m->mode ||
1223 x->id.proto != m->proto)
1224 continue;
1225 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1226 m->old_family) ||
1227 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1228 m->old_family))
1229 continue;
1230 xfrm_state_hold(x);
1231 return x;
1232 }
1233 }
1234
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001235 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001236}
1237EXPORT_SYMBOL(xfrm_migrate_state_find);
1238
1239struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1240 struct xfrm_migrate *m)
1241{
1242 struct xfrm_state *xc;
1243 int err;
1244
1245 xc = xfrm_state_clone(x, &err);
1246 if (!xc)
1247 return NULL;
1248
1249 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1250 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1251
1252 /* add state */
1253 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1254 /* a care is needed when the destination address of the
1255 state is to be updated as it is a part of triplet */
1256 xfrm_state_insert(xc);
1257 } else {
1258 if ((err = xfrm_state_add(xc)) < 0)
1259 goto error;
1260 }
1261
1262 return xc;
1263error:
1264 kfree(xc);
1265 return NULL;
1266}
1267EXPORT_SYMBOL(xfrm_state_migrate);
1268#endif
1269
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270int xfrm_state_update(struct xfrm_state *x)
1271{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 struct xfrm_state *x1;
1273 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001274 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001277 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
1279 err = -ESRCH;
1280 if (!x1)
1281 goto out;
1282
1283 if (xfrm_state_kern(x1)) {
1284 xfrm_state_put(x1);
1285 err = -EEXIST;
1286 goto out;
1287 }
1288
1289 if (x1->km.state == XFRM_STATE_ACQ) {
1290 __xfrm_state_insert(x);
1291 x = NULL;
1292 }
1293 err = 0;
1294
1295out:
1296 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
1298 if (err)
1299 return err;
1300
1301 if (!x) {
1302 xfrm_state_delete(x1);
1303 xfrm_state_put(x1);
1304 return 0;
1305 }
1306
1307 err = -EINVAL;
1308 spin_lock_bh(&x1->lock);
1309 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1310 if (x->encap && x1->encap)
1311 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001312 if (x->coaddr && x1->coaddr) {
1313 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1314 }
1315 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1316 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1318 x1->km.dying = 0;
1319
David S. Millera47f0ce2006-08-24 03:54:22 -07001320 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 if (x1->curlft.use_time)
1322 xfrm_state_check_expire(x1);
1323
1324 err = 0;
1325 }
1326 spin_unlock_bh(&x1->lock);
1327
1328 xfrm_state_put(x1);
1329
1330 return err;
1331}
1332EXPORT_SYMBOL(xfrm_state_update);
1333
1334int xfrm_state_check_expire(struct xfrm_state *x)
1335{
1336 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001337 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
1339 if (x->km.state != XFRM_STATE_VALID)
1340 return -EINVAL;
1341
1342 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1343 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001344 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001345 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 return -EINVAL;
1347 }
1348
1349 if (!x->km.dying &&
1350 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001351 x->curlft.packets >= x->lft.soft_packet_limit)) {
1352 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001353 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 return 0;
1356}
1357EXPORT_SYMBOL(xfrm_state_check_expire);
1358
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001360xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 unsigned short family)
1362{
1363 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001366 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 return x;
1369}
1370EXPORT_SYMBOL(xfrm_state_lookup);
1371
1372struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001373xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1374 u8 proto, unsigned short family)
1375{
1376 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001377
1378 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001379 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001380 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001381 return x;
1382}
1383EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1384
1385struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001386xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1387 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 int create, unsigned short family)
1389{
1390 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
1392 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001393 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001395
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 return x;
1397}
1398EXPORT_SYMBOL(xfrm_find_acq);
1399
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001400#ifdef CONFIG_XFRM_SUB_POLICY
1401int
1402xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1403 unsigned short family)
1404{
1405 int err = 0;
1406 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1407 if (!afinfo)
1408 return -EAFNOSUPPORT;
1409
1410 spin_lock_bh(&xfrm_state_lock);
1411 if (afinfo->tmpl_sort)
1412 err = afinfo->tmpl_sort(dst, src, n);
1413 spin_unlock_bh(&xfrm_state_lock);
1414 xfrm_state_put_afinfo(afinfo);
1415 return err;
1416}
1417EXPORT_SYMBOL(xfrm_tmpl_sort);
1418
1419int
1420xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1421 unsigned short family)
1422{
1423 int err = 0;
1424 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1425 if (!afinfo)
1426 return -EAFNOSUPPORT;
1427
1428 spin_lock_bh(&xfrm_state_lock);
1429 if (afinfo->state_sort)
1430 err = afinfo->state_sort(dst, src, n);
1431 spin_unlock_bh(&xfrm_state_lock);
1432 xfrm_state_put_afinfo(afinfo);
1433 return err;
1434}
1435EXPORT_SYMBOL(xfrm_state_sort);
1436#endif
1437
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438/* Silly enough, but I'm lazy to build resolution list */
1439
1440static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1441{
1442 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443
David S. Millerf034b5d2006-08-24 03:08:07 -07001444 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001445 struct hlist_node *entry;
1446 struct xfrm_state *x;
1447
1448 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1449 if (x->km.seq == seq &&
1450 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 xfrm_state_hold(x);
1452 return x;
1453 }
1454 }
1455 }
1456 return NULL;
1457}
1458
1459struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1460{
1461 struct xfrm_state *x;
1462
1463 spin_lock_bh(&xfrm_state_lock);
1464 x = __xfrm_find_acq_byseq(seq);
1465 spin_unlock_bh(&xfrm_state_lock);
1466 return x;
1467}
1468EXPORT_SYMBOL(xfrm_find_acq_byseq);
1469
1470u32 xfrm_get_acqseq(void)
1471{
1472 u32 res;
1473 static u32 acqseq;
1474 static DEFINE_SPINLOCK(acqseq_lock);
1475
1476 spin_lock_bh(&acqseq_lock);
1477 res = (++acqseq ? : ++acqseq);
1478 spin_unlock_bh(&acqseq_lock);
1479 return res;
1480}
1481EXPORT_SYMBOL(xfrm_get_acqseq);
1482
Herbert Xu658b2192007-10-09 13:29:52 -07001483int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484{
David S. Millerf034b5d2006-08-24 03:08:07 -07001485 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001487 int err = -ENOENT;
1488 __be32 minspi = htonl(low);
1489 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
Herbert Xu658b2192007-10-09 13:29:52 -07001491 spin_lock_bh(&x->lock);
1492 if (x->km.state == XFRM_STATE_DEAD)
1493 goto unlock;
1494
1495 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001497 goto unlock;
1498
1499 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 if (minspi == maxspi) {
1502 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1503 if (x0) {
1504 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001505 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 }
1507 x->id.spi = minspi;
1508 } else {
1509 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001510 for (h=0; h<high-low+1; h++) {
1511 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1513 if (x0 == NULL) {
1514 x->id.spi = htonl(spi);
1515 break;
1516 }
1517 xfrm_state_put(x0);
1518 }
1519 }
1520 if (x->id.spi) {
1521 spin_lock_bh(&xfrm_state_lock);
1522 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001523 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001525
1526 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 }
Herbert Xu658b2192007-10-09 13:29:52 -07001528
1529unlock:
1530 spin_unlock_bh(&x->lock);
1531
1532 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533}
1534EXPORT_SYMBOL(xfrm_alloc_spi);
1535
Timo Teras4c563f72008-02-28 21:31:08 -08001536int xfrm_state_walk(struct xfrm_state_walk *walk,
1537 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 void *data)
1539{
Timo Teras4c563f72008-02-28 21:31:08 -08001540 struct xfrm_state *old, *x, *last = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 int err = 0;
1542
Timo Teras4c563f72008-02-28 21:31:08 -08001543 if (walk->state == NULL && walk->count != 0)
1544 return 0;
1545
1546 old = x = walk->state;
1547 walk->state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 spin_lock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001549 if (x == NULL)
1550 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1551 list_for_each_entry_from(x, &xfrm_state_all, all) {
1552 if (x->km.state == XFRM_STATE_DEAD)
1553 continue;
1554 if (!xfrm_id_proto_match(x->id.proto, walk->proto))
1555 continue;
1556 if (last) {
1557 err = func(last, walk->count, data);
1558 if (err) {
1559 xfrm_state_hold(last);
1560 walk->state = last;
1561 goto out;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001562 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 }
Timo Teras4c563f72008-02-28 21:31:08 -08001564 last = x;
1565 walk->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 }
Timo Teras4c563f72008-02-28 21:31:08 -08001567 if (walk->count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 err = -ENOENT;
1569 goto out;
1570 }
Timo Teras4c563f72008-02-28 21:31:08 -08001571 if (last)
1572 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573out:
1574 spin_unlock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001575 if (old != NULL)
1576 xfrm_state_put(old);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 return err;
1578}
1579EXPORT_SYMBOL(xfrm_state_walk);
1580
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001581
1582void xfrm_replay_notify(struct xfrm_state *x, int event)
1583{
1584 struct km_event c;
1585 /* we send notify messages in case
1586 * 1. we updated on of the sequence numbers, and the seqno difference
1587 * is at least x->replay_maxdiff, in this case we also update the
1588 * timeout of our timer function
1589 * 2. if x->replay_maxage has elapsed since last update,
1590 * and there were changes
1591 *
1592 * The state structure must be locked!
1593 */
1594
1595 switch (event) {
1596 case XFRM_REPLAY_UPDATE:
1597 if (x->replay_maxdiff &&
1598 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001599 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1600 if (x->xflags & XFRM_TIME_DEFER)
1601 event = XFRM_REPLAY_TIMEOUT;
1602 else
1603 return;
1604 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001605
1606 break;
1607
1608 case XFRM_REPLAY_TIMEOUT:
1609 if ((x->replay.seq == x->preplay.seq) &&
1610 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001611 (x->replay.oseq == x->preplay.oseq)) {
1612 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001613 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001614 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001615
1616 break;
1617 }
1618
1619 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1620 c.event = XFRM_MSG_NEWAE;
1621 c.data.aevent = event;
1622 km_state_notify(x, &c);
1623
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001624 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001625 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001626 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001627}
1628
1629static void xfrm_replay_timer_handler(unsigned long data)
1630{
1631 struct xfrm_state *x = (struct xfrm_state*)data;
1632
1633 spin_lock(&x->lock);
1634
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001635 if (x->km.state == XFRM_STATE_VALID) {
1636 if (xfrm_aevent_is_on())
1637 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1638 else
1639 x->xflags |= XFRM_TIME_DEFER;
1640 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001641
1642 spin_unlock(&x->lock);
1643}
1644
Paul Mooreafeb14b2007-12-21 14:58:11 -08001645int xfrm_replay_check(struct xfrm_state *x,
1646 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647{
1648 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001649 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
1651 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001652 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 if (likely(seq > x->replay.seq))
1655 return 0;
1656
1657 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001658 if (diff >= min_t(unsigned int, x->props.replay_window,
1659 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001661 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 }
1663
1664 if (x->replay.bitmap & (1U << diff)) {
1665 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001666 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 }
1668 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001669
1670err:
1671 xfrm_audit_state_replay(x, skb, net_seq);
1672 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674
Al Viro61f46272006-09-27 18:48:33 -07001675void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676{
1677 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001678 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
1680 if (seq > x->replay.seq) {
1681 diff = seq - x->replay.seq;
1682 if (diff < x->props.replay_window)
1683 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1684 else
1685 x->replay.bitmap = 1;
1686 x->replay.seq = seq;
1687 } else {
1688 diff = x->replay.seq - seq;
1689 x->replay.bitmap |= (1U << diff);
1690 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001691
1692 if (xfrm_aevent_is_on())
1693 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
Denis Chengdf018122007-12-07 00:51:11 -08001696static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697static DEFINE_RWLOCK(xfrm_km_lock);
1698
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001699void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700{
1701 struct xfrm_mgr *km;
1702
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001703 read_lock(&xfrm_km_lock);
1704 list_for_each_entry(km, &xfrm_km_list, list)
1705 if (km->notify_policy)
1706 km->notify_policy(xp, dir, c);
1707 read_unlock(&xfrm_km_lock);
1708}
1709
1710void km_state_notify(struct xfrm_state *x, struct km_event *c)
1711{
1712 struct xfrm_mgr *km;
1713 read_lock(&xfrm_km_lock);
1714 list_for_each_entry(km, &xfrm_km_list, list)
1715 if (km->notify)
1716 km->notify(x, c);
1717 read_unlock(&xfrm_km_lock);
1718}
1719
1720EXPORT_SYMBOL(km_policy_notify);
1721EXPORT_SYMBOL(km_state_notify);
1722
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001723void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001724{
1725 struct km_event c;
1726
Herbert Xubf088672005-06-18 22:44:00 -07001727 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001728 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001729 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001730 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731
1732 if (hard)
1733 wake_up(&km_waitq);
1734}
1735
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001736EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001737/*
1738 * We send to all registered managers regardless of failure
1739 * We are happy with one success
1740*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001741int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001743 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 struct xfrm_mgr *km;
1745
1746 read_lock(&xfrm_km_lock);
1747 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001748 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1749 if (!acqret)
1750 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 }
1752 read_unlock(&xfrm_km_lock);
1753 return err;
1754}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001755EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756
Al Viro5d36b182006-11-08 00:24:06 -08001757int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758{
1759 int err = -EINVAL;
1760 struct xfrm_mgr *km;
1761
1762 read_lock(&xfrm_km_lock);
1763 list_for_each_entry(km, &xfrm_km_list, list) {
1764 if (km->new_mapping)
1765 err = km->new_mapping(x, ipaddr, sport);
1766 if (!err)
1767 break;
1768 }
1769 read_unlock(&xfrm_km_lock);
1770 return err;
1771}
1772EXPORT_SYMBOL(km_new_mapping);
1773
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001774void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001776 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Herbert Xubf088672005-06-18 22:44:00 -07001778 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001779 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001780 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001781 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782
1783 if (hard)
1784 wake_up(&km_waitq);
1785}
David S. Millera70fcb02006-03-20 19:18:52 -08001786EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001788#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001789int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1790 struct xfrm_migrate *m, int num_migrate)
1791{
1792 int err = -EINVAL;
1793 int ret;
1794 struct xfrm_mgr *km;
1795
1796 read_lock(&xfrm_km_lock);
1797 list_for_each_entry(km, &xfrm_km_list, list) {
1798 if (km->migrate) {
1799 ret = km->migrate(sel, dir, type, m, num_migrate);
1800 if (!ret)
1801 err = ret;
1802 }
1803 }
1804 read_unlock(&xfrm_km_lock);
1805 return err;
1806}
1807EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001808#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001809
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001810int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1811{
1812 int err = -EINVAL;
1813 int ret;
1814 struct xfrm_mgr *km;
1815
1816 read_lock(&xfrm_km_lock);
1817 list_for_each_entry(km, &xfrm_km_list, list) {
1818 if (km->report) {
1819 ret = km->report(proto, sel, addr);
1820 if (!ret)
1821 err = ret;
1822 }
1823 }
1824 read_unlock(&xfrm_km_lock);
1825 return err;
1826}
1827EXPORT_SYMBOL(km_report);
1828
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1830{
1831 int err;
1832 u8 *data;
1833 struct xfrm_mgr *km;
1834 struct xfrm_policy *pol = NULL;
1835
1836 if (optlen <= 0 || optlen > PAGE_SIZE)
1837 return -EMSGSIZE;
1838
1839 data = kmalloc(optlen, GFP_KERNEL);
1840 if (!data)
1841 return -ENOMEM;
1842
1843 err = -EFAULT;
1844 if (copy_from_user(data, optval, optlen))
1845 goto out;
1846
1847 err = -EINVAL;
1848 read_lock(&xfrm_km_lock);
1849 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001850 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 optlen, &err);
1852 if (err >= 0)
1853 break;
1854 }
1855 read_unlock(&xfrm_km_lock);
1856
1857 if (err >= 0) {
1858 xfrm_sk_policy_insert(sk, err, pol);
1859 xfrm_pol_put(pol);
1860 err = 0;
1861 }
1862
1863out:
1864 kfree(data);
1865 return err;
1866}
1867EXPORT_SYMBOL(xfrm_user_policy);
1868
1869int xfrm_register_km(struct xfrm_mgr *km)
1870{
1871 write_lock_bh(&xfrm_km_lock);
1872 list_add_tail(&km->list, &xfrm_km_list);
1873 write_unlock_bh(&xfrm_km_lock);
1874 return 0;
1875}
1876EXPORT_SYMBOL(xfrm_register_km);
1877
1878int xfrm_unregister_km(struct xfrm_mgr *km)
1879{
1880 write_lock_bh(&xfrm_km_lock);
1881 list_del(&km->list);
1882 write_unlock_bh(&xfrm_km_lock);
1883 return 0;
1884}
1885EXPORT_SYMBOL(xfrm_unregister_km);
1886
1887int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1888{
1889 int err = 0;
1890 if (unlikely(afinfo == NULL))
1891 return -EINVAL;
1892 if (unlikely(afinfo->family >= NPROTO))
1893 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001894 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1896 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001897 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001899 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 return err;
1901}
1902EXPORT_SYMBOL(xfrm_state_register_afinfo);
1903
1904int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1905{
1906 int err = 0;
1907 if (unlikely(afinfo == NULL))
1908 return -EINVAL;
1909 if (unlikely(afinfo->family >= NPROTO))
1910 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001911 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1913 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1914 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001915 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001918 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 return err;
1920}
1921EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1922
Herbert Xu17c2a422007-10-17 21:33:12 -07001923static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924{
1925 struct xfrm_state_afinfo *afinfo;
1926 if (unlikely(family >= NPROTO))
1927 return NULL;
1928 read_lock(&xfrm_state_afinfo_lock);
1929 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001930 if (unlikely(!afinfo))
1931 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 return afinfo;
1933}
1934
Herbert Xu17c2a422007-10-17 21:33:12 -07001935static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001936 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937{
Herbert Xu546be242006-05-27 23:03:58 -07001938 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939}
1940
1941/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1942void xfrm_state_delete_tunnel(struct xfrm_state *x)
1943{
1944 if (x->tunnel) {
1945 struct xfrm_state *t = x->tunnel;
1946
1947 if (atomic_read(&t->tunnel_users) == 2)
1948 xfrm_state_delete(t);
1949 atomic_dec(&t->tunnel_users);
1950 xfrm_state_put(t);
1951 x->tunnel = NULL;
1952 }
1953}
1954EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1955
1956int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1957{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001958 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
Patrick McHardyc5c25232007-04-09 11:47:18 -07001960 spin_lock_bh(&x->lock);
1961 if (x->km.state == XFRM_STATE_VALID &&
1962 x->type && x->type->get_mtu)
1963 res = x->type->get_mtu(x, mtu);
1964 else
Patrick McHardy28121612007-06-18 22:30:15 -07001965 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001966 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 return res;
1968}
1969
Herbert Xu72cb6962005-06-20 13:18:08 -07001970int xfrm_init_state(struct xfrm_state *x)
1971{
Herbert Xud094cd82005-06-20 13:19:41 -07001972 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001973 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07001974 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001975 int err;
1976
Herbert Xud094cd82005-06-20 13:19:41 -07001977 err = -EAFNOSUPPORT;
1978 afinfo = xfrm_state_get_afinfo(family);
1979 if (!afinfo)
1980 goto error;
1981
1982 err = 0;
1983 if (afinfo->init_flags)
1984 err = afinfo->init_flags(x);
1985
1986 xfrm_state_put_afinfo(afinfo);
1987
1988 if (err)
1989 goto error;
1990
1991 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001992
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07001993 if (x->sel.family != AF_UNSPEC) {
1994 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1995 if (inner_mode == NULL)
1996 goto error;
1997
1998 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1999 family != x->sel.family) {
2000 xfrm_put_mode(inner_mode);
2001 goto error;
2002 }
2003
2004 x->inner_mode = inner_mode;
2005 } else {
2006 struct xfrm_mode *inner_mode_iaf;
2007
2008 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2009 if (inner_mode == NULL)
2010 goto error;
2011
2012 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2013 xfrm_put_mode(inner_mode);
2014 goto error;
2015 }
2016
2017 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2018 if (inner_mode_iaf == NULL)
2019 goto error;
2020
2021 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2022 xfrm_put_mode(inner_mode_iaf);
2023 goto error;
2024 }
2025
2026 if (x->props.family == AF_INET) {
2027 x->inner_mode = inner_mode;
2028 x->inner_mode_iaf = inner_mode_iaf;
2029 } else {
2030 x->inner_mode = inner_mode_iaf;
2031 x->inner_mode_iaf = inner_mode;
2032 }
2033 }
Herbert Xu13996372007-10-17 21:35:51 -07002034
Herbert Xud094cd82005-06-20 13:19:41 -07002035 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002036 if (x->type == NULL)
2037 goto error;
2038
2039 err = x->type->init_state(x);
2040 if (err)
2041 goto error;
2042
Herbert Xu13996372007-10-17 21:35:51 -07002043 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2044 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002045 goto error;
2046
Herbert Xu72cb6962005-06-20 13:18:08 -07002047 x->km.state = XFRM_STATE_VALID;
2048
2049error:
2050 return err;
2051}
2052
2053EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002054
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055void __init xfrm_state_init(void)
2056{
David S. Millerf034b5d2006-08-24 03:08:07 -07002057 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058
David S. Millerf034b5d2006-08-24 03:08:07 -07002059 sz = sizeof(struct hlist_head) * 8;
2060
David S. Miller44e36b42006-08-24 04:50:50 -07002061 xfrm_state_bydst = xfrm_hash_alloc(sz);
2062 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2063 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002064 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2065 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2066 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2067
David Howellsc4028952006-11-22 14:57:56 +00002068 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069}
2070
Joy Lattenab5f5e82007-09-17 11:51:22 -07002071#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002072static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2073 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002074{
Paul Moore68277ac2007-12-20 20:49:33 -08002075 struct xfrm_sec_ctx *ctx = x->security;
2076 u32 spi = ntohl(x->id.spi);
2077
2078 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002079 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002080 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002081
2082 switch(x->props.family) {
2083 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002084 audit_log_format(audit_buf,
2085 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002086 NIPQUAD(x->props.saddr.a4),
2087 NIPQUAD(x->id.daddr.a4));
2088 break;
2089 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002090 audit_log_format(audit_buf,
2091 " src=" NIP6_FMT " dst=" NIP6_FMT,
2092 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2093 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002094 break;
2095 }
Paul Moore68277ac2007-12-20 20:49:33 -08002096
2097 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002098}
2099
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002100static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2101 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002102{
2103 struct iphdr *iph4;
2104 struct ipv6hdr *iph6;
2105
2106 switch (family) {
2107 case AF_INET:
2108 iph4 = ip_hdr(skb);
2109 audit_log_format(audit_buf,
2110 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2111 NIPQUAD(iph4->saddr),
2112 NIPQUAD(iph4->daddr));
2113 break;
2114 case AF_INET6:
2115 iph6 = ipv6_hdr(skb);
2116 audit_log_format(audit_buf,
2117 " src=" NIP6_FMT " dst=" NIP6_FMT
YOSHIFUJI Hideaki5e2c4332008-04-26 22:24:10 -07002118 " flowlbl=0x%x%02x%02x",
Paul Mooreafeb14b2007-12-21 14:58:11 -08002119 NIP6(iph6->saddr),
2120 NIP6(iph6->daddr),
2121 iph6->flow_lbl[0] & 0x0f,
2122 iph6->flow_lbl[1],
2123 iph6->flow_lbl[2]);
2124 break;
2125 }
2126}
2127
Paul Moore68277ac2007-12-20 20:49:33 -08002128void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002129 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002130{
2131 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002132
Paul Mooreafeb14b2007-12-21 14:58:11 -08002133 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002134 if (audit_buf == NULL)
2135 return;
Eric Paris25323862008-04-18 10:09:25 -04002136 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002137 xfrm_audit_helper_sainfo(x, audit_buf);
2138 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002139 audit_log_end(audit_buf);
2140}
2141EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2142
Paul Moore68277ac2007-12-20 20:49:33 -08002143void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002144 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002145{
2146 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002147
Paul Mooreafeb14b2007-12-21 14:58:11 -08002148 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002149 if (audit_buf == NULL)
2150 return;
Eric Paris25323862008-04-18 10:09:25 -04002151 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002152 xfrm_audit_helper_sainfo(x, audit_buf);
2153 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002154 audit_log_end(audit_buf);
2155}
2156EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002157
2158void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2159 struct sk_buff *skb)
2160{
2161 struct audit_buffer *audit_buf;
2162 u32 spi;
2163
2164 audit_buf = xfrm_audit_start("SA-replay-overflow");
2165 if (audit_buf == NULL)
2166 return;
2167 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2168 /* don't record the sequence number because it's inherent in this kind
2169 * of audit message */
2170 spi = ntohl(x->id.spi);
2171 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2172 audit_log_end(audit_buf);
2173}
2174EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2175
2176static void xfrm_audit_state_replay(struct xfrm_state *x,
2177 struct sk_buff *skb, __be32 net_seq)
2178{
2179 struct audit_buffer *audit_buf;
2180 u32 spi;
2181
2182 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2183 if (audit_buf == NULL)
2184 return;
2185 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2186 spi = ntohl(x->id.spi);
2187 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2188 spi, spi, ntohl(net_seq));
2189 audit_log_end(audit_buf);
2190}
2191
2192void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2193{
2194 struct audit_buffer *audit_buf;
2195
2196 audit_buf = xfrm_audit_start("SA-notfound");
2197 if (audit_buf == NULL)
2198 return;
2199 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2200 audit_log_end(audit_buf);
2201}
2202EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2203
2204void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2205 __be32 net_spi, __be32 net_seq)
2206{
2207 struct audit_buffer *audit_buf;
2208 u32 spi;
2209
2210 audit_buf = xfrm_audit_start("SA-notfound");
2211 if (audit_buf == NULL)
2212 return;
2213 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2214 spi = ntohl(net_spi);
2215 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2216 spi, spi, ntohl(net_seq));
2217 audit_log_end(audit_buf);
2218}
2219EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2220
2221void xfrm_audit_state_icvfail(struct xfrm_state *x,
2222 struct sk_buff *skb, u8 proto)
2223{
2224 struct audit_buffer *audit_buf;
2225 __be32 net_spi;
2226 __be32 net_seq;
2227
2228 audit_buf = xfrm_audit_start("SA-icv-failure");
2229 if (audit_buf == NULL)
2230 return;
2231 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2232 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2233 u32 spi = ntohl(net_spi);
2234 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2235 spi, spi, ntohl(net_seq));
2236 }
2237 audit_log_end(audit_buf);
2238}
2239EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002240#endif /* CONFIG_AUDITSYSCALL */