blob: 9880b792e6a52bbf4c4032f41468cf8b817c99b0 [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);
392 if (x->outer_mode)
393 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 if (x->type) {
395 x->type->destructor(x);
396 xfrm_put_type(x->type);
397 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800398 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 kfree(x);
400}
401
David Howellsc4028952006-11-22 14:57:56 +0000402static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
404 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700405 struct hlist_node *entry, *tmp;
406 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700409 gc_list.first = xfrm_state_gc_list.first;
410 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 spin_unlock_bh(&xfrm_state_gc_lock);
412
David S. Miller8f126e32006-08-24 02:45:07 -0700413 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 wake_up(&km_waitq);
417}
418
419static inline unsigned long make_jiffies(long secs)
420{
421 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
422 return MAX_SCHEDULE_TIMEOUT-1;
423 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900424 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
427static void xfrm_timer_handler(unsigned long data)
428{
429 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800430 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 long next = LONG_MAX;
432 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600433 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
435 spin_lock(&x->lock);
436 if (x->km.state == XFRM_STATE_DEAD)
437 goto out;
438 if (x->km.state == XFRM_STATE_EXPIRED)
439 goto expired;
440 if (x->lft.hard_add_expires_seconds) {
441 long tmo = x->lft.hard_add_expires_seconds +
442 x->curlft.add_time - now;
443 if (tmo <= 0)
444 goto expired;
445 if (tmo < next)
446 next = tmo;
447 }
448 if (x->lft.hard_use_expires_seconds) {
449 long tmo = x->lft.hard_use_expires_seconds +
450 (x->curlft.use_time ? : now) - now;
451 if (tmo <= 0)
452 goto expired;
453 if (tmo < next)
454 next = tmo;
455 }
456 if (x->km.dying)
457 goto resched;
458 if (x->lft.soft_add_expires_seconds) {
459 long tmo = x->lft.soft_add_expires_seconds +
460 x->curlft.add_time - now;
461 if (tmo <= 0)
462 warn = 1;
463 else if (tmo < next)
464 next = tmo;
465 }
466 if (x->lft.soft_use_expires_seconds) {
467 long tmo = x->lft.soft_use_expires_seconds +
468 (x->curlft.use_time ? : now) - now;
469 if (tmo <= 0)
470 warn = 1;
471 else if (tmo < next)
472 next = tmo;
473 }
474
Herbert Xu4666faa2005-06-18 22:43:22 -0700475 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800477 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700479 if (next != LONG_MAX)
480 mod_timer(&x->timer, jiffies + make_jiffies(next));
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 goto out;
483
484expired:
485 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
486 x->km.state = XFRM_STATE_EXPIRED;
487 wake_up(&km_waitq);
488 next = 2;
489 goto resched;
490 }
Joy Latten161a09e2006-11-27 13:11:54 -0600491
492 err = __xfrm_state_delete(x);
493 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800494 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Joy Lattenab5f5e82007-09-17 11:51:22 -0700496 xfrm_audit_state_delete(x, err ? 0 : 1,
Al Viro0c11b942008-01-10 04:20:52 -0500497 audit_get_loginuid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499out:
500 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
David S. Miller0ac84752006-03-20 19:18:23 -0800503static void xfrm_replay_timer_handler(unsigned long data);
504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505struct xfrm_state *xfrm_state_alloc(void)
506{
507 struct xfrm_state *x;
508
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700509 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 atomic_set(&x->refcnt, 1);
513 atomic_set(&x->tunnel_users, 0);
Timo Teras4c563f72008-02-28 21:31:08 -0800514 INIT_LIST_HEAD(&x->all);
David S. Miller8f126e32006-08-24 02:45:07 -0700515 INIT_HLIST_NODE(&x->bydst);
516 INIT_HLIST_NODE(&x->bysrc);
517 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800518 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
519 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
520 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800521 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 x->lft.soft_byte_limit = XFRM_INF;
523 x->lft.soft_packet_limit = XFRM_INF;
524 x->lft.hard_byte_limit = XFRM_INF;
525 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800526 x->replay_maxage = 0;
527 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 spin_lock_init(&x->lock);
529 }
530 return x;
531}
532EXPORT_SYMBOL(xfrm_state_alloc);
533
534void __xfrm_state_destroy(struct xfrm_state *x)
535{
536 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
537
Timo Teras4c563f72008-02-28 21:31:08 -0800538 spin_lock_bh(&xfrm_state_lock);
539 list_del(&x->all);
540 spin_unlock_bh(&xfrm_state_lock);
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700543 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 spin_unlock_bh(&xfrm_state_gc_lock);
545 schedule_work(&xfrm_state_gc_work);
546}
547EXPORT_SYMBOL(__xfrm_state_destroy);
548
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800549int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700551 int err = -ESRCH;
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 if (x->km.state != XFRM_STATE_DEAD) {
554 x->km.state = XFRM_STATE_DEAD;
555 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700556 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700557 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700558 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700559 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700560 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 /* All xfrm_state objects are created by xfrm_state_alloc.
564 * The xfrm_state_alloc call gives a reference, and that
565 * is what we are dropping here.
566 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800567 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700568 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700570
571 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800573EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700575int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700577 int err;
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700580 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700582
583 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}
585EXPORT_SYMBOL(xfrm_state_delete);
586
Joy Latten4aa2e622007-06-04 19:05:57 -0400587#ifdef CONFIG_SECURITY_NETWORK_XFRM
588static inline int
589xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
Joy Latten4aa2e622007-06-04 19:05:57 -0400591 int i, err = 0;
592
593 for (i = 0; i <= xfrm_state_hmask; i++) {
594 struct hlist_node *entry;
595 struct xfrm_state *x;
596
597 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
598 if (xfrm_id_proto_match(x->id.proto, proto) &&
599 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700600 xfrm_audit_state_delete(x, 0,
601 audit_info->loginuid,
602 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400603 return err;
604 }
605 }
606 }
607
608 return err;
609}
610#else
611static inline int
612xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
613{
614 return 0;
615}
616#endif
617
618int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
619{
620 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400623 err = xfrm_state_flush_secctx_check(proto, audit_info);
624 if (err)
625 goto out;
626
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700627 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700628 struct hlist_node *entry;
629 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700631 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700633 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 xfrm_state_hold(x);
635 spin_unlock_bh(&xfrm_state_lock);
636
Joy Latten161a09e2006-11-27 13:11:54 -0600637 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700638 xfrm_audit_state_delete(x, err ? 0 : 1,
639 audit_info->loginuid,
640 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 xfrm_state_put(x);
642
643 spin_lock_bh(&xfrm_state_lock);
644 goto restart;
645 }
646 }
647 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400648 err = 0;
649
650out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 spin_unlock_bh(&xfrm_state_lock);
652 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400653 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654}
655EXPORT_SYMBOL(xfrm_state_flush);
656
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700657void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700658{
659 spin_lock_bh(&xfrm_state_lock);
660 si->sadcnt = xfrm_state_num;
661 si->sadhcnt = xfrm_state_hmask;
662 si->sadhmcnt = xfrm_state_hashmax;
663 spin_unlock_bh(&xfrm_state_lock);
664}
665EXPORT_SYMBOL(xfrm_sad_getinfo);
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667static int
668xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
669 struct xfrm_tmpl *tmpl,
670 xfrm_address_t *daddr, xfrm_address_t *saddr,
671 unsigned short family)
672{
673 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
674 if (!afinfo)
675 return -1;
676 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
677 xfrm_state_put_afinfo(afinfo);
678 return 0;
679}
680
Al Viroa94cfd12006-09-27 18:47:24 -0700681static 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 -0700682{
683 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
684 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700685 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700686
David S. Miller8f126e32006-08-24 02:45:07 -0700687 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700688 if (x->props.family != family ||
689 x->id.spi != spi ||
690 x->id.proto != proto)
691 continue;
692
693 switch (family) {
694 case AF_INET:
695 if (x->id.daddr.a4 != daddr->a4)
696 continue;
697 break;
698 case AF_INET6:
699 if (!ipv6_addr_equal((struct in6_addr *)daddr,
700 (struct in6_addr *)
701 x->id.daddr.a6))
702 continue;
703 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700704 }
David S. Milleredcd5822006-08-24 00:42:45 -0700705
706 xfrm_state_hold(x);
707 return x;
708 }
709
710 return NULL;
711}
712
713static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
714{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700715 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700716 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700717 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700718
David S. Miller8f126e32006-08-24 02:45:07 -0700719 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700720 if (x->props.family != family ||
721 x->id.proto != proto)
722 continue;
723
724 switch (family) {
725 case AF_INET:
726 if (x->id.daddr.a4 != daddr->a4 ||
727 x->props.saddr.a4 != saddr->a4)
728 continue;
729 break;
730 case AF_INET6:
731 if (!ipv6_addr_equal((struct in6_addr *)daddr,
732 (struct in6_addr *)
733 x->id.daddr.a6) ||
734 !ipv6_addr_equal((struct in6_addr *)saddr,
735 (struct in6_addr *)
736 x->props.saddr.a6))
737 continue;
738 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700739 }
David S. Milleredcd5822006-08-24 00:42:45 -0700740
741 xfrm_state_hold(x);
742 return x;
743 }
744
745 return NULL;
746}
747
748static inline struct xfrm_state *
749__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
750{
751 if (use_spi)
752 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
753 x->id.proto, family);
754 else
755 return __xfrm_state_lookup_byaddr(&x->id.daddr,
756 &x->props.saddr,
757 x->id.proto, family);
758}
759
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700760static void xfrm_hash_grow_check(int have_hash_collision)
761{
762 if (have_hash_collision &&
763 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
764 xfrm_state_num > xfrm_state_hmask)
765 schedule_work(&xfrm_hash_work);
766}
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900769xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 struct flowi *fl, struct xfrm_tmpl *tmpl,
771 struct xfrm_policy *pol, int *err,
772 unsigned short family)
773{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800774 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700775 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 struct xfrm_state *x, *x0;
777 int acquire_in_progress = 0;
778 int error = 0;
779 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800782 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700783 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if (x->props.family == family &&
785 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700786 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 xfrm_state_addr_check(x, daddr, saddr, family) &&
788 tmpl->mode == x->props.mode &&
789 tmpl->id.proto == x->id.proto &&
790 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
791 /* Resolution logic:
792 1. There is a valid state with matching selector.
793 Done.
794 2. Valid state with inappropriate selector. Skip.
795
796 Entering area of "sysdeps".
797
798 3. If state is not valid, selector is temporary,
799 it selects only session which triggered
800 previous resolution. Key manager will do
801 something to install a state with proper
802 selector.
803 */
804 if (x->km.state == XFRM_STATE_VALID) {
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 continue;
808 if (!best ||
809 best->km.dying > x->km.dying ||
810 (best->km.dying == x->km.dying &&
811 best->curlft.add_time < x->curlft.add_time))
812 best = x;
813 } else if (x->km.state == XFRM_STATE_ACQ) {
814 acquire_in_progress = 1;
815 } else if (x->km.state == XFRM_STATE_ERROR ||
816 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700817 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700818 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 error = -ESRCH;
820 }
821 }
822 }
823
824 x = best;
825 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700826 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700827 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
828 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 xfrm_state_put(x0);
830 error = -EEXIST;
831 goto out;
832 }
833 x = xfrm_state_alloc();
834 if (x == NULL) {
835 error = -ENOMEM;
836 goto out;
837 }
838 /* Initialize temporary selector matching only
839 * to current session. */
840 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
841
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700842 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
843 if (error) {
844 x->km.state = XFRM_STATE_DEAD;
845 xfrm_state_put(x);
846 x = NULL;
847 goto out;
848 }
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (km_query(x, tmpl, pol) == 0) {
851 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700852 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700853 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700854 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (x->id.spi) {
856 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700857 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 }
David S. Miller01e67d02007-05-25 00:41:38 -0700859 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
860 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700862 xfrm_state_num++;
863 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 } else {
865 x->km.state = XFRM_STATE_DEAD;
866 xfrm_state_put(x);
867 x = NULL;
868 error = -ESRCH;
869 }
870 }
871out:
872 if (x)
873 xfrm_state_hold(x);
874 else
875 *err = acquire_in_progress ? -EAGAIN : error;
876 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 return x;
878}
879
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700880struct xfrm_state *
881xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
882 unsigned short family, u8 mode, u8 proto, u32 reqid)
883{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800884 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700885 struct xfrm_state *rx = NULL, *x = NULL;
886 struct hlist_node *entry;
887
888 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800889 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700890 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
891 if (x->props.family == family &&
892 x->props.reqid == reqid &&
893 !(x->props.flags & XFRM_STATE_WILDRECV) &&
894 xfrm_state_addr_check(x, daddr, saddr, family) &&
895 mode == x->props.mode &&
896 proto == x->id.proto &&
897 x->km.state == XFRM_STATE_VALID) {
898 rx = x;
899 break;
900 }
901 }
902
903 if (rx)
904 xfrm_state_hold(rx);
905 spin_unlock(&xfrm_state_lock);
906
907
908 return rx;
909}
910EXPORT_SYMBOL(xfrm_stateonly_find);
911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912static void __xfrm_state_insert(struct xfrm_state *x)
913{
David S. Millera624c102006-08-24 03:24:33 -0700914 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
David S. Miller9d4a7062006-08-24 03:18:09 -0700916 x->genid = ++xfrm_state_genid;
917
Timo Teras4c563f72008-02-28 21:31:08 -0800918 list_add_tail(&x->all, &xfrm_state_all);
919
David S. Millerc1969f22006-08-24 04:00:03 -0700920 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
921 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700922 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700924 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700925 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700927 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700928 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
929 x->props.family);
930
David S. Miller8f126e32006-08-24 02:45:07 -0700931 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700932 }
933
David S. Millera47f0ce2006-08-24 03:54:22 -0700934 mod_timer(&x->timer, jiffies + HZ);
935 if (x->replay_maxage)
936 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700939
940 xfrm_state_num++;
941
David S. Miller918049f2006-10-12 22:03:24 -0700942 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943}
944
David S. Millerc7f5ea32006-08-24 03:29:04 -0700945/* xfrm_state_lock is held */
946static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
947{
948 unsigned short family = xnew->props.family;
949 u32 reqid = xnew->props.reqid;
950 struct xfrm_state *x;
951 struct hlist_node *entry;
952 unsigned int h;
953
David S. Millerc1969f22006-08-24 04:00:03 -0700954 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700955 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
956 if (x->props.family == family &&
957 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700958 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
959 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700960 x->genid = xfrm_state_genid;
961 }
962}
963
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964void xfrm_state_insert(struct xfrm_state *x)
965{
966 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700967 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 __xfrm_state_insert(x);
969 spin_unlock_bh(&xfrm_state_lock);
970}
971EXPORT_SYMBOL(xfrm_state_insert);
972
David S. Miller27708342006-08-24 00:13:10 -0700973/* xfrm_state_lock is held */
974static 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)
975{
David S. Millerc1969f22006-08-24 04:00:03 -0700976 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700977 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700978 struct xfrm_state *x;
979
David S. Miller8f126e32006-08-24 02:45:07 -0700980 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700981 if (x->props.reqid != reqid ||
982 x->props.mode != mode ||
983 x->props.family != family ||
984 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700985 x->id.spi != 0 ||
986 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700987 continue;
988
989 switch (family) {
990 case AF_INET:
991 if (x->id.daddr.a4 != daddr->a4 ||
992 x->props.saddr.a4 != saddr->a4)
993 continue;
994 break;
995 case AF_INET6:
996 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
997 (struct in6_addr *)daddr) ||
998 !ipv6_addr_equal((struct in6_addr *)
999 x->props.saddr.a6,
1000 (struct in6_addr *)saddr))
1001 continue;
1002 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001003 }
David S. Miller27708342006-08-24 00:13:10 -07001004
1005 xfrm_state_hold(x);
1006 return x;
1007 }
1008
1009 if (!create)
1010 return NULL;
1011
1012 x = xfrm_state_alloc();
1013 if (likely(x)) {
1014 switch (family) {
1015 case AF_INET:
1016 x->sel.daddr.a4 = daddr->a4;
1017 x->sel.saddr.a4 = saddr->a4;
1018 x->sel.prefixlen_d = 32;
1019 x->sel.prefixlen_s = 32;
1020 x->props.saddr.a4 = saddr->a4;
1021 x->id.daddr.a4 = daddr->a4;
1022 break;
1023
1024 case AF_INET6:
1025 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1026 (struct in6_addr *)daddr);
1027 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1028 (struct in6_addr *)saddr);
1029 x->sel.prefixlen_d = 128;
1030 x->sel.prefixlen_s = 128;
1031 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1032 (struct in6_addr *)saddr);
1033 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1034 (struct in6_addr *)daddr);
1035 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001036 }
David S. Miller27708342006-08-24 00:13:10 -07001037
1038 x->km.state = XFRM_STATE_ACQ;
1039 x->id.proto = proto;
1040 x->props.family = family;
1041 x->props.mode = mode;
1042 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001043 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001044 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001045 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001046 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001047 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001048 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001049 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001050
1051 xfrm_state_num++;
1052
1053 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001054 }
1055
1056 return x;
1057}
1058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1060
1061int xfrm_state_add(struct xfrm_state *x)
1062{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 struct xfrm_state *x1;
1064 int family;
1065 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001066 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
1070 spin_lock_bh(&xfrm_state_lock);
1071
David S. Milleredcd5822006-08-24 00:42:45 -07001072 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (x1) {
1074 xfrm_state_put(x1);
1075 x1 = NULL;
1076 err = -EEXIST;
1077 goto out;
1078 }
1079
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001080 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001082 if (x1 && ((x1->id.proto != x->id.proto) ||
1083 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 xfrm_state_put(x1);
1085 x1 = NULL;
1086 }
1087 }
1088
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001089 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001090 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1091 x->id.proto,
1092 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
David S. Millerc7f5ea32006-08-24 03:29:04 -07001094 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 __xfrm_state_insert(x);
1096 err = 0;
1097
1098out:
1099 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
1101 if (x1) {
1102 xfrm_state_delete(x1);
1103 xfrm_state_put(x1);
1104 }
1105
1106 return err;
1107}
1108EXPORT_SYMBOL(xfrm_state_add);
1109
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001110#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001111static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001112{
1113 int err = -ENOMEM;
1114 struct xfrm_state *x = xfrm_state_alloc();
1115 if (!x)
1116 goto error;
1117
1118 memcpy(&x->id, &orig->id, sizeof(x->id));
1119 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1120 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1121 x->props.mode = orig->props.mode;
1122 x->props.replay_window = orig->props.replay_window;
1123 x->props.reqid = orig->props.reqid;
1124 x->props.family = orig->props.family;
1125 x->props.saddr = orig->props.saddr;
1126
1127 if (orig->aalg) {
1128 x->aalg = xfrm_algo_clone(orig->aalg);
1129 if (!x->aalg)
1130 goto error;
1131 }
1132 x->props.aalgo = orig->props.aalgo;
1133
1134 if (orig->ealg) {
1135 x->ealg = xfrm_algo_clone(orig->ealg);
1136 if (!x->ealg)
1137 goto error;
1138 }
1139 x->props.ealgo = orig->props.ealgo;
1140
1141 if (orig->calg) {
1142 x->calg = xfrm_algo_clone(orig->calg);
1143 if (!x->calg)
1144 goto error;
1145 }
1146 x->props.calgo = orig->props.calgo;
1147
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001148 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001149 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1150 if (!x->encap)
1151 goto error;
1152 }
1153
1154 if (orig->coaddr) {
1155 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1156 GFP_KERNEL);
1157 if (!x->coaddr)
1158 goto error;
1159 }
1160
1161 err = xfrm_init_state(x);
1162 if (err)
1163 goto error;
1164
1165 x->props.flags = orig->props.flags;
1166
1167 x->curlft.add_time = orig->curlft.add_time;
1168 x->km.state = orig->km.state;
1169 x->km.seq = orig->km.seq;
1170
1171 return x;
1172
1173 error:
1174 if (errp)
1175 *errp = err;
1176 if (x) {
1177 kfree(x->aalg);
1178 kfree(x->ealg);
1179 kfree(x->calg);
1180 kfree(x->encap);
1181 kfree(x->coaddr);
1182 }
1183 kfree(x);
1184 return NULL;
1185}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001186
1187/* xfrm_state_lock is held */
1188struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1189{
1190 unsigned int h;
1191 struct xfrm_state *x;
1192 struct hlist_node *entry;
1193
1194 if (m->reqid) {
1195 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1196 m->reqid, m->old_family);
1197 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1198 if (x->props.mode != m->mode ||
1199 x->id.proto != m->proto)
1200 continue;
1201 if (m->reqid && x->props.reqid != m->reqid)
1202 continue;
1203 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1204 m->old_family) ||
1205 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1206 m->old_family))
1207 continue;
1208 xfrm_state_hold(x);
1209 return x;
1210 }
1211 } else {
1212 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1213 m->old_family);
1214 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1215 if (x->props.mode != m->mode ||
1216 x->id.proto != m->proto)
1217 continue;
1218 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1219 m->old_family) ||
1220 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1221 m->old_family))
1222 continue;
1223 xfrm_state_hold(x);
1224 return x;
1225 }
1226 }
1227
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001228 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001229}
1230EXPORT_SYMBOL(xfrm_migrate_state_find);
1231
1232struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1233 struct xfrm_migrate *m)
1234{
1235 struct xfrm_state *xc;
1236 int err;
1237
1238 xc = xfrm_state_clone(x, &err);
1239 if (!xc)
1240 return NULL;
1241
1242 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1243 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1244
1245 /* add state */
1246 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1247 /* a care is needed when the destination address of the
1248 state is to be updated as it is a part of triplet */
1249 xfrm_state_insert(xc);
1250 } else {
1251 if ((err = xfrm_state_add(xc)) < 0)
1252 goto error;
1253 }
1254
1255 return xc;
1256error:
1257 kfree(xc);
1258 return NULL;
1259}
1260EXPORT_SYMBOL(xfrm_state_migrate);
1261#endif
1262
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263int xfrm_state_update(struct xfrm_state *x)
1264{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 struct xfrm_state *x1;
1266 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001267 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001270 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
1272 err = -ESRCH;
1273 if (!x1)
1274 goto out;
1275
1276 if (xfrm_state_kern(x1)) {
1277 xfrm_state_put(x1);
1278 err = -EEXIST;
1279 goto out;
1280 }
1281
1282 if (x1->km.state == XFRM_STATE_ACQ) {
1283 __xfrm_state_insert(x);
1284 x = NULL;
1285 }
1286 err = 0;
1287
1288out:
1289 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
1291 if (err)
1292 return err;
1293
1294 if (!x) {
1295 xfrm_state_delete(x1);
1296 xfrm_state_put(x1);
1297 return 0;
1298 }
1299
1300 err = -EINVAL;
1301 spin_lock_bh(&x1->lock);
1302 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1303 if (x->encap && x1->encap)
1304 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001305 if (x->coaddr && x1->coaddr) {
1306 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1307 }
1308 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1309 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1311 x1->km.dying = 0;
1312
David S. Millera47f0ce2006-08-24 03:54:22 -07001313 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 if (x1->curlft.use_time)
1315 xfrm_state_check_expire(x1);
1316
1317 err = 0;
1318 }
1319 spin_unlock_bh(&x1->lock);
1320
1321 xfrm_state_put(x1);
1322
1323 return err;
1324}
1325EXPORT_SYMBOL(xfrm_state_update);
1326
1327int xfrm_state_check_expire(struct xfrm_state *x)
1328{
1329 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001330 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
1332 if (x->km.state != XFRM_STATE_VALID)
1333 return -EINVAL;
1334
1335 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1336 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001337 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001338 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 return -EINVAL;
1340 }
1341
1342 if (!x->km.dying &&
1343 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001344 x->curlft.packets >= x->lft.soft_packet_limit)) {
1345 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001346 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001347 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 return 0;
1349}
1350EXPORT_SYMBOL(xfrm_state_check_expire);
1351
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001353xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 unsigned short family)
1355{
1356 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001359 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 return x;
1362}
1363EXPORT_SYMBOL(xfrm_state_lookup);
1364
1365struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001366xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1367 u8 proto, unsigned short family)
1368{
1369 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001370
1371 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001372 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001373 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001374 return x;
1375}
1376EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1377
1378struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001379xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1380 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 int create, unsigned short family)
1382{
1383 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
1385 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001386 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001388
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return x;
1390}
1391EXPORT_SYMBOL(xfrm_find_acq);
1392
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001393#ifdef CONFIG_XFRM_SUB_POLICY
1394int
1395xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1396 unsigned short family)
1397{
1398 int err = 0;
1399 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1400 if (!afinfo)
1401 return -EAFNOSUPPORT;
1402
1403 spin_lock_bh(&xfrm_state_lock);
1404 if (afinfo->tmpl_sort)
1405 err = afinfo->tmpl_sort(dst, src, n);
1406 spin_unlock_bh(&xfrm_state_lock);
1407 xfrm_state_put_afinfo(afinfo);
1408 return err;
1409}
1410EXPORT_SYMBOL(xfrm_tmpl_sort);
1411
1412int
1413xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1414 unsigned short family)
1415{
1416 int err = 0;
1417 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1418 if (!afinfo)
1419 return -EAFNOSUPPORT;
1420
1421 spin_lock_bh(&xfrm_state_lock);
1422 if (afinfo->state_sort)
1423 err = afinfo->state_sort(dst, src, n);
1424 spin_unlock_bh(&xfrm_state_lock);
1425 xfrm_state_put_afinfo(afinfo);
1426 return err;
1427}
1428EXPORT_SYMBOL(xfrm_state_sort);
1429#endif
1430
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431/* Silly enough, but I'm lazy to build resolution list */
1432
1433static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1434{
1435 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
David S. Millerf034b5d2006-08-24 03:08:07 -07001437 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001438 struct hlist_node *entry;
1439 struct xfrm_state *x;
1440
1441 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1442 if (x->km.seq == seq &&
1443 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 xfrm_state_hold(x);
1445 return x;
1446 }
1447 }
1448 }
1449 return NULL;
1450}
1451
1452struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1453{
1454 struct xfrm_state *x;
1455
1456 spin_lock_bh(&xfrm_state_lock);
1457 x = __xfrm_find_acq_byseq(seq);
1458 spin_unlock_bh(&xfrm_state_lock);
1459 return x;
1460}
1461EXPORT_SYMBOL(xfrm_find_acq_byseq);
1462
1463u32 xfrm_get_acqseq(void)
1464{
1465 u32 res;
1466 static u32 acqseq;
1467 static DEFINE_SPINLOCK(acqseq_lock);
1468
1469 spin_lock_bh(&acqseq_lock);
1470 res = (++acqseq ? : ++acqseq);
1471 spin_unlock_bh(&acqseq_lock);
1472 return res;
1473}
1474EXPORT_SYMBOL(xfrm_get_acqseq);
1475
Herbert Xu658b2192007-10-09 13:29:52 -07001476int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477{
David S. Millerf034b5d2006-08-24 03:08:07 -07001478 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001480 int err = -ENOENT;
1481 __be32 minspi = htonl(low);
1482 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
Herbert Xu658b2192007-10-09 13:29:52 -07001484 spin_lock_bh(&x->lock);
1485 if (x->km.state == XFRM_STATE_DEAD)
1486 goto unlock;
1487
1488 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001490 goto unlock;
1491
1492 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
1494 if (minspi == maxspi) {
1495 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1496 if (x0) {
1497 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001498 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500 x->id.spi = minspi;
1501 } else {
1502 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001503 for (h=0; h<high-low+1; h++) {
1504 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1506 if (x0 == NULL) {
1507 x->id.spi = htonl(spi);
1508 break;
1509 }
1510 xfrm_state_put(x0);
1511 }
1512 }
1513 if (x->id.spi) {
1514 spin_lock_bh(&xfrm_state_lock);
1515 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001516 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001518
1519 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 }
Herbert Xu658b2192007-10-09 13:29:52 -07001521
1522unlock:
1523 spin_unlock_bh(&x->lock);
1524
1525 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526}
1527EXPORT_SYMBOL(xfrm_alloc_spi);
1528
Timo Teras4c563f72008-02-28 21:31:08 -08001529int xfrm_state_walk(struct xfrm_state_walk *walk,
1530 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 void *data)
1532{
Timo Teras4c563f72008-02-28 21:31:08 -08001533 struct xfrm_state *old, *x, *last = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 int err = 0;
1535
Timo Teras4c563f72008-02-28 21:31:08 -08001536 if (walk->state == NULL && walk->count != 0)
1537 return 0;
1538
1539 old = x = walk->state;
1540 walk->state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 spin_lock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001542 if (x == NULL)
1543 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1544 list_for_each_entry_from(x, &xfrm_state_all, all) {
1545 if (x->km.state == XFRM_STATE_DEAD)
1546 continue;
1547 if (!xfrm_id_proto_match(x->id.proto, walk->proto))
1548 continue;
1549 if (last) {
1550 err = func(last, walk->count, data);
1551 if (err) {
1552 xfrm_state_hold(last);
1553 walk->state = last;
1554 goto out;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 }
Timo Teras4c563f72008-02-28 21:31:08 -08001557 last = x;
1558 walk->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 }
Timo Teras4c563f72008-02-28 21:31:08 -08001560 if (walk->count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 err = -ENOENT;
1562 goto out;
1563 }
Timo Teras4c563f72008-02-28 21:31:08 -08001564 if (last)
1565 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566out:
1567 spin_unlock_bh(&xfrm_state_lock);
Timo Teras4c563f72008-02-28 21:31:08 -08001568 if (old != NULL)
1569 xfrm_state_put(old);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 return err;
1571}
1572EXPORT_SYMBOL(xfrm_state_walk);
1573
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001574
1575void xfrm_replay_notify(struct xfrm_state *x, int event)
1576{
1577 struct km_event c;
1578 /* we send notify messages in case
1579 * 1. we updated on of the sequence numbers, and the seqno difference
1580 * is at least x->replay_maxdiff, in this case we also update the
1581 * timeout of our timer function
1582 * 2. if x->replay_maxage has elapsed since last update,
1583 * and there were changes
1584 *
1585 * The state structure must be locked!
1586 */
1587
1588 switch (event) {
1589 case XFRM_REPLAY_UPDATE:
1590 if (x->replay_maxdiff &&
1591 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001592 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1593 if (x->xflags & XFRM_TIME_DEFER)
1594 event = XFRM_REPLAY_TIMEOUT;
1595 else
1596 return;
1597 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001598
1599 break;
1600
1601 case XFRM_REPLAY_TIMEOUT:
1602 if ((x->replay.seq == x->preplay.seq) &&
1603 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001604 (x->replay.oseq == x->preplay.oseq)) {
1605 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001606 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001607 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001608
1609 break;
1610 }
1611
1612 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1613 c.event = XFRM_MSG_NEWAE;
1614 c.data.aevent = event;
1615 km_state_notify(x, &c);
1616
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001617 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001618 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001619 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001620}
1621
1622static void xfrm_replay_timer_handler(unsigned long data)
1623{
1624 struct xfrm_state *x = (struct xfrm_state*)data;
1625
1626 spin_lock(&x->lock);
1627
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001628 if (x->km.state == XFRM_STATE_VALID) {
1629 if (xfrm_aevent_is_on())
1630 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1631 else
1632 x->xflags |= XFRM_TIME_DEFER;
1633 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001634
1635 spin_unlock(&x->lock);
1636}
1637
Paul Mooreafeb14b2007-12-21 14:58:11 -08001638int xfrm_replay_check(struct xfrm_state *x,
1639 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
1641 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001642 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
1644 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001645 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
1647 if (likely(seq > x->replay.seq))
1648 return 0;
1649
1650 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001651 if (diff >= min_t(unsigned int, x->props.replay_window,
1652 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001654 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 }
1656
1657 if (x->replay.bitmap & (1U << diff)) {
1658 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001659 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 }
1661 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001662
1663err:
1664 xfrm_audit_state_replay(x, skb, net_seq);
1665 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Al Viro61f46272006-09-27 18:48:33 -07001668void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669{
1670 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001671 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
1673 if (seq > x->replay.seq) {
1674 diff = seq - x->replay.seq;
1675 if (diff < x->props.replay_window)
1676 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1677 else
1678 x->replay.bitmap = 1;
1679 x->replay.seq = seq;
1680 } else {
1681 diff = x->replay.seq - seq;
1682 x->replay.bitmap |= (1U << diff);
1683 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001684
1685 if (xfrm_aevent_is_on())
1686 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Denis Chengdf018122007-12-07 00:51:11 -08001689static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690static DEFINE_RWLOCK(xfrm_km_lock);
1691
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001692void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693{
1694 struct xfrm_mgr *km;
1695
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001696 read_lock(&xfrm_km_lock);
1697 list_for_each_entry(km, &xfrm_km_list, list)
1698 if (km->notify_policy)
1699 km->notify_policy(xp, dir, c);
1700 read_unlock(&xfrm_km_lock);
1701}
1702
1703void km_state_notify(struct xfrm_state *x, struct km_event *c)
1704{
1705 struct xfrm_mgr *km;
1706 read_lock(&xfrm_km_lock);
1707 list_for_each_entry(km, &xfrm_km_list, list)
1708 if (km->notify)
1709 km->notify(x, c);
1710 read_unlock(&xfrm_km_lock);
1711}
1712
1713EXPORT_SYMBOL(km_policy_notify);
1714EXPORT_SYMBOL(km_state_notify);
1715
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001716void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001717{
1718 struct km_event c;
1719
Herbert Xubf088672005-06-18 22:44:00 -07001720 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001721 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001722 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001723 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724
1725 if (hard)
1726 wake_up(&km_waitq);
1727}
1728
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001729EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001730/*
1731 * We send to all registered managers regardless of failure
1732 * We are happy with one success
1733*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001734int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001736 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 struct xfrm_mgr *km;
1738
1739 read_lock(&xfrm_km_lock);
1740 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001741 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1742 if (!acqret)
1743 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 }
1745 read_unlock(&xfrm_km_lock);
1746 return err;
1747}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001748EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
Al Viro5d36b182006-11-08 00:24:06 -08001750int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
1752 int err = -EINVAL;
1753 struct xfrm_mgr *km;
1754
1755 read_lock(&xfrm_km_lock);
1756 list_for_each_entry(km, &xfrm_km_list, list) {
1757 if (km->new_mapping)
1758 err = km->new_mapping(x, ipaddr, sport);
1759 if (!err)
1760 break;
1761 }
1762 read_unlock(&xfrm_km_lock);
1763 return err;
1764}
1765EXPORT_SYMBOL(km_new_mapping);
1766
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001767void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001769 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Herbert Xubf088672005-06-18 22:44:00 -07001771 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001772 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001773 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001774 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
1776 if (hard)
1777 wake_up(&km_waitq);
1778}
David S. Millera70fcb02006-03-20 19:18:52 -08001779EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001781#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001782int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1783 struct xfrm_migrate *m, int num_migrate)
1784{
1785 int err = -EINVAL;
1786 int ret;
1787 struct xfrm_mgr *km;
1788
1789 read_lock(&xfrm_km_lock);
1790 list_for_each_entry(km, &xfrm_km_list, list) {
1791 if (km->migrate) {
1792 ret = km->migrate(sel, dir, type, m, num_migrate);
1793 if (!ret)
1794 err = ret;
1795 }
1796 }
1797 read_unlock(&xfrm_km_lock);
1798 return err;
1799}
1800EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001801#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001802
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001803int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1804{
1805 int err = -EINVAL;
1806 int ret;
1807 struct xfrm_mgr *km;
1808
1809 read_lock(&xfrm_km_lock);
1810 list_for_each_entry(km, &xfrm_km_list, list) {
1811 if (km->report) {
1812 ret = km->report(proto, sel, addr);
1813 if (!ret)
1814 err = ret;
1815 }
1816 }
1817 read_unlock(&xfrm_km_lock);
1818 return err;
1819}
1820EXPORT_SYMBOL(km_report);
1821
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1823{
1824 int err;
1825 u8 *data;
1826 struct xfrm_mgr *km;
1827 struct xfrm_policy *pol = NULL;
1828
1829 if (optlen <= 0 || optlen > PAGE_SIZE)
1830 return -EMSGSIZE;
1831
1832 data = kmalloc(optlen, GFP_KERNEL);
1833 if (!data)
1834 return -ENOMEM;
1835
1836 err = -EFAULT;
1837 if (copy_from_user(data, optval, optlen))
1838 goto out;
1839
1840 err = -EINVAL;
1841 read_lock(&xfrm_km_lock);
1842 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001843 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 optlen, &err);
1845 if (err >= 0)
1846 break;
1847 }
1848 read_unlock(&xfrm_km_lock);
1849
1850 if (err >= 0) {
1851 xfrm_sk_policy_insert(sk, err, pol);
1852 xfrm_pol_put(pol);
1853 err = 0;
1854 }
1855
1856out:
1857 kfree(data);
1858 return err;
1859}
1860EXPORT_SYMBOL(xfrm_user_policy);
1861
1862int xfrm_register_km(struct xfrm_mgr *km)
1863{
1864 write_lock_bh(&xfrm_km_lock);
1865 list_add_tail(&km->list, &xfrm_km_list);
1866 write_unlock_bh(&xfrm_km_lock);
1867 return 0;
1868}
1869EXPORT_SYMBOL(xfrm_register_km);
1870
1871int xfrm_unregister_km(struct xfrm_mgr *km)
1872{
1873 write_lock_bh(&xfrm_km_lock);
1874 list_del(&km->list);
1875 write_unlock_bh(&xfrm_km_lock);
1876 return 0;
1877}
1878EXPORT_SYMBOL(xfrm_unregister_km);
1879
1880int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1881{
1882 int err = 0;
1883 if (unlikely(afinfo == NULL))
1884 return -EINVAL;
1885 if (unlikely(afinfo->family >= NPROTO))
1886 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001887 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1889 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001890 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001892 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 return err;
1894}
1895EXPORT_SYMBOL(xfrm_state_register_afinfo);
1896
1897int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1898{
1899 int err = 0;
1900 if (unlikely(afinfo == NULL))
1901 return -EINVAL;
1902 if (unlikely(afinfo->family >= NPROTO))
1903 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001904 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1906 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1907 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001908 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001911 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return err;
1913}
1914EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1915
Herbert Xu17c2a422007-10-17 21:33:12 -07001916static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917{
1918 struct xfrm_state_afinfo *afinfo;
1919 if (unlikely(family >= NPROTO))
1920 return NULL;
1921 read_lock(&xfrm_state_afinfo_lock);
1922 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001923 if (unlikely(!afinfo))
1924 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 return afinfo;
1926}
1927
Herbert Xu17c2a422007-10-17 21:33:12 -07001928static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001929 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930{
Herbert Xu546be242006-05-27 23:03:58 -07001931 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932}
1933
1934/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1935void xfrm_state_delete_tunnel(struct xfrm_state *x)
1936{
1937 if (x->tunnel) {
1938 struct xfrm_state *t = x->tunnel;
1939
1940 if (atomic_read(&t->tunnel_users) == 2)
1941 xfrm_state_delete(t);
1942 atomic_dec(&t->tunnel_users);
1943 xfrm_state_put(t);
1944 x->tunnel = NULL;
1945 }
1946}
1947EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1948
1949int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1950{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001951 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952
Patrick McHardyc5c25232007-04-09 11:47:18 -07001953 spin_lock_bh(&x->lock);
1954 if (x->km.state == XFRM_STATE_VALID &&
1955 x->type && x->type->get_mtu)
1956 res = x->type->get_mtu(x, mtu);
1957 else
Patrick McHardy28121612007-06-18 22:30:15 -07001958 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001959 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 return res;
1961}
1962
Herbert Xu72cb6962005-06-20 13:18:08 -07001963int xfrm_init_state(struct xfrm_state *x)
1964{
Herbert Xud094cd82005-06-20 13:19:41 -07001965 struct xfrm_state_afinfo *afinfo;
1966 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001967 int err;
1968
Herbert Xud094cd82005-06-20 13:19:41 -07001969 err = -EAFNOSUPPORT;
1970 afinfo = xfrm_state_get_afinfo(family);
1971 if (!afinfo)
1972 goto error;
1973
1974 err = 0;
1975 if (afinfo->init_flags)
1976 err = afinfo->init_flags(x);
1977
1978 xfrm_state_put_afinfo(afinfo);
1979
1980 if (err)
1981 goto error;
1982
1983 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001984 x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1985 if (x->inner_mode == NULL)
1986 goto error;
1987
1988 if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1989 family != x->sel.family)
1990 goto error;
1991
Herbert Xud094cd82005-06-20 13:19:41 -07001992 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001993 if (x->type == NULL)
1994 goto error;
1995
1996 err = x->type->init_state(x);
1997 if (err)
1998 goto error;
1999
Herbert Xu13996372007-10-17 21:35:51 -07002000 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2001 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002002 goto error;
2003
Herbert Xu72cb6962005-06-20 13:18:08 -07002004 x->km.state = XFRM_STATE_VALID;
2005
2006error:
2007 return err;
2008}
2009
2010EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002011
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012void __init xfrm_state_init(void)
2013{
David S. Millerf034b5d2006-08-24 03:08:07 -07002014 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015
David S. Millerf034b5d2006-08-24 03:08:07 -07002016 sz = sizeof(struct hlist_head) * 8;
2017
David S. Miller44e36b42006-08-24 04:50:50 -07002018 xfrm_state_bydst = xfrm_hash_alloc(sz);
2019 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2020 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002021 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2022 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2023 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2024
David Howellsc4028952006-11-22 14:57:56 +00002025 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026}
2027
Joy Lattenab5f5e82007-09-17 11:51:22 -07002028#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002029static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2030 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002031{
Paul Moore68277ac2007-12-20 20:49:33 -08002032 struct xfrm_sec_ctx *ctx = x->security;
2033 u32 spi = ntohl(x->id.spi);
2034
2035 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002036 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002037 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002038
2039 switch(x->props.family) {
2040 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002041 audit_log_format(audit_buf,
2042 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002043 NIPQUAD(x->props.saddr.a4),
2044 NIPQUAD(x->id.daddr.a4));
2045 break;
2046 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002047 audit_log_format(audit_buf,
2048 " src=" NIP6_FMT " dst=" NIP6_FMT,
2049 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2050 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002051 break;
2052 }
Paul Moore68277ac2007-12-20 20:49:33 -08002053
2054 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002055}
2056
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002057static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2058 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002059{
2060 struct iphdr *iph4;
2061 struct ipv6hdr *iph6;
2062
2063 switch (family) {
2064 case AF_INET:
2065 iph4 = ip_hdr(skb);
2066 audit_log_format(audit_buf,
2067 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2068 NIPQUAD(iph4->saddr),
2069 NIPQUAD(iph4->daddr));
2070 break;
2071 case AF_INET6:
2072 iph6 = ipv6_hdr(skb);
2073 audit_log_format(audit_buf,
2074 " src=" NIP6_FMT " dst=" NIP6_FMT
2075 " flowlbl=0x%x%x%x",
2076 NIP6(iph6->saddr),
2077 NIP6(iph6->daddr),
2078 iph6->flow_lbl[0] & 0x0f,
2079 iph6->flow_lbl[1],
2080 iph6->flow_lbl[2]);
2081 break;
2082 }
2083}
2084
Paul Moore68277ac2007-12-20 20:49:33 -08002085void xfrm_audit_state_add(struct xfrm_state *x, int result,
2086 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002087{
2088 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002089
Paul Mooreafeb14b2007-12-21 14:58:11 -08002090 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002091 if (audit_buf == NULL)
2092 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002093 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2094 xfrm_audit_helper_sainfo(x, audit_buf);
2095 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002096 audit_log_end(audit_buf);
2097}
2098EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2099
Paul Moore68277ac2007-12-20 20:49:33 -08002100void xfrm_audit_state_delete(struct xfrm_state *x, int result,
2101 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002102{
2103 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002104
Paul Mooreafeb14b2007-12-21 14:58:11 -08002105 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002106 if (audit_buf == NULL)
2107 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002108 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2109 xfrm_audit_helper_sainfo(x, audit_buf);
2110 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002111 audit_log_end(audit_buf);
2112}
2113EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002114
2115void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2116 struct sk_buff *skb)
2117{
2118 struct audit_buffer *audit_buf;
2119 u32 spi;
2120
2121 audit_buf = xfrm_audit_start("SA-replay-overflow");
2122 if (audit_buf == NULL)
2123 return;
2124 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2125 /* don't record the sequence number because it's inherent in this kind
2126 * of audit message */
2127 spi = ntohl(x->id.spi);
2128 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2129 audit_log_end(audit_buf);
2130}
2131EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2132
2133static void xfrm_audit_state_replay(struct xfrm_state *x,
2134 struct sk_buff *skb, __be32 net_seq)
2135{
2136 struct audit_buffer *audit_buf;
2137 u32 spi;
2138
2139 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2140 if (audit_buf == NULL)
2141 return;
2142 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2143 spi = ntohl(x->id.spi);
2144 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2145 spi, spi, ntohl(net_seq));
2146 audit_log_end(audit_buf);
2147}
2148
2149void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2150{
2151 struct audit_buffer *audit_buf;
2152
2153 audit_buf = xfrm_audit_start("SA-notfound");
2154 if (audit_buf == NULL)
2155 return;
2156 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2157 audit_log_end(audit_buf);
2158}
2159EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2160
2161void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2162 __be32 net_spi, __be32 net_seq)
2163{
2164 struct audit_buffer *audit_buf;
2165 u32 spi;
2166
2167 audit_buf = xfrm_audit_start("SA-notfound");
2168 if (audit_buf == NULL)
2169 return;
2170 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2171 spi = ntohl(net_spi);
2172 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2173 spi, spi, ntohl(net_seq));
2174 audit_log_end(audit_buf);
2175}
2176EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2177
2178void xfrm_audit_state_icvfail(struct xfrm_state *x,
2179 struct sk_buff *skb, u8 proto)
2180{
2181 struct audit_buffer *audit_buf;
2182 __be32 net_spi;
2183 __be32 net_seq;
2184
2185 audit_buf = xfrm_audit_start("SA-icv-failure");
2186 if (audit_buf == NULL)
2187 return;
2188 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2189 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2190 u32 spi = ntohl(net_spi);
2191 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2192 spi, spi, ntohl(net_seq));
2193 }
2194 audit_log_end(audit_buf);
2195}
2196EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002197#endif /* CONFIG_AUDITSYSCALL */