blob: 37213f9f6a02c8c368ac0fdeb36dd7c08f3aa821 [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)
David S. Millera624c102006-08-24 03:24:33 -070038 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 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. Millera624c102006-08-24 03:24:33 -070058static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)
David S. Milleredcd5822006-08-24 00:42:45 -070059{
David S. Millera624c102006-08-24 03:24:33 -070060 return ntohl(addr->a4);
David S. Milleredcd5822006-08-24 00:42:45 -070061}
62
David S. Millera624c102006-08-24 03:24:33 -070063static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
David S. Milleredcd5822006-08-24 00:42:45 -070064{
David S. Millera624c102006-08-24 03:24:33 -070065 return ntohl(addr->a6[2]^addr->a6[3]);
David S. Milleredcd5822006-08-24 00:42:45 -070066}
67
David S. Millerc1969f22006-08-24 04:00:03 -070068static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
69{
70 return ntohl(daddr->a4 ^ saddr->a4);
71}
72
73static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
74{
75 return ntohl(daddr->a6[2] ^ daddr->a6[3] ^
76 saddr->a6[2] ^ saddr->a6[3]);
77}
78
79static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr,
80 xfrm_address_t *saddr,
David S. Millera624c102006-08-24 03:24:33 -070081 u32 reqid, unsigned short family,
82 unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070083{
David S. Millera624c102006-08-24 03:24:33 -070084 unsigned int h = family ^ reqid;
David S. Milleredcd5822006-08-24 00:42:45 -070085 switch (family) {
86 case AF_INET:
David S. Millerc1969f22006-08-24 04:00:03 -070087 h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
David S. Millera624c102006-08-24 03:24:33 -070088 break;
David S. Milleredcd5822006-08-24 00:42:45 -070089 case AF_INET6:
David S. Millerc1969f22006-08-24 04:00:03 -070090 h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
David S. Millera624c102006-08-24 03:24:33 -070091 break;
92 };
93 return (h ^ (h >> 16)) & hmask;
94}
95
David S. Millerc1969f22006-08-24 04:00:03 -070096static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
97 xfrm_address_t *saddr,
98 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070099 unsigned short family)
100{
David S. Millerc1969f22006-08-24 04:00:03 -0700101 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -0700102}
103
104static inline unsigned __xfrm_src_hash(xfrm_address_t *addr, unsigned short family,
105 unsigned int hmask)
106{
107 unsigned int h = family;
108 switch (family) {
109 case AF_INET:
110 h ^= __xfrm4_addr_hash(addr);
111 break;
112 case AF_INET6:
113 h ^= __xfrm6_addr_hash(addr);
114 break;
115 };
116 return (h ^ (h >> 16)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700117}
118
David S. Millerf034b5d2006-08-24 03:08:07 -0700119static inline unsigned xfrm_src_hash(xfrm_address_t *addr, unsigned short family)
120{
121 return __xfrm_src_hash(addr, family, xfrm_state_hmask);
122}
123
David S. Miller2575b652006-08-24 03:26:44 -0700124static inline unsigned int
David S. Millerc1969f22006-08-24 04:00:03 -0700125__xfrm_spi_hash(xfrm_address_t *daddr, u32 spi, u8 proto,
126 unsigned short family, unsigned int hmask)
David S. Millerf034b5d2006-08-24 03:08:07 -0700127{
David S. Miller2575b652006-08-24 03:26:44 -0700128 unsigned int h = spi ^ proto;
David S. Milleredcd5822006-08-24 00:42:45 -0700129 switch (family) {
130 case AF_INET:
David S. Millerc1969f22006-08-24 04:00:03 -0700131 h ^= __xfrm4_addr_hash(daddr);
David S. Miller2575b652006-08-24 03:26:44 -0700132 break;
David S. Milleredcd5822006-08-24 00:42:45 -0700133 case AF_INET6:
David S. Millerc1969f22006-08-24 04:00:03 -0700134 h ^= __xfrm6_addr_hash(daddr);
David S. Miller2575b652006-08-24 03:26:44 -0700135 break;
David S. Milleredcd5822006-08-24 00:42:45 -0700136 }
David S. Miller2575b652006-08-24 03:26:44 -0700137 return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700138}
139
David S. Millerf034b5d2006-08-24 03:08:07 -0700140static inline unsigned int
David S. Millerc1969f22006-08-24 04:00:03 -0700141xfrm_spi_hash(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -0700142{
David S. Millerc1969f22006-08-24 04:00:03 -0700143 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700144}
145
146static struct hlist_head *xfrm_state_hash_alloc(unsigned int sz)
147{
148 struct hlist_head *n;
149
150 if (sz <= PAGE_SIZE)
151 n = kmalloc(sz, GFP_KERNEL);
152 else if (hashdist)
153 n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
154 else
155 n = (struct hlist_head *)
156 __get_free_pages(GFP_KERNEL, get_order(sz));
157
158 if (n)
159 memset(n, 0, sz);
160
161 return n;
162}
163
164static void xfrm_state_hash_free(struct hlist_head *n, unsigned int sz)
165{
166 if (sz <= PAGE_SIZE)
167 kfree(n);
168 else if (hashdist)
169 vfree(n);
170 else
171 free_pages((unsigned long)n, get_order(sz));
172}
173
174static void xfrm_hash_transfer(struct hlist_head *list,
175 struct hlist_head *ndsttable,
176 struct hlist_head *nsrctable,
177 struct hlist_head *nspitable,
178 unsigned int nhashmask)
179{
180 struct hlist_node *entry, *tmp;
181 struct xfrm_state *x;
182
183 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
184 unsigned int h;
185
David S. Millerc1969f22006-08-24 04:00:03 -0700186 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
187 x->props.reqid, x->props.family,
188 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700189 hlist_add_head(&x->bydst, ndsttable+h);
190
191 h = __xfrm_src_hash(&x->props.saddr, x->props.family,
192 nhashmask);
193 hlist_add_head(&x->bysrc, nsrctable+h);
194
195 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
196 x->props.family, nhashmask);
197 hlist_add_head(&x->byspi, nspitable+h);
198 }
199}
200
201static unsigned long xfrm_hash_new_size(void)
202{
203 return ((xfrm_state_hmask + 1) << 1) *
204 sizeof(struct hlist_head);
205}
206
207static DEFINE_MUTEX(hash_resize_mutex);
208
209static void xfrm_hash_resize(void *__unused)
210{
211 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
212 unsigned long nsize, osize;
213 unsigned int nhashmask, ohashmask;
214 int i;
215
216 mutex_lock(&hash_resize_mutex);
217
218 nsize = xfrm_hash_new_size();
219 ndst = xfrm_state_hash_alloc(nsize);
220 if (!ndst)
221 goto out_unlock;
222 nsrc = xfrm_state_hash_alloc(nsize);
223 if (!nsrc) {
224 xfrm_state_hash_free(ndst, nsize);
225 goto out_unlock;
226 }
227 nspi = xfrm_state_hash_alloc(nsize);
228 if (!nspi) {
229 xfrm_state_hash_free(ndst, nsize);
230 xfrm_state_hash_free(nsrc, nsize);
231 goto out_unlock;
232 }
233
234 spin_lock_bh(&xfrm_state_lock);
235
236 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
237 for (i = xfrm_state_hmask; i >= 0; i--)
238 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
239 nhashmask);
240
241 odst = xfrm_state_bydst;
242 osrc = xfrm_state_bysrc;
243 ospi = xfrm_state_byspi;
244 ohashmask = xfrm_state_hmask;
245
246 xfrm_state_bydst = ndst;
247 xfrm_state_bysrc = nsrc;
248 xfrm_state_byspi = nspi;
249 xfrm_state_hmask = nhashmask;
250
251 spin_unlock_bh(&xfrm_state_lock);
252
253 osize = (ohashmask + 1) * sizeof(struct hlist_head);
254 xfrm_state_hash_free(odst, osize);
255 xfrm_state_hash_free(osrc, osize);
256 xfrm_state_hash_free(ospi, osize);
257
258out_unlock:
259 mutex_unlock(&hash_resize_mutex);
260}
261
262static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264DECLARE_WAIT_QUEUE_HEAD(km_waitq);
265EXPORT_SYMBOL(km_waitq);
266
267static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
268static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
269
270static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700271static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272static DEFINE_SPINLOCK(xfrm_state_gc_lock);
273
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800274int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
276static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
277static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
278
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800279int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800280void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282static void xfrm_state_gc_destroy(struct xfrm_state *x)
283{
David S. Millera47f0ce2006-08-24 03:54:22 -0700284 del_timer_sync(&x->timer);
285 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800286 kfree(x->aalg);
287 kfree(x->ealg);
288 kfree(x->calg);
289 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700290 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700291 if (x->mode)
292 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 if (x->type) {
294 x->type->destructor(x);
295 xfrm_put_type(x->type);
296 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800297 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 kfree(x);
299}
300
301static void xfrm_state_gc_task(void *data)
302{
303 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700304 struct hlist_node *entry, *tmp;
305 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700308 gc_list.first = xfrm_state_gc_list.first;
309 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 spin_unlock_bh(&xfrm_state_gc_lock);
311
David S. Miller8f126e32006-08-24 02:45:07 -0700312 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 wake_up(&km_waitq);
316}
317
318static inline unsigned long make_jiffies(long secs)
319{
320 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
321 return MAX_SCHEDULE_TIMEOUT-1;
322 else
323 return secs*HZ;
324}
325
326static void xfrm_timer_handler(unsigned long data)
327{
328 struct xfrm_state *x = (struct xfrm_state*)data;
329 unsigned long now = (unsigned long)xtime.tv_sec;
330 long next = LONG_MAX;
331 int warn = 0;
332
333 spin_lock(&x->lock);
334 if (x->km.state == XFRM_STATE_DEAD)
335 goto out;
336 if (x->km.state == XFRM_STATE_EXPIRED)
337 goto expired;
338 if (x->lft.hard_add_expires_seconds) {
339 long tmo = x->lft.hard_add_expires_seconds +
340 x->curlft.add_time - now;
341 if (tmo <= 0)
342 goto expired;
343 if (tmo < next)
344 next = tmo;
345 }
346 if (x->lft.hard_use_expires_seconds) {
347 long tmo = x->lft.hard_use_expires_seconds +
348 (x->curlft.use_time ? : now) - now;
349 if (tmo <= 0)
350 goto expired;
351 if (tmo < next)
352 next = tmo;
353 }
354 if (x->km.dying)
355 goto resched;
356 if (x->lft.soft_add_expires_seconds) {
357 long tmo = x->lft.soft_add_expires_seconds +
358 x->curlft.add_time - now;
359 if (tmo <= 0)
360 warn = 1;
361 else if (tmo < next)
362 next = tmo;
363 }
364 if (x->lft.soft_use_expires_seconds) {
365 long tmo = x->lft.soft_use_expires_seconds +
366 (x->curlft.use_time ? : now) - now;
367 if (tmo <= 0)
368 warn = 1;
369 else if (tmo < next)
370 next = tmo;
371 }
372
Herbert Xu4666faa2005-06-18 22:43:22 -0700373 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 if (warn)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800375 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700377 if (next != LONG_MAX)
378 mod_timer(&x->timer, jiffies + make_jiffies(next));
379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 goto out;
381
382expired:
383 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
384 x->km.state = XFRM_STATE_EXPIRED;
385 wake_up(&km_waitq);
386 next = 2;
387 goto resched;
388 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700389 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800390 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392out:
393 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
David S. Miller0ac84752006-03-20 19:18:23 -0800396static void xfrm_replay_timer_handler(unsigned long data);
397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398struct xfrm_state *xfrm_state_alloc(void)
399{
400 struct xfrm_state *x;
401
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700402 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 atomic_set(&x->refcnt, 1);
406 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700407 INIT_HLIST_NODE(&x->bydst);
408 INIT_HLIST_NODE(&x->bysrc);
409 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 init_timer(&x->timer);
411 x->timer.function = xfrm_timer_handler;
412 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800413 init_timer(&x->rtimer);
414 x->rtimer.function = xfrm_replay_timer_handler;
415 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 x->curlft.add_time = (unsigned long)xtime.tv_sec;
417 x->lft.soft_byte_limit = XFRM_INF;
418 x->lft.soft_packet_limit = XFRM_INF;
419 x->lft.hard_byte_limit = XFRM_INF;
420 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800421 x->replay_maxage = 0;
422 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 spin_lock_init(&x->lock);
424 }
425 return x;
426}
427EXPORT_SYMBOL(xfrm_state_alloc);
428
429void __xfrm_state_destroy(struct xfrm_state *x)
430{
431 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
432
433 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700434 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 spin_unlock_bh(&xfrm_state_gc_lock);
436 schedule_work(&xfrm_state_gc_work);
437}
438EXPORT_SYMBOL(__xfrm_state_destroy);
439
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800440int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700442 int err = -ESRCH;
443
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 if (x->km.state != XFRM_STATE_DEAD) {
445 x->km.state = XFRM_STATE_DEAD;
446 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700447 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700448 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700449 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700450 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700451 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 /* All xfrm_state objects are created by xfrm_state_alloc.
455 * The xfrm_state_alloc call gives a reference, and that
456 * is what we are dropping here.
457 */
Herbert Xu21380b82006-02-22 14:47:13 -0800458 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700459 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700461
462 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800464EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700466int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700468 int err;
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700471 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700473
474 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475}
476EXPORT_SYMBOL(xfrm_state_delete);
477
478void xfrm_state_flush(u8 proto)
479{
480 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
482 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -0700483 for (i = 0; i < xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700484 struct hlist_node *entry;
485 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700487 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700489 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 xfrm_state_hold(x);
491 spin_unlock_bh(&xfrm_state_lock);
492
493 xfrm_state_delete(x);
494 xfrm_state_put(x);
495
496 spin_lock_bh(&xfrm_state_lock);
497 goto restart;
498 }
499 }
500 }
501 spin_unlock_bh(&xfrm_state_lock);
502 wake_up(&km_waitq);
503}
504EXPORT_SYMBOL(xfrm_state_flush);
505
506static int
507xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
508 struct xfrm_tmpl *tmpl,
509 xfrm_address_t *daddr, xfrm_address_t *saddr,
510 unsigned short family)
511{
512 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
513 if (!afinfo)
514 return -1;
515 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
516 xfrm_state_put_afinfo(afinfo);
517 return 0;
518}
519
David S. Milleredcd5822006-08-24 00:42:45 -0700520static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family)
521{
522 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
523 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700524 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700525
David S. Miller8f126e32006-08-24 02:45:07 -0700526 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700527 if (x->props.family != family ||
528 x->id.spi != spi ||
529 x->id.proto != proto)
530 continue;
531
532 switch (family) {
533 case AF_INET:
534 if (x->id.daddr.a4 != daddr->a4)
535 continue;
536 break;
537 case AF_INET6:
538 if (!ipv6_addr_equal((struct in6_addr *)daddr,
539 (struct in6_addr *)
540 x->id.daddr.a6))
541 continue;
542 break;
543 };
544
545 xfrm_state_hold(x);
546 return x;
547 }
548
549 return NULL;
550}
551
552static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
553{
554 unsigned int h = xfrm_src_hash(saddr, family);
555 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700556 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700557
David S. Miller8f126e32006-08-24 02:45:07 -0700558 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700559 if (x->props.family != family ||
560 x->id.proto != proto)
561 continue;
562
563 switch (family) {
564 case AF_INET:
565 if (x->id.daddr.a4 != daddr->a4 ||
566 x->props.saddr.a4 != saddr->a4)
567 continue;
568 break;
569 case AF_INET6:
570 if (!ipv6_addr_equal((struct in6_addr *)daddr,
571 (struct in6_addr *)
572 x->id.daddr.a6) ||
573 !ipv6_addr_equal((struct in6_addr *)saddr,
574 (struct in6_addr *)
575 x->props.saddr.a6))
576 continue;
577 break;
578 };
579
580 xfrm_state_hold(x);
581 return x;
582 }
583
584 return NULL;
585}
586
587static inline struct xfrm_state *
588__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
589{
590 if (use_spi)
591 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
592 x->id.proto, family);
593 else
594 return __xfrm_state_lookup_byaddr(&x->id.daddr,
595 &x->props.saddr,
596 x->id.proto, family);
597}
598
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599struct xfrm_state *
600xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
601 struct flowi *fl, struct xfrm_tmpl *tmpl,
602 struct xfrm_policy *pol, int *err,
603 unsigned short family)
604{
David S. Millerc1969f22006-08-24 04:00:03 -0700605 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700606 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 struct xfrm_state *x, *x0;
608 int acquire_in_progress = 0;
609 int error = 0;
610 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700613 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 if (x->props.family == family &&
615 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700616 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 xfrm_state_addr_check(x, daddr, saddr, family) &&
618 tmpl->mode == x->props.mode &&
619 tmpl->id.proto == x->id.proto &&
620 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
621 /* Resolution logic:
622 1. There is a valid state with matching selector.
623 Done.
624 2. Valid state with inappropriate selector. Skip.
625
626 Entering area of "sysdeps".
627
628 3. If state is not valid, selector is temporary,
629 it selects only session which triggered
630 previous resolution. Key manager will do
631 something to install a state with proper
632 selector.
633 */
634 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800635 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700636 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 continue;
638 if (!best ||
639 best->km.dying > x->km.dying ||
640 (best->km.dying == x->km.dying &&
641 best->curlft.add_time < x->curlft.add_time))
642 best = x;
643 } else if (x->km.state == XFRM_STATE_ACQ) {
644 acquire_in_progress = 1;
645 } else if (x->km.state == XFRM_STATE_ERROR ||
646 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800647 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700648 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 error = -ESRCH;
650 }
651 }
652 }
653
654 x = best;
655 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700656 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700657 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
658 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 xfrm_state_put(x0);
660 error = -EEXIST;
661 goto out;
662 }
663 x = xfrm_state_alloc();
664 if (x == NULL) {
665 error = -ENOMEM;
666 goto out;
667 }
668 /* Initialize temporary selector matching only
669 * to current session. */
670 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
671
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700672 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
673 if (error) {
674 x->km.state = XFRM_STATE_DEAD;
675 xfrm_state_put(x);
676 x = NULL;
677 goto out;
678 }
679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 if (km_query(x, tmpl, pol) == 0) {
681 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700682 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller8f126e32006-08-24 02:45:07 -0700683 h = xfrm_src_hash(saddr, family);
684 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 if (x->id.spi) {
686 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700687 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 }
689 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
691 add_timer(&x->timer);
692 } else {
693 x->km.state = XFRM_STATE_DEAD;
694 xfrm_state_put(x);
695 x = NULL;
696 error = -ESRCH;
697 }
698 }
699out:
700 if (x)
701 xfrm_state_hold(x);
702 else
703 *err = acquire_in_progress ? -EAGAIN : error;
704 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 return x;
706}
707
708static void __xfrm_state_insert(struct xfrm_state *x)
709{
David S. Millera624c102006-08-24 03:24:33 -0700710 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
David S. Miller9d4a7062006-08-24 03:18:09 -0700712 x->genid = ++xfrm_state_genid;
713
David S. Millerc1969f22006-08-24 04:00:03 -0700714 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
715 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700716 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700718 h = xfrm_src_hash(&x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700719 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700721 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
722 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
723 x->props.family);
724
David S. Miller8f126e32006-08-24 02:45:07 -0700725 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700726 }
727
David S. Millera47f0ce2006-08-24 03:54:22 -0700728 mod_timer(&x->timer, jiffies + HZ);
729 if (x->replay_maxage)
730 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700733
734 xfrm_state_num++;
735
736 if (x->bydst.next != NULL &&
737 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
738 xfrm_state_num > xfrm_state_hmask)
739 schedule_work(&xfrm_hash_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740}
741
David S. Millerc7f5ea32006-08-24 03:29:04 -0700742/* xfrm_state_lock is held */
743static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
744{
745 unsigned short family = xnew->props.family;
746 u32 reqid = xnew->props.reqid;
747 struct xfrm_state *x;
748 struct hlist_node *entry;
749 unsigned int h;
750
David S. Millerc1969f22006-08-24 04:00:03 -0700751 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700752 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
753 if (x->props.family == family &&
754 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700755 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
756 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700757 x->genid = xfrm_state_genid;
758 }
759}
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761void xfrm_state_insert(struct xfrm_state *x)
762{
763 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700764 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 __xfrm_state_insert(x);
766 spin_unlock_bh(&xfrm_state_lock);
767}
768EXPORT_SYMBOL(xfrm_state_insert);
769
David S. Miller27708342006-08-24 00:13:10 -0700770/* xfrm_state_lock is held */
771static 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)
772{
David S. Millerc1969f22006-08-24 04:00:03 -0700773 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700774 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700775 struct xfrm_state *x;
776
David S. Miller8f126e32006-08-24 02:45:07 -0700777 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700778 if (x->props.reqid != reqid ||
779 x->props.mode != mode ||
780 x->props.family != family ||
781 x->km.state != XFRM_STATE_ACQ ||
782 x->id.spi != 0)
783 continue;
784
785 switch (family) {
786 case AF_INET:
787 if (x->id.daddr.a4 != daddr->a4 ||
788 x->props.saddr.a4 != saddr->a4)
789 continue;
790 break;
791 case AF_INET6:
792 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
793 (struct in6_addr *)daddr) ||
794 !ipv6_addr_equal((struct in6_addr *)
795 x->props.saddr.a6,
796 (struct in6_addr *)saddr))
797 continue;
798 break;
799 };
800
801 xfrm_state_hold(x);
802 return x;
803 }
804
805 if (!create)
806 return NULL;
807
808 x = xfrm_state_alloc();
809 if (likely(x)) {
810 switch (family) {
811 case AF_INET:
812 x->sel.daddr.a4 = daddr->a4;
813 x->sel.saddr.a4 = saddr->a4;
814 x->sel.prefixlen_d = 32;
815 x->sel.prefixlen_s = 32;
816 x->props.saddr.a4 = saddr->a4;
817 x->id.daddr.a4 = daddr->a4;
818 break;
819
820 case AF_INET6:
821 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
822 (struct in6_addr *)daddr);
823 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
824 (struct in6_addr *)saddr);
825 x->sel.prefixlen_d = 128;
826 x->sel.prefixlen_s = 128;
827 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
828 (struct in6_addr *)saddr);
829 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
830 (struct in6_addr *)daddr);
831 break;
832 };
833
834 x->km.state = XFRM_STATE_ACQ;
835 x->id.proto = proto;
836 x->props.family = family;
837 x->props.mode = mode;
838 x->props.reqid = reqid;
839 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
840 xfrm_state_hold(x);
841 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
842 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700843 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller27708342006-08-24 00:13:10 -0700844 h = xfrm_src_hash(saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700845 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700846 wake_up(&km_waitq);
847 }
848
849 return x;
850}
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
853
854int xfrm_state_add(struct xfrm_state *x)
855{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 struct xfrm_state *x1;
857 int family;
858 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700859 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
861 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
863 spin_lock_bh(&xfrm_state_lock);
864
David S. Milleredcd5822006-08-24 00:42:45 -0700865 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 if (x1) {
867 xfrm_state_put(x1);
868 x1 = NULL;
869 err = -EEXIST;
870 goto out;
871 }
872
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700873 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 x1 = __xfrm_find_acq_byseq(x->km.seq);
875 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
876 xfrm_state_put(x1);
877 x1 = NULL;
878 }
879 }
880
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700881 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700882 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
883 x->id.proto,
884 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
David S. Millerc7f5ea32006-08-24 03:29:04 -0700886 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 __xfrm_state_insert(x);
888 err = 0;
889
890out:
891 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
893 if (x1) {
894 xfrm_state_delete(x1);
895 xfrm_state_put(x1);
896 }
897
898 return err;
899}
900EXPORT_SYMBOL(xfrm_state_add);
901
902int xfrm_state_update(struct xfrm_state *x)
903{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 struct xfrm_state *x1;
905 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700906 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700909 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
911 err = -ESRCH;
912 if (!x1)
913 goto out;
914
915 if (xfrm_state_kern(x1)) {
916 xfrm_state_put(x1);
917 err = -EEXIST;
918 goto out;
919 }
920
921 if (x1->km.state == XFRM_STATE_ACQ) {
922 __xfrm_state_insert(x);
923 x = NULL;
924 }
925 err = 0;
926
927out:
928 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930 if (err)
931 return err;
932
933 if (!x) {
934 xfrm_state_delete(x1);
935 xfrm_state_put(x1);
936 return 0;
937 }
938
939 err = -EINVAL;
940 spin_lock_bh(&x1->lock);
941 if (likely(x1->km.state == XFRM_STATE_VALID)) {
942 if (x->encap && x1->encap)
943 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700944 if (x->coaddr && x1->coaddr) {
945 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
946 }
947 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
948 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
950 x1->km.dying = 0;
951
David S. Millera47f0ce2006-08-24 03:54:22 -0700952 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 if (x1->curlft.use_time)
954 xfrm_state_check_expire(x1);
955
956 err = 0;
957 }
958 spin_unlock_bh(&x1->lock);
959
960 xfrm_state_put(x1);
961
962 return err;
963}
964EXPORT_SYMBOL(xfrm_state_update);
965
966int xfrm_state_check_expire(struct xfrm_state *x)
967{
968 if (!x->curlft.use_time)
969 x->curlft.use_time = (unsigned long)xtime.tv_sec;
970
971 if (x->km.state != XFRM_STATE_VALID)
972 return -EINVAL;
973
974 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
975 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700976 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -0700977 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 return -EINVAL;
979 }
980
981 if (!x->km.dying &&
982 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700983 x->curlft.packets >= x->lft.soft_packet_limit)) {
984 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800985 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700986 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return 0;
988}
989EXPORT_SYMBOL(xfrm_state_check_expire);
990
991static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
992{
993 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
994 - skb_headroom(skb);
995
996 if (nhead > 0)
997 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
998
999 /* Check tail too... */
1000 return 0;
1001}
1002
1003int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1004{
1005 int err = xfrm_state_check_expire(x);
1006 if (err < 0)
1007 goto err;
1008 err = xfrm_state_check_space(x, skb);
1009err:
1010 return err;
1011}
1012EXPORT_SYMBOL(xfrm_state_check);
1013
1014struct xfrm_state *
1015xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
1016 unsigned short family)
1017{
1018 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
1020 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001021 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return x;
1024}
1025EXPORT_SYMBOL(xfrm_state_lookup);
1026
1027struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001028xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1029 u8 proto, unsigned short family)
1030{
1031 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001032
1033 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001034 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001035 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001036 return x;
1037}
1038EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1039
1040struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1042 xfrm_address_t *daddr, xfrm_address_t *saddr,
1043 int create, unsigned short family)
1044{
1045 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001048 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 return x;
1052}
1053EXPORT_SYMBOL(xfrm_find_acq);
1054
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001055#ifdef CONFIG_XFRM_SUB_POLICY
1056int
1057xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1058 unsigned short family)
1059{
1060 int err = 0;
1061 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1062 if (!afinfo)
1063 return -EAFNOSUPPORT;
1064
1065 spin_lock_bh(&xfrm_state_lock);
1066 if (afinfo->tmpl_sort)
1067 err = afinfo->tmpl_sort(dst, src, n);
1068 spin_unlock_bh(&xfrm_state_lock);
1069 xfrm_state_put_afinfo(afinfo);
1070 return err;
1071}
1072EXPORT_SYMBOL(xfrm_tmpl_sort);
1073
1074int
1075xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1076 unsigned short family)
1077{
1078 int err = 0;
1079 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1080 if (!afinfo)
1081 return -EAFNOSUPPORT;
1082
1083 spin_lock_bh(&xfrm_state_lock);
1084 if (afinfo->state_sort)
1085 err = afinfo->state_sort(dst, src, n);
1086 spin_unlock_bh(&xfrm_state_lock);
1087 xfrm_state_put_afinfo(afinfo);
1088 return err;
1089}
1090EXPORT_SYMBOL(xfrm_state_sort);
1091#endif
1092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093/* Silly enough, but I'm lazy to build resolution list */
1094
1095static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1096{
1097 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
David S. Millerf034b5d2006-08-24 03:08:07 -07001099 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001100 struct hlist_node *entry;
1101 struct xfrm_state *x;
1102
1103 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1104 if (x->km.seq == seq &&
1105 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 xfrm_state_hold(x);
1107 return x;
1108 }
1109 }
1110 }
1111 return NULL;
1112}
1113
1114struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1115{
1116 struct xfrm_state *x;
1117
1118 spin_lock_bh(&xfrm_state_lock);
1119 x = __xfrm_find_acq_byseq(seq);
1120 spin_unlock_bh(&xfrm_state_lock);
1121 return x;
1122}
1123EXPORT_SYMBOL(xfrm_find_acq_byseq);
1124
1125u32 xfrm_get_acqseq(void)
1126{
1127 u32 res;
1128 static u32 acqseq;
1129 static DEFINE_SPINLOCK(acqseq_lock);
1130
1131 spin_lock_bh(&acqseq_lock);
1132 res = (++acqseq ? : ++acqseq);
1133 spin_unlock_bh(&acqseq_lock);
1134 return res;
1135}
1136EXPORT_SYMBOL(xfrm_get_acqseq);
1137
1138void
1139xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
1140{
David S. Millerf034b5d2006-08-24 03:08:07 -07001141 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 struct xfrm_state *x0;
1143
1144 if (x->id.spi)
1145 return;
1146
1147 if (minspi == maxspi) {
1148 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1149 if (x0) {
1150 xfrm_state_put(x0);
1151 return;
1152 }
1153 x->id.spi = minspi;
1154 } else {
1155 u32 spi = 0;
1156 minspi = ntohl(minspi);
1157 maxspi = ntohl(maxspi);
1158 for (h=0; h<maxspi-minspi+1; h++) {
1159 spi = minspi + net_random()%(maxspi-minspi+1);
1160 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1161 if (x0 == NULL) {
1162 x->id.spi = htonl(spi);
1163 break;
1164 }
1165 xfrm_state_put(x0);
1166 }
1167 }
1168 if (x->id.spi) {
1169 spin_lock_bh(&xfrm_state_lock);
1170 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001171 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 spin_unlock_bh(&xfrm_state_lock);
1173 wake_up(&km_waitq);
1174 }
1175}
1176EXPORT_SYMBOL(xfrm_alloc_spi);
1177
1178int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1179 void *data)
1180{
1181 int i;
1182 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001183 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 int count = 0;
1185 int err = 0;
1186
1187 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001188 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001189 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001190 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 count++;
1192 }
1193 }
1194 if (count == 0) {
1195 err = -ENOENT;
1196 goto out;
1197 }
1198
David S. Millerf034b5d2006-08-24 03:08:07 -07001199 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001200 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001201 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 continue;
1203 err = func(x, --count, data);
1204 if (err)
1205 goto out;
1206 }
1207 }
1208out:
1209 spin_unlock_bh(&xfrm_state_lock);
1210 return err;
1211}
1212EXPORT_SYMBOL(xfrm_state_walk);
1213
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001214
1215void xfrm_replay_notify(struct xfrm_state *x, int event)
1216{
1217 struct km_event c;
1218 /* we send notify messages in case
1219 * 1. we updated on of the sequence numbers, and the seqno difference
1220 * is at least x->replay_maxdiff, in this case we also update the
1221 * timeout of our timer function
1222 * 2. if x->replay_maxage has elapsed since last update,
1223 * and there were changes
1224 *
1225 * The state structure must be locked!
1226 */
1227
1228 switch (event) {
1229 case XFRM_REPLAY_UPDATE:
1230 if (x->replay_maxdiff &&
1231 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001232 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1233 if (x->xflags & XFRM_TIME_DEFER)
1234 event = XFRM_REPLAY_TIMEOUT;
1235 else
1236 return;
1237 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001238
1239 break;
1240
1241 case XFRM_REPLAY_TIMEOUT:
1242 if ((x->replay.seq == x->preplay.seq) &&
1243 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001244 (x->replay.oseq == x->preplay.oseq)) {
1245 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001246 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001247 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001248
1249 break;
1250 }
1251
1252 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1253 c.event = XFRM_MSG_NEWAE;
1254 c.data.aevent = event;
1255 km_state_notify(x, &c);
1256
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001257 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001258 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001259 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001260}
David S. Millera70fcb02006-03-20 19:18:52 -08001261EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001262
1263static void xfrm_replay_timer_handler(unsigned long data)
1264{
1265 struct xfrm_state *x = (struct xfrm_state*)data;
1266
1267 spin_lock(&x->lock);
1268
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001269 if (x->km.state == XFRM_STATE_VALID) {
1270 if (xfrm_aevent_is_on())
1271 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1272 else
1273 x->xflags |= XFRM_TIME_DEFER;
1274 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001275
1276 spin_unlock(&x->lock);
1277}
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279int xfrm_replay_check(struct xfrm_state *x, u32 seq)
1280{
1281 u32 diff;
1282
1283 seq = ntohl(seq);
1284
1285 if (unlikely(seq == 0))
1286 return -EINVAL;
1287
1288 if (likely(seq > x->replay.seq))
1289 return 0;
1290
1291 diff = x->replay.seq - seq;
1292 if (diff >= x->props.replay_window) {
1293 x->stats.replay_window++;
1294 return -EINVAL;
1295 }
1296
1297 if (x->replay.bitmap & (1U << diff)) {
1298 x->stats.replay++;
1299 return -EINVAL;
1300 }
1301 return 0;
1302}
1303EXPORT_SYMBOL(xfrm_replay_check);
1304
1305void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
1306{
1307 u32 diff;
1308
1309 seq = ntohl(seq);
1310
1311 if (seq > x->replay.seq) {
1312 diff = seq - x->replay.seq;
1313 if (diff < x->props.replay_window)
1314 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1315 else
1316 x->replay.bitmap = 1;
1317 x->replay.seq = seq;
1318 } else {
1319 diff = x->replay.seq - seq;
1320 x->replay.bitmap |= (1U << diff);
1321 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001322
1323 if (xfrm_aevent_is_on())
1324 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325}
1326EXPORT_SYMBOL(xfrm_replay_advance);
1327
1328static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1329static DEFINE_RWLOCK(xfrm_km_lock);
1330
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001331void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
1333 struct xfrm_mgr *km;
1334
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001335 read_lock(&xfrm_km_lock);
1336 list_for_each_entry(km, &xfrm_km_list, list)
1337 if (km->notify_policy)
1338 km->notify_policy(xp, dir, c);
1339 read_unlock(&xfrm_km_lock);
1340}
1341
1342void km_state_notify(struct xfrm_state *x, struct km_event *c)
1343{
1344 struct xfrm_mgr *km;
1345 read_lock(&xfrm_km_lock);
1346 list_for_each_entry(km, &xfrm_km_list, list)
1347 if (km->notify)
1348 km->notify(x, c);
1349 read_unlock(&xfrm_km_lock);
1350}
1351
1352EXPORT_SYMBOL(km_policy_notify);
1353EXPORT_SYMBOL(km_state_notify);
1354
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001355void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001356{
1357 struct km_event c;
1358
Herbert Xubf08867f92005-06-18 22:44:00 -07001359 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001360 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001361 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001362 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 if (hard)
1365 wake_up(&km_waitq);
1366}
1367
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001368EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001369/*
1370 * We send to all registered managers regardless of failure
1371 * We are happy with one success
1372*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001373int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001375 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 struct xfrm_mgr *km;
1377
1378 read_lock(&xfrm_km_lock);
1379 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001380 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1381 if (!acqret)
1382 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 }
1384 read_unlock(&xfrm_km_lock);
1385 return err;
1386}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001387EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1390{
1391 int err = -EINVAL;
1392 struct xfrm_mgr *km;
1393
1394 read_lock(&xfrm_km_lock);
1395 list_for_each_entry(km, &xfrm_km_list, list) {
1396 if (km->new_mapping)
1397 err = km->new_mapping(x, ipaddr, sport);
1398 if (!err)
1399 break;
1400 }
1401 read_unlock(&xfrm_km_lock);
1402 return err;
1403}
1404EXPORT_SYMBOL(km_new_mapping);
1405
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001406void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001408 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
Herbert Xubf08867f92005-06-18 22:44:00 -07001410 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001411 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001412 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001413 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415 if (hard)
1416 wake_up(&km_waitq);
1417}
David S. Millera70fcb02006-03-20 19:18:52 -08001418EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001420int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1421{
1422 int err = -EINVAL;
1423 int ret;
1424 struct xfrm_mgr *km;
1425
1426 read_lock(&xfrm_km_lock);
1427 list_for_each_entry(km, &xfrm_km_list, list) {
1428 if (km->report) {
1429 ret = km->report(proto, sel, addr);
1430 if (!ret)
1431 err = ret;
1432 }
1433 }
1434 read_unlock(&xfrm_km_lock);
1435 return err;
1436}
1437EXPORT_SYMBOL(km_report);
1438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1440{
1441 int err;
1442 u8 *data;
1443 struct xfrm_mgr *km;
1444 struct xfrm_policy *pol = NULL;
1445
1446 if (optlen <= 0 || optlen > PAGE_SIZE)
1447 return -EMSGSIZE;
1448
1449 data = kmalloc(optlen, GFP_KERNEL);
1450 if (!data)
1451 return -ENOMEM;
1452
1453 err = -EFAULT;
1454 if (copy_from_user(data, optval, optlen))
1455 goto out;
1456
1457 err = -EINVAL;
1458 read_lock(&xfrm_km_lock);
1459 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001460 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 optlen, &err);
1462 if (err >= 0)
1463 break;
1464 }
1465 read_unlock(&xfrm_km_lock);
1466
1467 if (err >= 0) {
1468 xfrm_sk_policy_insert(sk, err, pol);
1469 xfrm_pol_put(pol);
1470 err = 0;
1471 }
1472
1473out:
1474 kfree(data);
1475 return err;
1476}
1477EXPORT_SYMBOL(xfrm_user_policy);
1478
1479int xfrm_register_km(struct xfrm_mgr *km)
1480{
1481 write_lock_bh(&xfrm_km_lock);
1482 list_add_tail(&km->list, &xfrm_km_list);
1483 write_unlock_bh(&xfrm_km_lock);
1484 return 0;
1485}
1486EXPORT_SYMBOL(xfrm_register_km);
1487
1488int xfrm_unregister_km(struct xfrm_mgr *km)
1489{
1490 write_lock_bh(&xfrm_km_lock);
1491 list_del(&km->list);
1492 write_unlock_bh(&xfrm_km_lock);
1493 return 0;
1494}
1495EXPORT_SYMBOL(xfrm_unregister_km);
1496
1497int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1498{
1499 int err = 0;
1500 if (unlikely(afinfo == NULL))
1501 return -EINVAL;
1502 if (unlikely(afinfo->family >= NPROTO))
1503 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001504 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1506 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001507 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001509 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 return err;
1511}
1512EXPORT_SYMBOL(xfrm_state_register_afinfo);
1513
1514int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1515{
1516 int err = 0;
1517 if (unlikely(afinfo == NULL))
1518 return -EINVAL;
1519 if (unlikely(afinfo->family >= NPROTO))
1520 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001521 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1523 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1524 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001525 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001528 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 return err;
1530}
1531EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1532
1533static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1534{
1535 struct xfrm_state_afinfo *afinfo;
1536 if (unlikely(family >= NPROTO))
1537 return NULL;
1538 read_lock(&xfrm_state_afinfo_lock);
1539 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001540 if (unlikely(!afinfo))
1541 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 return afinfo;
1543}
1544
1545static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1546{
Herbert Xu546be242006-05-27 23:03:58 -07001547 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548}
1549
1550/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1551void xfrm_state_delete_tunnel(struct xfrm_state *x)
1552{
1553 if (x->tunnel) {
1554 struct xfrm_state *t = x->tunnel;
1555
1556 if (atomic_read(&t->tunnel_users) == 2)
1557 xfrm_state_delete(t);
1558 atomic_dec(&t->tunnel_users);
1559 xfrm_state_put(t);
1560 x->tunnel = NULL;
1561 }
1562}
1563EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1564
Herbert Xu80b30c12005-10-15 10:58:30 +10001565/*
1566 * This function is NOT optimal. For example, with ESP it will give an
1567 * MTU that's usually two bytes short of being optimal. However, it will
1568 * usually give an answer that's a multiple of 4 provided the input is
1569 * also a multiple of 4.
1570 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1572{
1573 int res = mtu;
1574
1575 res -= x->props.header_len;
1576
1577 for (;;) {
1578 int m = res;
1579
1580 if (m < 68)
1581 return 68;
1582
1583 spin_lock_bh(&x->lock);
1584 if (x->km.state == XFRM_STATE_VALID &&
1585 x->type && x->type->get_max_size)
1586 m = x->type->get_max_size(x, m);
1587 else
1588 m += x->props.header_len;
1589 spin_unlock_bh(&x->lock);
1590
1591 if (m <= mtu)
1592 break;
1593 res -= (m - mtu);
1594 }
1595
1596 return res;
1597}
1598
Herbert Xu72cb6962005-06-20 13:18:08 -07001599int xfrm_init_state(struct xfrm_state *x)
1600{
Herbert Xud094cd82005-06-20 13:19:41 -07001601 struct xfrm_state_afinfo *afinfo;
1602 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001603 int err;
1604
Herbert Xud094cd82005-06-20 13:19:41 -07001605 err = -EAFNOSUPPORT;
1606 afinfo = xfrm_state_get_afinfo(family);
1607 if (!afinfo)
1608 goto error;
1609
1610 err = 0;
1611 if (afinfo->init_flags)
1612 err = afinfo->init_flags(x);
1613
1614 xfrm_state_put_afinfo(afinfo);
1615
1616 if (err)
1617 goto error;
1618
1619 err = -EPROTONOSUPPORT;
1620 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001621 if (x->type == NULL)
1622 goto error;
1623
1624 err = x->type->init_state(x);
1625 if (err)
1626 goto error;
1627
Herbert Xub59f45d2006-05-27 23:05:54 -07001628 x->mode = xfrm_get_mode(x->props.mode, family);
1629 if (x->mode == NULL)
1630 goto error;
1631
Herbert Xu72cb6962005-06-20 13:18:08 -07001632 x->km.state = XFRM_STATE_VALID;
1633
1634error:
1635 return err;
1636}
1637
1638EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639
1640void __init xfrm_state_init(void)
1641{
David S. Millerf034b5d2006-08-24 03:08:07 -07001642 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
David S. Millerf034b5d2006-08-24 03:08:07 -07001644 sz = sizeof(struct hlist_head) * 8;
1645
1646 xfrm_state_bydst = xfrm_state_hash_alloc(sz);
1647 xfrm_state_bysrc = xfrm_state_hash_alloc(sz);
1648 xfrm_state_byspi = xfrm_state_hash_alloc(sz);
1649 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1650 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1651 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1652
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1654}
1655