blob: 9ff00b7d6ad358ac2e24c72bc2124819304e9451 [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
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800259int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
262static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
263
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800264int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800265void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
267static void xfrm_state_gc_destroy(struct xfrm_state *x)
268{
269 if (del_timer(&x->timer))
270 BUG();
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800271 if (del_timer(&x->rtimer))
272 BUG();
Jesper Juhla51482b2005-11-08 09:41:34 -0800273 kfree(x->aalg);
274 kfree(x->ealg);
275 kfree(x->calg);
276 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700277 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700278 if (x->mode)
279 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 if (x->type) {
281 x->type->destructor(x);
282 xfrm_put_type(x->type);
283 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800284 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 kfree(x);
286}
287
288static void xfrm_state_gc_task(void *data)
289{
290 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700291 struct hlist_node *entry, *tmp;
292 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700295 gc_list.first = xfrm_state_gc_list.first;
296 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 spin_unlock_bh(&xfrm_state_gc_lock);
298
David S. Miller8f126e32006-08-24 02:45:07 -0700299 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 wake_up(&km_waitq);
303}
304
305static inline unsigned long make_jiffies(long secs)
306{
307 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
308 return MAX_SCHEDULE_TIMEOUT-1;
309 else
310 return secs*HZ;
311}
312
313static void xfrm_timer_handler(unsigned long data)
314{
315 struct xfrm_state *x = (struct xfrm_state*)data;
316 unsigned long now = (unsigned long)xtime.tv_sec;
317 long next = LONG_MAX;
318 int warn = 0;
319
320 spin_lock(&x->lock);
321 if (x->km.state == XFRM_STATE_DEAD)
322 goto out;
323 if (x->km.state == XFRM_STATE_EXPIRED)
324 goto expired;
325 if (x->lft.hard_add_expires_seconds) {
326 long tmo = x->lft.hard_add_expires_seconds +
327 x->curlft.add_time - now;
328 if (tmo <= 0)
329 goto expired;
330 if (tmo < next)
331 next = tmo;
332 }
333 if (x->lft.hard_use_expires_seconds) {
334 long tmo = x->lft.hard_use_expires_seconds +
335 (x->curlft.use_time ? : now) - now;
336 if (tmo <= 0)
337 goto expired;
338 if (tmo < next)
339 next = tmo;
340 }
341 if (x->km.dying)
342 goto resched;
343 if (x->lft.soft_add_expires_seconds) {
344 long tmo = x->lft.soft_add_expires_seconds +
345 x->curlft.add_time - now;
346 if (tmo <= 0)
347 warn = 1;
348 else if (tmo < next)
349 next = tmo;
350 }
351 if (x->lft.soft_use_expires_seconds) {
352 long tmo = x->lft.soft_use_expires_seconds +
353 (x->curlft.use_time ? : now) - now;
354 if (tmo <= 0)
355 warn = 1;
356 else if (tmo < next)
357 next = tmo;
358 }
359
Herbert Xu4666faa2005-06-18 22:43:22 -0700360 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800362 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363resched:
364 if (next != LONG_MAX &&
365 !mod_timer(&x->timer, jiffies + make_jiffies(next)))
366 xfrm_state_hold(x);
367 goto out;
368
369expired:
370 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
371 x->km.state = XFRM_STATE_EXPIRED;
372 wake_up(&km_waitq);
373 next = 2;
374 goto resched;
375 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700376 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800377 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379out:
380 spin_unlock(&x->lock);
381 xfrm_state_put(x);
382}
383
David S. Miller0ac84752006-03-20 19:18:23 -0800384static void xfrm_replay_timer_handler(unsigned long data);
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386struct xfrm_state *xfrm_state_alloc(void)
387{
388 struct xfrm_state *x;
389
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700390 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 atomic_set(&x->refcnt, 1);
394 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700395 INIT_HLIST_NODE(&x->bydst);
396 INIT_HLIST_NODE(&x->bysrc);
397 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 init_timer(&x->timer);
399 x->timer.function = xfrm_timer_handler;
400 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800401 init_timer(&x->rtimer);
402 x->rtimer.function = xfrm_replay_timer_handler;
403 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 x->curlft.add_time = (unsigned long)xtime.tv_sec;
405 x->lft.soft_byte_limit = XFRM_INF;
406 x->lft.soft_packet_limit = XFRM_INF;
407 x->lft.hard_byte_limit = XFRM_INF;
408 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800409 x->replay_maxage = 0;
410 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 spin_lock_init(&x->lock);
412 }
413 return x;
414}
415EXPORT_SYMBOL(xfrm_state_alloc);
416
417void __xfrm_state_destroy(struct xfrm_state *x)
418{
419 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
420
421 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700422 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 spin_unlock_bh(&xfrm_state_gc_lock);
424 schedule_work(&xfrm_state_gc_work);
425}
426EXPORT_SYMBOL(__xfrm_state_destroy);
427
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800428int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700430 int err = -ESRCH;
431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 if (x->km.state != XFRM_STATE_DEAD) {
433 x->km.state = XFRM_STATE_DEAD;
434 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700435 hlist_del(&x->bydst);
Herbert Xu21380b82006-02-22 14:47:13 -0800436 __xfrm_state_put(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700437 hlist_del(&x->bysrc);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700438 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 if (x->id.spi) {
David S. Miller8f126e32006-08-24 02:45:07 -0700440 hlist_del(&x->byspi);
Herbert Xu21380b82006-02-22 14:47:13 -0800441 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700443 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 spin_unlock(&xfrm_state_lock);
445 if (del_timer(&x->timer))
Herbert Xu21380b82006-02-22 14:47:13 -0800446 __xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800447 if (del_timer(&x->rtimer))
448 __xfrm_state_put(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 /* All xfrm_state objects are created by xfrm_state_alloc.
451 * The xfrm_state_alloc call gives a reference, and that
452 * is what we are dropping here.
453 */
Herbert Xu21380b82006-02-22 14:47:13 -0800454 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700455 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700457
458 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800460EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700462int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700464 int err;
465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700467 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700469
470 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472EXPORT_SYMBOL(xfrm_state_delete);
473
474void xfrm_state_flush(u8 proto)
475{
476 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -0700479 for (i = 0; i < xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700480 struct hlist_node *entry;
481 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700483 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700485 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 xfrm_state_hold(x);
487 spin_unlock_bh(&xfrm_state_lock);
488
489 xfrm_state_delete(x);
490 xfrm_state_put(x);
491
492 spin_lock_bh(&xfrm_state_lock);
493 goto restart;
494 }
495 }
496 }
497 spin_unlock_bh(&xfrm_state_lock);
498 wake_up(&km_waitq);
499}
500EXPORT_SYMBOL(xfrm_state_flush);
501
502static int
503xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
504 struct xfrm_tmpl *tmpl,
505 xfrm_address_t *daddr, xfrm_address_t *saddr,
506 unsigned short family)
507{
508 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
509 if (!afinfo)
510 return -1;
511 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
512 xfrm_state_put_afinfo(afinfo);
513 return 0;
514}
515
David S. Milleredcd5822006-08-24 00:42:45 -0700516static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family)
517{
518 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
519 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700520 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700521
David S. Miller8f126e32006-08-24 02:45:07 -0700522 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700523 if (x->props.family != family ||
524 x->id.spi != spi ||
525 x->id.proto != proto)
526 continue;
527
528 switch (family) {
529 case AF_INET:
530 if (x->id.daddr.a4 != daddr->a4)
531 continue;
532 break;
533 case AF_INET6:
534 if (!ipv6_addr_equal((struct in6_addr *)daddr,
535 (struct in6_addr *)
536 x->id.daddr.a6))
537 continue;
538 break;
539 };
540
541 xfrm_state_hold(x);
542 return x;
543 }
544
545 return NULL;
546}
547
548static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
549{
550 unsigned int h = xfrm_src_hash(saddr, family);
551 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700552 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700553
David S. Miller8f126e32006-08-24 02:45:07 -0700554 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700555 if (x->props.family != family ||
556 x->id.proto != proto)
557 continue;
558
559 switch (family) {
560 case AF_INET:
561 if (x->id.daddr.a4 != daddr->a4 ||
562 x->props.saddr.a4 != saddr->a4)
563 continue;
564 break;
565 case AF_INET6:
566 if (!ipv6_addr_equal((struct in6_addr *)daddr,
567 (struct in6_addr *)
568 x->id.daddr.a6) ||
569 !ipv6_addr_equal((struct in6_addr *)saddr,
570 (struct in6_addr *)
571 x->props.saddr.a6))
572 continue;
573 break;
574 };
575
576 xfrm_state_hold(x);
577 return x;
578 }
579
580 return NULL;
581}
582
583static inline struct xfrm_state *
584__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
585{
586 if (use_spi)
587 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
588 x->id.proto, family);
589 else
590 return __xfrm_state_lookup_byaddr(&x->id.daddr,
591 &x->props.saddr,
592 x->id.proto, family);
593}
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595struct xfrm_state *
596xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
597 struct flowi *fl, struct xfrm_tmpl *tmpl,
598 struct xfrm_policy *pol, int *err,
599 unsigned short family)
600{
David S. Millera624c102006-08-24 03:24:33 -0700601 unsigned int h = xfrm_dst_hash(daddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700602 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 struct xfrm_state *x, *x0;
604 int acquire_in_progress = 0;
605 int error = 0;
606 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700609 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 if (x->props.family == family &&
611 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700612 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 xfrm_state_addr_check(x, daddr, saddr, family) &&
614 tmpl->mode == x->props.mode &&
615 tmpl->id.proto == x->id.proto &&
616 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
617 /* Resolution logic:
618 1. There is a valid state with matching selector.
619 Done.
620 2. Valid state with inappropriate selector. Skip.
621
622 Entering area of "sysdeps".
623
624 3. If state is not valid, selector is temporary,
625 it selects only session which triggered
626 previous resolution. Key manager will do
627 something to install a state with proper
628 selector.
629 */
630 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800631 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700632 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 continue;
634 if (!best ||
635 best->km.dying > x->km.dying ||
636 (best->km.dying == x->km.dying &&
637 best->curlft.add_time < x->curlft.add_time))
638 best = x;
639 } else if (x->km.state == XFRM_STATE_ACQ) {
640 acquire_in_progress = 1;
641 } else if (x->km.state == XFRM_STATE_ERROR ||
642 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800643 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700644 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 error = -ESRCH;
646 }
647 }
648 }
649
650 x = best;
651 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700652 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700653 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
654 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 xfrm_state_put(x0);
656 error = -EEXIST;
657 goto out;
658 }
659 x = xfrm_state_alloc();
660 if (x == NULL) {
661 error = -ENOMEM;
662 goto out;
663 }
664 /* Initialize temporary selector matching only
665 * to current session. */
666 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
667
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700668 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
669 if (error) {
670 x->km.state = XFRM_STATE_DEAD;
671 xfrm_state_put(x);
672 x = NULL;
673 goto out;
674 }
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 if (km_query(x, tmpl, pol) == 0) {
677 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700678 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 xfrm_state_hold(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700680 h = xfrm_src_hash(saddr, family);
681 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700682 xfrm_state_hold(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (x->id.spi) {
684 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700685 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 xfrm_state_hold(x);
687 }
688 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
689 xfrm_state_hold(x);
690 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
691 add_timer(&x->timer);
692 } else {
693 x->km.state = XFRM_STATE_DEAD;
694 xfrm_state_put(x);
695 x = NULL;
696 error = -ESRCH;
697 }
698 }
699out:
700 if (x)
701 xfrm_state_hold(x);
702 else
703 *err = acquire_in_progress ? -EAGAIN : error;
704 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 return x;
706}
707
708static void __xfrm_state_insert(struct xfrm_state *x)
709{
David S. Millera624c102006-08-24 03:24:33 -0700710 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
David S. Miller9d4a7062006-08-24 03:18:09 -0700712 x->genid = ++xfrm_state_genid;
713
David S. Millera624c102006-08-24 03:24:33 -0700714 h = xfrm_dst_hash(&x->id.daddr, x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700715 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 xfrm_state_hold(x);
717
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700718 h = xfrm_src_hash(&x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700719 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 xfrm_state_hold(x);
721
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700722 if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
723 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
724 x->props.family);
725
David S. Miller8f126e32006-08-24 02:45:07 -0700726 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700727 xfrm_state_hold(x);
728 }
729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 if (!mod_timer(&x->timer, jiffies + HZ))
731 xfrm_state_hold(x);
732
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800733 if (x->replay_maxage &&
734 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
735 xfrm_state_hold(x);
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700738
739 xfrm_state_num++;
740
741 if (x->bydst.next != NULL &&
742 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
743 xfrm_state_num > xfrm_state_hmask)
744 schedule_work(&xfrm_hash_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745}
746
David S. Millerc7f5ea32006-08-24 03:29:04 -0700747/* xfrm_state_lock is held */
748static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
749{
750 unsigned short family = xnew->props.family;
751 u32 reqid = xnew->props.reqid;
752 struct xfrm_state *x;
753 struct hlist_node *entry;
754 unsigned int h;
755
756 h = xfrm_dst_hash(&xnew->id.daddr, reqid, family);
757 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
758 if (x->props.family == family &&
759 x->props.reqid == reqid &&
760 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family))
761 x->genid = xfrm_state_genid;
762 }
763}
764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765void xfrm_state_insert(struct xfrm_state *x)
766{
767 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700768 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 __xfrm_state_insert(x);
770 spin_unlock_bh(&xfrm_state_lock);
771}
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
David S. Millerc7f5ea32006-08-24 03:29:04 -0700892 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 __xfrm_state_insert(x);
894 err = 0;
895
896out:
897 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
899 if (x1) {
900 xfrm_state_delete(x1);
901 xfrm_state_put(x1);
902 }
903
904 return err;
905}
906EXPORT_SYMBOL(xfrm_state_add);
907
908int xfrm_state_update(struct xfrm_state *x)
909{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 struct xfrm_state *x1;
911 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700912 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700915 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
917 err = -ESRCH;
918 if (!x1)
919 goto out;
920
921 if (xfrm_state_kern(x1)) {
922 xfrm_state_put(x1);
923 err = -EEXIST;
924 goto out;
925 }
926
927 if (x1->km.state == XFRM_STATE_ACQ) {
928 __xfrm_state_insert(x);
929 x = NULL;
930 }
931 err = 0;
932
933out:
934 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 if (err)
937 return err;
938
939 if (!x) {
940 xfrm_state_delete(x1);
941 xfrm_state_put(x1);
942 return 0;
943 }
944
945 err = -EINVAL;
946 spin_lock_bh(&x1->lock);
947 if (likely(x1->km.state == XFRM_STATE_VALID)) {
948 if (x->encap && x1->encap)
949 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700950 if (x->coaddr && x1->coaddr) {
951 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
952 }
953 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
954 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
956 x1->km.dying = 0;
957
958 if (!mod_timer(&x1->timer, jiffies + HZ))
959 xfrm_state_hold(x1);
960 if (x1->curlft.use_time)
961 xfrm_state_check_expire(x1);
962
963 err = 0;
964 }
965 spin_unlock_bh(&x1->lock);
966
967 xfrm_state_put(x1);
968
969 return err;
970}
971EXPORT_SYMBOL(xfrm_state_update);
972
973int xfrm_state_check_expire(struct xfrm_state *x)
974{
975 if (!x->curlft.use_time)
976 x->curlft.use_time = (unsigned long)xtime.tv_sec;
977
978 if (x->km.state != XFRM_STATE_VALID)
979 return -EINVAL;
980
981 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
982 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700983 x->km.state = XFRM_STATE_EXPIRED;
984 if (!mod_timer(&x->timer, jiffies))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 xfrm_state_hold(x);
986 return -EINVAL;
987 }
988
989 if (!x->km.dying &&
990 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700991 x->curlft.packets >= x->lft.soft_packet_limit)) {
992 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800993 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700994 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 return 0;
996}
997EXPORT_SYMBOL(xfrm_state_check_expire);
998
999static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1000{
1001 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1002 - skb_headroom(skb);
1003
1004 if (nhead > 0)
1005 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1006
1007 /* Check tail too... */
1008 return 0;
1009}
1010
1011int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1012{
1013 int err = xfrm_state_check_expire(x);
1014 if (err < 0)
1015 goto err;
1016 err = xfrm_state_check_space(x, skb);
1017err:
1018 return err;
1019}
1020EXPORT_SYMBOL(xfrm_state_check);
1021
1022struct xfrm_state *
1023xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
1024 unsigned short family)
1025{
1026 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
1028 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001029 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 return x;
1032}
1033EXPORT_SYMBOL(xfrm_state_lookup);
1034
1035struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001036xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1037 u8 proto, unsigned short family)
1038{
1039 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001040
1041 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001042 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001043 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001044 return x;
1045}
1046EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1047
1048struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1050 xfrm_address_t *daddr, xfrm_address_t *saddr,
1051 int create, unsigned short family)
1052{
1053 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
1055 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001056 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return x;
1060}
1061EXPORT_SYMBOL(xfrm_find_acq);
1062
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001063#ifdef CONFIG_XFRM_SUB_POLICY
1064int
1065xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1066 unsigned short family)
1067{
1068 int err = 0;
1069 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1070 if (!afinfo)
1071 return -EAFNOSUPPORT;
1072
1073 spin_lock_bh(&xfrm_state_lock);
1074 if (afinfo->tmpl_sort)
1075 err = afinfo->tmpl_sort(dst, src, n);
1076 spin_unlock_bh(&xfrm_state_lock);
1077 xfrm_state_put_afinfo(afinfo);
1078 return err;
1079}
1080EXPORT_SYMBOL(xfrm_tmpl_sort);
1081
1082int
1083xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1084 unsigned short family)
1085{
1086 int err = 0;
1087 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1088 if (!afinfo)
1089 return -EAFNOSUPPORT;
1090
1091 spin_lock_bh(&xfrm_state_lock);
1092 if (afinfo->state_sort)
1093 err = afinfo->state_sort(dst, src, n);
1094 spin_unlock_bh(&xfrm_state_lock);
1095 xfrm_state_put_afinfo(afinfo);
1096 return err;
1097}
1098EXPORT_SYMBOL(xfrm_state_sort);
1099#endif
1100
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101/* Silly enough, but I'm lazy to build resolution list */
1102
1103static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1104{
1105 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
David S. Millerf034b5d2006-08-24 03:08:07 -07001107 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001108 struct hlist_node *entry;
1109 struct xfrm_state *x;
1110
1111 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1112 if (x->km.seq == seq &&
1113 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 xfrm_state_hold(x);
1115 return x;
1116 }
1117 }
1118 }
1119 return NULL;
1120}
1121
1122struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1123{
1124 struct xfrm_state *x;
1125
1126 spin_lock_bh(&xfrm_state_lock);
1127 x = __xfrm_find_acq_byseq(seq);
1128 spin_unlock_bh(&xfrm_state_lock);
1129 return x;
1130}
1131EXPORT_SYMBOL(xfrm_find_acq_byseq);
1132
1133u32 xfrm_get_acqseq(void)
1134{
1135 u32 res;
1136 static u32 acqseq;
1137 static DEFINE_SPINLOCK(acqseq_lock);
1138
1139 spin_lock_bh(&acqseq_lock);
1140 res = (++acqseq ? : ++acqseq);
1141 spin_unlock_bh(&acqseq_lock);
1142 return res;
1143}
1144EXPORT_SYMBOL(xfrm_get_acqseq);
1145
1146void
1147xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
1148{
David S. Millerf034b5d2006-08-24 03:08:07 -07001149 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 struct xfrm_state *x0;
1151
1152 if (x->id.spi)
1153 return;
1154
1155 if (minspi == maxspi) {
1156 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1157 if (x0) {
1158 xfrm_state_put(x0);
1159 return;
1160 }
1161 x->id.spi = minspi;
1162 } else {
1163 u32 spi = 0;
1164 minspi = ntohl(minspi);
1165 maxspi = ntohl(maxspi);
1166 for (h=0; h<maxspi-minspi+1; h++) {
1167 spi = minspi + net_random()%(maxspi-minspi+1);
1168 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1169 if (x0 == NULL) {
1170 x->id.spi = htonl(spi);
1171 break;
1172 }
1173 xfrm_state_put(x0);
1174 }
1175 }
1176 if (x->id.spi) {
1177 spin_lock_bh(&xfrm_state_lock);
1178 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001179 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 xfrm_state_hold(x);
1181 spin_unlock_bh(&xfrm_state_lock);
1182 wake_up(&km_waitq);
1183 }
1184}
1185EXPORT_SYMBOL(xfrm_alloc_spi);
1186
1187int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1188 void *data)
1189{
1190 int i;
1191 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001192 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 int count = 0;
1194 int err = 0;
1195
1196 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001197 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001198 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001199 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 count++;
1201 }
1202 }
1203 if (count == 0) {
1204 err = -ENOENT;
1205 goto out;
1206 }
1207
David S. Millerf034b5d2006-08-24 03:08:07 -07001208 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001209 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001210 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 continue;
1212 err = func(x, --count, data);
1213 if (err)
1214 goto out;
1215 }
1216 }
1217out:
1218 spin_unlock_bh(&xfrm_state_lock);
1219 return err;
1220}
1221EXPORT_SYMBOL(xfrm_state_walk);
1222
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001223
1224void xfrm_replay_notify(struct xfrm_state *x, int event)
1225{
1226 struct km_event c;
1227 /* we send notify messages in case
1228 * 1. we updated on of the sequence numbers, and the seqno difference
1229 * is at least x->replay_maxdiff, in this case we also update the
1230 * timeout of our timer function
1231 * 2. if x->replay_maxage has elapsed since last update,
1232 * and there were changes
1233 *
1234 * The state structure must be locked!
1235 */
1236
1237 switch (event) {
1238 case XFRM_REPLAY_UPDATE:
1239 if (x->replay_maxdiff &&
1240 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001241 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1242 if (x->xflags & XFRM_TIME_DEFER)
1243 event = XFRM_REPLAY_TIMEOUT;
1244 else
1245 return;
1246 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001247
1248 break;
1249
1250 case XFRM_REPLAY_TIMEOUT:
1251 if ((x->replay.seq == x->preplay.seq) &&
1252 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001253 (x->replay.oseq == x->preplay.oseq)) {
1254 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001255 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001256 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001257
1258 break;
1259 }
1260
1261 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1262 c.event = XFRM_MSG_NEWAE;
1263 c.data.aevent = event;
1264 km_state_notify(x, &c);
1265
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001266 if (x->replay_maxage &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001267 !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001268 xfrm_state_hold(x);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001269 x->xflags &= ~XFRM_TIME_DEFER;
1270 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001271}
David S. Millera70fcb02006-03-20 19:18:52 -08001272EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001273
1274static void xfrm_replay_timer_handler(unsigned long data)
1275{
1276 struct xfrm_state *x = (struct xfrm_state*)data;
1277
1278 spin_lock(&x->lock);
1279
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001280 if (x->km.state == XFRM_STATE_VALID) {
1281 if (xfrm_aevent_is_on())
1282 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1283 else
1284 x->xflags |= XFRM_TIME_DEFER;
1285 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001286
1287 spin_unlock(&x->lock);
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001288 xfrm_state_put(x);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001289}
1290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291int xfrm_replay_check(struct xfrm_state *x, u32 seq)
1292{
1293 u32 diff;
1294
1295 seq = ntohl(seq);
1296
1297 if (unlikely(seq == 0))
1298 return -EINVAL;
1299
1300 if (likely(seq > x->replay.seq))
1301 return 0;
1302
1303 diff = x->replay.seq - seq;
1304 if (diff >= x->props.replay_window) {
1305 x->stats.replay_window++;
1306 return -EINVAL;
1307 }
1308
1309 if (x->replay.bitmap & (1U << diff)) {
1310 x->stats.replay++;
1311 return -EINVAL;
1312 }
1313 return 0;
1314}
1315EXPORT_SYMBOL(xfrm_replay_check);
1316
1317void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
1318{
1319 u32 diff;
1320
1321 seq = ntohl(seq);
1322
1323 if (seq > x->replay.seq) {
1324 diff = seq - x->replay.seq;
1325 if (diff < x->props.replay_window)
1326 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1327 else
1328 x->replay.bitmap = 1;
1329 x->replay.seq = seq;
1330 } else {
1331 diff = x->replay.seq - seq;
1332 x->replay.bitmap |= (1U << diff);
1333 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001334
1335 if (xfrm_aevent_is_on())
1336 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
1338EXPORT_SYMBOL(xfrm_replay_advance);
1339
1340static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1341static DEFINE_RWLOCK(xfrm_km_lock);
1342
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001343void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
1345 struct xfrm_mgr *km;
1346
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001347 read_lock(&xfrm_km_lock);
1348 list_for_each_entry(km, &xfrm_km_list, list)
1349 if (km->notify_policy)
1350 km->notify_policy(xp, dir, c);
1351 read_unlock(&xfrm_km_lock);
1352}
1353
1354void km_state_notify(struct xfrm_state *x, struct km_event *c)
1355{
1356 struct xfrm_mgr *km;
1357 read_lock(&xfrm_km_lock);
1358 list_for_each_entry(km, &xfrm_km_list, list)
1359 if (km->notify)
1360 km->notify(x, c);
1361 read_unlock(&xfrm_km_lock);
1362}
1363
1364EXPORT_SYMBOL(km_policy_notify);
1365EXPORT_SYMBOL(km_state_notify);
1366
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001367void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001368{
1369 struct km_event c;
1370
Herbert Xubf088672005-06-18 22:44:00 -07001371 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001372 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001373 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001374 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
1376 if (hard)
1377 wake_up(&km_waitq);
1378}
1379
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001380EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001381/*
1382 * We send to all registered managers regardless of failure
1383 * We are happy with one success
1384*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001385int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001387 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 struct xfrm_mgr *km;
1389
1390 read_lock(&xfrm_km_lock);
1391 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001392 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1393 if (!acqret)
1394 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 }
1396 read_unlock(&xfrm_km_lock);
1397 return err;
1398}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001399EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
1401int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1402{
1403 int err = -EINVAL;
1404 struct xfrm_mgr *km;
1405
1406 read_lock(&xfrm_km_lock);
1407 list_for_each_entry(km, &xfrm_km_list, list) {
1408 if (km->new_mapping)
1409 err = km->new_mapping(x, ipaddr, sport);
1410 if (!err)
1411 break;
1412 }
1413 read_unlock(&xfrm_km_lock);
1414 return err;
1415}
1416EXPORT_SYMBOL(km_new_mapping);
1417
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001418void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001420 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
Herbert Xubf088672005-06-18 22:44:00 -07001422 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001423 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001424 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001425 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426
1427 if (hard)
1428 wake_up(&km_waitq);
1429}
David S. Millera70fcb02006-03-20 19:18:52 -08001430EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001432int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1433{
1434 int err = -EINVAL;
1435 int ret;
1436 struct xfrm_mgr *km;
1437
1438 read_lock(&xfrm_km_lock);
1439 list_for_each_entry(km, &xfrm_km_list, list) {
1440 if (km->report) {
1441 ret = km->report(proto, sel, addr);
1442 if (!ret)
1443 err = ret;
1444 }
1445 }
1446 read_unlock(&xfrm_km_lock);
1447 return err;
1448}
1449EXPORT_SYMBOL(km_report);
1450
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1452{
1453 int err;
1454 u8 *data;
1455 struct xfrm_mgr *km;
1456 struct xfrm_policy *pol = NULL;
1457
1458 if (optlen <= 0 || optlen > PAGE_SIZE)
1459 return -EMSGSIZE;
1460
1461 data = kmalloc(optlen, GFP_KERNEL);
1462 if (!data)
1463 return -ENOMEM;
1464
1465 err = -EFAULT;
1466 if (copy_from_user(data, optval, optlen))
1467 goto out;
1468
1469 err = -EINVAL;
1470 read_lock(&xfrm_km_lock);
1471 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001472 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 optlen, &err);
1474 if (err >= 0)
1475 break;
1476 }
1477 read_unlock(&xfrm_km_lock);
1478
1479 if (err >= 0) {
1480 xfrm_sk_policy_insert(sk, err, pol);
1481 xfrm_pol_put(pol);
1482 err = 0;
1483 }
1484
1485out:
1486 kfree(data);
1487 return err;
1488}
1489EXPORT_SYMBOL(xfrm_user_policy);
1490
1491int xfrm_register_km(struct xfrm_mgr *km)
1492{
1493 write_lock_bh(&xfrm_km_lock);
1494 list_add_tail(&km->list, &xfrm_km_list);
1495 write_unlock_bh(&xfrm_km_lock);
1496 return 0;
1497}
1498EXPORT_SYMBOL(xfrm_register_km);
1499
1500int xfrm_unregister_km(struct xfrm_mgr *km)
1501{
1502 write_lock_bh(&xfrm_km_lock);
1503 list_del(&km->list);
1504 write_unlock_bh(&xfrm_km_lock);
1505 return 0;
1506}
1507EXPORT_SYMBOL(xfrm_unregister_km);
1508
1509int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1510{
1511 int err = 0;
1512 if (unlikely(afinfo == NULL))
1513 return -EINVAL;
1514 if (unlikely(afinfo->family >= NPROTO))
1515 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001516 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1518 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001519 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001521 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 return err;
1523}
1524EXPORT_SYMBOL(xfrm_state_register_afinfo);
1525
1526int xfrm_state_unregister_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 (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1535 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1536 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001537 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001540 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 return err;
1542}
1543EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1544
1545static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1546{
1547 struct xfrm_state_afinfo *afinfo;
1548 if (unlikely(family >= NPROTO))
1549 return NULL;
1550 read_lock(&xfrm_state_afinfo_lock);
1551 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001552 if (unlikely(!afinfo))
1553 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 return afinfo;
1555}
1556
1557static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1558{
Herbert Xu546be242006-05-27 23:03:58 -07001559 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560}
1561
1562/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1563void xfrm_state_delete_tunnel(struct xfrm_state *x)
1564{
1565 if (x->tunnel) {
1566 struct xfrm_state *t = x->tunnel;
1567
1568 if (atomic_read(&t->tunnel_users) == 2)
1569 xfrm_state_delete(t);
1570 atomic_dec(&t->tunnel_users);
1571 xfrm_state_put(t);
1572 x->tunnel = NULL;
1573 }
1574}
1575EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1576
Herbert Xu80b30c12005-10-15 10:58:30 +10001577/*
1578 * This function is NOT optimal. For example, with ESP it will give an
1579 * MTU that's usually two bytes short of being optimal. However, it will
1580 * usually give an answer that's a multiple of 4 provided the input is
1581 * also a multiple of 4.
1582 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1584{
1585 int res = mtu;
1586
1587 res -= x->props.header_len;
1588
1589 for (;;) {
1590 int m = res;
1591
1592 if (m < 68)
1593 return 68;
1594
1595 spin_lock_bh(&x->lock);
1596 if (x->km.state == XFRM_STATE_VALID &&
1597 x->type && x->type->get_max_size)
1598 m = x->type->get_max_size(x, m);
1599 else
1600 m += x->props.header_len;
1601 spin_unlock_bh(&x->lock);
1602
1603 if (m <= mtu)
1604 break;
1605 res -= (m - mtu);
1606 }
1607
1608 return res;
1609}
1610
Herbert Xu72cb6962005-06-20 13:18:08 -07001611int xfrm_init_state(struct xfrm_state *x)
1612{
Herbert Xud094cd82005-06-20 13:19:41 -07001613 struct xfrm_state_afinfo *afinfo;
1614 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001615 int err;
1616
Herbert Xud094cd82005-06-20 13:19:41 -07001617 err = -EAFNOSUPPORT;
1618 afinfo = xfrm_state_get_afinfo(family);
1619 if (!afinfo)
1620 goto error;
1621
1622 err = 0;
1623 if (afinfo->init_flags)
1624 err = afinfo->init_flags(x);
1625
1626 xfrm_state_put_afinfo(afinfo);
1627
1628 if (err)
1629 goto error;
1630
1631 err = -EPROTONOSUPPORT;
1632 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001633 if (x->type == NULL)
1634 goto error;
1635
1636 err = x->type->init_state(x);
1637 if (err)
1638 goto error;
1639
Herbert Xub59f45d2006-05-27 23:05:54 -07001640 x->mode = xfrm_get_mode(x->props.mode, family);
1641 if (x->mode == NULL)
1642 goto error;
1643
Herbert Xu72cb6962005-06-20 13:18:08 -07001644 x->km.state = XFRM_STATE_VALID;
1645
1646error:
1647 return err;
1648}
1649
1650EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
1652void __init xfrm_state_init(void)
1653{
David S. Millerf034b5d2006-08-24 03:08:07 -07001654 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655
David S. Millerf034b5d2006-08-24 03:08:07 -07001656 sz = sizeof(struct hlist_head) * 8;
1657
1658 xfrm_state_bydst = xfrm_state_hash_alloc(sz);
1659 xfrm_state_bysrc = xfrm_state_hash_alloc(sz);
1660 xfrm_state_byspi = xfrm_state_hash_alloc(sz);
1661 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1662 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1663 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1664
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1666}
1667