blob: 508337f97249d421e524013cdd83c86b6cbbf210 [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;
Herbert Xu12a169e2008-10-01 07:03:24 -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{
Herbert Xu12a169e2008-10-01 07:03:24 -0700406 struct xfrm_state *x;
407 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);
Herbert Xu12a169e2008-10-01 07:03:24 -0700411 hlist_move_list(&xfrm_state_gc_list, &gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 spin_unlock_bh(&xfrm_state_gc_lock);
413
Herbert Xu12a169e2008-10-01 07:03:24 -0700414 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 wake_up(&km_waitq);
418}
419
420static inline unsigned long make_jiffies(long secs)
421{
422 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
423 return MAX_SCHEDULE_TIMEOUT-1;
424 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900425 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
428static void xfrm_timer_handler(unsigned long data)
429{
430 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800431 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 long next = LONG_MAX;
433 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600434 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 spin_lock(&x->lock);
437 if (x->km.state == XFRM_STATE_DEAD)
438 goto out;
439 if (x->km.state == XFRM_STATE_EXPIRED)
440 goto expired;
441 if (x->lft.hard_add_expires_seconds) {
442 long tmo = x->lft.hard_add_expires_seconds +
443 x->curlft.add_time - now;
444 if (tmo <= 0)
445 goto expired;
446 if (tmo < next)
447 next = tmo;
448 }
449 if (x->lft.hard_use_expires_seconds) {
450 long tmo = x->lft.hard_use_expires_seconds +
451 (x->curlft.use_time ? : now) - now;
452 if (tmo <= 0)
453 goto expired;
454 if (tmo < next)
455 next = tmo;
456 }
457 if (x->km.dying)
458 goto resched;
459 if (x->lft.soft_add_expires_seconds) {
460 long tmo = x->lft.soft_add_expires_seconds +
461 x->curlft.add_time - now;
462 if (tmo <= 0)
463 warn = 1;
464 else if (tmo < next)
465 next = tmo;
466 }
467 if (x->lft.soft_use_expires_seconds) {
468 long tmo = x->lft.soft_use_expires_seconds +
469 (x->curlft.use_time ? : now) - now;
470 if (tmo <= 0)
471 warn = 1;
472 else if (tmo < next)
473 next = tmo;
474 }
475
Herbert Xu4666faa2005-06-18 22:43:22 -0700476 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800478 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700480 if (next != LONG_MAX)
481 mod_timer(&x->timer, jiffies + make_jiffies(next));
482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 goto out;
484
485expired:
486 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
487 x->km.state = XFRM_STATE_EXPIRED;
488 wake_up(&km_waitq);
489 next = 2;
490 goto resched;
491 }
Joy Latten161a09e2006-11-27 13:11:54 -0600492
493 err = __xfrm_state_delete(x);
494 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800495 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Joy Lattenab5f5e82007-09-17 11:51:22 -0700497 xfrm_audit_state_delete(x, err ? 0 : 1,
Eric Paris25323862008-04-18 10:09:25 -0400498 audit_get_loginuid(current),
499 audit_get_sessionid(current), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501out:
502 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503}
504
David S. Miller0ac84752006-03-20 19:18:23 -0800505static void xfrm_replay_timer_handler(unsigned long data);
506
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507struct xfrm_state *xfrm_state_alloc(void)
508{
509 struct xfrm_state *x;
510
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700511 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 atomic_set(&x->refcnt, 1);
515 atomic_set(&x->tunnel_users, 0);
Herbert Xu12a169e2008-10-01 07:03:24 -0700516 INIT_LIST_HEAD(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700517 INIT_HLIST_NODE(&x->bydst);
518 INIT_HLIST_NODE(&x->bysrc);
519 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800520 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
521 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
522 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800523 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 x->lft.soft_byte_limit = XFRM_INF;
525 x->lft.soft_packet_limit = XFRM_INF;
526 x->lft.hard_byte_limit = XFRM_INF;
527 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800528 x->replay_maxage = 0;
529 x->replay_maxdiff = 0;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700530 x->inner_mode = NULL;
531 x->inner_mode_iaf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 spin_lock_init(&x->lock);
533 }
534 return x;
535}
536EXPORT_SYMBOL(xfrm_state_alloc);
537
538void __xfrm_state_destroy(struct xfrm_state *x)
539{
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700540 WARN_ON(x->km.state != XFRM_STATE_DEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 spin_lock_bh(&xfrm_state_gc_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -0700543 hlist_add_head(&x->gclist, &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);
Herbert Xu12a169e2008-10-01 07:03:24 -0700556 list_del(&x->km.all);
David S. Miller8f126e32006-08-24 02:45:07 -0700557 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700558 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700559 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700560 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700561 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 /* All xfrm_state objects are created by xfrm_state_alloc.
565 * The xfrm_state_alloc call gives a reference, and that
566 * is what we are dropping here.
567 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800568 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700569 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700571
572 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800574EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700576int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700578 int err;
579
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700581 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700583
584 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586EXPORT_SYMBOL(xfrm_state_delete);
587
Joy Latten4aa2e622007-06-04 19:05:57 -0400588#ifdef CONFIG_SECURITY_NETWORK_XFRM
589static inline int
590xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
Joy Latten4aa2e622007-06-04 19:05:57 -0400592 int i, err = 0;
593
594 for (i = 0; i <= xfrm_state_hmask; i++) {
595 struct hlist_node *entry;
596 struct xfrm_state *x;
597
598 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
599 if (xfrm_id_proto_match(x->id.proto, proto) &&
600 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700601 xfrm_audit_state_delete(x, 0,
602 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400603 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700604 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400605 return err;
606 }
607 }
608 }
609
610 return err;
611}
612#else
613static inline int
614xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
615{
616 return 0;
617}
618#endif
619
620int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
621{
622 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
624 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400625 err = xfrm_state_flush_secctx_check(proto, audit_info);
626 if (err)
627 goto out;
628
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700629 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700630 struct hlist_node *entry;
631 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700633 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700635 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 xfrm_state_hold(x);
637 spin_unlock_bh(&xfrm_state_lock);
638
Joy Latten161a09e2006-11-27 13:11:54 -0600639 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700640 xfrm_audit_state_delete(x, err ? 0 : 1,
641 audit_info->loginuid,
Eric Paris25323862008-04-18 10:09:25 -0400642 audit_info->sessionid,
Joy Lattenab5f5e82007-09-17 11:51:22 -0700643 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 xfrm_state_put(x);
645
646 spin_lock_bh(&xfrm_state_lock);
647 goto restart;
648 }
649 }
650 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400651 err = 0;
652
653out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 spin_unlock_bh(&xfrm_state_lock);
655 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400656 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657}
658EXPORT_SYMBOL(xfrm_state_flush);
659
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700660void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700661{
662 spin_lock_bh(&xfrm_state_lock);
663 si->sadcnt = xfrm_state_num;
664 si->sadhcnt = xfrm_state_hmask;
665 si->sadhmcnt = xfrm_state_hashmax;
666 spin_unlock_bh(&xfrm_state_lock);
667}
668EXPORT_SYMBOL(xfrm_sad_getinfo);
669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670static int
671xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
672 struct xfrm_tmpl *tmpl,
673 xfrm_address_t *daddr, xfrm_address_t *saddr,
674 unsigned short family)
675{
676 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
677 if (!afinfo)
678 return -1;
679 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
680 xfrm_state_put_afinfo(afinfo);
681 return 0;
682}
683
Al Viroa94cfd12006-09-27 18:47:24 -0700684static 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 -0700685{
686 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
687 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700688 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700689
David S. Miller8f126e32006-08-24 02:45:07 -0700690 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700691 if (x->props.family != family ||
692 x->id.spi != spi ||
693 x->id.proto != proto)
694 continue;
695
696 switch (family) {
697 case AF_INET:
698 if (x->id.daddr.a4 != daddr->a4)
699 continue;
700 break;
701 case AF_INET6:
702 if (!ipv6_addr_equal((struct in6_addr *)daddr,
703 (struct in6_addr *)
704 x->id.daddr.a6))
705 continue;
706 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700707 }
David S. Milleredcd5822006-08-24 00:42:45 -0700708
709 xfrm_state_hold(x);
710 return x;
711 }
712
713 return NULL;
714}
715
716static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
717{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700718 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700719 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700720 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700721
David S. Miller8f126e32006-08-24 02:45:07 -0700722 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700723 if (x->props.family != family ||
724 x->id.proto != proto)
725 continue;
726
727 switch (family) {
728 case AF_INET:
729 if (x->id.daddr.a4 != daddr->a4 ||
730 x->props.saddr.a4 != saddr->a4)
731 continue;
732 break;
733 case AF_INET6:
734 if (!ipv6_addr_equal((struct in6_addr *)daddr,
735 (struct in6_addr *)
736 x->id.daddr.a6) ||
737 !ipv6_addr_equal((struct in6_addr *)saddr,
738 (struct in6_addr *)
739 x->props.saddr.a6))
740 continue;
741 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700742 }
David S. Milleredcd5822006-08-24 00:42:45 -0700743
744 xfrm_state_hold(x);
745 return x;
746 }
747
748 return NULL;
749}
750
751static inline struct xfrm_state *
752__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
753{
754 if (use_spi)
755 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
756 x->id.proto, family);
757 else
758 return __xfrm_state_lookup_byaddr(&x->id.daddr,
759 &x->props.saddr,
760 x->id.proto, family);
761}
762
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700763static void xfrm_hash_grow_check(int have_hash_collision)
764{
765 if (have_hash_collision &&
766 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
767 xfrm_state_num > xfrm_state_hmask)
768 schedule_work(&xfrm_hash_work);
769}
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900772xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 struct flowi *fl, struct xfrm_tmpl *tmpl,
774 struct xfrm_policy *pol, int *err,
775 unsigned short family)
776{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800777 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700778 struct hlist_node *entry;
David S. Miller37b08e32008-09-02 20:14:15 -0700779 struct xfrm_state *x, *x0, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 int acquire_in_progress = 0;
781 int error = 0;
782 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900783
David S. Miller37b08e32008-09-02 20:14:15 -0700784 to_put = NULL;
785
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800787 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700788 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (x->props.family == family &&
790 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700791 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 xfrm_state_addr_check(x, daddr, saddr, family) &&
793 tmpl->mode == x->props.mode &&
794 tmpl->id.proto == x->id.proto &&
795 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
796 /* Resolution logic:
797 1. There is a valid state with matching selector.
798 Done.
799 2. Valid state with inappropriate selector. Skip.
800
801 Entering area of "sysdeps".
802
803 3. If state is not valid, selector is temporary,
804 it selects only session which triggered
805 previous resolution. Key manager will do
806 something to install a state with proper
807 selector.
808 */
809 if (x->km.state == XFRM_STATE_VALID) {
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -0700810 if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700811 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 continue;
813 if (!best ||
814 best->km.dying > x->km.dying ||
815 (best->km.dying == x->km.dying &&
816 best->curlft.add_time < x->curlft.add_time))
817 best = x;
818 } else if (x->km.state == XFRM_STATE_ACQ) {
819 acquire_in_progress = 1;
820 } else if (x->km.state == XFRM_STATE_ERROR ||
821 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700822 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700823 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 error = -ESRCH;
825 }
826 }
827 }
828
829 x = best;
830 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700831 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700832 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
833 tmpl->id.proto, family)) != NULL) {
David S. Miller37b08e32008-09-02 20:14:15 -0700834 to_put = x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 error = -EEXIST;
836 goto out;
837 }
838 x = xfrm_state_alloc();
839 if (x == NULL) {
840 error = -ENOMEM;
841 goto out;
842 }
843 /* Initialize temporary selector matching only
844 * to current session. */
845 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
846
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700847 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
848 if (error) {
849 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700850 to_put = x;
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700851 x = NULL;
852 goto out;
853 }
854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (km_query(x, tmpl, pol) == 0) {
856 x->km.state = XFRM_STATE_ACQ;
Herbert Xu12a169e2008-10-01 07:03:24 -0700857 list_add(&x->km.all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -0700858 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700859 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700860 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 if (x->id.spi) {
862 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700863 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
David S. Miller01e67d02007-05-25 00:41:38 -0700865 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
866 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700868 xfrm_state_num++;
869 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 } else {
871 x->km.state = XFRM_STATE_DEAD;
David S. Miller37b08e32008-09-02 20:14:15 -0700872 to_put = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 x = NULL;
874 error = -ESRCH;
875 }
876 }
877out:
878 if (x)
879 xfrm_state_hold(x);
880 else
881 *err = acquire_in_progress ? -EAGAIN : error;
882 spin_unlock_bh(&xfrm_state_lock);
David S. Miller37b08e32008-09-02 20:14:15 -0700883 if (to_put)
884 xfrm_state_put(to_put);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 return x;
886}
887
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700888struct xfrm_state *
889xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
890 unsigned short family, u8 mode, u8 proto, u32 reqid)
891{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800892 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700893 struct xfrm_state *rx = NULL, *x = NULL;
894 struct hlist_node *entry;
895
896 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800897 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700898 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
899 if (x->props.family == family &&
900 x->props.reqid == reqid &&
901 !(x->props.flags & XFRM_STATE_WILDRECV) &&
902 xfrm_state_addr_check(x, daddr, saddr, family) &&
903 mode == x->props.mode &&
904 proto == x->id.proto &&
905 x->km.state == XFRM_STATE_VALID) {
906 rx = x;
907 break;
908 }
909 }
910
911 if (rx)
912 xfrm_state_hold(rx);
913 spin_unlock(&xfrm_state_lock);
914
915
916 return rx;
917}
918EXPORT_SYMBOL(xfrm_stateonly_find);
919
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920static void __xfrm_state_insert(struct xfrm_state *x)
921{
David S. Millera624c102006-08-24 03:24:33 -0700922 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
David S. Miller9d4a7062006-08-24 03:18:09 -0700924 x->genid = ++xfrm_state_genid;
925
Herbert Xu12a169e2008-10-01 07:03:24 -0700926 list_add(&x->km.all, &xfrm_state_all);
Timo Teras4c563f72008-02-28 21:31:08 -0800927
David S. Millerc1969f22006-08-24 04:00:03 -0700928 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
929 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700930 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700932 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700933 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700935 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700936 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
937 x->props.family);
938
David S. Miller8f126e32006-08-24 02:45:07 -0700939 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700940 }
941
David S. Millera47f0ce2006-08-24 03:54:22 -0700942 mod_timer(&x->timer, jiffies + HZ);
943 if (x->replay_maxage)
944 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800945
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700947
948 xfrm_state_num++;
949
David S. Miller918049f2006-10-12 22:03:24 -0700950 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951}
952
David S. Millerc7f5ea32006-08-24 03:29:04 -0700953/* xfrm_state_lock is held */
954static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
955{
956 unsigned short family = xnew->props.family;
957 u32 reqid = xnew->props.reqid;
958 struct xfrm_state *x;
959 struct hlist_node *entry;
960 unsigned int h;
961
David S. Millerc1969f22006-08-24 04:00:03 -0700962 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700963 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
964 if (x->props.family == family &&
965 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700966 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
967 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700968 x->genid = xfrm_state_genid;
969 }
970}
971
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972void xfrm_state_insert(struct xfrm_state *x)
973{
974 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700975 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 __xfrm_state_insert(x);
977 spin_unlock_bh(&xfrm_state_lock);
978}
979EXPORT_SYMBOL(xfrm_state_insert);
980
David S. Miller27708342006-08-24 00:13:10 -0700981/* xfrm_state_lock is held */
982static 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)
983{
David S. Millerc1969f22006-08-24 04:00:03 -0700984 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700985 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700986 struct xfrm_state *x;
987
David S. Miller8f126e32006-08-24 02:45:07 -0700988 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700989 if (x->props.reqid != reqid ||
990 x->props.mode != mode ||
991 x->props.family != family ||
992 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700993 x->id.spi != 0 ||
994 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700995 continue;
996
997 switch (family) {
998 case AF_INET:
999 if (x->id.daddr.a4 != daddr->a4 ||
1000 x->props.saddr.a4 != saddr->a4)
1001 continue;
1002 break;
1003 case AF_INET6:
1004 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
1005 (struct in6_addr *)daddr) ||
1006 !ipv6_addr_equal((struct in6_addr *)
1007 x->props.saddr.a6,
1008 (struct in6_addr *)saddr))
1009 continue;
1010 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001011 }
David S. Miller27708342006-08-24 00:13:10 -07001012
1013 xfrm_state_hold(x);
1014 return x;
1015 }
1016
1017 if (!create)
1018 return NULL;
1019
1020 x = xfrm_state_alloc();
1021 if (likely(x)) {
1022 switch (family) {
1023 case AF_INET:
1024 x->sel.daddr.a4 = daddr->a4;
1025 x->sel.saddr.a4 = saddr->a4;
1026 x->sel.prefixlen_d = 32;
1027 x->sel.prefixlen_s = 32;
1028 x->props.saddr.a4 = saddr->a4;
1029 x->id.daddr.a4 = daddr->a4;
1030 break;
1031
1032 case AF_INET6:
1033 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1034 (struct in6_addr *)daddr);
1035 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1036 (struct in6_addr *)saddr);
1037 x->sel.prefixlen_d = 128;
1038 x->sel.prefixlen_s = 128;
1039 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1040 (struct in6_addr *)saddr);
1041 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1042 (struct in6_addr *)daddr);
1043 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001044 }
David S. Miller27708342006-08-24 00:13:10 -07001045
1046 x->km.state = XFRM_STATE_ACQ;
1047 x->id.proto = proto;
1048 x->props.family = family;
1049 x->props.mode = mode;
1050 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001051 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001052 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001053 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001054 add_timer(&x->timer);
Herbert Xu12a169e2008-10-01 07:03:24 -07001055 list_add(&x->km.all, &xfrm_state_all);
David S. Miller8f126e32006-08-24 02:45:07 -07001056 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001057 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001058 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001059
1060 xfrm_state_num++;
1061
1062 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001063 }
1064
1065 return x;
1066}
1067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1069
1070int xfrm_state_add(struct xfrm_state *x)
1071{
David S. Miller37b08e32008-09-02 20:14:15 -07001072 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 int family;
1074 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001075 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
David S. Miller37b08e32008-09-02 20:14:15 -07001079 to_put = NULL;
1080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 spin_lock_bh(&xfrm_state_lock);
1082
David S. Milleredcd5822006-08-24 00:42:45 -07001083 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 if (x1) {
David S. Miller37b08e32008-09-02 20:14:15 -07001085 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 x1 = NULL;
1087 err = -EEXIST;
1088 goto out;
1089 }
1090
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001091 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001093 if (x1 && ((x1->id.proto != x->id.proto) ||
1094 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
David S. Miller37b08e32008-09-02 20:14:15 -07001095 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 x1 = NULL;
1097 }
1098 }
1099
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001100 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001101 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1102 x->id.proto,
1103 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
David S. Millerc7f5ea32006-08-24 03:29:04 -07001105 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 __xfrm_state_insert(x);
1107 err = 0;
1108
1109out:
1110 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111
1112 if (x1) {
1113 xfrm_state_delete(x1);
1114 xfrm_state_put(x1);
1115 }
1116
David S. Miller37b08e32008-09-02 20:14:15 -07001117 if (to_put)
1118 xfrm_state_put(to_put);
1119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 return err;
1121}
1122EXPORT_SYMBOL(xfrm_state_add);
1123
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001124#ifdef CONFIG_XFRM_MIGRATE
Eric Dumazet66663512008-01-08 01:35:52 -08001125static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001126{
1127 int err = -ENOMEM;
1128 struct xfrm_state *x = xfrm_state_alloc();
1129 if (!x)
1130 goto error;
1131
1132 memcpy(&x->id, &orig->id, sizeof(x->id));
1133 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1134 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1135 x->props.mode = orig->props.mode;
1136 x->props.replay_window = orig->props.replay_window;
1137 x->props.reqid = orig->props.reqid;
1138 x->props.family = orig->props.family;
1139 x->props.saddr = orig->props.saddr;
1140
1141 if (orig->aalg) {
1142 x->aalg = xfrm_algo_clone(orig->aalg);
1143 if (!x->aalg)
1144 goto error;
1145 }
1146 x->props.aalgo = orig->props.aalgo;
1147
1148 if (orig->ealg) {
1149 x->ealg = xfrm_algo_clone(orig->ealg);
1150 if (!x->ealg)
1151 goto error;
1152 }
1153 x->props.ealgo = orig->props.ealgo;
1154
1155 if (orig->calg) {
1156 x->calg = xfrm_algo_clone(orig->calg);
1157 if (!x->calg)
1158 goto error;
1159 }
1160 x->props.calgo = orig->props.calgo;
1161
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001162 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001163 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1164 if (!x->encap)
1165 goto error;
1166 }
1167
1168 if (orig->coaddr) {
1169 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1170 GFP_KERNEL);
1171 if (!x->coaddr)
1172 goto error;
1173 }
1174
1175 err = xfrm_init_state(x);
1176 if (err)
1177 goto error;
1178
1179 x->props.flags = orig->props.flags;
1180
1181 x->curlft.add_time = orig->curlft.add_time;
1182 x->km.state = orig->km.state;
1183 x->km.seq = orig->km.seq;
1184
1185 return x;
1186
1187 error:
1188 if (errp)
1189 *errp = err;
1190 if (x) {
1191 kfree(x->aalg);
1192 kfree(x->ealg);
1193 kfree(x->calg);
1194 kfree(x->encap);
1195 kfree(x->coaddr);
1196 }
1197 kfree(x);
1198 return NULL;
1199}
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001200
1201/* xfrm_state_lock is held */
1202struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1203{
1204 unsigned int h;
1205 struct xfrm_state *x;
1206 struct hlist_node *entry;
1207
1208 if (m->reqid) {
1209 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1210 m->reqid, m->old_family);
1211 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1212 if (x->props.mode != m->mode ||
1213 x->id.proto != m->proto)
1214 continue;
1215 if (m->reqid && x->props.reqid != m->reqid)
1216 continue;
1217 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1218 m->old_family) ||
1219 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1220 m->old_family))
1221 continue;
1222 xfrm_state_hold(x);
1223 return x;
1224 }
1225 } else {
1226 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1227 m->old_family);
1228 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1229 if (x->props.mode != m->mode ||
1230 x->id.proto != m->proto)
1231 continue;
1232 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1233 m->old_family) ||
1234 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1235 m->old_family))
1236 continue;
1237 xfrm_state_hold(x);
1238 return x;
1239 }
1240 }
1241
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001242 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001243}
1244EXPORT_SYMBOL(xfrm_migrate_state_find);
1245
1246struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1247 struct xfrm_migrate *m)
1248{
1249 struct xfrm_state *xc;
1250 int err;
1251
1252 xc = xfrm_state_clone(x, &err);
1253 if (!xc)
1254 return NULL;
1255
1256 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1257 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1258
1259 /* add state */
1260 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1261 /* a care is needed when the destination address of the
1262 state is to be updated as it is a part of triplet */
1263 xfrm_state_insert(xc);
1264 } else {
1265 if ((err = xfrm_state_add(xc)) < 0)
1266 goto error;
1267 }
1268
1269 return xc;
1270error:
1271 kfree(xc);
1272 return NULL;
1273}
1274EXPORT_SYMBOL(xfrm_state_migrate);
1275#endif
1276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277int xfrm_state_update(struct xfrm_state *x)
1278{
David S. Miller37b08e32008-09-02 20:14:15 -07001279 struct xfrm_state *x1, *to_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001281 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
David S. Miller37b08e32008-09-02 20:14:15 -07001283 to_put = NULL;
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001286 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
1288 err = -ESRCH;
1289 if (!x1)
1290 goto out;
1291
1292 if (xfrm_state_kern(x1)) {
David S. Miller37b08e32008-09-02 20:14:15 -07001293 to_put = x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 err = -EEXIST;
1295 goto out;
1296 }
1297
1298 if (x1->km.state == XFRM_STATE_ACQ) {
1299 __xfrm_state_insert(x);
1300 x = NULL;
1301 }
1302 err = 0;
1303
1304out:
1305 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
David S. Miller37b08e32008-09-02 20:14:15 -07001307 if (to_put)
1308 xfrm_state_put(to_put);
1309
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 if (err)
1311 return err;
1312
1313 if (!x) {
1314 xfrm_state_delete(x1);
1315 xfrm_state_put(x1);
1316 return 0;
1317 }
1318
1319 err = -EINVAL;
1320 spin_lock_bh(&x1->lock);
1321 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1322 if (x->encap && x1->encap)
1323 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001324 if (x->coaddr && x1->coaddr) {
1325 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1326 }
1327 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1328 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1330 x1->km.dying = 0;
1331
David S. Millera47f0ce2006-08-24 03:54:22 -07001332 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 if (x1->curlft.use_time)
1334 xfrm_state_check_expire(x1);
1335
1336 err = 0;
1337 }
1338 spin_unlock_bh(&x1->lock);
1339
1340 xfrm_state_put(x1);
1341
1342 return err;
1343}
1344EXPORT_SYMBOL(xfrm_state_update);
1345
1346int xfrm_state_check_expire(struct xfrm_state *x)
1347{
1348 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001349 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
1351 if (x->km.state != XFRM_STATE_VALID)
1352 return -EINVAL;
1353
1354 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1355 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001356 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001357 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 return -EINVAL;
1359 }
1360
1361 if (!x->km.dying &&
1362 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001363 x->curlft.packets >= x->lft.soft_packet_limit)) {
1364 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001365 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001366 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 return 0;
1368}
1369EXPORT_SYMBOL(xfrm_state_check_expire);
1370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001372xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 unsigned short family)
1374{
1375 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
1377 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001378 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return x;
1381}
1382EXPORT_SYMBOL(xfrm_state_lookup);
1383
1384struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001385xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1386 u8 proto, unsigned short family)
1387{
1388 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001389
1390 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001391 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001392 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001393 return x;
1394}
1395EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1396
1397struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001398xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1399 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 int create, unsigned short family)
1401{
1402 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403
1404 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001405 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001407
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 return x;
1409}
1410EXPORT_SYMBOL(xfrm_find_acq);
1411
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001412#ifdef CONFIG_XFRM_SUB_POLICY
1413int
1414xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1415 unsigned short family)
1416{
1417 int err = 0;
1418 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1419 if (!afinfo)
1420 return -EAFNOSUPPORT;
1421
1422 spin_lock_bh(&xfrm_state_lock);
1423 if (afinfo->tmpl_sort)
1424 err = afinfo->tmpl_sort(dst, src, n);
1425 spin_unlock_bh(&xfrm_state_lock);
1426 xfrm_state_put_afinfo(afinfo);
1427 return err;
1428}
1429EXPORT_SYMBOL(xfrm_tmpl_sort);
1430
1431int
1432xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1433 unsigned short family)
1434{
1435 int err = 0;
1436 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1437 if (!afinfo)
1438 return -EAFNOSUPPORT;
1439
1440 spin_lock_bh(&xfrm_state_lock);
1441 if (afinfo->state_sort)
1442 err = afinfo->state_sort(dst, src, n);
1443 spin_unlock_bh(&xfrm_state_lock);
1444 xfrm_state_put_afinfo(afinfo);
1445 return err;
1446}
1447EXPORT_SYMBOL(xfrm_state_sort);
1448#endif
1449
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450/* Silly enough, but I'm lazy to build resolution list */
1451
1452static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1453{
1454 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
David S. Millerf034b5d2006-08-24 03:08:07 -07001456 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001457 struct hlist_node *entry;
1458 struct xfrm_state *x;
1459
1460 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1461 if (x->km.seq == seq &&
1462 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 xfrm_state_hold(x);
1464 return x;
1465 }
1466 }
1467 }
1468 return NULL;
1469}
1470
1471struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1472{
1473 struct xfrm_state *x;
1474
1475 spin_lock_bh(&xfrm_state_lock);
1476 x = __xfrm_find_acq_byseq(seq);
1477 spin_unlock_bh(&xfrm_state_lock);
1478 return x;
1479}
1480EXPORT_SYMBOL(xfrm_find_acq_byseq);
1481
1482u32 xfrm_get_acqseq(void)
1483{
1484 u32 res;
1485 static u32 acqseq;
1486 static DEFINE_SPINLOCK(acqseq_lock);
1487
1488 spin_lock_bh(&acqseq_lock);
1489 res = (++acqseq ? : ++acqseq);
1490 spin_unlock_bh(&acqseq_lock);
1491 return res;
1492}
1493EXPORT_SYMBOL(xfrm_get_acqseq);
1494
Herbert Xu658b2192007-10-09 13:29:52 -07001495int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496{
David S. Millerf034b5d2006-08-24 03:08:07 -07001497 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001499 int err = -ENOENT;
1500 __be32 minspi = htonl(low);
1501 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
Herbert Xu658b2192007-10-09 13:29:52 -07001503 spin_lock_bh(&x->lock);
1504 if (x->km.state == XFRM_STATE_DEAD)
1505 goto unlock;
1506
1507 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001509 goto unlock;
1510
1511 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 if (minspi == maxspi) {
1514 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1515 if (x0) {
1516 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001517 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 }
1519 x->id.spi = minspi;
1520 } else {
1521 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001522 for (h=0; h<high-low+1; h++) {
1523 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1525 if (x0 == NULL) {
1526 x->id.spi = htonl(spi);
1527 break;
1528 }
1529 xfrm_state_put(x0);
1530 }
1531 }
1532 if (x->id.spi) {
1533 spin_lock_bh(&xfrm_state_lock);
1534 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001535 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001537
1538 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 }
Herbert Xu658b2192007-10-09 13:29:52 -07001540
1541unlock:
1542 spin_unlock_bh(&x->lock);
1543
1544 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545}
1546EXPORT_SYMBOL(xfrm_alloc_spi);
1547
Timo Teras4c563f72008-02-28 21:31:08 -08001548int xfrm_state_walk(struct xfrm_state_walk *walk,
1549 int (*func)(struct xfrm_state *, int, void*),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 void *data)
1551{
Herbert Xu12a169e2008-10-01 07:03:24 -07001552 struct xfrm_state *state;
1553 struct xfrm_state_walk *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 int err = 0;
1555
Herbert Xu12a169e2008-10-01 07:03:24 -07001556 if (walk->seq != 0 && list_empty(&walk->all))
Timo Teras4c563f72008-02-28 21:31:08 -08001557 return 0;
1558
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 spin_lock_bh(&xfrm_state_lock);
Herbert Xu12a169e2008-10-01 07:03:24 -07001560 if (list_empty(&walk->all))
1561 x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all);
1562 else
1563 x = list_entry(&walk->all, struct xfrm_state_walk, all);
Timo Teras4c563f72008-02-28 21:31:08 -08001564 list_for_each_entry_from(x, &xfrm_state_all, all) {
Herbert Xu12a169e2008-10-01 07:03:24 -07001565 if (x->state == XFRM_STATE_DEAD)
Timo Teras4c563f72008-02-28 21:31:08 -08001566 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001567 state = container_of(x, struct xfrm_state, km);
1568 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
Timo Teras4c563f72008-02-28 21:31:08 -08001569 continue;
Herbert Xu12a169e2008-10-01 07:03:24 -07001570 err = func(state, walk->seq, data);
1571 if (err) {
1572 list_move_tail(&walk->all, &x->all);
1573 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001575 walk->seq++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001577 if (walk->seq == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 err = -ENOENT;
1579 goto out;
1580 }
Herbert Xu12a169e2008-10-01 07:03:24 -07001581 list_del_init(&walk->all);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582out:
1583 spin_unlock_bh(&xfrm_state_lock);
1584 return err;
1585}
1586EXPORT_SYMBOL(xfrm_state_walk);
1587
Herbert Xu5c182452008-09-22 19:48:19 -07001588void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1589{
Herbert Xu12a169e2008-10-01 07:03:24 -07001590 INIT_LIST_HEAD(&walk->all);
Herbert Xu5c182452008-09-22 19:48:19 -07001591 walk->proto = proto;
Herbert Xu12a169e2008-10-01 07:03:24 -07001592 walk->state = XFRM_STATE_DEAD;
1593 walk->seq = 0;
Herbert Xu5c182452008-09-22 19:48:19 -07001594}
1595EXPORT_SYMBOL(xfrm_state_walk_init);
1596
Herbert Xuabb81c42008-09-09 19:58:29 -07001597void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1598{
Herbert Xu12a169e2008-10-01 07:03:24 -07001599 if (list_empty(&walk->all))
Herbert Xu5c182452008-09-22 19:48:19 -07001600 return;
Herbert Xu5c182452008-09-22 19:48:19 -07001601
Herbert Xu12a169e2008-10-01 07:03:24 -07001602 spin_lock_bh(&xfrm_state_lock);
1603 list_del(&walk->all);
1604 spin_lock_bh(&xfrm_state_lock);
Herbert Xuabb81c42008-09-09 19:58:29 -07001605}
1606EXPORT_SYMBOL(xfrm_state_walk_done);
1607
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001608
1609void xfrm_replay_notify(struct xfrm_state *x, int event)
1610{
1611 struct km_event c;
1612 /* we send notify messages in case
1613 * 1. we updated on of the sequence numbers, and the seqno difference
1614 * is at least x->replay_maxdiff, in this case we also update the
1615 * timeout of our timer function
1616 * 2. if x->replay_maxage has elapsed since last update,
1617 * and there were changes
1618 *
1619 * The state structure must be locked!
1620 */
1621
1622 switch (event) {
1623 case XFRM_REPLAY_UPDATE:
1624 if (x->replay_maxdiff &&
1625 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001626 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1627 if (x->xflags & XFRM_TIME_DEFER)
1628 event = XFRM_REPLAY_TIMEOUT;
1629 else
1630 return;
1631 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001632
1633 break;
1634
1635 case XFRM_REPLAY_TIMEOUT:
1636 if ((x->replay.seq == x->preplay.seq) &&
1637 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001638 (x->replay.oseq == x->preplay.oseq)) {
1639 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001640 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001641 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001642
1643 break;
1644 }
1645
1646 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1647 c.event = XFRM_MSG_NEWAE;
1648 c.data.aevent = event;
1649 km_state_notify(x, &c);
1650
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001651 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001652 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001653 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001654}
1655
1656static void xfrm_replay_timer_handler(unsigned long data)
1657{
1658 struct xfrm_state *x = (struct xfrm_state*)data;
1659
1660 spin_lock(&x->lock);
1661
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001662 if (x->km.state == XFRM_STATE_VALID) {
1663 if (xfrm_aevent_is_on())
1664 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1665 else
1666 x->xflags |= XFRM_TIME_DEFER;
1667 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001668
1669 spin_unlock(&x->lock);
1670}
1671
Paul Mooreafeb14b2007-12-21 14:58:11 -08001672int xfrm_replay_check(struct xfrm_state *x,
1673 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674{
1675 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001676 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
1678 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001679 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
1681 if (likely(seq > x->replay.seq))
1682 return 0;
1683
1684 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001685 if (diff >= min_t(unsigned int, x->props.replay_window,
1686 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001688 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 }
1690
1691 if (x->replay.bitmap & (1U << diff)) {
1692 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001693 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 }
1695 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001696
1697err:
1698 xfrm_audit_state_replay(x, skb, net_seq);
1699 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
Al Viro61f46272006-09-27 18:48:33 -07001702void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
1704 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001705 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
1707 if (seq > x->replay.seq) {
1708 diff = seq - x->replay.seq;
1709 if (diff < x->props.replay_window)
1710 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1711 else
1712 x->replay.bitmap = 1;
1713 x->replay.seq = seq;
1714 } else {
1715 diff = x->replay.seq - seq;
1716 x->replay.bitmap |= (1U << diff);
1717 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001718
1719 if (xfrm_aevent_is_on())
1720 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722
Denis Chengdf018122007-12-07 00:51:11 -08001723static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724static DEFINE_RWLOCK(xfrm_km_lock);
1725
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001726void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727{
1728 struct xfrm_mgr *km;
1729
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001730 read_lock(&xfrm_km_lock);
1731 list_for_each_entry(km, &xfrm_km_list, list)
1732 if (km->notify_policy)
1733 km->notify_policy(xp, dir, c);
1734 read_unlock(&xfrm_km_lock);
1735}
1736
1737void km_state_notify(struct xfrm_state *x, struct km_event *c)
1738{
1739 struct xfrm_mgr *km;
1740 read_lock(&xfrm_km_lock);
1741 list_for_each_entry(km, &xfrm_km_list, list)
1742 if (km->notify)
1743 km->notify(x, c);
1744 read_unlock(&xfrm_km_lock);
1745}
1746
1747EXPORT_SYMBOL(km_policy_notify);
1748EXPORT_SYMBOL(km_state_notify);
1749
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001750void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001751{
1752 struct km_event c;
1753
Herbert Xubf088672005-06-18 22:44:00 -07001754 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001755 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001756 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001757 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 if (hard)
1760 wake_up(&km_waitq);
1761}
1762
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001763EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001764/*
1765 * We send to all registered managers regardless of failure
1766 * We are happy with one success
1767*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001768int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001770 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 struct xfrm_mgr *km;
1772
1773 read_lock(&xfrm_km_lock);
1774 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001775 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1776 if (!acqret)
1777 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 }
1779 read_unlock(&xfrm_km_lock);
1780 return err;
1781}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001782EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783
Al Viro5d36b182006-11-08 00:24:06 -08001784int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785{
1786 int err = -EINVAL;
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->new_mapping)
1792 err = km->new_mapping(x, ipaddr, sport);
1793 if (!err)
1794 break;
1795 }
1796 read_unlock(&xfrm_km_lock);
1797 return err;
1798}
1799EXPORT_SYMBOL(km_new_mapping);
1800
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001801void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001803 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Herbert Xubf088672005-06-18 22:44:00 -07001805 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001806 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001807 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001808 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
1810 if (hard)
1811 wake_up(&km_waitq);
1812}
David S. Millera70fcb02006-03-20 19:18:52 -08001813EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001815#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001816int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001817 struct xfrm_migrate *m, int num_migrate,
1818 struct xfrm_kmaddress *k)
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001819{
1820 int err = -EINVAL;
1821 int ret;
1822 struct xfrm_mgr *km;
1823
1824 read_lock(&xfrm_km_lock);
1825 list_for_each_entry(km, &xfrm_km_list, list) {
1826 if (km->migrate) {
Arnaud Ebalard13c1d182008-10-05 13:33:42 -07001827 ret = km->migrate(sel, dir, type, m, num_migrate, k);
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001828 if (!ret)
1829 err = ret;
1830 }
1831 }
1832 read_unlock(&xfrm_km_lock);
1833 return err;
1834}
1835EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001836#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001837
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001838int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1839{
1840 int err = -EINVAL;
1841 int ret;
1842 struct xfrm_mgr *km;
1843
1844 read_lock(&xfrm_km_lock);
1845 list_for_each_entry(km, &xfrm_km_list, list) {
1846 if (km->report) {
1847 ret = km->report(proto, sel, addr);
1848 if (!ret)
1849 err = ret;
1850 }
1851 }
1852 read_unlock(&xfrm_km_lock);
1853 return err;
1854}
1855EXPORT_SYMBOL(km_report);
1856
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1858{
1859 int err;
1860 u8 *data;
1861 struct xfrm_mgr *km;
1862 struct xfrm_policy *pol = NULL;
1863
1864 if (optlen <= 0 || optlen > PAGE_SIZE)
1865 return -EMSGSIZE;
1866
1867 data = kmalloc(optlen, GFP_KERNEL);
1868 if (!data)
1869 return -ENOMEM;
1870
1871 err = -EFAULT;
1872 if (copy_from_user(data, optval, optlen))
1873 goto out;
1874
1875 err = -EINVAL;
1876 read_lock(&xfrm_km_lock);
1877 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001878 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 optlen, &err);
1880 if (err >= 0)
1881 break;
1882 }
1883 read_unlock(&xfrm_km_lock);
1884
1885 if (err >= 0) {
1886 xfrm_sk_policy_insert(sk, err, pol);
1887 xfrm_pol_put(pol);
1888 err = 0;
1889 }
1890
1891out:
1892 kfree(data);
1893 return err;
1894}
1895EXPORT_SYMBOL(xfrm_user_policy);
1896
1897int xfrm_register_km(struct xfrm_mgr *km)
1898{
1899 write_lock_bh(&xfrm_km_lock);
1900 list_add_tail(&km->list, &xfrm_km_list);
1901 write_unlock_bh(&xfrm_km_lock);
1902 return 0;
1903}
1904EXPORT_SYMBOL(xfrm_register_km);
1905
1906int xfrm_unregister_km(struct xfrm_mgr *km)
1907{
1908 write_lock_bh(&xfrm_km_lock);
1909 list_del(&km->list);
1910 write_unlock_bh(&xfrm_km_lock);
1911 return 0;
1912}
1913EXPORT_SYMBOL(xfrm_unregister_km);
1914
1915int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1916{
1917 int err = 0;
1918 if (unlikely(afinfo == NULL))
1919 return -EINVAL;
1920 if (unlikely(afinfo->family >= NPROTO))
1921 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001922 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1924 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001925 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001927 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 return err;
1929}
1930EXPORT_SYMBOL(xfrm_state_register_afinfo);
1931
1932int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1933{
1934 int err = 0;
1935 if (unlikely(afinfo == NULL))
1936 return -EINVAL;
1937 if (unlikely(afinfo->family >= NPROTO))
1938 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001939 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1941 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1942 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001943 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001946 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 return err;
1948}
1949EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1950
Herbert Xu17c2a422007-10-17 21:33:12 -07001951static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952{
1953 struct xfrm_state_afinfo *afinfo;
1954 if (unlikely(family >= NPROTO))
1955 return NULL;
1956 read_lock(&xfrm_state_afinfo_lock);
1957 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001958 if (unlikely(!afinfo))
1959 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 return afinfo;
1961}
1962
Herbert Xu17c2a422007-10-17 21:33:12 -07001963static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Eric Dumazet9a429c42008-01-01 21:58:02 -08001964 __releases(xfrm_state_afinfo_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965{
Herbert Xu546be242006-05-27 23:03:58 -07001966 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967}
1968
1969/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1970void xfrm_state_delete_tunnel(struct xfrm_state *x)
1971{
1972 if (x->tunnel) {
1973 struct xfrm_state *t = x->tunnel;
1974
1975 if (atomic_read(&t->tunnel_users) == 2)
1976 xfrm_state_delete(t);
1977 atomic_dec(&t->tunnel_users);
1978 xfrm_state_put(t);
1979 x->tunnel = NULL;
1980 }
1981}
1982EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1983
1984int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1985{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001986 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
Patrick McHardyc5c25232007-04-09 11:47:18 -07001988 spin_lock_bh(&x->lock);
1989 if (x->km.state == XFRM_STATE_VALID &&
1990 x->type && x->type->get_mtu)
1991 res = x->type->get_mtu(x, mtu);
1992 else
Patrick McHardy28121612007-06-18 22:30:15 -07001993 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001994 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 return res;
1996}
1997
Herbert Xu72cb6962005-06-20 13:18:08 -07001998int xfrm_init_state(struct xfrm_state *x)
1999{
Herbert Xud094cd82005-06-20 13:19:41 -07002000 struct xfrm_state_afinfo *afinfo;
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002001 struct xfrm_mode *inner_mode;
Herbert Xud094cd82005-06-20 13:19:41 -07002002 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07002003 int err;
2004
Herbert Xud094cd82005-06-20 13:19:41 -07002005 err = -EAFNOSUPPORT;
2006 afinfo = xfrm_state_get_afinfo(family);
2007 if (!afinfo)
2008 goto error;
2009
2010 err = 0;
2011 if (afinfo->init_flags)
2012 err = afinfo->init_flags(x);
2013
2014 xfrm_state_put_afinfo(afinfo);
2015
2016 if (err)
2017 goto error;
2018
2019 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07002020
Kazunori MIYAZAWAdf9dcb42008-03-24 14:51:51 -07002021 if (x->sel.family != AF_UNSPEC) {
2022 inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2023 if (inner_mode == NULL)
2024 goto error;
2025
2026 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2027 family != x->sel.family) {
2028 xfrm_put_mode(inner_mode);
2029 goto error;
2030 }
2031
2032 x->inner_mode = inner_mode;
2033 } else {
2034 struct xfrm_mode *inner_mode_iaf;
2035
2036 inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2037 if (inner_mode == NULL)
2038 goto error;
2039
2040 if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2041 xfrm_put_mode(inner_mode);
2042 goto error;
2043 }
2044
2045 inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2046 if (inner_mode_iaf == NULL)
2047 goto error;
2048
2049 if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2050 xfrm_put_mode(inner_mode_iaf);
2051 goto error;
2052 }
2053
2054 if (x->props.family == AF_INET) {
2055 x->inner_mode = inner_mode;
2056 x->inner_mode_iaf = inner_mode_iaf;
2057 } else {
2058 x->inner_mode = inner_mode_iaf;
2059 x->inner_mode_iaf = inner_mode;
2060 }
2061 }
Herbert Xu13996372007-10-17 21:35:51 -07002062
Herbert Xud094cd82005-06-20 13:19:41 -07002063 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07002064 if (x->type == NULL)
2065 goto error;
2066
2067 err = x->type->init_state(x);
2068 if (err)
2069 goto error;
2070
Herbert Xu13996372007-10-17 21:35:51 -07002071 x->outer_mode = xfrm_get_mode(x->props.mode, family);
2072 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07002073 goto error;
2074
Herbert Xu72cb6962005-06-20 13:18:08 -07002075 x->km.state = XFRM_STATE_VALID;
2076
2077error:
2078 return err;
2079}
2080
2081EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09002082
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083void __init xfrm_state_init(void)
2084{
David S. Millerf034b5d2006-08-24 03:08:07 -07002085 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
David S. Millerf034b5d2006-08-24 03:08:07 -07002087 sz = sizeof(struct hlist_head) * 8;
2088
David S. Miller44e36b42006-08-24 04:50:50 -07002089 xfrm_state_bydst = xfrm_hash_alloc(sz);
2090 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2091 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002092 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2093 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2094 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2095
David Howellsc4028952006-11-22 14:57:56 +00002096 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097}
2098
Joy Lattenab5f5e82007-09-17 11:51:22 -07002099#ifdef CONFIG_AUDITSYSCALL
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002100static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2101 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002102{
Paul Moore68277ac2007-12-20 20:49:33 -08002103 struct xfrm_sec_ctx *ctx = x->security;
2104 u32 spi = ntohl(x->id.spi);
2105
2106 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002107 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002108 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002109
2110 switch(x->props.family) {
2111 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002112 audit_log_format(audit_buf,
2113 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002114 NIPQUAD(x->props.saddr.a4),
2115 NIPQUAD(x->id.daddr.a4));
2116 break;
2117 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002118 audit_log_format(audit_buf,
2119 " src=" NIP6_FMT " dst=" NIP6_FMT,
2120 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2121 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002122 break;
2123 }
Paul Moore68277ac2007-12-20 20:49:33 -08002124
2125 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002126}
2127
Ilpo Järvinencf35f432008-01-05 23:13:20 -08002128static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2129 struct audit_buffer *audit_buf)
Paul Mooreafeb14b2007-12-21 14:58:11 -08002130{
2131 struct iphdr *iph4;
2132 struct ipv6hdr *iph6;
2133
2134 switch (family) {
2135 case AF_INET:
2136 iph4 = ip_hdr(skb);
2137 audit_log_format(audit_buf,
2138 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2139 NIPQUAD(iph4->saddr),
2140 NIPQUAD(iph4->daddr));
2141 break;
2142 case AF_INET6:
2143 iph6 = ipv6_hdr(skb);
2144 audit_log_format(audit_buf,
2145 " src=" NIP6_FMT " dst=" NIP6_FMT
YOSHIFUJI Hideaki5e2c4332008-04-26 22:24:10 -07002146 " flowlbl=0x%x%02x%02x",
Paul Mooreafeb14b2007-12-21 14:58:11 -08002147 NIP6(iph6->saddr),
2148 NIP6(iph6->daddr),
2149 iph6->flow_lbl[0] & 0x0f,
2150 iph6->flow_lbl[1],
2151 iph6->flow_lbl[2]);
2152 break;
2153 }
2154}
2155
Paul Moore68277ac2007-12-20 20:49:33 -08002156void xfrm_audit_state_add(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002157 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002158{
2159 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002160
Paul Mooreafeb14b2007-12-21 14:58:11 -08002161 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002162 if (audit_buf == NULL)
2163 return;
Eric Paris25323862008-04-18 10:09:25 -04002164 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002165 xfrm_audit_helper_sainfo(x, audit_buf);
2166 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002167 audit_log_end(audit_buf);
2168}
2169EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2170
Paul Moore68277ac2007-12-20 20:49:33 -08002171void xfrm_audit_state_delete(struct xfrm_state *x, int result,
Eric Paris25323862008-04-18 10:09:25 -04002172 uid_t auid, u32 sessionid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002173{
2174 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002175
Paul Mooreafeb14b2007-12-21 14:58:11 -08002176 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002177 if (audit_buf == NULL)
2178 return;
Eric Paris25323862008-04-18 10:09:25 -04002179 xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002180 xfrm_audit_helper_sainfo(x, audit_buf);
2181 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002182 audit_log_end(audit_buf);
2183}
2184EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002185
2186void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2187 struct sk_buff *skb)
2188{
2189 struct audit_buffer *audit_buf;
2190 u32 spi;
2191
2192 audit_buf = xfrm_audit_start("SA-replay-overflow");
2193 if (audit_buf == NULL)
2194 return;
2195 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2196 /* don't record the sequence number because it's inherent in this kind
2197 * of audit message */
2198 spi = ntohl(x->id.spi);
2199 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2200 audit_log_end(audit_buf);
2201}
2202EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2203
2204static void xfrm_audit_state_replay(struct xfrm_state *x,
2205 struct sk_buff *skb, __be32 net_seq)
2206{
2207 struct audit_buffer *audit_buf;
2208 u32 spi;
2209
2210 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2211 if (audit_buf == NULL)
2212 return;
2213 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2214 spi = ntohl(x->id.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}
2219
2220void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2221{
2222 struct audit_buffer *audit_buf;
2223
2224 audit_buf = xfrm_audit_start("SA-notfound");
2225 if (audit_buf == NULL)
2226 return;
2227 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2228 audit_log_end(audit_buf);
2229}
2230EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2231
2232void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2233 __be32 net_spi, __be32 net_seq)
2234{
2235 struct audit_buffer *audit_buf;
2236 u32 spi;
2237
2238 audit_buf = xfrm_audit_start("SA-notfound");
2239 if (audit_buf == NULL)
2240 return;
2241 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2242 spi = ntohl(net_spi);
2243 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2244 spi, spi, ntohl(net_seq));
2245 audit_log_end(audit_buf);
2246}
2247EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2248
2249void xfrm_audit_state_icvfail(struct xfrm_state *x,
2250 struct sk_buff *skb, u8 proto)
2251{
2252 struct audit_buffer *audit_buf;
2253 __be32 net_spi;
2254 __be32 net_seq;
2255
2256 audit_buf = xfrm_audit_start("SA-icv-failure");
2257 if (audit_buf == NULL)
2258 return;
2259 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2260 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2261 u32 spi = ntohl(net_spi);
2262 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2263 spi, spi, ntohl(net_seq));
2264 }
2265 audit_log_end(audit_buf);
2266}
2267EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002268#endif /* CONFIG_AUDITSYSCALL */