blob: f927b7330f025cca4c05ed0dd7157edd2723924e [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/cache.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <asm/uaccess.h>
23
David S. Miller44e36b42006-08-24 04:50:50 -070024#include "xfrm_hash.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. Millerc1969f22006-08-24 04:00:03 -070058static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
59 xfrm_address_t *saddr,
60 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070061 unsigned short family)
62{
David S. Millerc1969f22006-08-24 04:00:03 -070063 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070064}
65
David S. Miller44e36b42006-08-24 04:50:50 -070066static inline unsigned int xfrm_src_hash(xfrm_address_t *addr,
67 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070068{
69 return __xfrm_src_hash(addr, family, xfrm_state_hmask);
70}
71
David S. Miller2575b652006-08-24 03:26:44 -070072static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070073xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070074{
David S. Millerc1969f22006-08-24 04:00:03 -070075 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070076}
77
David S. Millerf034b5d2006-08-24 03:08:07 -070078static void xfrm_hash_transfer(struct hlist_head *list,
79 struct hlist_head *ndsttable,
80 struct hlist_head *nsrctable,
81 struct hlist_head *nspitable,
82 unsigned int nhashmask)
83{
84 struct hlist_node *entry, *tmp;
85 struct xfrm_state *x;
86
87 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
88 unsigned int h;
89
David S. Millerc1969f22006-08-24 04:00:03 -070090 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
91 x->props.reqid, x->props.family,
92 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070093 hlist_add_head(&x->bydst, ndsttable+h);
94
95 h = __xfrm_src_hash(&x->props.saddr, x->props.family,
96 nhashmask);
97 hlist_add_head(&x->bysrc, nsrctable+h);
98
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -070099 if (x->id.spi) {
100 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
101 x->id.proto, x->props.family,
102 nhashmask);
103 hlist_add_head(&x->byspi, nspitable+h);
104 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700105 }
106}
107
108static unsigned long xfrm_hash_new_size(void)
109{
110 return ((xfrm_state_hmask + 1) << 1) *
111 sizeof(struct hlist_head);
112}
113
114static DEFINE_MUTEX(hash_resize_mutex);
115
116static void xfrm_hash_resize(void *__unused)
117{
118 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
119 unsigned long nsize, osize;
120 unsigned int nhashmask, ohashmask;
121 int i;
122
123 mutex_lock(&hash_resize_mutex);
124
125 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700126 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700127 if (!ndst)
128 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700129 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700130 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700131 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700132 goto out_unlock;
133 }
David S. Miller44e36b42006-08-24 04:50:50 -0700134 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700135 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700136 xfrm_hash_free(ndst, nsize);
137 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700138 goto out_unlock;
139 }
140
141 spin_lock_bh(&xfrm_state_lock);
142
143 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
144 for (i = xfrm_state_hmask; i >= 0; i--)
145 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
146 nhashmask);
147
148 odst = xfrm_state_bydst;
149 osrc = xfrm_state_bysrc;
150 ospi = xfrm_state_byspi;
151 ohashmask = xfrm_state_hmask;
152
153 xfrm_state_bydst = ndst;
154 xfrm_state_bysrc = nsrc;
155 xfrm_state_byspi = nspi;
156 xfrm_state_hmask = nhashmask;
157
158 spin_unlock_bh(&xfrm_state_lock);
159
160 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700161 xfrm_hash_free(odst, osize);
162 xfrm_hash_free(osrc, osize);
163 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700164
165out_unlock:
166 mutex_unlock(&hash_resize_mutex);
167}
168
169static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171DECLARE_WAIT_QUEUE_HEAD(km_waitq);
172EXPORT_SYMBOL(km_waitq);
173
174static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
175static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
176
177static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700178static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179static DEFINE_SPINLOCK(xfrm_state_gc_lock);
180
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800181int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
183static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
184static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
185
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800186int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800187void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
189static void xfrm_state_gc_destroy(struct xfrm_state *x)
190{
David S. Millera47f0ce2006-08-24 03:54:22 -0700191 del_timer_sync(&x->timer);
192 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800193 kfree(x->aalg);
194 kfree(x->ealg);
195 kfree(x->calg);
196 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700197 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700198 if (x->mode)
199 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 if (x->type) {
201 x->type->destructor(x);
202 xfrm_put_type(x->type);
203 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800204 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 kfree(x);
206}
207
208static void xfrm_state_gc_task(void *data)
209{
210 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700211 struct hlist_node *entry, *tmp;
212 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700215 gc_list.first = xfrm_state_gc_list.first;
216 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 spin_unlock_bh(&xfrm_state_gc_lock);
218
David S. Miller8f126e32006-08-24 02:45:07 -0700219 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 wake_up(&km_waitq);
223}
224
225static inline unsigned long make_jiffies(long secs)
226{
227 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
228 return MAX_SCHEDULE_TIMEOUT-1;
229 else
230 return secs*HZ;
231}
232
233static void xfrm_timer_handler(unsigned long data)
234{
235 struct xfrm_state *x = (struct xfrm_state*)data;
236 unsigned long now = (unsigned long)xtime.tv_sec;
237 long next = LONG_MAX;
238 int warn = 0;
239
240 spin_lock(&x->lock);
241 if (x->km.state == XFRM_STATE_DEAD)
242 goto out;
243 if (x->km.state == XFRM_STATE_EXPIRED)
244 goto expired;
245 if (x->lft.hard_add_expires_seconds) {
246 long tmo = x->lft.hard_add_expires_seconds +
247 x->curlft.add_time - now;
248 if (tmo <= 0)
249 goto expired;
250 if (tmo < next)
251 next = tmo;
252 }
253 if (x->lft.hard_use_expires_seconds) {
254 long tmo = x->lft.hard_use_expires_seconds +
255 (x->curlft.use_time ? : now) - now;
256 if (tmo <= 0)
257 goto expired;
258 if (tmo < next)
259 next = tmo;
260 }
261 if (x->km.dying)
262 goto resched;
263 if (x->lft.soft_add_expires_seconds) {
264 long tmo = x->lft.soft_add_expires_seconds +
265 x->curlft.add_time - now;
266 if (tmo <= 0)
267 warn = 1;
268 else if (tmo < next)
269 next = tmo;
270 }
271 if (x->lft.soft_use_expires_seconds) {
272 long tmo = x->lft.soft_use_expires_seconds +
273 (x->curlft.use_time ? : now) - now;
274 if (tmo <= 0)
275 warn = 1;
276 else if (tmo < next)
277 next = tmo;
278 }
279
Herbert Xu4666faa2005-06-18 22:43:22 -0700280 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800282 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700284 if (next != LONG_MAX)
285 mod_timer(&x->timer, jiffies + make_jiffies(next));
286
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 goto out;
288
289expired:
290 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
291 x->km.state = XFRM_STATE_EXPIRED;
292 wake_up(&km_waitq);
293 next = 2;
294 goto resched;
295 }
Herbert Xu4666faa2005-06-18 22:43:22 -0700296 if (!__xfrm_state_delete(x) && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800297 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299out:
300 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}
302
David S. Miller0ac84752006-03-20 19:18:23 -0800303static void xfrm_replay_timer_handler(unsigned long data);
304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305struct xfrm_state *xfrm_state_alloc(void)
306{
307 struct xfrm_state *x;
308
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700309 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 atomic_set(&x->refcnt, 1);
313 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700314 INIT_HLIST_NODE(&x->bydst);
315 INIT_HLIST_NODE(&x->bysrc);
316 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 init_timer(&x->timer);
318 x->timer.function = xfrm_timer_handler;
319 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800320 init_timer(&x->rtimer);
321 x->rtimer.function = xfrm_replay_timer_handler;
322 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 x->curlft.add_time = (unsigned long)xtime.tv_sec;
324 x->lft.soft_byte_limit = XFRM_INF;
325 x->lft.soft_packet_limit = XFRM_INF;
326 x->lft.hard_byte_limit = XFRM_INF;
327 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800328 x->replay_maxage = 0;
329 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 spin_lock_init(&x->lock);
331 }
332 return x;
333}
334EXPORT_SYMBOL(xfrm_state_alloc);
335
336void __xfrm_state_destroy(struct xfrm_state *x)
337{
338 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
339
340 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700341 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 spin_unlock_bh(&xfrm_state_gc_lock);
343 schedule_work(&xfrm_state_gc_work);
344}
345EXPORT_SYMBOL(__xfrm_state_destroy);
346
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800347int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700349 int err = -ESRCH;
350
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 if (x->km.state != XFRM_STATE_DEAD) {
352 x->km.state = XFRM_STATE_DEAD;
353 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700354 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700355 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700356 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700357 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700358 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 /* All xfrm_state objects are created by xfrm_state_alloc.
362 * The xfrm_state_alloc call gives a reference, and that
363 * is what we are dropping here.
364 */
Herbert Xu21380b82006-02-22 14:47:13 -0800365 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700366 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700368
369 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800371EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700373int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700375 int err;
376
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700378 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700380
381 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382}
383EXPORT_SYMBOL(xfrm_state_delete);
384
385void xfrm_state_flush(u8 proto)
386{
387 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389 spin_lock_bh(&xfrm_state_lock);
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700390 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700391 struct hlist_node *entry;
392 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700394 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700396 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 xfrm_state_hold(x);
398 spin_unlock_bh(&xfrm_state_lock);
399
400 xfrm_state_delete(x);
401 xfrm_state_put(x);
402
403 spin_lock_bh(&xfrm_state_lock);
404 goto restart;
405 }
406 }
407 }
408 spin_unlock_bh(&xfrm_state_lock);
409 wake_up(&km_waitq);
410}
411EXPORT_SYMBOL(xfrm_state_flush);
412
413static int
414xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
415 struct xfrm_tmpl *tmpl,
416 xfrm_address_t *daddr, xfrm_address_t *saddr,
417 unsigned short family)
418{
419 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
420 if (!afinfo)
421 return -1;
422 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
423 xfrm_state_put_afinfo(afinfo);
424 return 0;
425}
426
Al Viroa94cfd12006-09-27 18:47:24 -0700427static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Milleredcd5822006-08-24 00:42:45 -0700428{
429 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
430 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700431 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700432
David S. Miller8f126e32006-08-24 02:45:07 -0700433 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700434 if (x->props.family != family ||
435 x->id.spi != spi ||
436 x->id.proto != proto)
437 continue;
438
439 switch (family) {
440 case AF_INET:
441 if (x->id.daddr.a4 != daddr->a4)
442 continue;
443 break;
444 case AF_INET6:
445 if (!ipv6_addr_equal((struct in6_addr *)daddr,
446 (struct in6_addr *)
447 x->id.daddr.a6))
448 continue;
449 break;
450 };
451
452 xfrm_state_hold(x);
453 return x;
454 }
455
456 return NULL;
457}
458
459static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
460{
461 unsigned int h = xfrm_src_hash(saddr, family);
462 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700463 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700464
David S. Miller8f126e32006-08-24 02:45:07 -0700465 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700466 if (x->props.family != family ||
467 x->id.proto != proto)
468 continue;
469
470 switch (family) {
471 case AF_INET:
472 if (x->id.daddr.a4 != daddr->a4 ||
473 x->props.saddr.a4 != saddr->a4)
474 continue;
475 break;
476 case AF_INET6:
477 if (!ipv6_addr_equal((struct in6_addr *)daddr,
478 (struct in6_addr *)
479 x->id.daddr.a6) ||
480 !ipv6_addr_equal((struct in6_addr *)saddr,
481 (struct in6_addr *)
482 x->props.saddr.a6))
483 continue;
484 break;
485 };
486
487 xfrm_state_hold(x);
488 return x;
489 }
490
491 return NULL;
492}
493
494static inline struct xfrm_state *
495__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
496{
497 if (use_spi)
498 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
499 x->id.proto, family);
500 else
501 return __xfrm_state_lookup_byaddr(&x->id.daddr,
502 &x->props.saddr,
503 x->id.proto, family);
504}
505
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506struct xfrm_state *
507xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
508 struct flowi *fl, struct xfrm_tmpl *tmpl,
509 struct xfrm_policy *pol, int *err,
510 unsigned short family)
511{
David S. Millerc1969f22006-08-24 04:00:03 -0700512 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700513 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 struct xfrm_state *x, *x0;
515 int acquire_in_progress = 0;
516 int error = 0;
517 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700520 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (x->props.family == family &&
522 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700523 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 xfrm_state_addr_check(x, daddr, saddr, family) &&
525 tmpl->mode == x->props.mode &&
526 tmpl->id.proto == x->id.proto &&
527 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
528 /* Resolution logic:
529 1. There is a valid state with matching selector.
530 Done.
531 2. Valid state with inappropriate selector. Skip.
532
533 Entering area of "sysdeps".
534
535 3. If state is not valid, selector is temporary,
536 it selects only session which triggered
537 previous resolution. Key manager will do
538 something to install a state with proper
539 selector.
540 */
541 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800542 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700543 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 continue;
545 if (!best ||
546 best->km.dying > x->km.dying ||
547 (best->km.dying == x->km.dying &&
548 best->curlft.add_time < x->curlft.add_time))
549 best = x;
550 } else if (x->km.state == XFRM_STATE_ACQ) {
551 acquire_in_progress = 1;
552 } else if (x->km.state == XFRM_STATE_ERROR ||
553 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800554 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700555 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 error = -ESRCH;
557 }
558 }
559 }
560
561 x = best;
562 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700563 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700564 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
565 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 xfrm_state_put(x0);
567 error = -EEXIST;
568 goto out;
569 }
570 x = xfrm_state_alloc();
571 if (x == NULL) {
572 error = -ENOMEM;
573 goto out;
574 }
575 /* Initialize temporary selector matching only
576 * to current session. */
577 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
578
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700579 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
580 if (error) {
581 x->km.state = XFRM_STATE_DEAD;
582 xfrm_state_put(x);
583 x = NULL;
584 goto out;
585 }
586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 if (km_query(x, tmpl, pol) == 0) {
588 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700589 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller8f126e32006-08-24 02:45:07 -0700590 h = xfrm_src_hash(saddr, family);
591 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 if (x->id.spi) {
593 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700594 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 }
596 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
598 add_timer(&x->timer);
599 } else {
600 x->km.state = XFRM_STATE_DEAD;
601 xfrm_state_put(x);
602 x = NULL;
603 error = -ESRCH;
604 }
605 }
606out:
607 if (x)
608 xfrm_state_hold(x);
609 else
610 *err = acquire_in_progress ? -EAGAIN : error;
611 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return x;
613}
614
615static void __xfrm_state_insert(struct xfrm_state *x)
616{
David S. Millera624c102006-08-24 03:24:33 -0700617 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
David S. Miller9d4a7062006-08-24 03:18:09 -0700619 x->genid = ++xfrm_state_genid;
620
David S. Millerc1969f22006-08-24 04:00:03 -0700621 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
622 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700623 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700625 h = xfrm_src_hash(&x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700626 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700628 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700629 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
630 x->props.family);
631
David S. Miller8f126e32006-08-24 02:45:07 -0700632 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700633 }
634
David S. Millera47f0ce2006-08-24 03:54:22 -0700635 mod_timer(&x->timer, jiffies + HZ);
636 if (x->replay_maxage)
637 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700640
641 xfrm_state_num++;
642
643 if (x->bydst.next != NULL &&
644 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
645 xfrm_state_num > xfrm_state_hmask)
646 schedule_work(&xfrm_hash_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647}
648
David S. Millerc7f5ea32006-08-24 03:29:04 -0700649/* xfrm_state_lock is held */
650static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
651{
652 unsigned short family = xnew->props.family;
653 u32 reqid = xnew->props.reqid;
654 struct xfrm_state *x;
655 struct hlist_node *entry;
656 unsigned int h;
657
David S. Millerc1969f22006-08-24 04:00:03 -0700658 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700659 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
660 if (x->props.family == family &&
661 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700662 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
663 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700664 x->genid = xfrm_state_genid;
665 }
666}
667
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668void xfrm_state_insert(struct xfrm_state *x)
669{
670 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700671 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 __xfrm_state_insert(x);
673 spin_unlock_bh(&xfrm_state_lock);
674}
675EXPORT_SYMBOL(xfrm_state_insert);
676
David S. Miller27708342006-08-24 00:13:10 -0700677/* xfrm_state_lock is held */
678static 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)
679{
David S. Millerc1969f22006-08-24 04:00:03 -0700680 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700681 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700682 struct xfrm_state *x;
683
David S. Miller8f126e32006-08-24 02:45:07 -0700684 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700685 if (x->props.reqid != reqid ||
686 x->props.mode != mode ||
687 x->props.family != family ||
688 x->km.state != XFRM_STATE_ACQ ||
689 x->id.spi != 0)
690 continue;
691
692 switch (family) {
693 case AF_INET:
694 if (x->id.daddr.a4 != daddr->a4 ||
695 x->props.saddr.a4 != saddr->a4)
696 continue;
697 break;
698 case AF_INET6:
699 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
700 (struct in6_addr *)daddr) ||
701 !ipv6_addr_equal((struct in6_addr *)
702 x->props.saddr.a6,
703 (struct in6_addr *)saddr))
704 continue;
705 break;
706 };
707
708 xfrm_state_hold(x);
709 return x;
710 }
711
712 if (!create)
713 return NULL;
714
715 x = xfrm_state_alloc();
716 if (likely(x)) {
717 switch (family) {
718 case AF_INET:
719 x->sel.daddr.a4 = daddr->a4;
720 x->sel.saddr.a4 = saddr->a4;
721 x->sel.prefixlen_d = 32;
722 x->sel.prefixlen_s = 32;
723 x->props.saddr.a4 = saddr->a4;
724 x->id.daddr.a4 = daddr->a4;
725 break;
726
727 case AF_INET6:
728 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
729 (struct in6_addr *)daddr);
730 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
731 (struct in6_addr *)saddr);
732 x->sel.prefixlen_d = 128;
733 x->sel.prefixlen_s = 128;
734 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
735 (struct in6_addr *)saddr);
736 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
737 (struct in6_addr *)daddr);
738 break;
739 };
740
741 x->km.state = XFRM_STATE_ACQ;
742 x->id.proto = proto;
743 x->props.family = family;
744 x->props.mode = mode;
745 x->props.reqid = reqid;
746 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
747 xfrm_state_hold(x);
748 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
749 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700750 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
David S. Miller27708342006-08-24 00:13:10 -0700751 h = xfrm_src_hash(saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700752 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700753 wake_up(&km_waitq);
754 }
755
756 return x;
757}
758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
760
761int xfrm_state_add(struct xfrm_state *x)
762{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 struct xfrm_state *x1;
764 int family;
765 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700766 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
768 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
770 spin_lock_bh(&xfrm_state_lock);
771
David S. Milleredcd5822006-08-24 00:42:45 -0700772 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 if (x1) {
774 xfrm_state_put(x1);
775 x1 = NULL;
776 err = -EEXIST;
777 goto out;
778 }
779
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700780 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 x1 = __xfrm_find_acq_byseq(x->km.seq);
782 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
783 xfrm_state_put(x1);
784 x1 = NULL;
785 }
786 }
787
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700788 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700789 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
790 x->id.proto,
791 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
David S. Millerc7f5ea32006-08-24 03:29:04 -0700793 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 __xfrm_state_insert(x);
795 err = 0;
796
797out:
798 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 if (x1) {
801 xfrm_state_delete(x1);
802 xfrm_state_put(x1);
803 }
804
805 return err;
806}
807EXPORT_SYMBOL(xfrm_state_add);
808
809int xfrm_state_update(struct xfrm_state *x)
810{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 struct xfrm_state *x1;
812 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700813 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700816 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 err = -ESRCH;
819 if (!x1)
820 goto out;
821
822 if (xfrm_state_kern(x1)) {
823 xfrm_state_put(x1);
824 err = -EEXIST;
825 goto out;
826 }
827
828 if (x1->km.state == XFRM_STATE_ACQ) {
829 __xfrm_state_insert(x);
830 x = NULL;
831 }
832 err = 0;
833
834out:
835 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 if (err)
838 return err;
839
840 if (!x) {
841 xfrm_state_delete(x1);
842 xfrm_state_put(x1);
843 return 0;
844 }
845
846 err = -EINVAL;
847 spin_lock_bh(&x1->lock);
848 if (likely(x1->km.state == XFRM_STATE_VALID)) {
849 if (x->encap && x1->encap)
850 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700851 if (x->coaddr && x1->coaddr) {
852 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
853 }
854 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
855 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
857 x1->km.dying = 0;
858
David S. Millera47f0ce2006-08-24 03:54:22 -0700859 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 if (x1->curlft.use_time)
861 xfrm_state_check_expire(x1);
862
863 err = 0;
864 }
865 spin_unlock_bh(&x1->lock);
866
867 xfrm_state_put(x1);
868
869 return err;
870}
871EXPORT_SYMBOL(xfrm_state_update);
872
873int xfrm_state_check_expire(struct xfrm_state *x)
874{
875 if (!x->curlft.use_time)
876 x->curlft.use_time = (unsigned long)xtime.tv_sec;
877
878 if (x->km.state != XFRM_STATE_VALID)
879 return -EINVAL;
880
881 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
882 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700883 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -0700884 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 return -EINVAL;
886 }
887
888 if (!x->km.dying &&
889 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700890 x->curlft.packets >= x->lft.soft_packet_limit)) {
891 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800892 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700893 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 return 0;
895}
896EXPORT_SYMBOL(xfrm_state_check_expire);
897
898static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
899{
900 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
901 - skb_headroom(skb);
902
903 if (nhead > 0)
904 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
905
906 /* Check tail too... */
907 return 0;
908}
909
910int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
911{
912 int err = xfrm_state_check_expire(x);
913 if (err < 0)
914 goto err;
915 err = xfrm_state_check_space(x, skb);
916err:
917 return err;
918}
919EXPORT_SYMBOL(xfrm_state_check);
920
921struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -0700922xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 unsigned short family)
924{
925 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
927 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700928 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 return x;
931}
932EXPORT_SYMBOL(xfrm_state_lookup);
933
934struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700935xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
936 u8 proto, unsigned short family)
937{
938 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700939
940 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700941 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700942 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700943 return x;
944}
945EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
946
947struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
949 xfrm_address_t *daddr, xfrm_address_t *saddr,
950 int create, unsigned short family)
951{
952 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
954 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -0700955 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -0700957
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 return x;
959}
960EXPORT_SYMBOL(xfrm_find_acq);
961
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -0700962#ifdef CONFIG_XFRM_SUB_POLICY
963int
964xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
965 unsigned short family)
966{
967 int err = 0;
968 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
969 if (!afinfo)
970 return -EAFNOSUPPORT;
971
972 spin_lock_bh(&xfrm_state_lock);
973 if (afinfo->tmpl_sort)
974 err = afinfo->tmpl_sort(dst, src, n);
975 spin_unlock_bh(&xfrm_state_lock);
976 xfrm_state_put_afinfo(afinfo);
977 return err;
978}
979EXPORT_SYMBOL(xfrm_tmpl_sort);
980
981int
982xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
983 unsigned short family)
984{
985 int err = 0;
986 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
987 if (!afinfo)
988 return -EAFNOSUPPORT;
989
990 spin_lock_bh(&xfrm_state_lock);
991 if (afinfo->state_sort)
992 err = afinfo->state_sort(dst, src, n);
993 spin_unlock_bh(&xfrm_state_lock);
994 xfrm_state_put_afinfo(afinfo);
995 return err;
996}
997EXPORT_SYMBOL(xfrm_state_sort);
998#endif
999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000/* Silly enough, but I'm lazy to build resolution list */
1001
1002static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1003{
1004 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
David S. Millerf034b5d2006-08-24 03:08:07 -07001006 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001007 struct hlist_node *entry;
1008 struct xfrm_state *x;
1009
1010 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1011 if (x->km.seq == seq &&
1012 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 xfrm_state_hold(x);
1014 return x;
1015 }
1016 }
1017 }
1018 return NULL;
1019}
1020
1021struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1022{
1023 struct xfrm_state *x;
1024
1025 spin_lock_bh(&xfrm_state_lock);
1026 x = __xfrm_find_acq_byseq(seq);
1027 spin_unlock_bh(&xfrm_state_lock);
1028 return x;
1029}
1030EXPORT_SYMBOL(xfrm_find_acq_byseq);
1031
1032u32 xfrm_get_acqseq(void)
1033{
1034 u32 res;
1035 static u32 acqseq;
1036 static DEFINE_SPINLOCK(acqseq_lock);
1037
1038 spin_lock_bh(&acqseq_lock);
1039 res = (++acqseq ? : ++acqseq);
1040 spin_unlock_bh(&acqseq_lock);
1041 return res;
1042}
1043EXPORT_SYMBOL(xfrm_get_acqseq);
1044
1045void
Al Viro26977b42006-09-27 18:47:05 -07001046xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047{
David S. Millerf034b5d2006-08-24 03:08:07 -07001048 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 struct xfrm_state *x0;
1050
1051 if (x->id.spi)
1052 return;
1053
1054 if (minspi == maxspi) {
1055 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1056 if (x0) {
1057 xfrm_state_put(x0);
1058 return;
1059 }
1060 x->id.spi = minspi;
1061 } else {
1062 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001063 u32 low = ntohl(minspi);
1064 u32 high = ntohl(maxspi);
1065 for (h=0; h<high-low+1; h++) {
1066 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1068 if (x0 == NULL) {
1069 x->id.spi = htonl(spi);
1070 break;
1071 }
1072 xfrm_state_put(x0);
1073 }
1074 }
1075 if (x->id.spi) {
1076 spin_lock_bh(&xfrm_state_lock);
1077 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001078 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 spin_unlock_bh(&xfrm_state_lock);
1080 wake_up(&km_waitq);
1081 }
1082}
1083EXPORT_SYMBOL(xfrm_alloc_spi);
1084
1085int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1086 void *data)
1087{
1088 int i;
1089 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -07001090 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 int count = 0;
1092 int err = 0;
1093
1094 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001095 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001096 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001097 if (xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 count++;
1099 }
1100 }
1101 if (count == 0) {
1102 err = -ENOENT;
1103 goto out;
1104 }
1105
David S. Millerf034b5d2006-08-24 03:08:07 -07001106 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001107 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Masahide NAKAMURA57947082006-09-22 15:06:24 -07001108 if (!xfrm_id_proto_match(x->id.proto, proto))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 continue;
1110 err = func(x, --count, data);
1111 if (err)
1112 goto out;
1113 }
1114 }
1115out:
1116 spin_unlock_bh(&xfrm_state_lock);
1117 return err;
1118}
1119EXPORT_SYMBOL(xfrm_state_walk);
1120
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001121
1122void xfrm_replay_notify(struct xfrm_state *x, int event)
1123{
1124 struct km_event c;
1125 /* we send notify messages in case
1126 * 1. we updated on of the sequence numbers, and the seqno difference
1127 * is at least x->replay_maxdiff, in this case we also update the
1128 * timeout of our timer function
1129 * 2. if x->replay_maxage has elapsed since last update,
1130 * and there were changes
1131 *
1132 * The state structure must be locked!
1133 */
1134
1135 switch (event) {
1136 case XFRM_REPLAY_UPDATE:
1137 if (x->replay_maxdiff &&
1138 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001139 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1140 if (x->xflags & XFRM_TIME_DEFER)
1141 event = XFRM_REPLAY_TIMEOUT;
1142 else
1143 return;
1144 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001145
1146 break;
1147
1148 case XFRM_REPLAY_TIMEOUT:
1149 if ((x->replay.seq == x->preplay.seq) &&
1150 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001151 (x->replay.oseq == x->preplay.oseq)) {
1152 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001153 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001154 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001155
1156 break;
1157 }
1158
1159 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1160 c.event = XFRM_MSG_NEWAE;
1161 c.data.aevent = event;
1162 km_state_notify(x, &c);
1163
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001164 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001165 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001166 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001167}
David S. Millera70fcb02006-03-20 19:18:52 -08001168EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001169
1170static void xfrm_replay_timer_handler(unsigned long data)
1171{
1172 struct xfrm_state *x = (struct xfrm_state*)data;
1173
1174 spin_lock(&x->lock);
1175
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001176 if (x->km.state == XFRM_STATE_VALID) {
1177 if (xfrm_aevent_is_on())
1178 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1179 else
1180 x->xflags |= XFRM_TIME_DEFER;
1181 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001182
1183 spin_unlock(&x->lock);
1184}
1185
Al Viroa252cc22006-09-27 18:48:18 -07001186int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187{
1188 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001189 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 if (unlikely(seq == 0))
1192 return -EINVAL;
1193
1194 if (likely(seq > x->replay.seq))
1195 return 0;
1196
1197 diff = x->replay.seq - seq;
1198 if (diff >= x->props.replay_window) {
1199 x->stats.replay_window++;
1200 return -EINVAL;
1201 }
1202
1203 if (x->replay.bitmap & (1U << diff)) {
1204 x->stats.replay++;
1205 return -EINVAL;
1206 }
1207 return 0;
1208}
1209EXPORT_SYMBOL(xfrm_replay_check);
1210
Al Viro61f46272006-09-27 18:48:33 -07001211void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212{
1213 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001214 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
1216 if (seq > x->replay.seq) {
1217 diff = seq - x->replay.seq;
1218 if (diff < x->props.replay_window)
1219 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1220 else
1221 x->replay.bitmap = 1;
1222 x->replay.seq = seq;
1223 } else {
1224 diff = x->replay.seq - seq;
1225 x->replay.bitmap |= (1U << diff);
1226 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001227
1228 if (xfrm_aevent_is_on())
1229 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230}
1231EXPORT_SYMBOL(xfrm_replay_advance);
1232
1233static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1234static DEFINE_RWLOCK(xfrm_km_lock);
1235
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001236void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237{
1238 struct xfrm_mgr *km;
1239
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001240 read_lock(&xfrm_km_lock);
1241 list_for_each_entry(km, &xfrm_km_list, list)
1242 if (km->notify_policy)
1243 km->notify_policy(xp, dir, c);
1244 read_unlock(&xfrm_km_lock);
1245}
1246
1247void km_state_notify(struct xfrm_state *x, struct km_event *c)
1248{
1249 struct xfrm_mgr *km;
1250 read_lock(&xfrm_km_lock);
1251 list_for_each_entry(km, &xfrm_km_list, list)
1252 if (km->notify)
1253 km->notify(x, c);
1254 read_unlock(&xfrm_km_lock);
1255}
1256
1257EXPORT_SYMBOL(km_policy_notify);
1258EXPORT_SYMBOL(km_state_notify);
1259
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001260void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001261{
1262 struct km_event c;
1263
Herbert Xubf088672005-06-18 22:44:00 -07001264 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001265 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001266 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001267 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
1269 if (hard)
1270 wake_up(&km_waitq);
1271}
1272
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001273EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001274/*
1275 * We send to all registered managers regardless of failure
1276 * We are happy with one success
1277*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001278int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001280 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 struct xfrm_mgr *km;
1282
1283 read_lock(&xfrm_km_lock);
1284 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001285 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1286 if (!acqret)
1287 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 }
1289 read_unlock(&xfrm_km_lock);
1290 return err;
1291}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001292EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
1294int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
1295{
1296 int err = -EINVAL;
1297 struct xfrm_mgr *km;
1298
1299 read_lock(&xfrm_km_lock);
1300 list_for_each_entry(km, &xfrm_km_list, list) {
1301 if (km->new_mapping)
1302 err = km->new_mapping(x, ipaddr, sport);
1303 if (!err)
1304 break;
1305 }
1306 read_unlock(&xfrm_km_lock);
1307 return err;
1308}
1309EXPORT_SYMBOL(km_new_mapping);
1310
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001311void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001313 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
Herbert Xubf088672005-06-18 22:44:00 -07001315 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001316 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001317 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001318 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
1320 if (hard)
1321 wake_up(&km_waitq);
1322}
David S. Millera70fcb02006-03-20 19:18:52 -08001323EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001325int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1326{
1327 int err = -EINVAL;
1328 int ret;
1329 struct xfrm_mgr *km;
1330
1331 read_lock(&xfrm_km_lock);
1332 list_for_each_entry(km, &xfrm_km_list, list) {
1333 if (km->report) {
1334 ret = km->report(proto, sel, addr);
1335 if (!ret)
1336 err = ret;
1337 }
1338 }
1339 read_unlock(&xfrm_km_lock);
1340 return err;
1341}
1342EXPORT_SYMBOL(km_report);
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1345{
1346 int err;
1347 u8 *data;
1348 struct xfrm_mgr *km;
1349 struct xfrm_policy *pol = NULL;
1350
1351 if (optlen <= 0 || optlen > PAGE_SIZE)
1352 return -EMSGSIZE;
1353
1354 data = kmalloc(optlen, GFP_KERNEL);
1355 if (!data)
1356 return -ENOMEM;
1357
1358 err = -EFAULT;
1359 if (copy_from_user(data, optval, optlen))
1360 goto out;
1361
1362 err = -EINVAL;
1363 read_lock(&xfrm_km_lock);
1364 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001365 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 optlen, &err);
1367 if (err >= 0)
1368 break;
1369 }
1370 read_unlock(&xfrm_km_lock);
1371
1372 if (err >= 0) {
1373 xfrm_sk_policy_insert(sk, err, pol);
1374 xfrm_pol_put(pol);
1375 err = 0;
1376 }
1377
1378out:
1379 kfree(data);
1380 return err;
1381}
1382EXPORT_SYMBOL(xfrm_user_policy);
1383
1384int xfrm_register_km(struct xfrm_mgr *km)
1385{
1386 write_lock_bh(&xfrm_km_lock);
1387 list_add_tail(&km->list, &xfrm_km_list);
1388 write_unlock_bh(&xfrm_km_lock);
1389 return 0;
1390}
1391EXPORT_SYMBOL(xfrm_register_km);
1392
1393int xfrm_unregister_km(struct xfrm_mgr *km)
1394{
1395 write_lock_bh(&xfrm_km_lock);
1396 list_del(&km->list);
1397 write_unlock_bh(&xfrm_km_lock);
1398 return 0;
1399}
1400EXPORT_SYMBOL(xfrm_unregister_km);
1401
1402int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1403{
1404 int err = 0;
1405 if (unlikely(afinfo == NULL))
1406 return -EINVAL;
1407 if (unlikely(afinfo->family >= NPROTO))
1408 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001409 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1411 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001412 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001414 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 return err;
1416}
1417EXPORT_SYMBOL(xfrm_state_register_afinfo);
1418
1419int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1420{
1421 int err = 0;
1422 if (unlikely(afinfo == NULL))
1423 return -EINVAL;
1424 if (unlikely(afinfo->family >= NPROTO))
1425 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001426 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1428 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1429 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001430 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001433 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 return err;
1435}
1436EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1437
1438static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
1439{
1440 struct xfrm_state_afinfo *afinfo;
1441 if (unlikely(family >= NPROTO))
1442 return NULL;
1443 read_lock(&xfrm_state_afinfo_lock);
1444 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001445 if (unlikely(!afinfo))
1446 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return afinfo;
1448}
1449
1450static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
1451{
Herbert Xu546be242006-05-27 23:03:58 -07001452 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453}
1454
1455/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1456void xfrm_state_delete_tunnel(struct xfrm_state *x)
1457{
1458 if (x->tunnel) {
1459 struct xfrm_state *t = x->tunnel;
1460
1461 if (atomic_read(&t->tunnel_users) == 2)
1462 xfrm_state_delete(t);
1463 atomic_dec(&t->tunnel_users);
1464 xfrm_state_put(t);
1465 x->tunnel = NULL;
1466 }
1467}
1468EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1469
Herbert Xu80b30c12005-10-15 10:58:30 +10001470/*
1471 * This function is NOT optimal. For example, with ESP it will give an
1472 * MTU that's usually two bytes short of being optimal. However, it will
1473 * usually give an answer that's a multiple of 4 provided the input is
1474 * also a multiple of 4.
1475 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1477{
1478 int res = mtu;
1479
1480 res -= x->props.header_len;
1481
1482 for (;;) {
1483 int m = res;
1484
1485 if (m < 68)
1486 return 68;
1487
1488 spin_lock_bh(&x->lock);
1489 if (x->km.state == XFRM_STATE_VALID &&
1490 x->type && x->type->get_max_size)
1491 m = x->type->get_max_size(x, m);
1492 else
1493 m += x->props.header_len;
1494 spin_unlock_bh(&x->lock);
1495
1496 if (m <= mtu)
1497 break;
1498 res -= (m - mtu);
1499 }
1500
1501 return res;
1502}
1503
Herbert Xu72cb6962005-06-20 13:18:08 -07001504int xfrm_init_state(struct xfrm_state *x)
1505{
Herbert Xud094cd82005-06-20 13:19:41 -07001506 struct xfrm_state_afinfo *afinfo;
1507 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001508 int err;
1509
Herbert Xud094cd82005-06-20 13:19:41 -07001510 err = -EAFNOSUPPORT;
1511 afinfo = xfrm_state_get_afinfo(family);
1512 if (!afinfo)
1513 goto error;
1514
1515 err = 0;
1516 if (afinfo->init_flags)
1517 err = afinfo->init_flags(x);
1518
1519 xfrm_state_put_afinfo(afinfo);
1520
1521 if (err)
1522 goto error;
1523
1524 err = -EPROTONOSUPPORT;
1525 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001526 if (x->type == NULL)
1527 goto error;
1528
1529 err = x->type->init_state(x);
1530 if (err)
1531 goto error;
1532
Herbert Xub59f45d2006-05-27 23:05:54 -07001533 x->mode = xfrm_get_mode(x->props.mode, family);
1534 if (x->mode == NULL)
1535 goto error;
1536
Herbert Xu72cb6962005-06-20 13:18:08 -07001537 x->km.state = XFRM_STATE_VALID;
1538
1539error:
1540 return err;
1541}
1542
1543EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
1545void __init xfrm_state_init(void)
1546{
David S. Millerf034b5d2006-08-24 03:08:07 -07001547 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
David S. Millerf034b5d2006-08-24 03:08:07 -07001549 sz = sizeof(struct hlist_head) * 8;
1550
David S. Miller44e36b42006-08-24 04:50:50 -07001551 xfrm_state_bydst = xfrm_hash_alloc(sz);
1552 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1553 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001554 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1555 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1556 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1557
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
1559}
1560