blob: 98200397e098d1be5807c2cb396da6fb0229de55 [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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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
764void xfrm_state_insert(struct xfrm_state *x)
765{
766 spin_lock_bh(&xfrm_state_lock);
767 __xfrm_state_insert(x);
768 spin_unlock_bh(&xfrm_state_lock);
David S. Miller399c1802005-12-19 14:23:23 -0800769
770 xfrm_flush_all_bundles();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771}
772EXPORT_SYMBOL(xfrm_state_insert);
773
David S. Miller27708342006-08-24 00:13:10 -0700774/* xfrm_state_lock is held */
775static 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)
776{
David S. Millera624c102006-08-24 03:24:33 -0700777 unsigned int h = xfrm_dst_hash(daddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700778 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700779 struct xfrm_state *x;
780
David S. Miller8f126e32006-08-24 02:45:07 -0700781 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700782 if (x->props.reqid != reqid ||
783 x->props.mode != mode ||
784 x->props.family != family ||
785 x->km.state != XFRM_STATE_ACQ ||
786 x->id.spi != 0)
787 continue;
788
789 switch (family) {
790 case AF_INET:
791 if (x->id.daddr.a4 != daddr->a4 ||
792 x->props.saddr.a4 != saddr->a4)
793 continue;
794 break;
795 case AF_INET6:
796 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
797 (struct in6_addr *)daddr) ||
798 !ipv6_addr_equal((struct in6_addr *)
799 x->props.saddr.a6,
800 (struct in6_addr *)saddr))
801 continue;
802 break;
803 };
804
805 xfrm_state_hold(x);
806 return x;
807 }
808
809 if (!create)
810 return NULL;
811
812 x = xfrm_state_alloc();
813 if (likely(x)) {
814 switch (family) {
815 case AF_INET:
816 x->sel.daddr.a4 = daddr->a4;
817 x->sel.saddr.a4 = saddr->a4;
818 x->sel.prefixlen_d = 32;
819 x->sel.prefixlen_s = 32;
820 x->props.saddr.a4 = saddr->a4;
821 x->id.daddr.a4 = daddr->a4;
822 break;
823
824 case AF_INET6:
825 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
826 (struct in6_addr *)daddr);
827 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
828 (struct in6_addr *)saddr);
829 x->sel.prefixlen_d = 128;
830 x->sel.prefixlen_s = 128;
831 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
832 (struct in6_addr *)saddr);
833 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
834 (struct in6_addr *)daddr);
835 break;
836 };
837
838 x->km.state = XFRM_STATE_ACQ;
839 x->id.proto = proto;
840 x->props.family = family;
841 x->props.mode = mode;
842 x->props.reqid = reqid;
843 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
844 xfrm_state_hold(x);
845 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
846 add_timer(&x->timer);
847 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700848 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller27708342006-08-24 00:13:10 -0700849 h = xfrm_src_hash(saddr, family);
850 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700851 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700852 wake_up(&km_waitq);
853 }
854
855 return x;
856}
857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
859
860int xfrm_state_add(struct xfrm_state *x)
861{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 struct xfrm_state *x1;
863 int family;
864 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700865 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 spin_lock_bh(&xfrm_state_lock);
870
David S. Milleredcd5822006-08-24 00:42:45 -0700871 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 if (x1) {
873 xfrm_state_put(x1);
874 x1 = NULL;
875 err = -EEXIST;
876 goto out;
877 }
878
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700879 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 x1 = __xfrm_find_acq_byseq(x->km.seq);
881 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
882 xfrm_state_put(x1);
883 x1 = NULL;
884 }
885 }
886
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700887 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700888 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
889 x->id.proto,
890 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892 __xfrm_state_insert(x);
893 err = 0;
894
895out:
896 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
David S. Miller399c1802005-12-19 14:23:23 -0800898 if (!err)
899 xfrm_flush_all_bundles();
900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (x1) {
902 xfrm_state_delete(x1);
903 xfrm_state_put(x1);
904 }
905
906 return err;
907}
908EXPORT_SYMBOL(xfrm_state_add);
909
910int xfrm_state_update(struct xfrm_state *x)
911{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 struct xfrm_state *x1;
913 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700914 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700917 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919 err = -ESRCH;
920 if (!x1)
921 goto out;
922
923 if (xfrm_state_kern(x1)) {
924 xfrm_state_put(x1);
925 err = -EEXIST;
926 goto out;
927 }
928
929 if (x1->km.state == XFRM_STATE_ACQ) {
930 __xfrm_state_insert(x);
931 x = NULL;
932 }
933 err = 0;
934
935out:
936 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 if (err)
939 return err;
940
941 if (!x) {
942 xfrm_state_delete(x1);
943 xfrm_state_put(x1);
944 return 0;
945 }
946
947 err = -EINVAL;
948 spin_lock_bh(&x1->lock);
949 if (likely(x1->km.state == XFRM_STATE_VALID)) {
950 if (x->encap && x1->encap)
951 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700952 if (x->coaddr && x1->coaddr) {
953 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
954 }
955 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
956 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
958 x1->km.dying = 0;
959
960 if (!mod_timer(&x1->timer, jiffies + HZ))
961 xfrm_state_hold(x1);
962 if (x1->curlft.use_time)
963 xfrm_state_check_expire(x1);
964
965 err = 0;
966 }
967 spin_unlock_bh(&x1->lock);
968
969 xfrm_state_put(x1);
970
971 return err;
972}
973EXPORT_SYMBOL(xfrm_state_update);
974
975int xfrm_state_check_expire(struct xfrm_state *x)
976{
977 if (!x->curlft.use_time)
978 x->curlft.use_time = (unsigned long)xtime.tv_sec;
979
980 if (x->km.state != XFRM_STATE_VALID)
981 return -EINVAL;
982
983 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
984 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700985 x->km.state = XFRM_STATE_EXPIRED;
986 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 xfrm_state_hold(x);
988 return -EINVAL;
989 }
990
991 if (!x->km.dying &&
992 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700993 x->curlft.packets >= x->lft.soft_packet_limit)) {
994 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -0800995 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700996 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 return 0;
998}
999EXPORT_SYMBOL(xfrm_state_check_expire);
1000
1001static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1002{
1003 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1004 - skb_headroom(skb);
1005
1006 if (nhead > 0)
1007 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1008
1009 /* Check tail too... */
1010 return 0;
1011}
1012
1013int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1014{
1015 int err = xfrm_state_check_expire(x);
1016 if (err < 0)
1017 goto err;
1018 err = xfrm_state_check_space(x, skb);
1019err:
1020 return err;
1021}
1022EXPORT_SYMBOL(xfrm_state_check);
1023
1024struct xfrm_state *
1025xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
1026 unsigned short family)
1027{
1028 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029
1030 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001031 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 return x;
1034}
1035EXPORT_SYMBOL(xfrm_state_lookup);
1036
1037struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001038xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1039 u8 proto, unsigned short family)
1040{
1041 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001042
1043 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001044 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001045 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001046 return x;
1047}
1048EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1049
1050struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1052 xfrm_address_t *daddr, xfrm_address_t *saddr,
1053 int create, unsigned short family)
1054{
1055 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
1057 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001058 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return x;
1062}
1063EXPORT_SYMBOL(xfrm_find_acq);
1064
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001065#ifdef CONFIG_XFRM_SUB_POLICY
1066int
1067xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1068 unsigned short family)
1069{
1070 int err = 0;
1071 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1072 if (!afinfo)
1073 return -EAFNOSUPPORT;
1074
1075 spin_lock_bh(&xfrm_state_lock);
1076 if (afinfo->tmpl_sort)
1077 err = afinfo->tmpl_sort(dst, src, n);
1078 spin_unlock_bh(&xfrm_state_lock);
1079 xfrm_state_put_afinfo(afinfo);
1080 return err;
1081}
1082EXPORT_SYMBOL(xfrm_tmpl_sort);
1083
1084int
1085xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1086 unsigned short family)
1087{
1088 int err = 0;
1089 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1090 if (!afinfo)
1091 return -EAFNOSUPPORT;
1092
1093 spin_lock_bh(&xfrm_state_lock);
1094 if (afinfo->state_sort)
1095 err = afinfo->state_sort(dst, src, n);
1096 spin_unlock_bh(&xfrm_state_lock);
1097 xfrm_state_put_afinfo(afinfo);
1098 return err;
1099}
1100EXPORT_SYMBOL(xfrm_state_sort);
1101#endif
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103/* Silly enough, but I'm lazy to build resolution list */
1104
1105static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1106{
1107 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
David S. Millerf034b5d2006-08-24 03:08:07 -07001109 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001110 struct hlist_node *entry;
1111 struct xfrm_state *x;
1112
1113 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1114 if (x->km.seq == seq &&
1115 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 xfrm_state_hold(x);
1117 return x;
1118 }
1119 }
1120 }
1121 return NULL;
1122}
1123
1124struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1125{
1126 struct xfrm_state *x;
1127
1128 spin_lock_bh(&xfrm_state_lock);
1129 x = __xfrm_find_acq_byseq(seq);
1130 spin_unlock_bh(&xfrm_state_lock);
1131 return x;
1132}
1133EXPORT_SYMBOL(xfrm_find_acq_byseq);
1134
1135u32 xfrm_get_acqseq(void)
1136{
1137 u32 res;
1138 static u32 acqseq;
1139 static DEFINE_SPINLOCK(acqseq_lock);
1140
1141 spin_lock_bh(&acqseq_lock);
1142 res = (++acqseq ? : ++acqseq);
1143 spin_unlock_bh(&acqseq_lock);
1144 return res;
1145}
1146EXPORT_SYMBOL(xfrm_get_acqseq);
1147
1148void
1149xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
1150{
David S. Millerf034b5d2006-08-24 03:08:07 -07001151 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 struct xfrm_state *x0;
1153
1154 if (x->id.spi)
1155 return;
1156
1157 if (minspi == maxspi) {
1158 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1159 if (x0) {
1160 xfrm_state_put(x0);
1161 return;
1162 }
1163 x->id.spi = minspi;
1164 } else {
1165 u32 spi = 0;
1166 minspi = ntohl(minspi);
1167 maxspi = ntohl(maxspi);
1168 for (h=0; h<maxspi-minspi+1; h++) {
1169 spi = minspi + net_random()%(maxspi-minspi+1);
1170 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1171 if (x0 == NULL) {
1172 x->id.spi = htonl(spi);
1173 break;
1174 }
1175 xfrm_state_put(x0);
1176 }
1177 }
1178 if (x->id.spi) {
1179 spin_lock_bh(&xfrm_state_lock);
1180 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001181 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 xfrm_state_hold(x);
1183 spin_unlock_bh(&xfrm_state_lock);
1184 wake_up(&km_waitq);
1185 }
1186}
1187EXPORT_SYMBOL(xfrm_alloc_spi);
1188
1189int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1190 void *data)
1191{
1192 int i;
1193 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001194 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 int count = 0;
1196 int err = 0;
1197
1198 spin_lock_bh(&xfrm_state_lock);
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 count++;
1203 }
1204 }
1205 if (count == 0) {
1206 err = -ENOENT;
1207 goto out;
1208 }
1209
David S. Millerf034b5d2006-08-24 03:08:07 -07001210 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001211 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001212 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 continue;
1214 err = func(x, --count, data);
1215 if (err)
1216 goto out;
1217 }
1218 }
1219out:
1220 spin_unlock_bh(&xfrm_state_lock);
1221 return err;
1222}
1223EXPORT_SYMBOL(xfrm_state_walk);
1224
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001225
1226void xfrm_replay_notify(struct xfrm_state *x, int event)
1227{
1228 struct km_event c;
1229 /* we send notify messages in case
1230 * 1. we updated on of the sequence numbers, and the seqno difference
1231 * is at least x->replay_maxdiff, in this case we also update the
1232 * timeout of our timer function
1233 * 2. if x->replay_maxage has elapsed since last update,
1234 * and there were changes
1235 *
1236 * The state structure must be locked!
1237 */
1238
1239 switch (event) {
1240 case XFRM_REPLAY_UPDATE:
1241 if (x->replay_maxdiff &&
1242 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001243 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1244 if (x->xflags & XFRM_TIME_DEFER)
1245 event = XFRM_REPLAY_TIMEOUT;
1246 else
1247 return;
1248 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001249
1250 break;
1251
1252 case XFRM_REPLAY_TIMEOUT:
1253 if ((x->replay.seq == x->preplay.seq) &&
1254 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001255 (x->replay.oseq == x->preplay.oseq)) {
1256 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001257 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001258 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001259
1260 break;
1261 }
1262
1263 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1264 c.event = XFRM_MSG_NEWAE;
1265 c.data.aevent = event;
1266 km_state_notify(x, &c);
1267
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001268 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001269 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001270 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001271 x->xflags &= ~XFRM_TIME_DEFER;
1272 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001273}
David S. Millera70fcb02006-03-20 19:18:52 -08001274EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001275
1276static void xfrm_replay_timer_handler(unsigned long data)
1277{
1278 struct xfrm_state *x = (struct xfrm_state*)data;
1279
1280 spin_lock(&x->lock);
1281
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001282 if (x->km.state == XFRM_STATE_VALID) {
1283 if (xfrm_aevent_is_on())
1284 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1285 else
1286 x->xflags |= XFRM_TIME_DEFER;
1287 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001288
1289 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001290 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001291}
1292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293int xfrm_replay_check(struct xfrm_state *x, u32 seq)
1294{
1295 u32 diff;
1296
1297 seq = ntohl(seq);
1298
1299 if (unlikely(seq == 0))
1300 return -EINVAL;
1301
1302 if (likely(seq > x->replay.seq))
1303 return 0;
1304
1305 diff = x->replay.seq - seq;
1306 if (diff >= x->props.replay_window) {
1307 x->stats.replay_window++;
1308 return -EINVAL;
1309 }
1310
1311 if (x->replay.bitmap & (1U << diff)) {
1312 x->stats.replay++;
1313 return -EINVAL;
1314 }
1315 return 0;
1316}
1317EXPORT_SYMBOL(xfrm_replay_check);
1318
1319void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
1320{
1321 u32 diff;
1322
1323 seq = ntohl(seq);
1324
1325 if (seq > x->replay.seq) {
1326 diff = seq - x->replay.seq;
1327 if (diff < x->props.replay_window)
1328 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1329 else
1330 x->replay.bitmap = 1;
1331 x->replay.seq = seq;
1332 } else {
1333 diff = x->replay.seq - seq;
1334 x->replay.bitmap |= (1U << diff);
1335 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001336
1337 if (xfrm_aevent_is_on())
1338 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339}
1340EXPORT_SYMBOL(xfrm_replay_advance);
1341
1342static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1343static DEFINE_RWLOCK(xfrm_km_lock);
1344
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001345void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346{
1347 struct xfrm_mgr *km;
1348
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001349 read_lock(&xfrm_km_lock);
1350 list_for_each_entry(km, &xfrm_km_list, list)
1351 if (km->notify_policy)
1352 km->notify_policy(xp, dir, c);
1353 read_unlock(&xfrm_km_lock);
1354}
1355
1356void km_state_notify(struct xfrm_state *x, struct km_event *c)
1357{
1358 struct xfrm_mgr *km;
1359 read_lock(&xfrm_km_lock);
1360 list_for_each_entry(km, &xfrm_km_list, list)
1361 if (km->notify)
1362 km->notify(x, c);
1363 read_unlock(&xfrm_km_lock);
1364}
1365
1366EXPORT_SYMBOL(km_policy_notify);
1367EXPORT_SYMBOL(km_state_notify);
1368
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001369void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001370{
1371 struct km_event c;
1372
Herbert Xubf08867f92005-06-18 22:44:00 -07001373 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001374 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001375 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001376 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
1378 if (hard)
1379 wake_up(&km_waitq);
1380}
1381
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001382EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001383/*
1384 * We send to all registered managers regardless of failure
1385 * We are happy with one success
1386*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001387int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001389 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 struct xfrm_mgr *km;
1391
1392 read_lock(&xfrm_km_lock);
1393 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001394 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1395 if (!acqret)
1396 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 }
1398 read_unlock(&xfrm_km_lock);
1399 return err;
1400}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001401EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1404{
1405 int err = -EINVAL;
1406 struct xfrm_mgr *km;
1407
1408 read_lock(&xfrm_km_lock);
1409 list_for_each_entry(km, &xfrm_km_list, list) {
1410 if (km->new_mapping)
1411 err = km->new_mapping(x, ipaddr, sport);
1412 if (!err)
1413 break;
1414 }
1415 read_unlock(&xfrm_km_lock);
1416 return err;
1417}
1418EXPORT_SYMBOL(km_new_mapping);
1419
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001420void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001422 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
Herbert Xubf08867f92005-06-18 22:44:00 -07001424 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001425 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001426 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001427 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
1429 if (hard)
1430 wake_up(&km_waitq);
1431}
David S. Millera70fcb02006-03-20 19:18:52 -08001432EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001434int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1435{
1436 int err = -EINVAL;
1437 int ret;
1438 struct xfrm_mgr *km;
1439
1440 read_lock(&xfrm_km_lock);
1441 list_for_each_entry(km, &xfrm_km_list, list) {
1442 if (km->report) {
1443 ret = km->report(proto, sel, addr);
1444 if (!ret)
1445 err = ret;
1446 }
1447 }
1448 read_unlock(&xfrm_km_lock);
1449 return err;
1450}
1451EXPORT_SYMBOL(km_report);
1452
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1454{
1455 int err;
1456 u8 *data;
1457 struct xfrm_mgr *km;
1458 struct xfrm_policy *pol = NULL;
1459
1460 if (optlen <= 0 || optlen > PAGE_SIZE)
1461 return -EMSGSIZE;
1462
1463 data = kmalloc(optlen, GFP_KERNEL);
1464 if (!data)
1465 return -ENOMEM;
1466
1467 err = -EFAULT;
1468 if (copy_from_user(data, optval, optlen))
1469 goto out;
1470
1471 err = -EINVAL;
1472 read_lock(&xfrm_km_lock);
1473 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001474 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 optlen, &err);
1476 if (err >= 0)
1477 break;
1478 }
1479 read_unlock(&xfrm_km_lock);
1480
1481 if (err >= 0) {
1482 xfrm_sk_policy_insert(sk, err, pol);
1483 xfrm_pol_put(pol);
1484 err = 0;
1485 }
1486
1487out:
1488 kfree(data);
1489 return err;
1490}
1491EXPORT_SYMBOL(xfrm_user_policy);
1492
1493int xfrm_register_km(struct xfrm_mgr *km)
1494{
1495 write_lock_bh(&xfrm_km_lock);
1496 list_add_tail(&km->list, &xfrm_km_list);
1497 write_unlock_bh(&xfrm_km_lock);
1498 return 0;
1499}
1500EXPORT_SYMBOL(xfrm_register_km);
1501
1502int xfrm_unregister_km(struct xfrm_mgr *km)
1503{
1504 write_lock_bh(&xfrm_km_lock);
1505 list_del(&km->list);
1506 write_unlock_bh(&xfrm_km_lock);
1507 return 0;
1508}
1509EXPORT_SYMBOL(xfrm_unregister_km);
1510
1511int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1512{
1513 int err = 0;
1514 if (unlikely(afinfo == NULL))
1515 return -EINVAL;
1516 if (unlikely(afinfo->family >= NPROTO))
1517 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001518 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1520 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001521 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001523 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 return err;
1525}
1526EXPORT_SYMBOL(xfrm_state_register_afinfo);
1527
1528int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1529{
1530 int err = 0;
1531 if (unlikely(afinfo == NULL))
1532 return -EINVAL;
1533 if (unlikely(afinfo->family >= NPROTO))
1534 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001535 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1537 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1538 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001539 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001542 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 return err;
1544}
1545EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1546
1547static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1548{
1549 struct xfrm_state_afinfo *afinfo;
1550 if (unlikely(family >= NPROTO))
1551 return NULL;
1552 read_lock(&xfrm_state_afinfo_lock);
1553 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001554 if (unlikely(!afinfo))
1555 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 return afinfo;
1557}
1558
1559static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1560{
Herbert Xu546be242006-05-27 23:03:58 -07001561 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562}
1563
1564/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1565void xfrm_state_delete_tunnel(struct xfrm_state *x)
1566{
1567 if (x->tunnel) {
1568 struct xfrm_state *t = x->tunnel;
1569
1570 if (atomic_read(&t->tunnel_users) == 2)
1571 xfrm_state_delete(t);
1572 atomic_dec(&t->tunnel_users);
1573 xfrm_state_put(t);
1574 x->tunnel = NULL;
1575 }
1576}
1577EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1578
Herbert Xu80b30c12005-10-15 10:58:30 +10001579/*
1580 * This function is NOT optimal. For example, with ESP it will give an
1581 * MTU that's usually two bytes short of being optimal. However, it will
1582 * usually give an answer that's a multiple of 4 provided the input is
1583 * also a multiple of 4.
1584 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1586{
1587 int res = mtu;
1588
1589 res -= x->props.header_len;
1590
1591 for (;;) {
1592 int m = res;
1593
1594 if (m < 68)
1595 return 68;
1596
1597 spin_lock_bh(&x->lock);
1598 if (x->km.state == XFRM_STATE_VALID &&
1599 x->type && x->type->get_max_size)
1600 m = x->type->get_max_size(x, m);
1601 else
1602 m += x->props.header_len;
1603 spin_unlock_bh(&x->lock);
1604
1605 if (m <= mtu)
1606 break;
1607 res -= (m - mtu);
1608 }
1609
1610 return res;
1611}
1612
Herbert Xu72cb6962005-06-20 13:18:08 -07001613int xfrm_init_state(struct xfrm_state *x)
1614{
Herbert Xud094cd82005-06-20 13:19:41 -07001615 struct xfrm_state_afinfo *afinfo;
1616 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001617 int err;
1618
Herbert Xud094cd82005-06-20 13:19:41 -07001619 err = -EAFNOSUPPORT;
1620 afinfo = xfrm_state_get_afinfo(family);
1621 if (!afinfo)
1622 goto error;
1623
1624 err = 0;
1625 if (afinfo->init_flags)
1626 err = afinfo->init_flags(x);
1627
1628 xfrm_state_put_afinfo(afinfo);
1629
1630 if (err)
1631 goto error;
1632
1633 err = -EPROTONOSUPPORT;
1634 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001635 if (x->type == NULL)
1636 goto error;
1637
1638 err = x->type->init_state(x);
1639 if (err)
1640 goto error;
1641
Herbert Xub59f45d2006-05-27 23:05:54 -07001642 x->mode = xfrm_get_mode(x->props.mode, family);
1643 if (x->mode == NULL)
1644 goto error;
1645
Herbert Xu72cb6962005-06-20 13:18:08 -07001646 x->km.state = XFRM_STATE_VALID;
1647
1648error:
1649 return err;
1650}
1651
1652EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654void __init xfrm_state_init(void)
1655{
David S. Millerf034b5d2006-08-24 03:08:07 -07001656 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
David S. Millerf034b5d2006-08-24 03:08:07 -07001658 sz = sizeof(struct hlist_head) * 8;
1659
1660 xfrm_state_bydst = xfrm_state_hash_alloc(sz);
1661 xfrm_state_bysrc = xfrm_state_hash_alloc(sz);
1662 xfrm_state_byspi = xfrm_state_hash_alloc(sz);
1663 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1664 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1665 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1668}
1669