blob: 77ef796c9d0dc4f6c157249d1677139d066894b9 [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. Millera624c102006-08-24 03:24:33 -070068static inline unsigned int __xfrm_dst_hash(xfrm_address_t *addr,
69 u32 reqid, unsigned short family,
70 unsigned int hmask)
David S. Milleredcd5822006-08-24 00:42:45 -070071{
David S. Millera624c102006-08-24 03:24:33 -070072 unsigned int h = family ^ reqid;
David S. Milleredcd5822006-08-24 00:42:45 -070073 switch (family) {
74 case AF_INET:
David S. Millera624c102006-08-24 03:24:33 -070075 h ^= __xfrm4_addr_hash(addr);
76 break;
David S. Milleredcd5822006-08-24 00:42:45 -070077 case AF_INET6:
David S. Millera624c102006-08-24 03:24:33 -070078 h ^= __xfrm6_addr_hash(addr);
79 break;
80 };
81 return (h ^ (h >> 16)) & hmask;
82}
83
84static inline unsigned int xfrm_dst_hash(xfrm_address_t *addr, u32 reqid,
85 unsigned short family)
86{
87 return __xfrm_dst_hash(addr, reqid, family, xfrm_state_hmask);
88}
89
90static inline unsigned __xfrm_src_hash(xfrm_address_t *addr, unsigned short family,
91 unsigned int hmask)
92{
93 unsigned int h = family;
94 switch (family) {
95 case AF_INET:
96 h ^= __xfrm4_addr_hash(addr);
97 break;
98 case AF_INET6:
99 h ^= __xfrm6_addr_hash(addr);
100 break;
101 };
102 return (h ^ (h >> 16)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700103}
104
David S. Millerf034b5d2006-08-24 03:08:07 -0700105static inline unsigned xfrm_src_hash(xfrm_address_t *addr, unsigned short family)
106{
107 return __xfrm_src_hash(addr, family, xfrm_state_hmask);
108}
109
David S. Miller2575b652006-08-24 03:26:44 -0700110static inline unsigned int
111__xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family,
112 unsigned int hmask)
David S. Millerf034b5d2006-08-24 03:08:07 -0700113{
David S. Miller2575b652006-08-24 03:26:44 -0700114 unsigned int h = spi ^ proto;
David S. Milleredcd5822006-08-24 00:42:45 -0700115 switch (family) {
116 case AF_INET:
David S. Miller2575b652006-08-24 03:26:44 -0700117 h ^= __xfrm4_addr_hash(addr);
118 break;
David S. Milleredcd5822006-08-24 00:42:45 -0700119 case AF_INET6:
David S. Miller2575b652006-08-24 03:26:44 -0700120 h ^= __xfrm6_addr_hash(addr);
121 break;
David S. Milleredcd5822006-08-24 00:42:45 -0700122 }
David S. Miller2575b652006-08-24 03:26:44 -0700123 return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
David S. Milleredcd5822006-08-24 00:42:45 -0700124}
125
David S. Millerf034b5d2006-08-24 03:08:07 -0700126static inline unsigned int
127xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family)
128{
129 return __xfrm_spi_hash(addr, spi, proto, family, xfrm_state_hmask);
130}
131
132static struct hlist_head *xfrm_state_hash_alloc(unsigned int sz)
133{
134 struct hlist_head *n;
135
136 if (sz <= PAGE_SIZE)
137 n = kmalloc(sz, GFP_KERNEL);
138 else if (hashdist)
139 n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
140 else
141 n = (struct hlist_head *)
142 __get_free_pages(GFP_KERNEL, get_order(sz));
143
144 if (n)
145 memset(n, 0, sz);
146
147 return n;
148}
149
150static void xfrm_state_hash_free(struct hlist_head *n, unsigned int sz)
151{
152 if (sz <= PAGE_SIZE)
153 kfree(n);
154 else if (hashdist)
155 vfree(n);
156 else
157 free_pages((unsigned long)n, get_order(sz));
158}
159
160static void xfrm_hash_transfer(struct hlist_head *list,
161 struct hlist_head *ndsttable,
162 struct hlist_head *nsrctable,
163 struct hlist_head *nspitable,
164 unsigned int nhashmask)
165{
166 struct hlist_node *entry, *tmp;
167 struct xfrm_state *x;
168
169 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
170 unsigned int h;
171
David S. Millera624c102006-08-24 03:24:33 -0700172 h = __xfrm_dst_hash(&x->id.daddr, x->props.reqid,
173 x->props.family, nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -0700174 hlist_add_head(&x->bydst, ndsttable+h);
175
176 h = __xfrm_src_hash(&x->props.saddr, x->props.family,
177 nhashmask);
178 hlist_add_head(&x->bysrc, nsrctable+h);
179
180 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
181 x->props.family, nhashmask);
182 hlist_add_head(&x->byspi, nspitable+h);
183 }
184}
185
186static unsigned long xfrm_hash_new_size(void)
187{
188 return ((xfrm_state_hmask + 1) << 1) *
189 sizeof(struct hlist_head);
190}
191
192static DEFINE_MUTEX(hash_resize_mutex);
193
194static void xfrm_hash_resize(void *__unused)
195{
196 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
197 unsigned long nsize, osize;
198 unsigned int nhashmask, ohashmask;
199 int i;
200
201 mutex_lock(&hash_resize_mutex);
202
203 nsize = xfrm_hash_new_size();
204 ndst = xfrm_state_hash_alloc(nsize);
205 if (!ndst)
206 goto out_unlock;
207 nsrc = xfrm_state_hash_alloc(nsize);
208 if (!nsrc) {
209 xfrm_state_hash_free(ndst, nsize);
210 goto out_unlock;
211 }
212 nspi = xfrm_state_hash_alloc(nsize);
213 if (!nspi) {
214 xfrm_state_hash_free(ndst, nsize);
215 xfrm_state_hash_free(nsrc, nsize);
216 goto out_unlock;
217 }
218
219 spin_lock_bh(&xfrm_state_lock);
220
221 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
222 for (i = xfrm_state_hmask; i >= 0; i--)
223 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
224 nhashmask);
225
226 odst = xfrm_state_bydst;
227 osrc = xfrm_state_bysrc;
228 ospi = xfrm_state_byspi;
229 ohashmask = xfrm_state_hmask;
230
231 xfrm_state_bydst = ndst;
232 xfrm_state_bysrc = nsrc;
233 xfrm_state_byspi = nspi;
234 xfrm_state_hmask = nhashmask;
235
236 spin_unlock_bh(&xfrm_state_lock);
237
238 osize = (ohashmask + 1) * sizeof(struct hlist_head);
239 xfrm_state_hash_free(odst, osize);
240 xfrm_state_hash_free(osrc, osize);
241 xfrm_state_hash_free(ospi, osize);
242
243out_unlock:
244 mutex_unlock(&hash_resize_mutex);
245}
246
247static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249DECLARE_WAIT_QUEUE_HEAD(km_waitq);
250EXPORT_SYMBOL(km_waitq);
251
252static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
253static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
254
255static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700256static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257static DEFINE_SPINLOCK(xfrm_state_gc_lock);
258
259static int xfrm_state_gc_flush_bundles;
260
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800261int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
264static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
265
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800266int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800267void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269static void xfrm_state_gc_destroy(struct xfrm_state *x)
270{
271 if (del_timer(&x->timer))
272 BUG();
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800273 if (del_timer(&x->rtimer))
274 BUG();
Jesper Juhla51482b2005-11-08 09:41:34 -0800275 kfree(x->aalg);
276 kfree(x->ealg);
277 kfree(x->calg);
278 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700279 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700280 if (x->mode)
281 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 if (x->type) {
283 x->type->destructor(x);
284 xfrm_put_type(x->type);
285 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800286 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 kfree(x);
288}
289
290static void xfrm_state_gc_task(void *data)
291{
292 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700293 struct hlist_node *entry, *tmp;
294 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 if (xfrm_state_gc_flush_bundles) {
297 xfrm_state_gc_flush_bundles = 0;
298 xfrm_flush_bundles();
299 }
300
301 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700302 gc_list.first = xfrm_state_gc_list.first;
303 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 spin_unlock_bh(&xfrm_state_gc_lock);
305
David S. Miller8f126e32006-08-24 02:45:07 -0700306 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 wake_up(&km_waitq);
310}
311
312static inline unsigned long make_jiffies(long secs)
313{
314 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
315 return MAX_SCHEDULE_TIMEOUT-1;
316 else
317 return secs*HZ;
318}
319
320static void xfrm_timer_handler(unsigned long data)
321{
322 struct xfrm_state *x = (struct xfrm_state*)data;
323 unsigned long now = (unsigned long)xtime.tv_sec;
324 long next = LONG_MAX;
325 int warn = 0;
326
327 spin_lock(&x->lock);
328 if (x->km.state == XFRM_STATE_DEAD)
329 goto out;
330 if (x->km.state == XFRM_STATE_EXPIRED)
331 goto expired;
332 if (x->lft.hard_add_expires_seconds) {
333 long tmo = x->lft.hard_add_expires_seconds +
334 x->curlft.add_time - now;
335 if (tmo <= 0)
336 goto expired;
337 if (tmo < next)
338 next = tmo;
339 }
340 if (x->lft.hard_use_expires_seconds) {
341 long tmo = x->lft.hard_use_expires_seconds +
342 (x->curlft.use_time ? : now) - now;
343 if (tmo <= 0)
344 goto expired;
345 if (tmo < next)
346 next = tmo;
347 }
348 if (x->km.dying)
349 goto resched;
350 if (x->lft.soft_add_expires_seconds) {
351 long tmo = x->lft.soft_add_expires_seconds +
352 x->curlft.add_time - now;
353 if (tmo <= 0)
354 warn = 1;
355 else if (tmo < next)
356 next = tmo;
357 }
358 if (x->lft.soft_use_expires_seconds) {
359 long tmo = x->lft.soft_use_expires_seconds +
360 (x->curlft.use_time ? : now) - now;
361 if (tmo <= 0)
362 warn = 1;
363 else if (tmo < next)
364 next = tmo;
365 }
366
Herbert Xu4666faa2005-06-18 22:43:22 -0700367 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800369 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370resched:
371 if (next != LONG_MAX &&
372 !mod_timer(&x->timer, jiffies + make_jiffies(next)))
373 xfrm_state_hold(x);
374 goto out;
375
376expired:
377 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
378 x->km.state = XFRM_STATE_EXPIRED;
379 wake_up(&km_waitq);
380 next = 2;
381 goto resched;
382 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700383 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800384 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
386out:
387 spin_unlock(&x->lock);
388 xfrm_state_put(x);
389}
390
David S. Miller0ac84752006-03-20 19:18:23 -0800391static void xfrm_replay_timer_handler(unsigned long data);
392
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393struct xfrm_state *xfrm_state_alloc(void)
394{
395 struct xfrm_state *x;
396
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700397 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 atomic_set(&x->refcnt, 1);
401 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700402 INIT_HLIST_NODE(&x->bydst);
403 INIT_HLIST_NODE(&x->bysrc);
404 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 init_timer(&x->timer);
406 x->timer.function = xfrm_timer_handler;
407 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800408 init_timer(&x->rtimer);
409 x->rtimer.function = xfrm_replay_timer_handler;
410 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 x->curlft.add_time = (unsigned long)xtime.tv_sec;
412 x->lft.soft_byte_limit = XFRM_INF;
413 x->lft.soft_packet_limit = XFRM_INF;
414 x->lft.hard_byte_limit = XFRM_INF;
415 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800416 x->replay_maxage = 0;
417 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 spin_lock_init(&x->lock);
419 }
420 return x;
421}
422EXPORT_SYMBOL(xfrm_state_alloc);
423
424void __xfrm_state_destroy(struct xfrm_state *x)
425{
426 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
427
428 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700429 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 spin_unlock_bh(&xfrm_state_gc_lock);
431 schedule_work(&xfrm_state_gc_work);
432}
433EXPORT_SYMBOL(__xfrm_state_destroy);
434
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800435int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700437 int err = -ESRCH;
438
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 if (x->km.state != XFRM_STATE_DEAD) {
440 x->km.state = XFRM_STATE_DEAD;
441 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700442 hlist_del(&x->bydst);
Herbert Xu21380b82006-02-22 14:47:13 -0800443 __xfrm_state_put(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700444 hlist_del(&x->bysrc);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700445 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 if (x->id.spi) {
David S. Miller8f126e32006-08-24 02:45:07 -0700447 hlist_del(&x->byspi);
Herbert Xu21380b82006-02-22 14:47:13 -0800448 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700450 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 spin_unlock(&xfrm_state_lock);
452 if (del_timer(&x->timer))
Herbert Xu21380b82006-02-22 14:47:13 -0800453 __xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800454 if (del_timer(&x->rtimer))
455 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
457 /* The number two in this test is the reference
458 * mentioned in the comment below plus the reference
459 * our caller holds. A larger value means that
460 * there are DSTs attached to this xfrm_state.
461 */
462 if (atomic_read(&x->refcnt) > 2) {
463 xfrm_state_gc_flush_bundles = 1;
464 schedule_work(&xfrm_state_gc_work);
465 }
466
467 /* All xfrm_state objects are created by xfrm_state_alloc.
468 * The xfrm_state_alloc call gives a reference, and that
469 * is what we are dropping here.
470 */
Herbert Xu21380b82006-02-22 14:47:13 -0800471 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700472 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700474
475 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800477EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700479int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700481 int err;
482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700484 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700486
487 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489EXPORT_SYMBOL(xfrm_state_delete);
490
491void xfrm_state_flush(u8 proto)
492{
493 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
495 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -0700496 for (i = 0; i < xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700497 struct hlist_node *entry;
498 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700500 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700502 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 xfrm_state_hold(x);
504 spin_unlock_bh(&xfrm_state_lock);
505
506 xfrm_state_delete(x);
507 xfrm_state_put(x);
508
509 spin_lock_bh(&xfrm_state_lock);
510 goto restart;
511 }
512 }
513 }
514 spin_unlock_bh(&xfrm_state_lock);
515 wake_up(&km_waitq);
516}
517EXPORT_SYMBOL(xfrm_state_flush);
518
519static int
520xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
521 struct xfrm_tmpl *tmpl,
522 xfrm_address_t *daddr, xfrm_address_t *saddr,
523 unsigned short family)
524{
525 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
526 if (!afinfo)
527 return -1;
528 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
529 xfrm_state_put_afinfo(afinfo);
530 return 0;
531}
532
David S. Milleredcd5822006-08-24 00:42:45 -0700533static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family)
534{
535 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
536 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700537 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700538
David S. Miller8f126e32006-08-24 02:45:07 -0700539 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700540 if (x->props.family != family ||
541 x->id.spi != spi ||
542 x->id.proto != proto)
543 continue;
544
545 switch (family) {
546 case AF_INET:
547 if (x->id.daddr.a4 != daddr->a4)
548 continue;
549 break;
550 case AF_INET6:
551 if (!ipv6_addr_equal((struct in6_addr *)daddr,
552 (struct in6_addr *)
553 x->id.daddr.a6))
554 continue;
555 break;
556 };
557
558 xfrm_state_hold(x);
559 return x;
560 }
561
562 return NULL;
563}
564
565static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
566{
567 unsigned int h = xfrm_src_hash(saddr, family);
568 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700569 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700570
David S. Miller8f126e32006-08-24 02:45:07 -0700571 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700572 if (x->props.family != family ||
573 x->id.proto != proto)
574 continue;
575
576 switch (family) {
577 case AF_INET:
578 if (x->id.daddr.a4 != daddr->a4 ||
579 x->props.saddr.a4 != saddr->a4)
580 continue;
581 break;
582 case AF_INET6:
583 if (!ipv6_addr_equal((struct in6_addr *)daddr,
584 (struct in6_addr *)
585 x->id.daddr.a6) ||
586 !ipv6_addr_equal((struct in6_addr *)saddr,
587 (struct in6_addr *)
588 x->props.saddr.a6))
589 continue;
590 break;
591 };
592
593 xfrm_state_hold(x);
594 return x;
595 }
596
597 return NULL;
598}
599
600static inline struct xfrm_state *
601__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
602{
603 if (use_spi)
604 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
605 x->id.proto, family);
606 else
607 return __xfrm_state_lookup_byaddr(&x->id.daddr,
608 &x->props.saddr,
609 x->id.proto, family);
610}
611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612struct xfrm_state *
613xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
614 struct flowi *fl, struct xfrm_tmpl *tmpl,
615 struct xfrm_policy *pol, int *err,
616 unsigned short family)
617{
David S. Millera624c102006-08-24 03:24:33 -0700618 unsigned int h = xfrm_dst_hash(daddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700619 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 struct xfrm_state *x, *x0;
621 int acquire_in_progress = 0;
622 int error = 0;
623 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700626 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 if (x->props.family == family &&
628 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700629 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 xfrm_state_addr_check(x, daddr, saddr, family) &&
631 tmpl->mode == x->props.mode &&
632 tmpl->id.proto == x->id.proto &&
633 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
634 /* Resolution logic:
635 1. There is a valid state with matching selector.
636 Done.
637 2. Valid state with inappropriate selector. Skip.
638
639 Entering area of "sysdeps".
640
641 3. If state is not valid, selector is temporary,
642 it selects only session which triggered
643 previous resolution. Key manager will do
644 something to install a state with proper
645 selector.
646 */
647 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800648 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700649 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 continue;
651 if (!best ||
652 best->km.dying > x->km.dying ||
653 (best->km.dying == x->km.dying &&
654 best->curlft.add_time < x->curlft.add_time))
655 best = x;
656 } else if (x->km.state == XFRM_STATE_ACQ) {
657 acquire_in_progress = 1;
658 } else if (x->km.state == XFRM_STATE_ERROR ||
659 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800660 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700661 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 error = -ESRCH;
663 }
664 }
665 }
666
667 x = best;
668 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700669 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700670 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
671 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 xfrm_state_put(x0);
673 error = -EEXIST;
674 goto out;
675 }
676 x = xfrm_state_alloc();
677 if (x == NULL) {
678 error = -ENOMEM;
679 goto out;
680 }
681 /* Initialize temporary selector matching only
682 * to current session. */
683 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
684
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700685 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
686 if (error) {
687 x->km.state = XFRM_STATE_DEAD;
688 xfrm_state_put(x);
689 x = NULL;
690 goto out;
691 }
692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 if (km_query(x, tmpl, pol) == 0) {
694 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700695 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700697 h = xfrm_src_hash(saddr, family);
698 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700699 xfrm_state_hold(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 if (x->id.spi) {
701 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700702 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 xfrm_state_hold(x);
704 }
705 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
706 xfrm_state_hold(x);
707 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
708 add_timer(&x->timer);
709 } else {
710 x->km.state = XFRM_STATE_DEAD;
711 xfrm_state_put(x);
712 x = NULL;
713 error = -ESRCH;
714 }
715 }
716out:
717 if (x)
718 xfrm_state_hold(x);
719 else
720 *err = acquire_in_progress ? -EAGAIN : error;
721 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 return x;
723}
724
725static void __xfrm_state_insert(struct xfrm_state *x)
726{
David S. Millera624c102006-08-24 03:24:33 -0700727 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
David S. Miller9d4a7062006-08-24 03:18:09 -0700729 x->genid = ++xfrm_state_genid;
730
David S. Millera624c102006-08-24 03:24:33 -0700731 h = xfrm_dst_hash(&x->id.daddr, x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700732 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 xfrm_state_hold(x);
734
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700735 h = xfrm_src_hash(&x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700736 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 xfrm_state_hold(x);
738
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700739 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
740 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
741 x->props.family);
742
David S. Miller8f126e32006-08-24 02:45:07 -0700743 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700744 xfrm_state_hold(x);
745 }
746
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 if (!mod_timer(&x->timer, jiffies + HZ))
748 xfrm_state_hold(x);
749
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800750 if (x->replay_maxage &&
751 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
752 xfrm_state_hold(x);
753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700755
756 xfrm_state_num++;
757
758 if (x->bydst.next != NULL &&
759 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
760 xfrm_state_num > xfrm_state_hmask)
761 schedule_work(&xfrm_hash_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762}
763
David S. Millerc7f5ea32006-08-24 03:29:04 -0700764/* xfrm_state_lock is held */
765static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
766{
767 unsigned short family = xnew->props.family;
768 u32 reqid = xnew->props.reqid;
769 struct xfrm_state *x;
770 struct hlist_node *entry;
771 unsigned int h;
772
773 h = xfrm_dst_hash(&xnew->id.daddr, reqid, family);
774 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
775 if (x->props.family == family &&
776 x->props.reqid == reqid &&
777 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family))
778 x->genid = xfrm_state_genid;
779 }
780}
781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782void xfrm_state_insert(struct xfrm_state *x)
783{
784 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700785 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 __xfrm_state_insert(x);
787 spin_unlock_bh(&xfrm_state_lock);
788}
789EXPORT_SYMBOL(xfrm_state_insert);
790
David S. Miller27708342006-08-24 00:13:10 -0700791/* xfrm_state_lock is held */
792static 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)
793{
David S. Millera624c102006-08-24 03:24:33 -0700794 unsigned int h = xfrm_dst_hash(daddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700795 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700796 struct xfrm_state *x;
797
David S. Miller8f126e32006-08-24 02:45:07 -0700798 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700799 if (x->props.reqid != reqid ||
800 x->props.mode != mode ||
801 x->props.family != family ||
802 x->km.state != XFRM_STATE_ACQ ||
803 x->id.spi != 0)
804 continue;
805
806 switch (family) {
807 case AF_INET:
808 if (x->id.daddr.a4 != daddr->a4 ||
809 x->props.saddr.a4 != saddr->a4)
810 continue;
811 break;
812 case AF_INET6:
813 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
814 (struct in6_addr *)daddr) ||
815 !ipv6_addr_equal((struct in6_addr *)
816 x->props.saddr.a6,
817 (struct in6_addr *)saddr))
818 continue;
819 break;
820 };
821
822 xfrm_state_hold(x);
823 return x;
824 }
825
826 if (!create)
827 return NULL;
828
829 x = xfrm_state_alloc();
830 if (likely(x)) {
831 switch (family) {
832 case AF_INET:
833 x->sel.daddr.a4 = daddr->a4;
834 x->sel.saddr.a4 = saddr->a4;
835 x->sel.prefixlen_d = 32;
836 x->sel.prefixlen_s = 32;
837 x->props.saddr.a4 = saddr->a4;
838 x->id.daddr.a4 = daddr->a4;
839 break;
840
841 case AF_INET6:
842 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
843 (struct in6_addr *)daddr);
844 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
845 (struct in6_addr *)saddr);
846 x->sel.prefixlen_d = 128;
847 x->sel.prefixlen_s = 128;
848 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
849 (struct in6_addr *)saddr);
850 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
851 (struct in6_addr *)daddr);
852 break;
853 };
854
855 x->km.state = XFRM_STATE_ACQ;
856 x->id.proto = proto;
857 x->props.family = family;
858 x->props.mode = mode;
859 x->props.reqid = reqid;
860 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
861 xfrm_state_hold(x);
862 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
863 add_timer(&x->timer);
864 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700865 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller27708342006-08-24 00:13:10 -0700866 h = xfrm_src_hash(saddr, family);
867 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700868 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700869 wake_up(&km_waitq);
870 }
871
872 return x;
873}
874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
876
877int xfrm_state_add(struct xfrm_state *x)
878{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 struct xfrm_state *x1;
880 int family;
881 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700882 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
886 spin_lock_bh(&xfrm_state_lock);
887
David S. Milleredcd5822006-08-24 00:42:45 -0700888 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 if (x1) {
890 xfrm_state_put(x1);
891 x1 = NULL;
892 err = -EEXIST;
893 goto out;
894 }
895
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700896 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 x1 = __xfrm_find_acq_byseq(x->km.seq);
898 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
899 xfrm_state_put(x1);
900 x1 = NULL;
901 }
902 }
903
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700904 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700905 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
906 x->id.proto,
907 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
David S. Millerc7f5ea32006-08-24 03:29:04 -0700909 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 __xfrm_state_insert(x);
911 err = 0;
912
913out:
914 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 if (x1) {
917 xfrm_state_delete(x1);
918 xfrm_state_put(x1);
919 }
920
921 return err;
922}
923EXPORT_SYMBOL(xfrm_state_add);
924
925int xfrm_state_update(struct xfrm_state *x)
926{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 struct xfrm_state *x1;
928 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700929 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700932 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
934 err = -ESRCH;
935 if (!x1)
936 goto out;
937
938 if (xfrm_state_kern(x1)) {
939 xfrm_state_put(x1);
940 err = -EEXIST;
941 goto out;
942 }
943
944 if (x1->km.state == XFRM_STATE_ACQ) {
945 __xfrm_state_insert(x);
946 x = NULL;
947 }
948 err = 0;
949
950out:
951 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953 if (err)
954 return err;
955
956 if (!x) {
957 xfrm_state_delete(x1);
958 xfrm_state_put(x1);
959 return 0;
960 }
961
962 err = -EINVAL;
963 spin_lock_bh(&x1->lock);
964 if (likely(x1->km.state == XFRM_STATE_VALID)) {
965 if (x->encap && x1->encap)
966 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700967 if (x->coaddr && x1->coaddr) {
968 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
969 }
970 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
971 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
973 x1->km.dying = 0;
974
975 if (!mod_timer(&x1->timer, jiffies + HZ))
976 xfrm_state_hold(x1);
977 if (x1->curlft.use_time)
978 xfrm_state_check_expire(x1);
979
980 err = 0;
981 }
982 spin_unlock_bh(&x1->lock);
983
984 xfrm_state_put(x1);
985
986 return err;
987}
988EXPORT_SYMBOL(xfrm_state_update);
989
990int xfrm_state_check_expire(struct xfrm_state *x)
991{
992 if (!x->curlft.use_time)
993 x->curlft.use_time = (unsigned long)xtime.tv_sec;
994
995 if (x->km.state != XFRM_STATE_VALID)
996 return -EINVAL;
997
998 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
999 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001000 x->km.state = XFRM_STATE_EXPIRED;
1001 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 xfrm_state_hold(x);
1003 return -EINVAL;
1004 }
1005
1006 if (!x->km.dying &&
1007 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001008 x->curlft.packets >= x->lft.soft_packet_limit)) {
1009 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001010 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001011 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 return 0;
1013}
1014EXPORT_SYMBOL(xfrm_state_check_expire);
1015
1016static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1017{
1018 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1019 - skb_headroom(skb);
1020
1021 if (nhead > 0)
1022 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1023
1024 /* Check tail too... */
1025 return 0;
1026}
1027
1028int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1029{
1030 int err = xfrm_state_check_expire(x);
1031 if (err < 0)
1032 goto err;
1033 err = xfrm_state_check_space(x, skb);
1034err:
1035 return err;
1036}
1037EXPORT_SYMBOL(xfrm_state_check);
1038
1039struct xfrm_state *
1040xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
1041 unsigned short family)
1042{
1043 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
1045 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001046 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 return x;
1049}
1050EXPORT_SYMBOL(xfrm_state_lookup);
1051
1052struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001053xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1054 u8 proto, unsigned short family)
1055{
1056 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001057
1058 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001059 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001060 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001061 return x;
1062}
1063EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1064
1065struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1067 xfrm_address_t *daddr, xfrm_address_t *saddr,
1068 int create, unsigned short family)
1069{
1070 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001073 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return x;
1077}
1078EXPORT_SYMBOL(xfrm_find_acq);
1079
Masahide NAKAMURA41a49cc32006-08-23 22:48:31 -07001080#ifdef CONFIG_XFRM_SUB_POLICY
1081int
1082xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1083 unsigned short family)
1084{
1085 int err = 0;
1086 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1087 if (!afinfo)
1088 return -EAFNOSUPPORT;
1089
1090 spin_lock_bh(&xfrm_state_lock);
1091 if (afinfo->tmpl_sort)
1092 err = afinfo->tmpl_sort(dst, src, n);
1093 spin_unlock_bh(&xfrm_state_lock);
1094 xfrm_state_put_afinfo(afinfo);
1095 return err;
1096}
1097EXPORT_SYMBOL(xfrm_tmpl_sort);
1098
1099int
1100xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1101 unsigned short family)
1102{
1103 int err = 0;
1104 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1105 if (!afinfo)
1106 return -EAFNOSUPPORT;
1107
1108 spin_lock_bh(&xfrm_state_lock);
1109 if (afinfo->state_sort)
1110 err = afinfo->state_sort(dst, src, n);
1111 spin_unlock_bh(&xfrm_state_lock);
1112 xfrm_state_put_afinfo(afinfo);
1113 return err;
1114}
1115EXPORT_SYMBOL(xfrm_state_sort);
1116#endif
1117
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118/* Silly enough, but I'm lazy to build resolution list */
1119
1120static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1121{
1122 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
David S. Millerf034b5d2006-08-24 03:08:07 -07001124 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001125 struct hlist_node *entry;
1126 struct xfrm_state *x;
1127
1128 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1129 if (x->km.seq == seq &&
1130 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 xfrm_state_hold(x);
1132 return x;
1133 }
1134 }
1135 }
1136 return NULL;
1137}
1138
1139struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1140{
1141 struct xfrm_state *x;
1142
1143 spin_lock_bh(&xfrm_state_lock);
1144 x = __xfrm_find_acq_byseq(seq);
1145 spin_unlock_bh(&xfrm_state_lock);
1146 return x;
1147}
1148EXPORT_SYMBOL(xfrm_find_acq_byseq);
1149
1150u32 xfrm_get_acqseq(void)
1151{
1152 u32 res;
1153 static u32 acqseq;
1154 static DEFINE_SPINLOCK(acqseq_lock);
1155
1156 spin_lock_bh(&acqseq_lock);
1157 res = (++acqseq ? : ++acqseq);
1158 spin_unlock_bh(&acqseq_lock);
1159 return res;
1160}
1161EXPORT_SYMBOL(xfrm_get_acqseq);
1162
1163void
1164xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
1165{
David S. Millerf034b5d2006-08-24 03:08:07 -07001166 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 struct xfrm_state *x0;
1168
1169 if (x->id.spi)
1170 return;
1171
1172 if (minspi == maxspi) {
1173 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1174 if (x0) {
1175 xfrm_state_put(x0);
1176 return;
1177 }
1178 x->id.spi = minspi;
1179 } else {
1180 u32 spi = 0;
1181 minspi = ntohl(minspi);
1182 maxspi = ntohl(maxspi);
1183 for (h=0; h<maxspi-minspi+1; h++) {
1184 spi = minspi + net_random()%(maxspi-minspi+1);
1185 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1186 if (x0 == NULL) {
1187 x->id.spi = htonl(spi);
1188 break;
1189 }
1190 xfrm_state_put(x0);
1191 }
1192 }
1193 if (x->id.spi) {
1194 spin_lock_bh(&xfrm_state_lock);
1195 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001196 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 xfrm_state_hold(x);
1198 spin_unlock_bh(&xfrm_state_lock);
1199 wake_up(&km_waitq);
1200 }
1201}
1202EXPORT_SYMBOL(xfrm_alloc_spi);
1203
1204int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1205 void *data)
1206{
1207 int i;
1208 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001209 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 int count = 0;
1211 int err = 0;
1212
1213 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001214 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001215 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001216 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 count++;
1218 }
1219 }
1220 if (count == 0) {
1221 err = -ENOENT;
1222 goto out;
1223 }
1224
David S. Millerf034b5d2006-08-24 03:08:07 -07001225 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001226 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001227 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 continue;
1229 err = func(x, --count, data);
1230 if (err)
1231 goto out;
1232 }
1233 }
1234out:
1235 spin_unlock_bh(&xfrm_state_lock);
1236 return err;
1237}
1238EXPORT_SYMBOL(xfrm_state_walk);
1239
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001240
1241void xfrm_replay_notify(struct xfrm_state *x, int event)
1242{
1243 struct km_event c;
1244 /* we send notify messages in case
1245 * 1. we updated on of the sequence numbers, and the seqno difference
1246 * is at least x->replay_maxdiff, in this case we also update the
1247 * timeout of our timer function
1248 * 2. if x->replay_maxage has elapsed since last update,
1249 * and there were changes
1250 *
1251 * The state structure must be locked!
1252 */
1253
1254 switch (event) {
1255 case XFRM_REPLAY_UPDATE:
1256 if (x->replay_maxdiff &&
1257 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001258 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1259 if (x->xflags & XFRM_TIME_DEFER)
1260 event = XFRM_REPLAY_TIMEOUT;
1261 else
1262 return;
1263 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001264
1265 break;
1266
1267 case XFRM_REPLAY_TIMEOUT:
1268 if ((x->replay.seq == x->preplay.seq) &&
1269 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001270 (x->replay.oseq == x->preplay.oseq)) {
1271 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001272 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001273 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001274
1275 break;
1276 }
1277
1278 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1279 c.event = XFRM_MSG_NEWAE;
1280 c.data.aevent = event;
1281 km_state_notify(x, &c);
1282
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001283 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001284 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001285 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001286 x->xflags &= ~XFRM_TIME_DEFER;
1287 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001288}
David S. Millera70fcb02006-03-20 19:18:52 -08001289EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001290
1291static void xfrm_replay_timer_handler(unsigned long data)
1292{
1293 struct xfrm_state *x = (struct xfrm_state*)data;
1294
1295 spin_lock(&x->lock);
1296
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001297 if (x->km.state == XFRM_STATE_VALID) {
1298 if (xfrm_aevent_is_on())
1299 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1300 else
1301 x->xflags |= XFRM_TIME_DEFER;
1302 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001303
1304 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001305 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001306}
1307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308int xfrm_replay_check(struct xfrm_state *x, u32 seq)
1309{
1310 u32 diff;
1311
1312 seq = ntohl(seq);
1313
1314 if (unlikely(seq == 0))
1315 return -EINVAL;
1316
1317 if (likely(seq > x->replay.seq))
1318 return 0;
1319
1320 diff = x->replay.seq - seq;
1321 if (diff >= x->props.replay_window) {
1322 x->stats.replay_window++;
1323 return -EINVAL;
1324 }
1325
1326 if (x->replay.bitmap & (1U << diff)) {
1327 x->stats.replay++;
1328 return -EINVAL;
1329 }
1330 return 0;
1331}
1332EXPORT_SYMBOL(xfrm_replay_check);
1333
1334void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
1335{
1336 u32 diff;
1337
1338 seq = ntohl(seq);
1339
1340 if (seq > x->replay.seq) {
1341 diff = seq - x->replay.seq;
1342 if (diff < x->props.replay_window)
1343 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1344 else
1345 x->replay.bitmap = 1;
1346 x->replay.seq = seq;
1347 } else {
1348 diff = x->replay.seq - seq;
1349 x->replay.bitmap |= (1U << diff);
1350 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001351
1352 if (xfrm_aevent_is_on())
1353 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355EXPORT_SYMBOL(xfrm_replay_advance);
1356
1357static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1358static DEFINE_RWLOCK(xfrm_km_lock);
1359
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001360void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361{
1362 struct xfrm_mgr *km;
1363
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001364 read_lock(&xfrm_km_lock);
1365 list_for_each_entry(km, &xfrm_km_list, list)
1366 if (km->notify_policy)
1367 km->notify_policy(xp, dir, c);
1368 read_unlock(&xfrm_km_lock);
1369}
1370
1371void km_state_notify(struct xfrm_state *x, struct km_event *c)
1372{
1373 struct xfrm_mgr *km;
1374 read_lock(&xfrm_km_lock);
1375 list_for_each_entry(km, &xfrm_km_list, list)
1376 if (km->notify)
1377 km->notify(x, c);
1378 read_unlock(&xfrm_km_lock);
1379}
1380
1381EXPORT_SYMBOL(km_policy_notify);
1382EXPORT_SYMBOL(km_state_notify);
1383
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001384void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001385{
1386 struct km_event c;
1387
Herbert Xubf088672005-06-18 22:44:00 -07001388 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001389 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001390 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001391 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 if (hard)
1394 wake_up(&km_waitq);
1395}
1396
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001397EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001398/*
1399 * We send to all registered managers regardless of failure
1400 * We are happy with one success
1401*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001402int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001404 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 struct xfrm_mgr *km;
1406
1407 read_lock(&xfrm_km_lock);
1408 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001409 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1410 if (!acqret)
1411 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 }
1413 read_unlock(&xfrm_km_lock);
1414 return err;
1415}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001416EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
1418int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1419{
1420 int err = -EINVAL;
1421 struct xfrm_mgr *km;
1422
1423 read_lock(&xfrm_km_lock);
1424 list_for_each_entry(km, &xfrm_km_list, list) {
1425 if (km->new_mapping)
1426 err = km->new_mapping(x, ipaddr, sport);
1427 if (!err)
1428 break;
1429 }
1430 read_unlock(&xfrm_km_lock);
1431 return err;
1432}
1433EXPORT_SYMBOL(km_new_mapping);
1434
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001435void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001437 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
Herbert Xubf088672005-06-18 22:44:00 -07001439 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001440 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001441 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001442 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443
1444 if (hard)
1445 wake_up(&km_waitq);
1446}
David S. Millera70fcb02006-03-20 19:18:52 -08001447EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001449int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1450{
1451 int err = -EINVAL;
1452 int ret;
1453 struct xfrm_mgr *km;
1454
1455 read_lock(&xfrm_km_lock);
1456 list_for_each_entry(km, &xfrm_km_list, list) {
1457 if (km->report) {
1458 ret = km->report(proto, sel, addr);
1459 if (!ret)
1460 err = ret;
1461 }
1462 }
1463 read_unlock(&xfrm_km_lock);
1464 return err;
1465}
1466EXPORT_SYMBOL(km_report);
1467
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1469{
1470 int err;
1471 u8 *data;
1472 struct xfrm_mgr *km;
1473 struct xfrm_policy *pol = NULL;
1474
1475 if (optlen <= 0 || optlen > PAGE_SIZE)
1476 return -EMSGSIZE;
1477
1478 data = kmalloc(optlen, GFP_KERNEL);
1479 if (!data)
1480 return -ENOMEM;
1481
1482 err = -EFAULT;
1483 if (copy_from_user(data, optval, optlen))
1484 goto out;
1485
1486 err = -EINVAL;
1487 read_lock(&xfrm_km_lock);
1488 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001489 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 optlen, &err);
1491 if (err >= 0)
1492 break;
1493 }
1494 read_unlock(&xfrm_km_lock);
1495
1496 if (err >= 0) {
1497 xfrm_sk_policy_insert(sk, err, pol);
1498 xfrm_pol_put(pol);
1499 err = 0;
1500 }
1501
1502out:
1503 kfree(data);
1504 return err;
1505}
1506EXPORT_SYMBOL(xfrm_user_policy);
1507
1508int xfrm_register_km(struct xfrm_mgr *km)
1509{
1510 write_lock_bh(&xfrm_km_lock);
1511 list_add_tail(&km->list, &xfrm_km_list);
1512 write_unlock_bh(&xfrm_km_lock);
1513 return 0;
1514}
1515EXPORT_SYMBOL(xfrm_register_km);
1516
1517int xfrm_unregister_km(struct xfrm_mgr *km)
1518{
1519 write_lock_bh(&xfrm_km_lock);
1520 list_del(&km->list);
1521 write_unlock_bh(&xfrm_km_lock);
1522 return 0;
1523}
1524EXPORT_SYMBOL(xfrm_unregister_km);
1525
1526int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1527{
1528 int err = 0;
1529 if (unlikely(afinfo == NULL))
1530 return -EINVAL;
1531 if (unlikely(afinfo->family >= NPROTO))
1532 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001533 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1535 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001536 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001538 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return err;
1540}
1541EXPORT_SYMBOL(xfrm_state_register_afinfo);
1542
1543int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1544{
1545 int err = 0;
1546 if (unlikely(afinfo == NULL))
1547 return -EINVAL;
1548 if (unlikely(afinfo->family >= NPROTO))
1549 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001550 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1552 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1553 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001554 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001557 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 return err;
1559}
1560EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1561
1562static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1563{
1564 struct xfrm_state_afinfo *afinfo;
1565 if (unlikely(family >= NPROTO))
1566 return NULL;
1567 read_lock(&xfrm_state_afinfo_lock);
1568 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001569 if (unlikely(!afinfo))
1570 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 return afinfo;
1572}
1573
1574static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1575{
Herbert Xu546be242006-05-27 23:03:58 -07001576 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577}
1578
1579/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1580void xfrm_state_delete_tunnel(struct xfrm_state *x)
1581{
1582 if (x->tunnel) {
1583 struct xfrm_state *t = x->tunnel;
1584
1585 if (atomic_read(&t->tunnel_users) == 2)
1586 xfrm_state_delete(t);
1587 atomic_dec(&t->tunnel_users);
1588 xfrm_state_put(t);
1589 x->tunnel = NULL;
1590 }
1591}
1592EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1593
Herbert Xu80b30c12005-10-15 10:58:30 +10001594/*
1595 * This function is NOT optimal. For example, with ESP it will give an
1596 * MTU that's usually two bytes short of being optimal. However, it will
1597 * usually give an answer that's a multiple of 4 provided the input is
1598 * also a multiple of 4.
1599 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1601{
1602 int res = mtu;
1603
1604 res -= x->props.header_len;
1605
1606 for (;;) {
1607 int m = res;
1608
1609 if (m < 68)
1610 return 68;
1611
1612 spin_lock_bh(&x->lock);
1613 if (x->km.state == XFRM_STATE_VALID &&
1614 x->type && x->type->get_max_size)
1615 m = x->type->get_max_size(x, m);
1616 else
1617 m += x->props.header_len;
1618 spin_unlock_bh(&x->lock);
1619
1620 if (m <= mtu)
1621 break;
1622 res -= (m - mtu);
1623 }
1624
1625 return res;
1626}
1627
Herbert Xu72cb6962005-06-20 13:18:08 -07001628int xfrm_init_state(struct xfrm_state *x)
1629{
Herbert Xud094cd82005-06-20 13:19:41 -07001630 struct xfrm_state_afinfo *afinfo;
1631 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001632 int err;
1633
Herbert Xud094cd82005-06-20 13:19:41 -07001634 err = -EAFNOSUPPORT;
1635 afinfo = xfrm_state_get_afinfo(family);
1636 if (!afinfo)
1637 goto error;
1638
1639 err = 0;
1640 if (afinfo->init_flags)
1641 err = afinfo->init_flags(x);
1642
1643 xfrm_state_put_afinfo(afinfo);
1644
1645 if (err)
1646 goto error;
1647
1648 err = -EPROTONOSUPPORT;
1649 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001650 if (x->type == NULL)
1651 goto error;
1652
1653 err = x->type->init_state(x);
1654 if (err)
1655 goto error;
1656
Herbert Xub59f45d2006-05-27 23:05:54 -07001657 x->mode = xfrm_get_mode(x->props.mode, family);
1658 if (x->mode == NULL)
1659 goto error;
1660
Herbert Xu72cb6962005-06-20 13:18:08 -07001661 x->km.state = XFRM_STATE_VALID;
1662
1663error:
1664 return err;
1665}
1666
1667EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669void __init xfrm_state_init(void)
1670{
David S. Millerf034b5d2006-08-24 03:08:07 -07001671 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
David S. Millerf034b5d2006-08-24 03:08:07 -07001673 sz = sizeof(struct hlist_head) * 8;
1674
1675 xfrm_state_bydst = xfrm_state_hash_alloc(sz);
1676 xfrm_state_bysrc = xfrm_state_hash_alloc(sz);
1677 xfrm_state_byspi = xfrm_state_hash_alloc(sz);
1678 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1679 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1680 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1681
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1683}
1684