blob: 6bf876c866dfec49be76391c2cfa22d4430e8a74 [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 */
David S. Millerf034b5d2006-08-24 03:08:07 -070053static struct hlist_head *xfrm_state_bydst __read_mostly;
54static struct hlist_head *xfrm_state_bysrc __read_mostly;
55static struct hlist_head *xfrm_state_byspi __read_mostly;
56static unsigned int xfrm_state_hmask __read_mostly;
57static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
58static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070059static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Herbert Xu17c2a422007-10-17 21:33:12 -070061static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
62static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
63
Paul Mooreafeb14b2007-12-21 14:58:11 -080064#ifdef CONFIG_AUDITSYSCALL
65static void xfrm_audit_state_replay(struct xfrm_state *x,
66 struct sk_buff *skb, __be32 net_seq);
67#else
68#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
69#endif /* CONFIG_AUDITSYSCALL */
70
David S. Millerc1969f22006-08-24 04:00:03 -070071static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
72 xfrm_address_t *saddr,
73 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070074 unsigned short family)
75{
David S. Millerc1969f22006-08-24 04:00:03 -070076 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070077}
78
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070079static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
80 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070081 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070082{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070083 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070084}
85
David S. Miller2575b652006-08-24 03:26:44 -070086static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070087xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070088{
David S. Millerc1969f22006-08-24 04:00:03 -070089 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070090}
91
David S. Millerf034b5d2006-08-24 03:08:07 -070092static void xfrm_hash_transfer(struct hlist_head *list,
93 struct hlist_head *ndsttable,
94 struct hlist_head *nsrctable,
95 struct hlist_head *nspitable,
96 unsigned int nhashmask)
97{
98 struct hlist_node *entry, *tmp;
99 struct xfrm_state *x;
100
101 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
102 unsigned int h;
103
David S. Millerc1969f22006-08-24 04:00:03 -0700104 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
105 x->props.reqid, x->props.family,
106 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700107 hlist_add_head(&x->bydst, ndsttable+h);
108
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700109 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
110 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700111 nhashmask);
112 hlist_add_head(&x->bysrc, nsrctable+h);
113
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700114 if (x->id.spi) {
115 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
116 x->id.proto, x->props.family,
117 nhashmask);
118 hlist_add_head(&x->byspi, nspitable+h);
119 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700120 }
121}
122
123static unsigned long xfrm_hash_new_size(void)
124{
125 return ((xfrm_state_hmask + 1) << 1) *
126 sizeof(struct hlist_head);
127}
128
129static DEFINE_MUTEX(hash_resize_mutex);
130
David Howellsc4028952006-11-22 14:57:56 +0000131static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700132{
133 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
134 unsigned long nsize, osize;
135 unsigned int nhashmask, ohashmask;
136 int i;
137
138 mutex_lock(&hash_resize_mutex);
139
140 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700141 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 if (!ndst)
143 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700144 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700145 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700146 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700147 goto out_unlock;
148 }
David S. Miller44e36b42006-08-24 04:50:50 -0700149 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700150 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700151 xfrm_hash_free(ndst, nsize);
152 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700153 goto out_unlock;
154 }
155
156 spin_lock_bh(&xfrm_state_lock);
157
158 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
159 for (i = xfrm_state_hmask; i >= 0; i--)
160 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
161 nhashmask);
162
163 odst = xfrm_state_bydst;
164 osrc = xfrm_state_bysrc;
165 ospi = xfrm_state_byspi;
166 ohashmask = xfrm_state_hmask;
167
168 xfrm_state_bydst = ndst;
169 xfrm_state_bysrc = nsrc;
170 xfrm_state_byspi = nspi;
171 xfrm_state_hmask = nhashmask;
172
173 spin_unlock_bh(&xfrm_state_lock);
174
175 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700176 xfrm_hash_free(odst, osize);
177 xfrm_hash_free(osrc, osize);
178 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700179
180out_unlock:
181 mutex_unlock(&hash_resize_mutex);
182}
183
David Howellsc4028952006-11-22 14:57:56 +0000184static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186DECLARE_WAIT_QUEUE_HEAD(km_waitq);
187EXPORT_SYMBOL(km_waitq);
188
189static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
190static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
191
192static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700193static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194static DEFINE_SPINLOCK(xfrm_state_gc_lock);
195
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800196int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800198int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800199void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700201static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
202{
203 struct xfrm_state_afinfo *afinfo;
204 if (unlikely(family >= NPROTO))
205 return NULL;
206 write_lock_bh(&xfrm_state_afinfo_lock);
207 afinfo = xfrm_state_afinfo[family];
208 if (unlikely(!afinfo))
209 write_unlock_bh(&xfrm_state_afinfo_lock);
210 return afinfo;
211}
212
213static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
214{
215 write_unlock_bh(&xfrm_state_afinfo_lock);
216}
217
218int xfrm_register_type(struct xfrm_type *type, unsigned short family)
219{
220 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
221 struct xfrm_type **typemap;
222 int err = 0;
223
224 if (unlikely(afinfo == NULL))
225 return -EAFNOSUPPORT;
226 typemap = afinfo->type_map;
227
228 if (likely(typemap[type->proto] == NULL))
229 typemap[type->proto] = type;
230 else
231 err = -EEXIST;
232 xfrm_state_unlock_afinfo(afinfo);
233 return err;
234}
235EXPORT_SYMBOL(xfrm_register_type);
236
237int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
238{
239 struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
240 struct xfrm_type **typemap;
241 int err = 0;
242
243 if (unlikely(afinfo == NULL))
244 return -EAFNOSUPPORT;
245 typemap = afinfo->type_map;
246
247 if (unlikely(typemap[type->proto] != type))
248 err = -ENOENT;
249 else
250 typemap[type->proto] = NULL;
251 xfrm_state_unlock_afinfo(afinfo);
252 return err;
253}
254EXPORT_SYMBOL(xfrm_unregister_type);
255
256static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
257{
258 struct xfrm_state_afinfo *afinfo;
259 struct xfrm_type **typemap;
260 struct xfrm_type *type;
261 int modload_attempted = 0;
262
263retry:
264 afinfo = xfrm_state_get_afinfo(family);
265 if (unlikely(afinfo == NULL))
266 return NULL;
267 typemap = afinfo->type_map;
268
269 type = typemap[proto];
270 if (unlikely(type && !try_module_get(type->owner)))
271 type = NULL;
272 if (!type && !modload_attempted) {
273 xfrm_state_put_afinfo(afinfo);
274 request_module("xfrm-type-%d-%d", family, proto);
275 modload_attempted = 1;
276 goto retry;
277 }
278
279 xfrm_state_put_afinfo(afinfo);
280 return type;
281}
282
283static void xfrm_put_type(struct xfrm_type *type)
284{
285 module_put(type->owner);
286}
287
288int xfrm_register_mode(struct xfrm_mode *mode, int family)
289{
290 struct xfrm_state_afinfo *afinfo;
291 struct xfrm_mode **modemap;
292 int err;
293
294 if (unlikely(mode->encap >= XFRM_MODE_MAX))
295 return -EINVAL;
296
297 afinfo = xfrm_state_lock_afinfo(family);
298 if (unlikely(afinfo == NULL))
299 return -EAFNOSUPPORT;
300
301 err = -EEXIST;
302 modemap = afinfo->mode_map;
Herbert Xu17c2a422007-10-17 21:33:12 -0700303 if (modemap[mode->encap])
304 goto out;
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700305
Herbert Xu17c2a422007-10-17 21:33:12 -0700306 err = -ENOENT;
307 if (!try_module_get(afinfo->owner))
308 goto out;
309
310 mode->afinfo = afinfo;
311 modemap[mode->encap] = mode;
312 err = 0;
313
314out:
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700315 xfrm_state_unlock_afinfo(afinfo);
316 return err;
317}
318EXPORT_SYMBOL(xfrm_register_mode);
319
320int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
321{
322 struct xfrm_state_afinfo *afinfo;
323 struct xfrm_mode **modemap;
324 int err;
325
326 if (unlikely(mode->encap >= XFRM_MODE_MAX))
327 return -EINVAL;
328
329 afinfo = xfrm_state_lock_afinfo(family);
330 if (unlikely(afinfo == NULL))
331 return -EAFNOSUPPORT;
332
333 err = -ENOENT;
334 modemap = afinfo->mode_map;
335 if (likely(modemap[mode->encap] == mode)) {
336 modemap[mode->encap] = NULL;
Herbert Xu17c2a422007-10-17 21:33:12 -0700337 module_put(mode->afinfo->owner);
Herbert Xuaa5d62c2007-10-17 21:31:12 -0700338 err = 0;
339 }
340
341 xfrm_state_unlock_afinfo(afinfo);
342 return err;
343}
344EXPORT_SYMBOL(xfrm_unregister_mode);
345
346static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
347{
348 struct xfrm_state_afinfo *afinfo;
349 struct xfrm_mode *mode;
350 int modload_attempted = 0;
351
352 if (unlikely(encap >= XFRM_MODE_MAX))
353 return NULL;
354
355retry:
356 afinfo = xfrm_state_get_afinfo(family);
357 if (unlikely(afinfo == NULL))
358 return NULL;
359
360 mode = afinfo->mode_map[encap];
361 if (unlikely(mode && !try_module_get(mode->owner)))
362 mode = NULL;
363 if (!mode && !modload_attempted) {
364 xfrm_state_put_afinfo(afinfo);
365 request_module("xfrm-mode-%d-%d", family, encap);
366 modload_attempted = 1;
367 goto retry;
368 }
369
370 xfrm_state_put_afinfo(afinfo);
371 return mode;
372}
373
374static void xfrm_put_mode(struct xfrm_mode *mode)
375{
376 module_put(mode->owner);
377}
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379static void xfrm_state_gc_destroy(struct xfrm_state *x)
380{
David S. Millera47f0ce2006-08-24 03:54:22 -0700381 del_timer_sync(&x->timer);
382 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800383 kfree(x->aalg);
384 kfree(x->ealg);
385 kfree(x->calg);
386 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700387 kfree(x->coaddr);
Herbert Xu13996372007-10-17 21:35:51 -0700388 if (x->inner_mode)
389 xfrm_put_mode(x->inner_mode);
390 if (x->outer_mode)
391 xfrm_put_mode(x->outer_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 if (x->type) {
393 x->type->destructor(x);
394 xfrm_put_type(x->type);
395 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800396 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 kfree(x);
398}
399
David Howellsc4028952006-11-22 14:57:56 +0000400static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
402 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700403 struct hlist_node *entry, *tmp;
404 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700407 gc_list.first = xfrm_state_gc_list.first;
408 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 spin_unlock_bh(&xfrm_state_gc_lock);
410
David S. Miller8f126e32006-08-24 02:45:07 -0700411 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 wake_up(&km_waitq);
415}
416
417static inline unsigned long make_jiffies(long secs)
418{
419 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
420 return MAX_SCHEDULE_TIMEOUT-1;
421 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900422 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423}
424
425static void xfrm_timer_handler(unsigned long data)
426{
427 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800428 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 long next = LONG_MAX;
430 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600431 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
433 spin_lock(&x->lock);
434 if (x->km.state == XFRM_STATE_DEAD)
435 goto out;
436 if (x->km.state == XFRM_STATE_EXPIRED)
437 goto expired;
438 if (x->lft.hard_add_expires_seconds) {
439 long tmo = x->lft.hard_add_expires_seconds +
440 x->curlft.add_time - now;
441 if (tmo <= 0)
442 goto expired;
443 if (tmo < next)
444 next = tmo;
445 }
446 if (x->lft.hard_use_expires_seconds) {
447 long tmo = x->lft.hard_use_expires_seconds +
448 (x->curlft.use_time ? : now) - now;
449 if (tmo <= 0)
450 goto expired;
451 if (tmo < next)
452 next = tmo;
453 }
454 if (x->km.dying)
455 goto resched;
456 if (x->lft.soft_add_expires_seconds) {
457 long tmo = x->lft.soft_add_expires_seconds +
458 x->curlft.add_time - now;
459 if (tmo <= 0)
460 warn = 1;
461 else if (tmo < next)
462 next = tmo;
463 }
464 if (x->lft.soft_use_expires_seconds) {
465 long tmo = x->lft.soft_use_expires_seconds +
466 (x->curlft.use_time ? : now) - now;
467 if (tmo <= 0)
468 warn = 1;
469 else if (tmo < next)
470 next = tmo;
471 }
472
Herbert Xu4666faa2005-06-18 22:43:22 -0700473 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800475 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700477 if (next != LONG_MAX)
478 mod_timer(&x->timer, jiffies + make_jiffies(next));
479
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 goto out;
481
482expired:
483 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
484 x->km.state = XFRM_STATE_EXPIRED;
485 wake_up(&km_waitq);
486 next = 2;
487 goto resched;
488 }
Joy Latten161a09e2006-11-27 13:11:54 -0600489
490 err = __xfrm_state_delete(x);
491 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800492 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Joy Lattenab5f5e82007-09-17 11:51:22 -0700494 xfrm_audit_state_delete(x, err ? 0 : 1,
495 audit_get_loginuid(current->audit_context), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497out:
498 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
David S. Miller0ac84752006-03-20 19:18:23 -0800501static void xfrm_replay_timer_handler(unsigned long data);
502
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503struct xfrm_state *xfrm_state_alloc(void)
504{
505 struct xfrm_state *x;
506
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700507 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 atomic_set(&x->refcnt, 1);
511 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700512 INIT_HLIST_NODE(&x->bydst);
513 INIT_HLIST_NODE(&x->bysrc);
514 INIT_HLIST_NODE(&x->byspi);
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800515 setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
516 setup_timer(&x->rtimer, xfrm_replay_timer_handler,
517 (unsigned long)x);
James Morris9d729f72007-03-04 16:12:44 -0800518 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 x->lft.soft_byte_limit = XFRM_INF;
520 x->lft.soft_packet_limit = XFRM_INF;
521 x->lft.hard_byte_limit = XFRM_INF;
522 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800523 x->replay_maxage = 0;
524 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 spin_lock_init(&x->lock);
526 }
527 return x;
528}
529EXPORT_SYMBOL(xfrm_state_alloc);
530
531void __xfrm_state_destroy(struct xfrm_state *x)
532{
533 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
534
535 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700536 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 spin_unlock_bh(&xfrm_state_gc_lock);
538 schedule_work(&xfrm_state_gc_work);
539}
540EXPORT_SYMBOL(__xfrm_state_destroy);
541
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800542int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700544 int err = -ESRCH;
545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 if (x->km.state != XFRM_STATE_DEAD) {
547 x->km.state = XFRM_STATE_DEAD;
548 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700549 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700550 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700551 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700552 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700553 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 /* All xfrm_state objects are created by xfrm_state_alloc.
557 * The xfrm_state_alloc call gives a reference, and that
558 * is what we are dropping here.
559 */
Patrick McHardy5dba4792007-11-27 11:10:07 +0800560 xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700561 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700563
564 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800566EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700568int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700570 int err;
571
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700573 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700575
576 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577}
578EXPORT_SYMBOL(xfrm_state_delete);
579
Joy Latten4aa2e622007-06-04 19:05:57 -0400580#ifdef CONFIG_SECURITY_NETWORK_XFRM
581static inline int
582xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Joy Latten4aa2e622007-06-04 19:05:57 -0400584 int i, err = 0;
585
586 for (i = 0; i <= xfrm_state_hmask; i++) {
587 struct hlist_node *entry;
588 struct xfrm_state *x;
589
590 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
591 if (xfrm_id_proto_match(x->id.proto, proto) &&
592 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700593 xfrm_audit_state_delete(x, 0,
594 audit_info->loginuid,
595 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400596 return err;
597 }
598 }
599 }
600
601 return err;
602}
603#else
604static inline int
605xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
606{
607 return 0;
608}
609#endif
610
611int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
612{
613 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400616 err = xfrm_state_flush_secctx_check(proto, audit_info);
617 if (err)
618 goto out;
619
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700620 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700621 struct hlist_node *entry;
622 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700624 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700626 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 xfrm_state_hold(x);
628 spin_unlock_bh(&xfrm_state_lock);
629
Joy Latten161a09e2006-11-27 13:11:54 -0600630 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700631 xfrm_audit_state_delete(x, err ? 0 : 1,
632 audit_info->loginuid,
633 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 xfrm_state_put(x);
635
636 spin_lock_bh(&xfrm_state_lock);
637 goto restart;
638 }
639 }
640 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400641 err = 0;
642
643out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 spin_unlock_bh(&xfrm_state_lock);
645 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400646 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647}
648EXPORT_SYMBOL(xfrm_state_flush);
649
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700650void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700651{
652 spin_lock_bh(&xfrm_state_lock);
653 si->sadcnt = xfrm_state_num;
654 si->sadhcnt = xfrm_state_hmask;
655 si->sadhmcnt = xfrm_state_hashmax;
656 spin_unlock_bh(&xfrm_state_lock);
657}
658EXPORT_SYMBOL(xfrm_sad_getinfo);
659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660static int
661xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
662 struct xfrm_tmpl *tmpl,
663 xfrm_address_t *daddr, xfrm_address_t *saddr,
664 unsigned short family)
665{
666 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
667 if (!afinfo)
668 return -1;
669 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
670 xfrm_state_put_afinfo(afinfo);
671 return 0;
672}
673
Al Viroa94cfd12006-09-27 18:47:24 -0700674static 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 -0700675{
676 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
677 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700678 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700679
David S. Miller8f126e32006-08-24 02:45:07 -0700680 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700681 if (x->props.family != family ||
682 x->id.spi != spi ||
683 x->id.proto != proto)
684 continue;
685
686 switch (family) {
687 case AF_INET:
688 if (x->id.daddr.a4 != daddr->a4)
689 continue;
690 break;
691 case AF_INET6:
692 if (!ipv6_addr_equal((struct in6_addr *)daddr,
693 (struct in6_addr *)
694 x->id.daddr.a6))
695 continue;
696 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700697 }
David S. Milleredcd5822006-08-24 00:42:45 -0700698
699 xfrm_state_hold(x);
700 return x;
701 }
702
703 return NULL;
704}
705
706static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
707{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700708 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700709 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700710 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700711
David S. Miller8f126e32006-08-24 02:45:07 -0700712 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700713 if (x->props.family != family ||
714 x->id.proto != proto)
715 continue;
716
717 switch (family) {
718 case AF_INET:
719 if (x->id.daddr.a4 != daddr->a4 ||
720 x->props.saddr.a4 != saddr->a4)
721 continue;
722 break;
723 case AF_INET6:
724 if (!ipv6_addr_equal((struct in6_addr *)daddr,
725 (struct in6_addr *)
726 x->id.daddr.a6) ||
727 !ipv6_addr_equal((struct in6_addr *)saddr,
728 (struct in6_addr *)
729 x->props.saddr.a6))
730 continue;
731 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700732 }
David S. Milleredcd5822006-08-24 00:42:45 -0700733
734 xfrm_state_hold(x);
735 return x;
736 }
737
738 return NULL;
739}
740
741static inline struct xfrm_state *
742__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
743{
744 if (use_spi)
745 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
746 x->id.proto, family);
747 else
748 return __xfrm_state_lookup_byaddr(&x->id.daddr,
749 &x->props.saddr,
750 x->id.proto, family);
751}
752
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700753static void xfrm_hash_grow_check(int have_hash_collision)
754{
755 if (have_hash_collision &&
756 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
757 xfrm_state_num > xfrm_state_hmask)
758 schedule_work(&xfrm_hash_work);
759}
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900762xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 struct flowi *fl, struct xfrm_tmpl *tmpl,
764 struct xfrm_policy *pol, int *err,
765 unsigned short family)
766{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800767 unsigned int h;
David S. Miller8f126e32006-08-24 02:45:07 -0700768 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 struct xfrm_state *x, *x0;
770 int acquire_in_progress = 0;
771 int error = 0;
772 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 spin_lock_bh(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800775 h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700776 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 if (x->props.family == family &&
778 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700779 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 xfrm_state_addr_check(x, daddr, saddr, family) &&
781 tmpl->mode == x->props.mode &&
782 tmpl->id.proto == x->id.proto &&
783 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
784 /* Resolution logic:
785 1. There is a valid state with matching selector.
786 Done.
787 2. Valid state with inappropriate selector. Skip.
788
789 Entering area of "sysdeps".
790
791 3. If state is not valid, selector is temporary,
792 it selects only session which triggered
793 previous resolution. Key manager will do
794 something to install a state with proper
795 selector.
796 */
797 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700798 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700799 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 continue;
801 if (!best ||
802 best->km.dying > x->km.dying ||
803 (best->km.dying == x->km.dying &&
804 best->curlft.add_time < x->curlft.add_time))
805 best = x;
806 } else if (x->km.state == XFRM_STATE_ACQ) {
807 acquire_in_progress = 1;
808 } else if (x->km.state == XFRM_STATE_ERROR ||
809 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700810 if (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 error = -ESRCH;
813 }
814 }
815 }
816
817 x = best;
818 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700819 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700820 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
821 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 xfrm_state_put(x0);
823 error = -EEXIST;
824 goto out;
825 }
826 x = xfrm_state_alloc();
827 if (x == NULL) {
828 error = -ENOMEM;
829 goto out;
830 }
831 /* Initialize temporary selector matching only
832 * to current session. */
833 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
834
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700835 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
836 if (error) {
837 x->km.state = XFRM_STATE_DEAD;
838 xfrm_state_put(x);
839 x = NULL;
840 goto out;
841 }
842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 if (km_query(x, tmpl, pol) == 0) {
844 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700845 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700846 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700847 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 if (x->id.spi) {
849 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700850 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 }
David S. Miller01e67d02007-05-25 00:41:38 -0700852 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
853 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700855 xfrm_state_num++;
856 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 } else {
858 x->km.state = XFRM_STATE_DEAD;
859 xfrm_state_put(x);
860 x = NULL;
861 error = -ESRCH;
862 }
863 }
864out:
865 if (x)
866 xfrm_state_hold(x);
867 else
868 *err = acquire_in_progress ? -EAGAIN : error;
869 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 return x;
871}
872
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700873struct xfrm_state *
874xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
875 unsigned short family, u8 mode, u8 proto, u32 reqid)
876{
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800877 unsigned int h;
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700878 struct xfrm_state *rx = NULL, *x = NULL;
879 struct hlist_node *entry;
880
881 spin_lock(&xfrm_state_lock);
Pavel Emelyanov4bda4f22007-12-14 11:38:04 -0800882 h = xfrm_dst_hash(daddr, saddr, reqid, family);
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700883 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
884 if (x->props.family == family &&
885 x->props.reqid == reqid &&
886 !(x->props.flags & XFRM_STATE_WILDRECV) &&
887 xfrm_state_addr_check(x, daddr, saddr, family) &&
888 mode == x->props.mode &&
889 proto == x->id.proto &&
890 x->km.state == XFRM_STATE_VALID) {
891 rx = x;
892 break;
893 }
894 }
895
896 if (rx)
897 xfrm_state_hold(rx);
898 spin_unlock(&xfrm_state_lock);
899
900
901 return rx;
902}
903EXPORT_SYMBOL(xfrm_stateonly_find);
904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905static void __xfrm_state_insert(struct xfrm_state *x)
906{
David S. Millera624c102006-08-24 03:24:33 -0700907 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
David S. Miller9d4a7062006-08-24 03:18:09 -0700909 x->genid = ++xfrm_state_genid;
910
David S. Millerc1969f22006-08-24 04:00:03 -0700911 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
912 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700913 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700915 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700916 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700918 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700919 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
920 x->props.family);
921
David S. Miller8f126e32006-08-24 02:45:07 -0700922 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700923 }
924
David S. Millera47f0ce2006-08-24 03:54:22 -0700925 mod_timer(&x->timer, jiffies + HZ);
926 if (x->replay_maxage)
927 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800928
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700930
931 xfrm_state_num++;
932
David S. Miller918049f2006-10-12 22:03:24 -0700933 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934}
935
David S. Millerc7f5ea32006-08-24 03:29:04 -0700936/* xfrm_state_lock is held */
937static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
938{
939 unsigned short family = xnew->props.family;
940 u32 reqid = xnew->props.reqid;
941 struct xfrm_state *x;
942 struct hlist_node *entry;
943 unsigned int h;
944
David S. Millerc1969f22006-08-24 04:00:03 -0700945 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700946 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
947 if (x->props.family == family &&
948 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700949 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
950 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700951 x->genid = xfrm_state_genid;
952 }
953}
954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955void xfrm_state_insert(struct xfrm_state *x)
956{
957 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700958 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 __xfrm_state_insert(x);
960 spin_unlock_bh(&xfrm_state_lock);
961}
962EXPORT_SYMBOL(xfrm_state_insert);
963
David S. Miller27708342006-08-24 00:13:10 -0700964/* xfrm_state_lock is held */
965static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
966{
David S. Millerc1969f22006-08-24 04:00:03 -0700967 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700968 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700969 struct xfrm_state *x;
970
David S. Miller8f126e32006-08-24 02:45:07 -0700971 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700972 if (x->props.reqid != reqid ||
973 x->props.mode != mode ||
974 x->props.family != family ||
975 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700976 x->id.spi != 0 ||
977 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700978 continue;
979
980 switch (family) {
981 case AF_INET:
982 if (x->id.daddr.a4 != daddr->a4 ||
983 x->props.saddr.a4 != saddr->a4)
984 continue;
985 break;
986 case AF_INET6:
987 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
988 (struct in6_addr *)daddr) ||
989 !ipv6_addr_equal((struct in6_addr *)
990 x->props.saddr.a6,
991 (struct in6_addr *)saddr))
992 continue;
993 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700994 }
David S. Miller27708342006-08-24 00:13:10 -0700995
996 xfrm_state_hold(x);
997 return x;
998 }
999
1000 if (!create)
1001 return NULL;
1002
1003 x = xfrm_state_alloc();
1004 if (likely(x)) {
1005 switch (family) {
1006 case AF_INET:
1007 x->sel.daddr.a4 = daddr->a4;
1008 x->sel.saddr.a4 = saddr->a4;
1009 x->sel.prefixlen_d = 32;
1010 x->sel.prefixlen_s = 32;
1011 x->props.saddr.a4 = saddr->a4;
1012 x->id.daddr.a4 = daddr->a4;
1013 break;
1014
1015 case AF_INET6:
1016 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
1017 (struct in6_addr *)daddr);
1018 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
1019 (struct in6_addr *)saddr);
1020 x->sel.prefixlen_d = 128;
1021 x->sel.prefixlen_s = 128;
1022 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
1023 (struct in6_addr *)saddr);
1024 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
1025 (struct in6_addr *)daddr);
1026 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001027 }
David S. Miller27708342006-08-24 00:13:10 -07001028
1029 x->km.state = XFRM_STATE_ACQ;
1030 x->id.proto = proto;
1031 x->props.family = family;
1032 x->props.mode = mode;
1033 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -07001034 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -07001035 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -07001036 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -07001037 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -07001038 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -07001039 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -07001040 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller918049f2006-10-12 22:03:24 -07001041
1042 xfrm_state_num++;
1043
1044 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -07001045 }
1046
1047 return x;
1048}
1049
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
1051
1052int xfrm_state_add(struct xfrm_state *x)
1053{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 struct xfrm_state *x1;
1055 int family;
1056 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001057 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
1059 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
1061 spin_lock_bh(&xfrm_state_lock);
1062
David S. Milleredcd5822006-08-24 00:42:45 -07001063 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 if (x1) {
1065 xfrm_state_put(x1);
1066 x1 = NULL;
1067 err = -EEXIST;
1068 goto out;
1069 }
1070
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001071 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -07001073 if (x1 && ((x1->id.proto != x->id.proto) ||
1074 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 xfrm_state_put(x1);
1076 x1 = NULL;
1077 }
1078 }
1079
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001080 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -07001081 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
1082 x->id.proto,
1083 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
David S. Millerc7f5ea32006-08-24 03:29:04 -07001085 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 __xfrm_state_insert(x);
1087 err = 0;
1088
1089out:
1090 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
1092 if (x1) {
1093 xfrm_state_delete(x1);
1094 xfrm_state_put(x1);
1095 }
1096
1097 return err;
1098}
1099EXPORT_SYMBOL(xfrm_state_add);
1100
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001101#ifdef CONFIG_XFRM_MIGRATE
1102struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
1103{
1104 int err = -ENOMEM;
1105 struct xfrm_state *x = xfrm_state_alloc();
1106 if (!x)
1107 goto error;
1108
1109 memcpy(&x->id, &orig->id, sizeof(x->id));
1110 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
1111 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
1112 x->props.mode = orig->props.mode;
1113 x->props.replay_window = orig->props.replay_window;
1114 x->props.reqid = orig->props.reqid;
1115 x->props.family = orig->props.family;
1116 x->props.saddr = orig->props.saddr;
1117
1118 if (orig->aalg) {
1119 x->aalg = xfrm_algo_clone(orig->aalg);
1120 if (!x->aalg)
1121 goto error;
1122 }
1123 x->props.aalgo = orig->props.aalgo;
1124
1125 if (orig->ealg) {
1126 x->ealg = xfrm_algo_clone(orig->ealg);
1127 if (!x->ealg)
1128 goto error;
1129 }
1130 x->props.ealgo = orig->props.ealgo;
1131
1132 if (orig->calg) {
1133 x->calg = xfrm_algo_clone(orig->calg);
1134 if (!x->calg)
1135 goto error;
1136 }
1137 x->props.calgo = orig->props.calgo;
1138
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001139 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001140 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
1141 if (!x->encap)
1142 goto error;
1143 }
1144
1145 if (orig->coaddr) {
1146 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
1147 GFP_KERNEL);
1148 if (!x->coaddr)
1149 goto error;
1150 }
1151
1152 err = xfrm_init_state(x);
1153 if (err)
1154 goto error;
1155
1156 x->props.flags = orig->props.flags;
1157
1158 x->curlft.add_time = orig->curlft.add_time;
1159 x->km.state = orig->km.state;
1160 x->km.seq = orig->km.seq;
1161
1162 return x;
1163
1164 error:
1165 if (errp)
1166 *errp = err;
1167 if (x) {
1168 kfree(x->aalg);
1169 kfree(x->ealg);
1170 kfree(x->calg);
1171 kfree(x->encap);
1172 kfree(x->coaddr);
1173 }
1174 kfree(x);
1175 return NULL;
1176}
1177EXPORT_SYMBOL(xfrm_state_clone);
1178
1179/* xfrm_state_lock is held */
1180struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
1181{
1182 unsigned int h;
1183 struct xfrm_state *x;
1184 struct hlist_node *entry;
1185
1186 if (m->reqid) {
1187 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
1188 m->reqid, m->old_family);
1189 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1190 if (x->props.mode != m->mode ||
1191 x->id.proto != m->proto)
1192 continue;
1193 if (m->reqid && x->props.reqid != m->reqid)
1194 continue;
1195 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1196 m->old_family) ||
1197 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1198 m->old_family))
1199 continue;
1200 xfrm_state_hold(x);
1201 return x;
1202 }
1203 } else {
1204 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1205 m->old_family);
1206 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1207 if (x->props.mode != m->mode ||
1208 x->id.proto != m->proto)
1209 continue;
1210 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1211 m->old_family) ||
1212 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1213 m->old_family))
1214 continue;
1215 xfrm_state_hold(x);
1216 return x;
1217 }
1218 }
1219
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001220 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001221}
1222EXPORT_SYMBOL(xfrm_migrate_state_find);
1223
1224struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1225 struct xfrm_migrate *m)
1226{
1227 struct xfrm_state *xc;
1228 int err;
1229
1230 xc = xfrm_state_clone(x, &err);
1231 if (!xc)
1232 return NULL;
1233
1234 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1235 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1236
1237 /* add state */
1238 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1239 /* a care is needed when the destination address of the
1240 state is to be updated as it is a part of triplet */
1241 xfrm_state_insert(xc);
1242 } else {
1243 if ((err = xfrm_state_add(xc)) < 0)
1244 goto error;
1245 }
1246
1247 return xc;
1248error:
1249 kfree(xc);
1250 return NULL;
1251}
1252EXPORT_SYMBOL(xfrm_state_migrate);
1253#endif
1254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255int xfrm_state_update(struct xfrm_state *x)
1256{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 struct xfrm_state *x1;
1258 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001259 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001262 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 err = -ESRCH;
1265 if (!x1)
1266 goto out;
1267
1268 if (xfrm_state_kern(x1)) {
1269 xfrm_state_put(x1);
1270 err = -EEXIST;
1271 goto out;
1272 }
1273
1274 if (x1->km.state == XFRM_STATE_ACQ) {
1275 __xfrm_state_insert(x);
1276 x = NULL;
1277 }
1278 err = 0;
1279
1280out:
1281 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
1283 if (err)
1284 return err;
1285
1286 if (!x) {
1287 xfrm_state_delete(x1);
1288 xfrm_state_put(x1);
1289 return 0;
1290 }
1291
1292 err = -EINVAL;
1293 spin_lock_bh(&x1->lock);
1294 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1295 if (x->encap && x1->encap)
1296 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001297 if (x->coaddr && x1->coaddr) {
1298 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1299 }
1300 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1301 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1303 x1->km.dying = 0;
1304
David S. Millera47f0ce2006-08-24 03:54:22 -07001305 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (x1->curlft.use_time)
1307 xfrm_state_check_expire(x1);
1308
1309 err = 0;
1310 }
1311 spin_unlock_bh(&x1->lock);
1312
1313 xfrm_state_put(x1);
1314
1315 return err;
1316}
1317EXPORT_SYMBOL(xfrm_state_update);
1318
1319int xfrm_state_check_expire(struct xfrm_state *x)
1320{
1321 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001322 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 if (x->km.state != XFRM_STATE_VALID)
1325 return -EINVAL;
1326
1327 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1328 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001329 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001330 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return -EINVAL;
1332 }
1333
1334 if (!x->km.dying &&
1335 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001336 x->curlft.packets >= x->lft.soft_packet_limit)) {
1337 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001338 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001339 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 return 0;
1341}
1342EXPORT_SYMBOL(xfrm_state_check_expire);
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001345xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 unsigned short family)
1347{
1348 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
1350 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001351 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 return x;
1354}
1355EXPORT_SYMBOL(xfrm_state_lookup);
1356
1357struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001358xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1359 u8 proto, unsigned short family)
1360{
1361 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001362
1363 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001364 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001365 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001366 return x;
1367}
1368EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1369
1370struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001371xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1372 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 int create, 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. Miller27708342006-08-24 00:13:10 -07001378 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 return x;
1382}
1383EXPORT_SYMBOL(xfrm_find_acq);
1384
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001385#ifdef CONFIG_XFRM_SUB_POLICY
1386int
1387xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1388 unsigned short family)
1389{
1390 int err = 0;
1391 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1392 if (!afinfo)
1393 return -EAFNOSUPPORT;
1394
1395 spin_lock_bh(&xfrm_state_lock);
1396 if (afinfo->tmpl_sort)
1397 err = afinfo->tmpl_sort(dst, src, n);
1398 spin_unlock_bh(&xfrm_state_lock);
1399 xfrm_state_put_afinfo(afinfo);
1400 return err;
1401}
1402EXPORT_SYMBOL(xfrm_tmpl_sort);
1403
1404int
1405xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1406 unsigned short family)
1407{
1408 int err = 0;
1409 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1410 if (!afinfo)
1411 return -EAFNOSUPPORT;
1412
1413 spin_lock_bh(&xfrm_state_lock);
1414 if (afinfo->state_sort)
1415 err = afinfo->state_sort(dst, src, n);
1416 spin_unlock_bh(&xfrm_state_lock);
1417 xfrm_state_put_afinfo(afinfo);
1418 return err;
1419}
1420EXPORT_SYMBOL(xfrm_state_sort);
1421#endif
1422
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423/* Silly enough, but I'm lazy to build resolution list */
1424
1425static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1426{
1427 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
David S. Millerf034b5d2006-08-24 03:08:07 -07001429 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001430 struct hlist_node *entry;
1431 struct xfrm_state *x;
1432
1433 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1434 if (x->km.seq == seq &&
1435 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 xfrm_state_hold(x);
1437 return x;
1438 }
1439 }
1440 }
1441 return NULL;
1442}
1443
1444struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1445{
1446 struct xfrm_state *x;
1447
1448 spin_lock_bh(&xfrm_state_lock);
1449 x = __xfrm_find_acq_byseq(seq);
1450 spin_unlock_bh(&xfrm_state_lock);
1451 return x;
1452}
1453EXPORT_SYMBOL(xfrm_find_acq_byseq);
1454
1455u32 xfrm_get_acqseq(void)
1456{
1457 u32 res;
1458 static u32 acqseq;
1459 static DEFINE_SPINLOCK(acqseq_lock);
1460
1461 spin_lock_bh(&acqseq_lock);
1462 res = (++acqseq ? : ++acqseq);
1463 spin_unlock_bh(&acqseq_lock);
1464 return res;
1465}
1466EXPORT_SYMBOL(xfrm_get_acqseq);
1467
Herbert Xu658b2192007-10-09 13:29:52 -07001468int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
David S. Millerf034b5d2006-08-24 03:08:07 -07001470 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 struct xfrm_state *x0;
Herbert Xu658b2192007-10-09 13:29:52 -07001472 int err = -ENOENT;
1473 __be32 minspi = htonl(low);
1474 __be32 maxspi = htonl(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
Herbert Xu658b2192007-10-09 13:29:52 -07001476 spin_lock_bh(&x->lock);
1477 if (x->km.state == XFRM_STATE_DEAD)
1478 goto unlock;
1479
1480 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 if (x->id.spi)
Herbert Xu658b2192007-10-09 13:29:52 -07001482 goto unlock;
1483
1484 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
1486 if (minspi == maxspi) {
1487 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1488 if (x0) {
1489 xfrm_state_put(x0);
Herbert Xu658b2192007-10-09 13:29:52 -07001490 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 }
1492 x->id.spi = minspi;
1493 } else {
1494 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001495 for (h=0; h<high-low+1; h++) {
1496 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1498 if (x0 == NULL) {
1499 x->id.spi = htonl(spi);
1500 break;
1501 }
1502 xfrm_state_put(x0);
1503 }
1504 }
1505 if (x->id.spi) {
1506 spin_lock_bh(&xfrm_state_lock);
1507 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001508 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 spin_unlock_bh(&xfrm_state_lock);
Herbert Xu658b2192007-10-09 13:29:52 -07001510
1511 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 }
Herbert Xu658b2192007-10-09 13:29:52 -07001513
1514unlock:
1515 spin_unlock_bh(&x->lock);
1516
1517 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518}
1519EXPORT_SYMBOL(xfrm_alloc_spi);
1520
1521int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1522 void *data)
1523{
1524 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001525 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001526 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 int count = 0;
1528 int err = 0;
1529
1530 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001531 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001532 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001533 if (!xfrm_id_proto_match(x->id.proto, proto))
1534 continue;
1535 if (last) {
1536 err = func(last, count, data);
1537 if (err)
1538 goto out;
1539 }
1540 last = x;
1541 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 }
1543 }
1544 if (count == 0) {
1545 err = -ENOENT;
1546 goto out;
1547 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001548 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549out:
1550 spin_unlock_bh(&xfrm_state_lock);
1551 return err;
1552}
1553EXPORT_SYMBOL(xfrm_state_walk);
1554
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001555
1556void xfrm_replay_notify(struct xfrm_state *x, int event)
1557{
1558 struct km_event c;
1559 /* we send notify messages in case
1560 * 1. we updated on of the sequence numbers, and the seqno difference
1561 * is at least x->replay_maxdiff, in this case we also update the
1562 * timeout of our timer function
1563 * 2. if x->replay_maxage has elapsed since last update,
1564 * and there were changes
1565 *
1566 * The state structure must be locked!
1567 */
1568
1569 switch (event) {
1570 case XFRM_REPLAY_UPDATE:
1571 if (x->replay_maxdiff &&
1572 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001573 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1574 if (x->xflags & XFRM_TIME_DEFER)
1575 event = XFRM_REPLAY_TIMEOUT;
1576 else
1577 return;
1578 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001579
1580 break;
1581
1582 case XFRM_REPLAY_TIMEOUT:
1583 if ((x->replay.seq == x->preplay.seq) &&
1584 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001585 (x->replay.oseq == x->preplay.oseq)) {
1586 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001587 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001588 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001589
1590 break;
1591 }
1592
1593 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1594 c.event = XFRM_MSG_NEWAE;
1595 c.data.aevent = event;
1596 km_state_notify(x, &c);
1597
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001598 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001599 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001600 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001601}
1602
1603static void xfrm_replay_timer_handler(unsigned long data)
1604{
1605 struct xfrm_state *x = (struct xfrm_state*)data;
1606
1607 spin_lock(&x->lock);
1608
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001609 if (x->km.state == XFRM_STATE_VALID) {
1610 if (xfrm_aevent_is_on())
1611 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1612 else
1613 x->xflags |= XFRM_TIME_DEFER;
1614 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001615
1616 spin_unlock(&x->lock);
1617}
1618
Paul Mooreafeb14b2007-12-21 14:58:11 -08001619int xfrm_replay_check(struct xfrm_state *x,
1620 struct sk_buff *skb, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621{
1622 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001623 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
1625 if (unlikely(seq == 0))
Paul Mooreafeb14b2007-12-21 14:58:11 -08001626 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
1628 if (likely(seq > x->replay.seq))
1629 return 0;
1630
1631 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001632 if (diff >= min_t(unsigned int, x->props.replay_window,
1633 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 x->stats.replay_window++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001635 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 }
1637
1638 if (x->replay.bitmap & (1U << diff)) {
1639 x->stats.replay++;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001640 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 }
1642 return 0;
Paul Mooreafeb14b2007-12-21 14:58:11 -08001643
1644err:
1645 xfrm_audit_state_replay(x, skb, net_seq);
1646 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647}
1648EXPORT_SYMBOL(xfrm_replay_check);
1649
Al Viro61f46272006-09-27 18:48:33 -07001650void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651{
1652 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001653 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
1655 if (seq > x->replay.seq) {
1656 diff = seq - x->replay.seq;
1657 if (diff < x->props.replay_window)
1658 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1659 else
1660 x->replay.bitmap = 1;
1661 x->replay.seq = seq;
1662 } else {
1663 diff = x->replay.seq - seq;
1664 x->replay.bitmap |= (1U << diff);
1665 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001666
1667 if (xfrm_aevent_is_on())
1668 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669}
1670EXPORT_SYMBOL(xfrm_replay_advance);
1671
Denis Chengdf018122007-12-07 00:51:11 -08001672static LIST_HEAD(xfrm_km_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673static DEFINE_RWLOCK(xfrm_km_lock);
1674
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001675void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676{
1677 struct xfrm_mgr *km;
1678
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001679 read_lock(&xfrm_km_lock);
1680 list_for_each_entry(km, &xfrm_km_list, list)
1681 if (km->notify_policy)
1682 km->notify_policy(xp, dir, c);
1683 read_unlock(&xfrm_km_lock);
1684}
1685
1686void km_state_notify(struct xfrm_state *x, struct km_event *c)
1687{
1688 struct xfrm_mgr *km;
1689 read_lock(&xfrm_km_lock);
1690 list_for_each_entry(km, &xfrm_km_list, list)
1691 if (km->notify)
1692 km->notify(x, c);
1693 read_unlock(&xfrm_km_lock);
1694}
1695
1696EXPORT_SYMBOL(km_policy_notify);
1697EXPORT_SYMBOL(km_state_notify);
1698
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001699void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001700{
1701 struct km_event c;
1702
Herbert Xubf088672005-06-18 22:44:00 -07001703 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001704 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001705 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001706 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
1708 if (hard)
1709 wake_up(&km_waitq);
1710}
1711
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001712EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001713/*
1714 * We send to all registered managers regardless of failure
1715 * We are happy with one success
1716*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001717int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001719 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 struct xfrm_mgr *km;
1721
1722 read_lock(&xfrm_km_lock);
1723 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001724 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1725 if (!acqret)
1726 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 }
1728 read_unlock(&xfrm_km_lock);
1729 return err;
1730}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001731EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732
Al Viro5d36b182006-11-08 00:24:06 -08001733int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734{
1735 int err = -EINVAL;
1736 struct xfrm_mgr *km;
1737
1738 read_lock(&xfrm_km_lock);
1739 list_for_each_entry(km, &xfrm_km_list, list) {
1740 if (km->new_mapping)
1741 err = km->new_mapping(x, ipaddr, sport);
1742 if (!err)
1743 break;
1744 }
1745 read_unlock(&xfrm_km_lock);
1746 return err;
1747}
1748EXPORT_SYMBOL(km_new_mapping);
1749
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001750void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001752 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753
Herbert Xubf088672005-06-18 22:44:00 -07001754 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001755 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001756 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001757 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 if (hard)
1760 wake_up(&km_waitq);
1761}
David S. Millera70fcb02006-03-20 19:18:52 -08001762EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001764#ifdef CONFIG_XFRM_MIGRATE
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001765int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1766 struct xfrm_migrate *m, int num_migrate)
1767{
1768 int err = -EINVAL;
1769 int ret;
1770 struct xfrm_mgr *km;
1771
1772 read_lock(&xfrm_km_lock);
1773 list_for_each_entry(km, &xfrm_km_list, list) {
1774 if (km->migrate) {
1775 ret = km->migrate(sel, dir, type, m, num_migrate);
1776 if (!ret)
1777 err = ret;
1778 }
1779 }
1780 read_unlock(&xfrm_km_lock);
1781 return err;
1782}
1783EXPORT_SYMBOL(km_migrate);
Eric Dumazet2d60abc2008-01-03 20:43:21 -08001784#endif
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001785
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001786int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1787{
1788 int err = -EINVAL;
1789 int ret;
1790 struct xfrm_mgr *km;
1791
1792 read_lock(&xfrm_km_lock);
1793 list_for_each_entry(km, &xfrm_km_list, list) {
1794 if (km->report) {
1795 ret = km->report(proto, sel, addr);
1796 if (!ret)
1797 err = ret;
1798 }
1799 }
1800 read_unlock(&xfrm_km_lock);
1801 return err;
1802}
1803EXPORT_SYMBOL(km_report);
1804
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1806{
1807 int err;
1808 u8 *data;
1809 struct xfrm_mgr *km;
1810 struct xfrm_policy *pol = NULL;
1811
1812 if (optlen <= 0 || optlen > PAGE_SIZE)
1813 return -EMSGSIZE;
1814
1815 data = kmalloc(optlen, GFP_KERNEL);
1816 if (!data)
1817 return -ENOMEM;
1818
1819 err = -EFAULT;
1820 if (copy_from_user(data, optval, optlen))
1821 goto out;
1822
1823 err = -EINVAL;
1824 read_lock(&xfrm_km_lock);
1825 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001826 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 optlen, &err);
1828 if (err >= 0)
1829 break;
1830 }
1831 read_unlock(&xfrm_km_lock);
1832
1833 if (err >= 0) {
1834 xfrm_sk_policy_insert(sk, err, pol);
1835 xfrm_pol_put(pol);
1836 err = 0;
1837 }
1838
1839out:
1840 kfree(data);
1841 return err;
1842}
1843EXPORT_SYMBOL(xfrm_user_policy);
1844
1845int xfrm_register_km(struct xfrm_mgr *km)
1846{
1847 write_lock_bh(&xfrm_km_lock);
1848 list_add_tail(&km->list, &xfrm_km_list);
1849 write_unlock_bh(&xfrm_km_lock);
1850 return 0;
1851}
1852EXPORT_SYMBOL(xfrm_register_km);
1853
1854int xfrm_unregister_km(struct xfrm_mgr *km)
1855{
1856 write_lock_bh(&xfrm_km_lock);
1857 list_del(&km->list);
1858 write_unlock_bh(&xfrm_km_lock);
1859 return 0;
1860}
1861EXPORT_SYMBOL(xfrm_unregister_km);
1862
1863int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1864{
1865 int err = 0;
1866 if (unlikely(afinfo == NULL))
1867 return -EINVAL;
1868 if (unlikely(afinfo->family >= NPROTO))
1869 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001870 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1872 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001873 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001875 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 return err;
1877}
1878EXPORT_SYMBOL(xfrm_state_register_afinfo);
1879
1880int xfrm_state_unregister_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 (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1889 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1890 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001891 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001894 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return err;
1896}
1897EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1898
Herbert Xu17c2a422007-10-17 21:33:12 -07001899static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900{
1901 struct xfrm_state_afinfo *afinfo;
1902 if (unlikely(family >= NPROTO))
1903 return NULL;
1904 read_lock(&xfrm_state_afinfo_lock);
1905 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001906 if (unlikely(!afinfo))
1907 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 return afinfo;
1909}
1910
Herbert Xu17c2a422007-10-17 21:33:12 -07001911static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912{
Herbert Xu546be242006-05-27 23:03:58 -07001913 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914}
1915
1916/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1917void xfrm_state_delete_tunnel(struct xfrm_state *x)
1918{
1919 if (x->tunnel) {
1920 struct xfrm_state *t = x->tunnel;
1921
1922 if (atomic_read(&t->tunnel_users) == 2)
1923 xfrm_state_delete(t);
1924 atomic_dec(&t->tunnel_users);
1925 xfrm_state_put(t);
1926 x->tunnel = NULL;
1927 }
1928}
1929EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1930
1931int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1932{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001933 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
Patrick McHardyc5c25232007-04-09 11:47:18 -07001935 spin_lock_bh(&x->lock);
1936 if (x->km.state == XFRM_STATE_VALID &&
1937 x->type && x->type->get_mtu)
1938 res = x->type->get_mtu(x, mtu);
1939 else
Patrick McHardy28121612007-06-18 22:30:15 -07001940 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001941 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 return res;
1943}
1944
Herbert Xu72cb6962005-06-20 13:18:08 -07001945int xfrm_init_state(struct xfrm_state *x)
1946{
Herbert Xud094cd82005-06-20 13:19:41 -07001947 struct xfrm_state_afinfo *afinfo;
1948 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001949 int err;
1950
Herbert Xud094cd82005-06-20 13:19:41 -07001951 err = -EAFNOSUPPORT;
1952 afinfo = xfrm_state_get_afinfo(family);
1953 if (!afinfo)
1954 goto error;
1955
1956 err = 0;
1957 if (afinfo->init_flags)
1958 err = afinfo->init_flags(x);
1959
1960 xfrm_state_put_afinfo(afinfo);
1961
1962 if (err)
1963 goto error;
1964
1965 err = -EPROTONOSUPPORT;
Herbert Xu13996372007-10-17 21:35:51 -07001966 x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1967 if (x->inner_mode == NULL)
1968 goto error;
1969
1970 if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1971 family != x->sel.family)
1972 goto error;
1973
Herbert Xud094cd82005-06-20 13:19:41 -07001974 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001975 if (x->type == NULL)
1976 goto error;
1977
1978 err = x->type->init_state(x);
1979 if (err)
1980 goto error;
1981
Herbert Xu13996372007-10-17 21:35:51 -07001982 x->outer_mode = xfrm_get_mode(x->props.mode, family);
1983 if (x->outer_mode == NULL)
Herbert Xub59f45d2006-05-27 23:05:54 -07001984 goto error;
1985
Herbert Xu72cb6962005-06-20 13:18:08 -07001986 x->km.state = XFRM_STATE_VALID;
1987
1988error:
1989 return err;
1990}
1991
1992EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001993
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994void __init xfrm_state_init(void)
1995{
David S. Millerf034b5d2006-08-24 03:08:07 -07001996 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
David S. Millerf034b5d2006-08-24 03:08:07 -07001998 sz = sizeof(struct hlist_head) * 8;
1999
David S. Miller44e36b42006-08-24 04:50:50 -07002000 xfrm_state_bydst = xfrm_hash_alloc(sz);
2001 xfrm_state_bysrc = xfrm_hash_alloc(sz);
2002 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07002003 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2004 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2005 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2006
David Howellsc4028952006-11-22 14:57:56 +00002007 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008}
2009
Joy Lattenab5f5e82007-09-17 11:51:22 -07002010#ifdef CONFIG_AUDITSYSCALL
Paul Mooreafeb14b2007-12-21 14:58:11 -08002011static inline void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2012 struct audit_buffer *audit_buf)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002013{
Paul Moore68277ac2007-12-20 20:49:33 -08002014 struct xfrm_sec_ctx *ctx = x->security;
2015 u32 spi = ntohl(x->id.spi);
2016
2017 if (ctx)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002018 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
Paul Moore68277ac2007-12-20 20:49:33 -08002019 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002020
2021 switch(x->props.family) {
2022 case AF_INET:
Paul Moore68277ac2007-12-20 20:49:33 -08002023 audit_log_format(audit_buf,
2024 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
Joy Lattenab5f5e82007-09-17 11:51:22 -07002025 NIPQUAD(x->props.saddr.a4),
2026 NIPQUAD(x->id.daddr.a4));
2027 break;
2028 case AF_INET6:
Paul Moore68277ac2007-12-20 20:49:33 -08002029 audit_log_format(audit_buf,
2030 " src=" NIP6_FMT " dst=" NIP6_FMT,
2031 NIP6(*(struct in6_addr *)x->props.saddr.a6),
2032 NIP6(*(struct in6_addr *)x->id.daddr.a6));
Joy Lattenab5f5e82007-09-17 11:51:22 -07002033 break;
2034 }
Paul Moore68277ac2007-12-20 20:49:33 -08002035
2036 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002037}
2038
Paul Mooreafeb14b2007-12-21 14:58:11 -08002039static inline void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2040 struct audit_buffer *audit_buf)
2041{
2042 struct iphdr *iph4;
2043 struct ipv6hdr *iph6;
2044
2045 switch (family) {
2046 case AF_INET:
2047 iph4 = ip_hdr(skb);
2048 audit_log_format(audit_buf,
2049 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2050 NIPQUAD(iph4->saddr),
2051 NIPQUAD(iph4->daddr));
2052 break;
2053 case AF_INET6:
2054 iph6 = ipv6_hdr(skb);
2055 audit_log_format(audit_buf,
2056 " src=" NIP6_FMT " dst=" NIP6_FMT
2057 " flowlbl=0x%x%x%x",
2058 NIP6(iph6->saddr),
2059 NIP6(iph6->daddr),
2060 iph6->flow_lbl[0] & 0x0f,
2061 iph6->flow_lbl[1],
2062 iph6->flow_lbl[2]);
2063 break;
2064 }
2065}
2066
Paul Moore68277ac2007-12-20 20:49:33 -08002067void xfrm_audit_state_add(struct xfrm_state *x, int result,
2068 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002069{
2070 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002071
Paul Mooreafeb14b2007-12-21 14:58:11 -08002072 audit_buf = xfrm_audit_start("SAD-add");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002073 if (audit_buf == NULL)
2074 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002075 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2076 xfrm_audit_helper_sainfo(x, audit_buf);
2077 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002078 audit_log_end(audit_buf);
2079}
2080EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2081
Paul Moore68277ac2007-12-20 20:49:33 -08002082void xfrm_audit_state_delete(struct xfrm_state *x, int result,
2083 u32 auid, u32 secid)
Joy Lattenab5f5e82007-09-17 11:51:22 -07002084{
2085 struct audit_buffer *audit_buf;
Joy Lattenab5f5e82007-09-17 11:51:22 -07002086
Paul Mooreafeb14b2007-12-21 14:58:11 -08002087 audit_buf = xfrm_audit_start("SAD-delete");
Joy Lattenab5f5e82007-09-17 11:51:22 -07002088 if (audit_buf == NULL)
2089 return;
Paul Mooreafeb14b2007-12-21 14:58:11 -08002090 xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2091 xfrm_audit_helper_sainfo(x, audit_buf);
2092 audit_log_format(audit_buf, " res=%u", result);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002093 audit_log_end(audit_buf);
2094}
2095EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
Paul Mooreafeb14b2007-12-21 14:58:11 -08002096
2097void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2098 struct sk_buff *skb)
2099{
2100 struct audit_buffer *audit_buf;
2101 u32 spi;
2102
2103 audit_buf = xfrm_audit_start("SA-replay-overflow");
2104 if (audit_buf == NULL)
2105 return;
2106 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2107 /* don't record the sequence number because it's inherent in this kind
2108 * of audit message */
2109 spi = ntohl(x->id.spi);
2110 audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2111 audit_log_end(audit_buf);
2112}
2113EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2114
2115static void xfrm_audit_state_replay(struct xfrm_state *x,
2116 struct sk_buff *skb, __be32 net_seq)
2117{
2118 struct audit_buffer *audit_buf;
2119 u32 spi;
2120
2121 audit_buf = xfrm_audit_start("SA-replayed-pkt");
2122 if (audit_buf == NULL)
2123 return;
2124 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2125 spi = ntohl(x->id.spi);
2126 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2127 spi, spi, ntohl(net_seq));
2128 audit_log_end(audit_buf);
2129}
2130
2131void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2132{
2133 struct audit_buffer *audit_buf;
2134
2135 audit_buf = xfrm_audit_start("SA-notfound");
2136 if (audit_buf == NULL)
2137 return;
2138 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2139 audit_log_end(audit_buf);
2140}
2141EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2142
2143void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2144 __be32 net_spi, __be32 net_seq)
2145{
2146 struct audit_buffer *audit_buf;
2147 u32 spi;
2148
2149 audit_buf = xfrm_audit_start("SA-notfound");
2150 if (audit_buf == NULL)
2151 return;
2152 xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2153 spi = ntohl(net_spi);
2154 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2155 spi, spi, ntohl(net_seq));
2156 audit_log_end(audit_buf);
2157}
2158EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2159
2160void xfrm_audit_state_icvfail(struct xfrm_state *x,
2161 struct sk_buff *skb, u8 proto)
2162{
2163 struct audit_buffer *audit_buf;
2164 __be32 net_spi;
2165 __be32 net_seq;
2166
2167 audit_buf = xfrm_audit_start("SA-icv-failure");
2168 if (audit_buf == NULL)
2169 return;
2170 xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2171 if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2172 u32 spi = ntohl(net_spi);
2173 audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2174 spi, spi, ntohl(net_seq));
2175 }
2176 audit_log_end(audit_buf);
2177}
2178EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
Joy Lattenab5f5e82007-09-17 11:51:22 -07002179#endif /* CONFIG_AUDITSYSCALL */