blob: 535d43c1472008aed20b526befae16b729db3514 [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/bootmem.h>
22#include <linux/vmalloc.h>
23#include <linux/cache.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/uaccess.h>
25
David S. Milleree857a72006-03-20 19:18:37 -080026struct sock *xfrm_nl;
27EXPORT_SYMBOL(xfrm_nl);
28
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080029u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080030EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
31
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080032u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080033EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/* Each xfrm_state may be linked to two tables:
36
37 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
38 2. Hash table by daddr to find what SAs exist for given
39 destination/tunnel endpoint. (output)
40 */
41
42static DEFINE_SPINLOCK(xfrm_state_lock);
43
44/* Hash table to find appropriate SA towards given target (endpoint
45 * of tunnel or destination of transport mode) allowed by selector.
46 *
47 * Main use is finding SA after policy selected tunnel or transport mode.
48 * Also, it can be used by ah/esp icmp error handler to find offending SA.
49 */
David S. Millerf034b5d2006-08-24 03:08:07 -070050static struct hlist_head *xfrm_state_bydst __read_mostly;
51static struct hlist_head *xfrm_state_bysrc __read_mostly;
52static struct hlist_head *xfrm_state_byspi __read_mostly;
53static unsigned int xfrm_state_hmask __read_mostly;
54static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
55static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070056static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
David S. Millerf034b5d2006-08-24 03:08:07 -070058static inline unsigned int __xfrm4_dst_hash(xfrm_address_t *addr, unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070059{
David S. Millerf034b5d2006-08-24 03:08:07 -070060 unsigned int h;
David S. Milleredcd5822006-08-24 00:42:45 -070061 h = ntohl(addr->a4);
David S. Millerf034b5d2006-08-24 03:08:07 -070062 h = (h ^ (h>>16)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -070063 return h;
64}
65
David S. Millerf034b5d2006-08-24 03:08:07 -070066static inline unsigned int __xfrm6_dst_hash(xfrm_address_t *addr, unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070067{
David S. Millerf034b5d2006-08-24 03:08:07 -070068 unsigned int h;
David S. Milleredcd5822006-08-24 00:42:45 -070069 h = ntohl(addr->a6[2]^addr->a6[3]);
David S. Millerf034b5d2006-08-24 03:08:07 -070070 h = (h ^ (h>>16)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -070071 return h;
72}
73
David S. Millerf034b5d2006-08-24 03:08:07 -070074static inline unsigned int __xfrm4_src_hash(xfrm_address_t *addr, unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070075{
David S. Millerf034b5d2006-08-24 03:08:07 -070076 return __xfrm4_dst_hash(addr, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -070077}
78
David S. Millerf034b5d2006-08-24 03:08:07 -070079static inline unsigned int __xfrm6_src_hash(xfrm_address_t *addr, unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070080{
David S. Millerf034b5d2006-08-24 03:08:07 -070081 return __xfrm6_dst_hash(addr, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -070082}
83
David S. Millerf034b5d2006-08-24 03:08:07 -070084static inline unsigned __xfrm_src_hash(xfrm_address_t *addr, unsigned short family, unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070085{
86 switch (family) {
87 case AF_INET:
David S. Millerf034b5d2006-08-24 03:08:07 -070088 return __xfrm4_src_hash(addr, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -070089 case AF_INET6:
David S. Millerf034b5d2006-08-24 03:08:07 -070090 return __xfrm6_src_hash(addr, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -070091 }
92 return 0;
93}
94
David S. Millerf034b5d2006-08-24 03:08:07 -070095static inline unsigned xfrm_src_hash(xfrm_address_t *addr, unsigned short family)
96{
97 return __xfrm_src_hash(addr, family, xfrm_state_hmask);
98}
99
100static inline unsigned int __xfrm_dst_hash(xfrm_address_t *addr, unsigned short family, unsigned int hmask)
David S. Miller27708342006-08-24 00:13:10 -0700101{
102 switch (family) {
103 case AF_INET:
David S. Millerf034b5d2006-08-24 03:08:07 -0700104 return __xfrm4_dst_hash(addr, hmask);
David S. Miller27708342006-08-24 00:13:10 -0700105 case AF_INET6:
David S. Millerf034b5d2006-08-24 03:08:07 -0700106 return __xfrm6_dst_hash(addr, hmask);
David S. Miller27708342006-08-24 00:13:10 -0700107 }
108 return 0;
109}
110
David S. Millerf034b5d2006-08-24 03:08:07 -0700111static inline unsigned int xfrm_dst_hash(xfrm_address_t *addr, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700112{
David S. Millerf034b5d2006-08-24 03:08:07 -0700113 return __xfrm_dst_hash(addr, family, xfrm_state_hmask);
114}
115
116static inline unsigned int __xfrm4_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto,
117 unsigned int hmask)
118{
119 unsigned int h;
David S. Milleredcd5822006-08-24 00:42:45 -0700120 h = ntohl(addr->a4^spi^proto);
David S. Millerf034b5d2006-08-24 03:08:07 -0700121 h = (h ^ (h>>10) ^ (h>>20)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700122 return h;
123}
124
David S. Millerf034b5d2006-08-24 03:08:07 -0700125static inline unsigned int __xfrm6_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto,
126 unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -0700127{
David S. Millerf034b5d2006-08-24 03:08:07 -0700128 unsigned int h;
David S. Milleredcd5822006-08-24 00:42:45 -0700129 h = ntohl(addr->a6[2]^addr->a6[3]^spi^proto);
David S. Millerf034b5d2006-08-24 03:08:07 -0700130 h = (h ^ (h>>10) ^ (h>>20)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700131 return h;
132}
133
David S. Millerf034b5d2006-08-24 03:08:07 -0700134static inline
135unsigned __xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family,
136 unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -0700137{
138 switch (family) {
139 case AF_INET:
David S. Millerf034b5d2006-08-24 03:08:07 -0700140 return __xfrm4_spi_hash(addr, spi, proto, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -0700141 case AF_INET6:
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 return __xfrm6_spi_hash(addr, spi, proto, hmask);
David S. Milleredcd5822006-08-24 00:42:45 -0700143 }
144 return 0; /*XXX*/
145}
146
David S. Millerf034b5d2006-08-24 03:08:07 -0700147static inline unsigned int
148xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family)
149{
150 return __xfrm_spi_hash(addr, spi, proto, family, xfrm_state_hmask);
151}
152
153static struct hlist_head *xfrm_state_hash_alloc(unsigned int sz)
154{
155 struct hlist_head *n;
156
157 if (sz <= PAGE_SIZE)
158 n = kmalloc(sz, GFP_KERNEL);
159 else if (hashdist)
160 n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
161 else
162 n = (struct hlist_head *)
163 __get_free_pages(GFP_KERNEL, get_order(sz));
164
165 if (n)
166 memset(n, 0, sz);
167
168 return n;
169}
170
171static void xfrm_state_hash_free(struct hlist_head *n, unsigned int sz)
172{
173 if (sz <= PAGE_SIZE)
174 kfree(n);
175 else if (hashdist)
176 vfree(n);
177 else
178 free_pages((unsigned long)n, get_order(sz));
179}
180
181static void xfrm_hash_transfer(struct hlist_head *list,
182 struct hlist_head *ndsttable,
183 struct hlist_head *nsrctable,
184 struct hlist_head *nspitable,
185 unsigned int nhashmask)
186{
187 struct hlist_node *entry, *tmp;
188 struct xfrm_state *x;
189
190 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
191 unsigned int h;
192
193 h = __xfrm_dst_hash(&x->id.daddr, x->props.family, nhashmask);
194 hlist_add_head(&x->bydst, ndsttable+h);
195
196 h = __xfrm_src_hash(&x->props.saddr, x->props.family,
197 nhashmask);
198 hlist_add_head(&x->bysrc, nsrctable+h);
199
200 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
201 x->props.family, nhashmask);
202 hlist_add_head(&x->byspi, nspitable+h);
203 }
204}
205
206static unsigned long xfrm_hash_new_size(void)
207{
208 return ((xfrm_state_hmask + 1) << 1) *
209 sizeof(struct hlist_head);
210}
211
212static DEFINE_MUTEX(hash_resize_mutex);
213
214static void xfrm_hash_resize(void *__unused)
215{
216 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
217 unsigned long nsize, osize;
218 unsigned int nhashmask, ohashmask;
219 int i;
220
221 mutex_lock(&hash_resize_mutex);
222
223 nsize = xfrm_hash_new_size();
224 ndst = xfrm_state_hash_alloc(nsize);
225 if (!ndst)
226 goto out_unlock;
227 nsrc = xfrm_state_hash_alloc(nsize);
228 if (!nsrc) {
229 xfrm_state_hash_free(ndst, nsize);
230 goto out_unlock;
231 }
232 nspi = xfrm_state_hash_alloc(nsize);
233 if (!nspi) {
234 xfrm_state_hash_free(ndst, nsize);
235 xfrm_state_hash_free(nsrc, nsize);
236 goto out_unlock;
237 }
238
239 spin_lock_bh(&xfrm_state_lock);
240
241 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
242 for (i = xfrm_state_hmask; i >= 0; i--)
243 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
244 nhashmask);
245
246 odst = xfrm_state_bydst;
247 osrc = xfrm_state_bysrc;
248 ospi = xfrm_state_byspi;
249 ohashmask = xfrm_state_hmask;
250
251 xfrm_state_bydst = ndst;
252 xfrm_state_bysrc = nsrc;
253 xfrm_state_byspi = nspi;
254 xfrm_state_hmask = nhashmask;
255
256 spin_unlock_bh(&xfrm_state_lock);
257
258 osize = (ohashmask + 1) * sizeof(struct hlist_head);
259 xfrm_state_hash_free(odst, osize);
260 xfrm_state_hash_free(osrc, osize);
261 xfrm_state_hash_free(ospi, osize);
262
263out_unlock:
264 mutex_unlock(&hash_resize_mutex);
265}
266
267static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269DECLARE_WAIT_QUEUE_HEAD(km_waitq);
270EXPORT_SYMBOL(km_waitq);
271
272static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
273static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
274
275static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700276static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277static DEFINE_SPINLOCK(xfrm_state_gc_lock);
278
279static int xfrm_state_gc_flush_bundles;
280
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800281int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
284static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
285
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800286int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800287void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289static void xfrm_state_gc_destroy(struct xfrm_state *x)
290{
291 if (del_timer(&x->timer))
292 BUG();
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800293 if (del_timer(&x->rtimer))
294 BUG();
Jesper Juhla51482b2005-11-08 09:41:34 -0800295 kfree(x->aalg);
296 kfree(x->ealg);
297 kfree(x->calg);
298 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700299 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700300 if (x->mode)
301 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (x->type) {
303 x->type->destructor(x);
304 xfrm_put_type(x->type);
305 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800306 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 kfree(x);
308}
309
310static void xfrm_state_gc_task(void *data)
311{
312 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700313 struct hlist_node *entry, *tmp;
314 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
316 if (xfrm_state_gc_flush_bundles) {
317 xfrm_state_gc_flush_bundles = 0;
318 xfrm_flush_bundles();
319 }
320
321 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700322 gc_list.first = xfrm_state_gc_list.first;
323 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 spin_unlock_bh(&xfrm_state_gc_lock);
325
David S. Miller8f126e32006-08-24 02:45:07 -0700326 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 wake_up(&km_waitq);
330}
331
332static inline unsigned long make_jiffies(long secs)
333{
334 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
335 return MAX_SCHEDULE_TIMEOUT-1;
336 else
337 return secs*HZ;
338}
339
340static void xfrm_timer_handler(unsigned long data)
341{
342 struct xfrm_state *x = (struct xfrm_state*)data;
343 unsigned long now = (unsigned long)xtime.tv_sec;
344 long next = LONG_MAX;
345 int warn = 0;
346
347 spin_lock(&x->lock);
348 if (x->km.state == XFRM_STATE_DEAD)
349 goto out;
350 if (x->km.state == XFRM_STATE_EXPIRED)
351 goto expired;
352 if (x->lft.hard_add_expires_seconds) {
353 long tmo = x->lft.hard_add_expires_seconds +
354 x->curlft.add_time - now;
355 if (tmo <= 0)
356 goto expired;
357 if (tmo < next)
358 next = tmo;
359 }
360 if (x->lft.hard_use_expires_seconds) {
361 long tmo = x->lft.hard_use_expires_seconds +
362 (x->curlft.use_time ? : now) - now;
363 if (tmo <= 0)
364 goto expired;
365 if (tmo < next)
366 next = tmo;
367 }
368 if (x->km.dying)
369 goto resched;
370 if (x->lft.soft_add_expires_seconds) {
371 long tmo = x->lft.soft_add_expires_seconds +
372 x->curlft.add_time - now;
373 if (tmo <= 0)
374 warn = 1;
375 else if (tmo < next)
376 next = tmo;
377 }
378 if (x->lft.soft_use_expires_seconds) {
379 long tmo = x->lft.soft_use_expires_seconds +
380 (x->curlft.use_time ? : now) - now;
381 if (tmo <= 0)
382 warn = 1;
383 else if (tmo < next)
384 next = tmo;
385 }
386
Herbert Xu4666faa2005-06-18 22:43:22 -0700387 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800389 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390resched:
391 if (next != LONG_MAX &&
392 !mod_timer(&x->timer, jiffies + make_jiffies(next)))
393 xfrm_state_hold(x);
394 goto out;
395
396expired:
397 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
398 x->km.state = XFRM_STATE_EXPIRED;
399 wake_up(&km_waitq);
400 next = 2;
401 goto resched;
402 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700403 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800404 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406out:
407 spin_unlock(&x->lock);
408 xfrm_state_put(x);
409}
410
David S. Miller0ac84752006-03-20 19:18:23 -0800411static void xfrm_replay_timer_handler(unsigned long data);
412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413struct xfrm_state *xfrm_state_alloc(void)
414{
415 struct xfrm_state *x;
416
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700417 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 atomic_set(&x->refcnt, 1);
421 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700422 INIT_HLIST_NODE(&x->bydst);
423 INIT_HLIST_NODE(&x->bysrc);
424 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 init_timer(&x->timer);
426 x->timer.function = xfrm_timer_handler;
427 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800428 init_timer(&x->rtimer);
429 x->rtimer.function = xfrm_replay_timer_handler;
430 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 x->curlft.add_time = (unsigned long)xtime.tv_sec;
432 x->lft.soft_byte_limit = XFRM_INF;
433 x->lft.soft_packet_limit = XFRM_INF;
434 x->lft.hard_byte_limit = XFRM_INF;
435 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800436 x->replay_maxage = 0;
437 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 spin_lock_init(&x->lock);
439 }
440 return x;
441}
442EXPORT_SYMBOL(xfrm_state_alloc);
443
444void __xfrm_state_destroy(struct xfrm_state *x)
445{
446 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
447
448 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700449 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 spin_unlock_bh(&xfrm_state_gc_lock);
451 schedule_work(&xfrm_state_gc_work);
452}
453EXPORT_SYMBOL(__xfrm_state_destroy);
454
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800455int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700457 int err = -ESRCH;
458
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (x->km.state != XFRM_STATE_DEAD) {
460 x->km.state = XFRM_STATE_DEAD;
461 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700462 hlist_del(&x->bydst);
Herbert Xu21380b82006-02-22 14:47:13 -0800463 __xfrm_state_put(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700464 hlist_del(&x->bysrc);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700465 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 if (x->id.spi) {
David S. Miller8f126e32006-08-24 02:45:07 -0700467 hlist_del(&x->byspi);
Herbert Xu21380b82006-02-22 14:47:13 -0800468 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700470 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 spin_unlock(&xfrm_state_lock);
472 if (del_timer(&x->timer))
Herbert Xu21380b82006-02-22 14:47:13 -0800473 __xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800474 if (del_timer(&x->rtimer))
475 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 /* The number two in this test is the reference
478 * mentioned in the comment below plus the reference
479 * our caller holds. A larger value means that
480 * there are DSTs attached to this xfrm_state.
481 */
482 if (atomic_read(&x->refcnt) > 2) {
483 xfrm_state_gc_flush_bundles = 1;
484 schedule_work(&xfrm_state_gc_work);
485 }
486
487 /* All xfrm_state objects are created by xfrm_state_alloc.
488 * The xfrm_state_alloc call gives a reference, and that
489 * is what we are dropping here.
490 */
Herbert Xu21380b82006-02-22 14:47:13 -0800491 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700492 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700494
495 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800497EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700499int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700501 int err;
502
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700504 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700506
507 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508}
509EXPORT_SYMBOL(xfrm_state_delete);
510
511void xfrm_state_flush(u8 proto)
512{
513 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
515 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -0700516 for (i = 0; i < xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700517 struct hlist_node *entry;
518 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700520 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700522 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 xfrm_state_hold(x);
524 spin_unlock_bh(&xfrm_state_lock);
525
526 xfrm_state_delete(x);
527 xfrm_state_put(x);
528
529 spin_lock_bh(&xfrm_state_lock);
530 goto restart;
531 }
532 }
533 }
534 spin_unlock_bh(&xfrm_state_lock);
535 wake_up(&km_waitq);
536}
537EXPORT_SYMBOL(xfrm_state_flush);
538
539static int
540xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
541 struct xfrm_tmpl *tmpl,
542 xfrm_address_t *daddr, xfrm_address_t *saddr,
543 unsigned short family)
544{
545 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
546 if (!afinfo)
547 return -1;
548 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
549 xfrm_state_put_afinfo(afinfo);
550 return 0;
551}
552
David S. Milleredcd5822006-08-24 00:42:45 -0700553static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family)
554{
555 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
556 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700557 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700558
David S. Miller8f126e32006-08-24 02:45:07 -0700559 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700560 if (x->props.family != family ||
561 x->id.spi != spi ||
562 x->id.proto != proto)
563 continue;
564
565 switch (family) {
566 case AF_INET:
567 if (x->id.daddr.a4 != daddr->a4)
568 continue;
569 break;
570 case AF_INET6:
571 if (!ipv6_addr_equal((struct in6_addr *)daddr,
572 (struct in6_addr *)
573 x->id.daddr.a6))
574 continue;
575 break;
576 };
577
578 xfrm_state_hold(x);
579 return x;
580 }
581
582 return NULL;
583}
584
585static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
586{
587 unsigned int h = xfrm_src_hash(saddr, family);
588 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700589 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700590
David S. Miller8f126e32006-08-24 02:45:07 -0700591 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700592 if (x->props.family != family ||
593 x->id.proto != proto)
594 continue;
595
596 switch (family) {
597 case AF_INET:
598 if (x->id.daddr.a4 != daddr->a4 ||
599 x->props.saddr.a4 != saddr->a4)
600 continue;
601 break;
602 case AF_INET6:
603 if (!ipv6_addr_equal((struct in6_addr *)daddr,
604 (struct in6_addr *)
605 x->id.daddr.a6) ||
606 !ipv6_addr_equal((struct in6_addr *)saddr,
607 (struct in6_addr *)
608 x->props.saddr.a6))
609 continue;
610 break;
611 };
612
613 xfrm_state_hold(x);
614 return x;
615 }
616
617 return NULL;
618}
619
620static inline struct xfrm_state *
621__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
622{
623 if (use_spi)
624 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
625 x->id.proto, family);
626 else
627 return __xfrm_state_lookup_byaddr(&x->id.daddr,
628 &x->props.saddr,
629 x->id.proto, family);
630}
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632struct xfrm_state *
633xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
634 struct flowi *fl, struct xfrm_tmpl *tmpl,
635 struct xfrm_policy *pol, int *err,
636 unsigned short family)
637{
David S. Miller8f126e32006-08-24 02:45:07 -0700638 unsigned int h = xfrm_dst_hash(daddr, family);
639 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 struct xfrm_state *x, *x0;
641 int acquire_in_progress = 0;
642 int error = 0;
643 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700646 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 if (x->props.family == family &&
648 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700649 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 xfrm_state_addr_check(x, daddr, saddr, family) &&
651 tmpl->mode == x->props.mode &&
652 tmpl->id.proto == x->id.proto &&
653 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
654 /* Resolution logic:
655 1. There is a valid state with matching selector.
656 Done.
657 2. Valid state with inappropriate selector. Skip.
658
659 Entering area of "sysdeps".
660
661 3. If state is not valid, selector is temporary,
662 it selects only session which triggered
663 previous resolution. Key manager will do
664 something to install a state with proper
665 selector.
666 */
667 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800668 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700669 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 continue;
671 if (!best ||
672 best->km.dying > x->km.dying ||
673 (best->km.dying == x->km.dying &&
674 best->curlft.add_time < x->curlft.add_time))
675 best = x;
676 } else if (x->km.state == XFRM_STATE_ACQ) {
677 acquire_in_progress = 1;
678 } else if (x->km.state == XFRM_STATE_ERROR ||
679 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800680 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700681 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 error = -ESRCH;
683 }
684 }
685 }
686
687 x = best;
688 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700689 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700690 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
691 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 xfrm_state_put(x0);
693 error = -EEXIST;
694 goto out;
695 }
696 x = xfrm_state_alloc();
697 if (x == NULL) {
698 error = -ENOMEM;
699 goto out;
700 }
701 /* Initialize temporary selector matching only
702 * to current session. */
703 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
704
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700705 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
706 if (error) {
707 x->km.state = XFRM_STATE_DEAD;
708 xfrm_state_put(x);
709 x = NULL;
710 goto out;
711 }
712
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 if (km_query(x, tmpl, pol) == 0) {
714 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700715 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700717 h = xfrm_src_hash(saddr, family);
718 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700719 xfrm_state_hold(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if (x->id.spi) {
721 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700722 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 xfrm_state_hold(x);
724 }
725 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
726 xfrm_state_hold(x);
727 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
728 add_timer(&x->timer);
729 } else {
730 x->km.state = XFRM_STATE_DEAD;
731 xfrm_state_put(x);
732 x = NULL;
733 error = -ESRCH;
734 }
735 }
736out:
737 if (x)
738 xfrm_state_hold(x);
739 else
740 *err = acquire_in_progress ? -EAGAIN : error;
741 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 return x;
743}
744
745static void __xfrm_state_insert(struct xfrm_state *x)
746{
David S. Millerf034b5d2006-08-24 03:08:07 -0700747 unsigned int h = xfrm_dst_hash(&x->id.daddr, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
David S. Miller9d4a7062006-08-24 03:18:09 -0700749 x->genid = ++xfrm_state_genid;
750
David S. Miller8f126e32006-08-24 02:45:07 -0700751 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 xfrm_state_hold(x);
753
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700754 h = xfrm_src_hash(&x->props.saddr, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
David S. Miller8f126e32006-08-24 02:45:07 -0700756 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 xfrm_state_hold(x);
758
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700759 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
760 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
761 x->props.family);
762
David S. Miller8f126e32006-08-24 02:45:07 -0700763 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700764 xfrm_state_hold(x);
765 }
766
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 if (!mod_timer(&x->timer, jiffies + HZ))
768 xfrm_state_hold(x);
769
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800770 if (x->replay_maxage &&
771 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
772 xfrm_state_hold(x);
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700775
776 xfrm_state_num++;
777
778 if (x->bydst.next != NULL &&
779 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
780 xfrm_state_num > xfrm_state_hmask)
781 schedule_work(&xfrm_hash_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782}
783
784void xfrm_state_insert(struct xfrm_state *x)
785{
786 spin_lock_bh(&xfrm_state_lock);
787 __xfrm_state_insert(x);
788 spin_unlock_bh(&xfrm_state_lock);
David S. Miller399c1802005-12-19 14:23:23 -0800789
790 xfrm_flush_all_bundles();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791}
792EXPORT_SYMBOL(xfrm_state_insert);
793
David S. Miller27708342006-08-24 00:13:10 -0700794/* xfrm_state_lock is held */
795static 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)
796{
797 unsigned int h = xfrm_dst_hash(daddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700798 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700799 struct xfrm_state *x;
800
David S. Miller8f126e32006-08-24 02:45:07 -0700801 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700802 if (x->props.reqid != reqid ||
803 x->props.mode != mode ||
804 x->props.family != family ||
805 x->km.state != XFRM_STATE_ACQ ||
806 x->id.spi != 0)
807 continue;
808
809 switch (family) {
810 case AF_INET:
811 if (x->id.daddr.a4 != daddr->a4 ||
812 x->props.saddr.a4 != saddr->a4)
813 continue;
814 break;
815 case AF_INET6:
816 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
817 (struct in6_addr *)daddr) ||
818 !ipv6_addr_equal((struct in6_addr *)
819 x->props.saddr.a6,
820 (struct in6_addr *)saddr))
821 continue;
822 break;
823 };
824
825 xfrm_state_hold(x);
826 return x;
827 }
828
829 if (!create)
830 return NULL;
831
832 x = xfrm_state_alloc();
833 if (likely(x)) {
834 switch (family) {
835 case AF_INET:
836 x->sel.daddr.a4 = daddr->a4;
837 x->sel.saddr.a4 = saddr->a4;
838 x->sel.prefixlen_d = 32;
839 x->sel.prefixlen_s = 32;
840 x->props.saddr.a4 = saddr->a4;
841 x->id.daddr.a4 = daddr->a4;
842 break;
843
844 case AF_INET6:
845 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
846 (struct in6_addr *)daddr);
847 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
848 (struct in6_addr *)saddr);
849 x->sel.prefixlen_d = 128;
850 x->sel.prefixlen_s = 128;
851 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
852 (struct in6_addr *)saddr);
853 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
854 (struct in6_addr *)daddr);
855 break;
856 };
857
858 x->km.state = XFRM_STATE_ACQ;
859 x->id.proto = proto;
860 x->props.family = family;
861 x->props.mode = mode;
862 x->props.reqid = reqid;
863 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
864 xfrm_state_hold(x);
865 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
866 add_timer(&x->timer);
867 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700868 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller27708342006-08-24 00:13:10 -0700869 h = xfrm_src_hash(saddr, family);
870 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700871 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700872 wake_up(&km_waitq);
873 }
874
875 return x;
876}
877
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
879
880int xfrm_state_add(struct xfrm_state *x)
881{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 struct xfrm_state *x1;
883 int family;
884 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700885 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889 spin_lock_bh(&xfrm_state_lock);
890
David S. Milleredcd5822006-08-24 00:42:45 -0700891 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (x1) {
893 xfrm_state_put(x1);
894 x1 = NULL;
895 err = -EEXIST;
896 goto out;
897 }
898
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700899 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 x1 = __xfrm_find_acq_byseq(x->km.seq);
901 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
902 xfrm_state_put(x1);
903 x1 = NULL;
904 }
905 }
906
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700907 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700908 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
909 x->id.proto,
910 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 __xfrm_state_insert(x);
913 err = 0;
914
915out:
916 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
David S. Miller399c1802005-12-19 14:23:23 -0800918 if (!err)
919 xfrm_flush_all_bundles();
920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (x1) {
922 xfrm_state_delete(x1);
923 xfrm_state_put(x1);
924 }
925
926 return err;
927}
928EXPORT_SYMBOL(xfrm_state_add);
929
930int xfrm_state_update(struct xfrm_state *x)
931{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 struct xfrm_state *x1;
933 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700934 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700937 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 err = -ESRCH;
940 if (!x1)
941 goto out;
942
943 if (xfrm_state_kern(x1)) {
944 xfrm_state_put(x1);
945 err = -EEXIST;
946 goto out;
947 }
948
949 if (x1->km.state == XFRM_STATE_ACQ) {
950 __xfrm_state_insert(x);
951 x = NULL;
952 }
953 err = 0;
954
955out:
956 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 if (err)
959 return err;
960
961 if (!x) {
962 xfrm_state_delete(x1);
963 xfrm_state_put(x1);
964 return 0;
965 }
966
967 err = -EINVAL;
968 spin_lock_bh(&x1->lock);
969 if (likely(x1->km.state == XFRM_STATE_VALID)) {
970 if (x->encap && x1->encap)
971 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700972 if (x->coaddr && x1->coaddr) {
973 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
974 }
975 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
976 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
978 x1->km.dying = 0;
979
980 if (!mod_timer(&x1->timer, jiffies + HZ))
981 xfrm_state_hold(x1);
982 if (x1->curlft.use_time)
983 xfrm_state_check_expire(x1);
984
985 err = 0;
986 }
987 spin_unlock_bh(&x1->lock);
988
989 xfrm_state_put(x1);
990
991 return err;
992}
993EXPORT_SYMBOL(xfrm_state_update);
994
995int xfrm_state_check_expire(struct xfrm_state *x)
996{
997 if (!x->curlft.use_time)
998 x->curlft.use_time = (unsigned long)xtime.tv_sec;
999
1000 if (x->km.state != XFRM_STATE_VALID)
1001 return -EINVAL;
1002
1003 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1004 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001005 x->km.state = XFRM_STATE_EXPIRED;
1006 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 xfrm_state_hold(x);
1008 return -EINVAL;
1009 }
1010
1011 if (!x->km.dying &&
1012 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001013 x->curlft.packets >= x->lft.soft_packet_limit)) {
1014 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001015 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001016 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 return 0;
1018}
1019EXPORT_SYMBOL(xfrm_state_check_expire);
1020
1021static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1022{
1023 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1024 - skb_headroom(skb);
1025
1026 if (nhead > 0)
1027 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1028
1029 /* Check tail too... */
1030 return 0;
1031}
1032
1033int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1034{
1035 int err = xfrm_state_check_expire(x);
1036 if (err < 0)
1037 goto err;
1038 err = xfrm_state_check_space(x, skb);
1039err:
1040 return err;
1041}
1042EXPORT_SYMBOL(xfrm_state_check);
1043
1044struct xfrm_state *
1045xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
1046 unsigned short family)
1047{
1048 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
1050 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001051 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 return x;
1054}
1055EXPORT_SYMBOL(xfrm_state_lookup);
1056
1057struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001058xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1059 u8 proto, unsigned short family)
1060{
1061 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001062
1063 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001064 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001065 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001066 return x;
1067}
1068EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1069
1070struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1072 xfrm_address_t *daddr, xfrm_address_t *saddr,
1073 int create, unsigned short family)
1074{
1075 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001078 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 return x;
1082}
1083EXPORT_SYMBOL(xfrm_find_acq);
1084
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001085#ifdef CONFIG_XFRM_SUB_POLICY
1086int
1087xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1088 unsigned short family)
1089{
1090 int err = 0;
1091 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1092 if (!afinfo)
1093 return -EAFNOSUPPORT;
1094
1095 spin_lock_bh(&xfrm_state_lock);
1096 if (afinfo->tmpl_sort)
1097 err = afinfo->tmpl_sort(dst, src, n);
1098 spin_unlock_bh(&xfrm_state_lock);
1099 xfrm_state_put_afinfo(afinfo);
1100 return err;
1101}
1102EXPORT_SYMBOL(xfrm_tmpl_sort);
1103
1104int
1105xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1106 unsigned short family)
1107{
1108 int err = 0;
1109 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1110 if (!afinfo)
1111 return -EAFNOSUPPORT;
1112
1113 spin_lock_bh(&xfrm_state_lock);
1114 if (afinfo->state_sort)
1115 err = afinfo->state_sort(dst, src, n);
1116 spin_unlock_bh(&xfrm_state_lock);
1117 xfrm_state_put_afinfo(afinfo);
1118 return err;
1119}
1120EXPORT_SYMBOL(xfrm_state_sort);
1121#endif
1122
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123/* Silly enough, but I'm lazy to build resolution list */
1124
1125static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1126{
1127 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
David S. Millerf034b5d2006-08-24 03:08:07 -07001129 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001130 struct hlist_node *entry;
1131 struct xfrm_state *x;
1132
1133 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1134 if (x->km.seq == seq &&
1135 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 xfrm_state_hold(x);
1137 return x;
1138 }
1139 }
1140 }
1141 return NULL;
1142}
1143
1144struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1145{
1146 struct xfrm_state *x;
1147
1148 spin_lock_bh(&xfrm_state_lock);
1149 x = __xfrm_find_acq_byseq(seq);
1150 spin_unlock_bh(&xfrm_state_lock);
1151 return x;
1152}
1153EXPORT_SYMBOL(xfrm_find_acq_byseq);
1154
1155u32 xfrm_get_acqseq(void)
1156{
1157 u32 res;
1158 static u32 acqseq;
1159 static DEFINE_SPINLOCK(acqseq_lock);
1160
1161 spin_lock_bh(&acqseq_lock);
1162 res = (++acqseq ? : ++acqseq);
1163 spin_unlock_bh(&acqseq_lock);
1164 return res;
1165}
1166EXPORT_SYMBOL(xfrm_get_acqseq);
1167
1168void
1169xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
1170{
David S. Millerf034b5d2006-08-24 03:08:07 -07001171 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 struct xfrm_state *x0;
1173
1174 if (x->id.spi)
1175 return;
1176
1177 if (minspi == maxspi) {
1178 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1179 if (x0) {
1180 xfrm_state_put(x0);
1181 return;
1182 }
1183 x->id.spi = minspi;
1184 } else {
1185 u32 spi = 0;
1186 minspi = ntohl(minspi);
1187 maxspi = ntohl(maxspi);
1188 for (h=0; h<maxspi-minspi+1; h++) {
1189 spi = minspi + net_random()%(maxspi-minspi+1);
1190 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1191 if (x0 == NULL) {
1192 x->id.spi = htonl(spi);
1193 break;
1194 }
1195 xfrm_state_put(x0);
1196 }
1197 }
1198 if (x->id.spi) {
1199 spin_lock_bh(&xfrm_state_lock);
1200 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001201 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 xfrm_state_hold(x);
1203 spin_unlock_bh(&xfrm_state_lock);
1204 wake_up(&km_waitq);
1205 }
1206}
1207EXPORT_SYMBOL(xfrm_alloc_spi);
1208
1209int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1210 void *data)
1211{
1212 int i;
1213 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001214 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 int count = 0;
1216 int err = 0;
1217
1218 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001219 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001220 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001221 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 count++;
1223 }
1224 }
1225 if (count == 0) {
1226 err = -ENOENT;
1227 goto out;
1228 }
1229
David S. Millerf034b5d2006-08-24 03:08:07 -07001230 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001231 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001232 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 continue;
1234 err = func(x, --count, data);
1235 if (err)
1236 goto out;
1237 }
1238 }
1239out:
1240 spin_unlock_bh(&xfrm_state_lock);
1241 return err;
1242}
1243EXPORT_SYMBOL(xfrm_state_walk);
1244
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001245
1246void xfrm_replay_notify(struct xfrm_state *x, int event)
1247{
1248 struct km_event c;
1249 /* we send notify messages in case
1250 * 1. we updated on of the sequence numbers, and the seqno difference
1251 * is at least x->replay_maxdiff, in this case we also update the
1252 * timeout of our timer function
1253 * 2. if x->replay_maxage has elapsed since last update,
1254 * and there were changes
1255 *
1256 * The state structure must be locked!
1257 */
1258
1259 switch (event) {
1260 case XFRM_REPLAY_UPDATE:
1261 if (x->replay_maxdiff &&
1262 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001263 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1264 if (x->xflags & XFRM_TIME_DEFER)
1265 event = XFRM_REPLAY_TIMEOUT;
1266 else
1267 return;
1268 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001269
1270 break;
1271
1272 case XFRM_REPLAY_TIMEOUT:
1273 if ((x->replay.seq == x->preplay.seq) &&
1274 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001275 (x->replay.oseq == x->preplay.oseq)) {
1276 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001277 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001278 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001279
1280 break;
1281 }
1282
1283 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1284 c.event = XFRM_MSG_NEWAE;
1285 c.data.aevent = event;
1286 km_state_notify(x, &c);
1287
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001288 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001289 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001290 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001291 x->xflags &= ~XFRM_TIME_DEFER;
1292 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001293}
David S. Millera70fcb02006-03-20 19:18:52 -08001294EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001295
1296static void xfrm_replay_timer_handler(unsigned long data)
1297{
1298 struct xfrm_state *x = (struct xfrm_state*)data;
1299
1300 spin_lock(&x->lock);
1301
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001302 if (x->km.state == XFRM_STATE_VALID) {
1303 if (xfrm_aevent_is_on())
1304 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1305 else
1306 x->xflags |= XFRM_TIME_DEFER;
1307 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001308
1309 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001310 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001311}
1312
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313int xfrm_replay_check(struct xfrm_state *x, u32 seq)
1314{
1315 u32 diff;
1316
1317 seq = ntohl(seq);
1318
1319 if (unlikely(seq == 0))
1320 return -EINVAL;
1321
1322 if (likely(seq > x->replay.seq))
1323 return 0;
1324
1325 diff = x->replay.seq - seq;
1326 if (diff >= x->props.replay_window) {
1327 x->stats.replay_window++;
1328 return -EINVAL;
1329 }
1330
1331 if (x->replay.bitmap & (1U << diff)) {
1332 x->stats.replay++;
1333 return -EINVAL;
1334 }
1335 return 0;
1336}
1337EXPORT_SYMBOL(xfrm_replay_check);
1338
1339void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
1340{
1341 u32 diff;
1342
1343 seq = ntohl(seq);
1344
1345 if (seq > x->replay.seq) {
1346 diff = seq - x->replay.seq;
1347 if (diff < x->props.replay_window)
1348 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1349 else
1350 x->replay.bitmap = 1;
1351 x->replay.seq = seq;
1352 } else {
1353 diff = x->replay.seq - seq;
1354 x->replay.bitmap |= (1U << diff);
1355 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001356
1357 if (xfrm_aevent_is_on())
1358 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359}
1360EXPORT_SYMBOL(xfrm_replay_advance);
1361
1362static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1363static DEFINE_RWLOCK(xfrm_km_lock);
1364
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001365void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366{
1367 struct xfrm_mgr *km;
1368
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001369 read_lock(&xfrm_km_lock);
1370 list_for_each_entry(km, &xfrm_km_list, list)
1371 if (km->notify_policy)
1372 km->notify_policy(xp, dir, c);
1373 read_unlock(&xfrm_km_lock);
1374}
1375
1376void km_state_notify(struct xfrm_state *x, struct km_event *c)
1377{
1378 struct xfrm_mgr *km;
1379 read_lock(&xfrm_km_lock);
1380 list_for_each_entry(km, &xfrm_km_list, list)
1381 if (km->notify)
1382 km->notify(x, c);
1383 read_unlock(&xfrm_km_lock);
1384}
1385
1386EXPORT_SYMBOL(km_policy_notify);
1387EXPORT_SYMBOL(km_state_notify);
1388
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001389void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001390{
1391 struct km_event c;
1392
Herbert Xubf088672005-06-18 22:44:00 -07001393 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001394 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001395 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001396 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397
1398 if (hard)
1399 wake_up(&km_waitq);
1400}
1401
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001402EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001403/*
1404 * We send to all registered managers regardless of failure
1405 * We are happy with one success
1406*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001407int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001409 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 struct xfrm_mgr *km;
1411
1412 read_lock(&xfrm_km_lock);
1413 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001414 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1415 if (!acqret)
1416 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 }
1418 read_unlock(&xfrm_km_lock);
1419 return err;
1420}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001421EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
1423int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1424{
1425 int err = -EINVAL;
1426 struct xfrm_mgr *km;
1427
1428 read_lock(&xfrm_km_lock);
1429 list_for_each_entry(km, &xfrm_km_list, list) {
1430 if (km->new_mapping)
1431 err = km->new_mapping(x, ipaddr, sport);
1432 if (!err)
1433 break;
1434 }
1435 read_unlock(&xfrm_km_lock);
1436 return err;
1437}
1438EXPORT_SYMBOL(km_new_mapping);
1439
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001440void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001442 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443
Herbert Xubf088672005-06-18 22:44:00 -07001444 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001445 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001446 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001447 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449 if (hard)
1450 wake_up(&km_waitq);
1451}
David S. Millera70fcb02006-03-20 19:18:52 -08001452EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001454int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1455{
1456 int err = -EINVAL;
1457 int ret;
1458 struct xfrm_mgr *km;
1459
1460 read_lock(&xfrm_km_lock);
1461 list_for_each_entry(km, &xfrm_km_list, list) {
1462 if (km->report) {
1463 ret = km->report(proto, sel, addr);
1464 if (!ret)
1465 err = ret;
1466 }
1467 }
1468 read_unlock(&xfrm_km_lock);
1469 return err;
1470}
1471EXPORT_SYMBOL(km_report);
1472
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1474{
1475 int err;
1476 u8 *data;
1477 struct xfrm_mgr *km;
1478 struct xfrm_policy *pol = NULL;
1479
1480 if (optlen <= 0 || optlen > PAGE_SIZE)
1481 return -EMSGSIZE;
1482
1483 data = kmalloc(optlen, GFP_KERNEL);
1484 if (!data)
1485 return -ENOMEM;
1486
1487 err = -EFAULT;
1488 if (copy_from_user(data, optval, optlen))
1489 goto out;
1490
1491 err = -EINVAL;
1492 read_lock(&xfrm_km_lock);
1493 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001494 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 optlen, &err);
1496 if (err >= 0)
1497 break;
1498 }
1499 read_unlock(&xfrm_km_lock);
1500
1501 if (err >= 0) {
1502 xfrm_sk_policy_insert(sk, err, pol);
1503 xfrm_pol_put(pol);
1504 err = 0;
1505 }
1506
1507out:
1508 kfree(data);
1509 return err;
1510}
1511EXPORT_SYMBOL(xfrm_user_policy);
1512
1513int xfrm_register_km(struct xfrm_mgr *km)
1514{
1515 write_lock_bh(&xfrm_km_lock);
1516 list_add_tail(&km->list, &xfrm_km_list);
1517 write_unlock_bh(&xfrm_km_lock);
1518 return 0;
1519}
1520EXPORT_SYMBOL(xfrm_register_km);
1521
1522int xfrm_unregister_km(struct xfrm_mgr *km)
1523{
1524 write_lock_bh(&xfrm_km_lock);
1525 list_del(&km->list);
1526 write_unlock_bh(&xfrm_km_lock);
1527 return 0;
1528}
1529EXPORT_SYMBOL(xfrm_unregister_km);
1530
1531int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1532{
1533 int err = 0;
1534 if (unlikely(afinfo == NULL))
1535 return -EINVAL;
1536 if (unlikely(afinfo->family >= NPROTO))
1537 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001538 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1540 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001541 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001543 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 return err;
1545}
1546EXPORT_SYMBOL(xfrm_state_register_afinfo);
1547
1548int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1549{
1550 int err = 0;
1551 if (unlikely(afinfo == NULL))
1552 return -EINVAL;
1553 if (unlikely(afinfo->family >= NPROTO))
1554 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001555 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1557 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1558 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001559 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001562 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 return err;
1564}
1565EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1566
1567static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1568{
1569 struct xfrm_state_afinfo *afinfo;
1570 if (unlikely(family >= NPROTO))
1571 return NULL;
1572 read_lock(&xfrm_state_afinfo_lock);
1573 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001574 if (unlikely(!afinfo))
1575 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 return afinfo;
1577}
1578
1579static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1580{
Herbert Xu546be242006-05-27 23:03:58 -07001581 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582}
1583
1584/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1585void xfrm_state_delete_tunnel(struct xfrm_state *x)
1586{
1587 if (x->tunnel) {
1588 struct xfrm_state *t = x->tunnel;
1589
1590 if (atomic_read(&t->tunnel_users) == 2)
1591 xfrm_state_delete(t);
1592 atomic_dec(&t->tunnel_users);
1593 xfrm_state_put(t);
1594 x->tunnel = NULL;
1595 }
1596}
1597EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1598
Herbert Xu80b30c12005-10-15 10:58:30 +10001599/*
1600 * This function is NOT optimal. For example, with ESP it will give an
1601 * MTU that's usually two bytes short of being optimal. However, it will
1602 * usually give an answer that's a multiple of 4 provided the input is
1603 * also a multiple of 4.
1604 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1606{
1607 int res = mtu;
1608
1609 res -= x->props.header_len;
1610
1611 for (;;) {
1612 int m = res;
1613
1614 if (m < 68)
1615 return 68;
1616
1617 spin_lock_bh(&x->lock);
1618 if (x->km.state == XFRM_STATE_VALID &&
1619 x->type && x->type->get_max_size)
1620 m = x->type->get_max_size(x, m);
1621 else
1622 m += x->props.header_len;
1623 spin_unlock_bh(&x->lock);
1624
1625 if (m <= mtu)
1626 break;
1627 res -= (m - mtu);
1628 }
1629
1630 return res;
1631}
1632
Herbert Xu72cb6962005-06-20 13:18:08 -07001633int xfrm_init_state(struct xfrm_state *x)
1634{
Herbert Xud094cd82005-06-20 13:19:41 -07001635 struct xfrm_state_afinfo *afinfo;
1636 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001637 int err;
1638
Herbert Xud094cd82005-06-20 13:19:41 -07001639 err = -EAFNOSUPPORT;
1640 afinfo = xfrm_state_get_afinfo(family);
1641 if (!afinfo)
1642 goto error;
1643
1644 err = 0;
1645 if (afinfo->init_flags)
1646 err = afinfo->init_flags(x);
1647
1648 xfrm_state_put_afinfo(afinfo);
1649
1650 if (err)
1651 goto error;
1652
1653 err = -EPROTONOSUPPORT;
1654 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001655 if (x->type == NULL)
1656 goto error;
1657
1658 err = x->type->init_state(x);
1659 if (err)
1660 goto error;
1661
Herbert Xub59f45d2006-05-27 23:05:54 -07001662 x->mode = xfrm_get_mode(x->props.mode, family);
1663 if (x->mode == NULL)
1664 goto error;
1665
Herbert Xu72cb6962005-06-20 13:18:08 -07001666 x->km.state = XFRM_STATE_VALID;
1667
1668error:
1669 return err;
1670}
1671
1672EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
1674void __init xfrm_state_init(void)
1675{
David S. Millerf034b5d2006-08-24 03:08:07 -07001676 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
David S. Millerf034b5d2006-08-24 03:08:07 -07001678 sz = sizeof(struct hlist_head) * 8;
1679
1680 xfrm_state_bydst = xfrm_state_hash_alloc(sz);
1681 xfrm_state_bysrc = xfrm_state_hash_alloc(sz);
1682 xfrm_state_byspi = xfrm_state_hash_alloc(sz);
1683 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1684 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1685 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1686
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1688}
1689