blob: 24f7bfd07af2f69f8443c36b263f93ef2cfdf72d [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>
Joy Latten161a09e2006-11-27 13:11:54 -060023#include <linux/audit.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
David S. Miller44e36b42006-08-24 04:50:50 -070025#include "xfrm_hash.h"
26
David S. Milleree857a72006-03-20 19:18:37 -080027struct sock *xfrm_nl;
28EXPORT_SYMBOL(xfrm_nl);
29
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080030u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080031EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -080033u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080034EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036/* Each xfrm_state may be linked to two tables:
37
38 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070039 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 destination/tunnel endpoint. (output)
41 */
42
43static DEFINE_SPINLOCK(xfrm_state_lock);
44
45/* Hash table to find appropriate SA towards given target (endpoint
46 * of tunnel or destination of transport mode) allowed by selector.
47 *
48 * Main use is finding SA after policy selected tunnel or transport mode.
49 * Also, it can be used by ah/esp icmp error handler to find offending SA.
50 */
David S. Millerf034b5d2006-08-24 03:08:07 -070051static struct hlist_head *xfrm_state_bydst __read_mostly;
52static struct hlist_head *xfrm_state_bysrc __read_mostly;
53static struct hlist_head *xfrm_state_byspi __read_mostly;
54static unsigned int xfrm_state_hmask __read_mostly;
55static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
56static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070057static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
David S. Millerc1969f22006-08-24 04:00:03 -070059static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
60 xfrm_address_t *saddr,
61 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070062 unsigned short family)
63{
David S. Millerc1969f22006-08-24 04:00:03 -070064 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070065}
66
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070067static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
68 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070069 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070070{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070071 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070072}
73
David S. Miller2575b652006-08-24 03:26:44 -070074static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070075xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070076{
David S. Millerc1969f22006-08-24 04:00:03 -070077 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070078}
79
David S. Millerf034b5d2006-08-24 03:08:07 -070080static void xfrm_hash_transfer(struct hlist_head *list,
81 struct hlist_head *ndsttable,
82 struct hlist_head *nsrctable,
83 struct hlist_head *nspitable,
84 unsigned int nhashmask)
85{
86 struct hlist_node *entry, *tmp;
87 struct xfrm_state *x;
88
89 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
90 unsigned int h;
91
David S. Millerc1969f22006-08-24 04:00:03 -070092 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
93 x->props.reqid, x->props.family,
94 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070095 hlist_add_head(&x->bydst, ndsttable+h);
96
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070097 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
98 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -070099 nhashmask);
100 hlist_add_head(&x->bysrc, nsrctable+h);
101
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700102 if (x->id.spi) {
103 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
104 x->id.proto, x->props.family,
105 nhashmask);
106 hlist_add_head(&x->byspi, nspitable+h);
107 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700108 }
109}
110
111static unsigned long xfrm_hash_new_size(void)
112{
113 return ((xfrm_state_hmask + 1) << 1) *
114 sizeof(struct hlist_head);
115}
116
117static DEFINE_MUTEX(hash_resize_mutex);
118
David Howellsc4028952006-11-22 14:57:56 +0000119static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700120{
121 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
122 unsigned long nsize, osize;
123 unsigned int nhashmask, ohashmask;
124 int i;
125
126 mutex_lock(&hash_resize_mutex);
127
128 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700129 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700130 if (!ndst)
131 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700132 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700133 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700134 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700135 goto out_unlock;
136 }
David S. Miller44e36b42006-08-24 04:50:50 -0700137 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700138 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700139 xfrm_hash_free(ndst, nsize);
140 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700141 goto out_unlock;
142 }
143
144 spin_lock_bh(&xfrm_state_lock);
145
146 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
147 for (i = xfrm_state_hmask; i >= 0; i--)
148 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
149 nhashmask);
150
151 odst = xfrm_state_bydst;
152 osrc = xfrm_state_bysrc;
153 ospi = xfrm_state_byspi;
154 ohashmask = xfrm_state_hmask;
155
156 xfrm_state_bydst = ndst;
157 xfrm_state_bysrc = nsrc;
158 xfrm_state_byspi = nspi;
159 xfrm_state_hmask = nhashmask;
160
161 spin_unlock_bh(&xfrm_state_lock);
162
163 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700164 xfrm_hash_free(odst, osize);
165 xfrm_hash_free(osrc, osize);
166 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700167
168out_unlock:
169 mutex_unlock(&hash_resize_mutex);
170}
171
David Howellsc4028952006-11-22 14:57:56 +0000172static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174DECLARE_WAIT_QUEUE_HEAD(km_waitq);
175EXPORT_SYMBOL(km_waitq);
176
177static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
178static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
179
180static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700181static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182static DEFINE_SPINLOCK(xfrm_state_gc_lock);
183
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800184int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
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
David Howellsc4028952006-11-22 14:57:56 +0000208static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
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;
Joy Latten161a09e2006-11-27 13:11:54 -0600239 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 spin_lock(&x->lock);
242 if (x->km.state == XFRM_STATE_DEAD)
243 goto out;
244 if (x->km.state == XFRM_STATE_EXPIRED)
245 goto expired;
246 if (x->lft.hard_add_expires_seconds) {
247 long tmo = x->lft.hard_add_expires_seconds +
248 x->curlft.add_time - now;
249 if (tmo <= 0)
250 goto expired;
251 if (tmo < next)
252 next = tmo;
253 }
254 if (x->lft.hard_use_expires_seconds) {
255 long tmo = x->lft.hard_use_expires_seconds +
256 (x->curlft.use_time ? : now) - now;
257 if (tmo <= 0)
258 goto expired;
259 if (tmo < next)
260 next = tmo;
261 }
262 if (x->km.dying)
263 goto resched;
264 if (x->lft.soft_add_expires_seconds) {
265 long tmo = x->lft.soft_add_expires_seconds +
266 x->curlft.add_time - now;
267 if (tmo <= 0)
268 warn = 1;
269 else if (tmo < next)
270 next = tmo;
271 }
272 if (x->lft.soft_use_expires_seconds) {
273 long tmo = x->lft.soft_use_expires_seconds +
274 (x->curlft.use_time ? : now) - now;
275 if (tmo <= 0)
276 warn = 1;
277 else if (tmo < next)
278 next = tmo;
279 }
280
Herbert Xu4666faa2005-06-18 22:43:22 -0700281 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800283 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700285 if (next != LONG_MAX)
286 mod_timer(&x->timer, jiffies + make_jiffies(next));
287
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 goto out;
289
290expired:
291 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
292 x->km.state = XFRM_STATE_EXPIRED;
293 wake_up(&km_waitq);
294 next = 2;
295 goto resched;
296 }
Joy Latten161a09e2006-11-27 13:11:54 -0600297
298 err = __xfrm_state_delete(x);
299 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800300 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Joy Latten161a09e2006-11-27 13:11:54 -0600302 xfrm_audit_log(audit_get_loginuid(current->audit_context), 0,
303 AUDIT_MAC_IPSEC_DELSA, err ? 0 : 1, NULL, x);
304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305out:
306 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
David S. Miller0ac84752006-03-20 19:18:23 -0800309static void xfrm_replay_timer_handler(unsigned long data);
310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311struct xfrm_state *xfrm_state_alloc(void)
312{
313 struct xfrm_state *x;
314
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700315 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 atomic_set(&x->refcnt, 1);
319 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700320 INIT_HLIST_NODE(&x->bydst);
321 INIT_HLIST_NODE(&x->bysrc);
322 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 init_timer(&x->timer);
324 x->timer.function = xfrm_timer_handler;
325 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800326 init_timer(&x->rtimer);
327 x->rtimer.function = xfrm_replay_timer_handler;
328 x->rtimer.data = (unsigned long)x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 x->curlft.add_time = (unsigned long)xtime.tv_sec;
330 x->lft.soft_byte_limit = XFRM_INF;
331 x->lft.soft_packet_limit = XFRM_INF;
332 x->lft.hard_byte_limit = XFRM_INF;
333 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800334 x->replay_maxage = 0;
335 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 spin_lock_init(&x->lock);
337 }
338 return x;
339}
340EXPORT_SYMBOL(xfrm_state_alloc);
341
342void __xfrm_state_destroy(struct xfrm_state *x)
343{
344 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
345
346 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700347 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 spin_unlock_bh(&xfrm_state_gc_lock);
349 schedule_work(&xfrm_state_gc_work);
350}
351EXPORT_SYMBOL(__xfrm_state_destroy);
352
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800353int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700355 int err = -ESRCH;
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 if (x->km.state != XFRM_STATE_DEAD) {
358 x->km.state = XFRM_STATE_DEAD;
359 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700360 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700361 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700362 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700363 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700364 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 /* All xfrm_state objects are created by xfrm_state_alloc.
368 * The xfrm_state_alloc call gives a reference, and that
369 * is what we are dropping here.
370 */
Herbert Xu21380b82006-02-22 14:47:13 -0800371 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700372 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700374
375 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800377EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700379int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700381 int err;
382
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700384 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700386
387 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388}
389EXPORT_SYMBOL(xfrm_state_delete);
390
Joy Latten161a09e2006-11-27 13:11:54 -0600391void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392{
393 int i;
Joy Latten161a09e2006-11-27 13:11:54 -0600394 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
396 spin_lock_bh(&xfrm_state_lock);
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700397 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700398 struct hlist_node *entry;
399 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700401 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700403 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 xfrm_state_hold(x);
405 spin_unlock_bh(&xfrm_state_lock);
406
Joy Latten161a09e2006-11-27 13:11:54 -0600407 err = xfrm_state_delete(x);
408 xfrm_audit_log(audit_info->loginuid,
409 audit_info->secid,
410 AUDIT_MAC_IPSEC_DELSA,
411 err ? 0 : 1, NULL, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 xfrm_state_put(x);
413
414 spin_lock_bh(&xfrm_state_lock);
415 goto restart;
416 }
417 }
418 }
419 spin_unlock_bh(&xfrm_state_lock);
420 wake_up(&km_waitq);
421}
422EXPORT_SYMBOL(xfrm_state_flush);
423
424static int
425xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
426 struct xfrm_tmpl *tmpl,
427 xfrm_address_t *daddr, xfrm_address_t *saddr,
428 unsigned short family)
429{
430 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
431 if (!afinfo)
432 return -1;
433 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
434 xfrm_state_put_afinfo(afinfo);
435 return 0;
436}
437
Al Viroa94cfd12006-09-27 18:47:24 -0700438static 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 -0700439{
440 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
441 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700442 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700443
David S. Miller8f126e32006-08-24 02:45:07 -0700444 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700445 if (x->props.family != family ||
446 x->id.spi != spi ||
447 x->id.proto != proto)
448 continue;
449
450 switch (family) {
451 case AF_INET:
452 if (x->id.daddr.a4 != daddr->a4)
453 continue;
454 break;
455 case AF_INET6:
456 if (!ipv6_addr_equal((struct in6_addr *)daddr,
457 (struct in6_addr *)
458 x->id.daddr.a6))
459 continue;
460 break;
461 };
462
463 xfrm_state_hold(x);
464 return x;
465 }
466
467 return NULL;
468}
469
470static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
471{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700472 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700473 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700474 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700475
David S. Miller8f126e32006-08-24 02:45:07 -0700476 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700477 if (x->props.family != family ||
478 x->id.proto != proto)
479 continue;
480
481 switch (family) {
482 case AF_INET:
483 if (x->id.daddr.a4 != daddr->a4 ||
484 x->props.saddr.a4 != saddr->a4)
485 continue;
486 break;
487 case AF_INET6:
488 if (!ipv6_addr_equal((struct in6_addr *)daddr,
489 (struct in6_addr *)
490 x->id.daddr.a6) ||
491 !ipv6_addr_equal((struct in6_addr *)saddr,
492 (struct in6_addr *)
493 x->props.saddr.a6))
494 continue;
495 break;
496 };
497
498 xfrm_state_hold(x);
499 return x;
500 }
501
502 return NULL;
503}
504
505static inline struct xfrm_state *
506__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
507{
508 if (use_spi)
509 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
510 x->id.proto, family);
511 else
512 return __xfrm_state_lookup_byaddr(&x->id.daddr,
513 &x->props.saddr,
514 x->id.proto, family);
515}
516
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700517static void xfrm_hash_grow_check(int have_hash_collision)
518{
519 if (have_hash_collision &&
520 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
521 xfrm_state_num > xfrm_state_hmask)
522 schedule_work(&xfrm_hash_work);
523}
524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525struct xfrm_state *
526xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
527 struct flowi *fl, struct xfrm_tmpl *tmpl,
528 struct xfrm_policy *pol, int *err,
529 unsigned short family)
530{
David S. Millerc1969f22006-08-24 04:00:03 -0700531 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700532 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 struct xfrm_state *x, *x0;
534 int acquire_in_progress = 0;
535 int error = 0;
536 struct xfrm_state *best = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700539 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 if (x->props.family == family &&
541 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700542 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 xfrm_state_addr_check(x, daddr, saddr, family) &&
544 tmpl->mode == x->props.mode &&
545 tmpl->id.proto == x->id.proto &&
546 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
547 /* Resolution logic:
548 1. There is a valid state with matching selector.
549 Done.
550 2. Valid state with inappropriate selector. Skip.
551
552 Entering area of "sysdeps".
553
554 3. If state is not valid, selector is temporary,
555 it selects only session which triggered
556 previous resolution. Key manager will do
557 something to install a state with proper
558 selector.
559 */
560 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800561 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700562 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 continue;
564 if (!best ||
565 best->km.dying > x->km.dying ||
566 (best->km.dying == x->km.dying &&
567 best->curlft.add_time < x->curlft.add_time))
568 best = x;
569 } else if (x->km.state == XFRM_STATE_ACQ) {
570 acquire_in_progress = 1;
571 } else if (x->km.state == XFRM_STATE_ERROR ||
572 x->km.state == XFRM_STATE_EXPIRED) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800573 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700574 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 error = -ESRCH;
576 }
577 }
578 }
579
580 x = best;
581 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700582 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700583 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
584 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 xfrm_state_put(x0);
586 error = -EEXIST;
587 goto out;
588 }
589 x = xfrm_state_alloc();
590 if (x == NULL) {
591 error = -ENOMEM;
592 goto out;
593 }
594 /* Initialize temporary selector matching only
595 * to current session. */
596 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
597
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700598 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
599 if (error) {
600 x->km.state = XFRM_STATE_DEAD;
601 xfrm_state_put(x);
602 x = NULL;
603 goto out;
604 }
605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 if (km_query(x, tmpl, pol) == 0) {
607 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700608 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700609 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700610 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 if (x->id.spi) {
612 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700613 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 }
615 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
617 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700618 xfrm_state_num++;
619 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 } else {
621 x->km.state = XFRM_STATE_DEAD;
622 xfrm_state_put(x);
623 x = NULL;
624 error = -ESRCH;
625 }
626 }
627out:
628 if (x)
629 xfrm_state_hold(x);
630 else
631 *err = acquire_in_progress ? -EAGAIN : error;
632 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return x;
634}
635
636static void __xfrm_state_insert(struct xfrm_state *x)
637{
David S. Millera624c102006-08-24 03:24:33 -0700638 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
David S. Miller9d4a7062006-08-24 03:18:09 -0700640 x->genid = ++xfrm_state_genid;
641
David S. Millerc1969f22006-08-24 04:00:03 -0700642 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
643 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700644 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700646 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700647 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700649 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700650 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
651 x->props.family);
652
David S. Miller8f126e32006-08-24 02:45:07 -0700653 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700654 }
655
David S. Millera47f0ce2006-08-24 03:54:22 -0700656 mod_timer(&x->timer, jiffies + HZ);
657 if (x->replay_maxage)
658 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700661
662 xfrm_state_num++;
663
David S. Miller918049f2006-10-12 22:03:24 -0700664 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665}
666
David S. Millerc7f5ea32006-08-24 03:29:04 -0700667/* xfrm_state_lock is held */
668static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
669{
670 unsigned short family = xnew->props.family;
671 u32 reqid = xnew->props.reqid;
672 struct xfrm_state *x;
673 struct hlist_node *entry;
674 unsigned int h;
675
David S. Millerc1969f22006-08-24 04:00:03 -0700676 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700677 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
678 if (x->props.family == family &&
679 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700680 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
681 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700682 x->genid = xfrm_state_genid;
683 }
684}
685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686void xfrm_state_insert(struct xfrm_state *x)
687{
688 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700689 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 __xfrm_state_insert(x);
691 spin_unlock_bh(&xfrm_state_lock);
692}
693EXPORT_SYMBOL(xfrm_state_insert);
694
David S. Miller27708342006-08-24 00:13:10 -0700695/* xfrm_state_lock is held */
696static 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)
697{
David S. Millerc1969f22006-08-24 04:00:03 -0700698 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700699 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700700 struct xfrm_state *x;
701
David S. Miller8f126e32006-08-24 02:45:07 -0700702 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700703 if (x->props.reqid != reqid ||
704 x->props.mode != mode ||
705 x->props.family != family ||
706 x->km.state != XFRM_STATE_ACQ ||
707 x->id.spi != 0)
708 continue;
709
710 switch (family) {
711 case AF_INET:
712 if (x->id.daddr.a4 != daddr->a4 ||
713 x->props.saddr.a4 != saddr->a4)
714 continue;
715 break;
716 case AF_INET6:
717 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
718 (struct in6_addr *)daddr) ||
719 !ipv6_addr_equal((struct in6_addr *)
720 x->props.saddr.a6,
721 (struct in6_addr *)saddr))
722 continue;
723 break;
724 };
725
726 xfrm_state_hold(x);
727 return x;
728 }
729
730 if (!create)
731 return NULL;
732
733 x = xfrm_state_alloc();
734 if (likely(x)) {
735 switch (family) {
736 case AF_INET:
737 x->sel.daddr.a4 = daddr->a4;
738 x->sel.saddr.a4 = saddr->a4;
739 x->sel.prefixlen_d = 32;
740 x->sel.prefixlen_s = 32;
741 x->props.saddr.a4 = saddr->a4;
742 x->id.daddr.a4 = daddr->a4;
743 break;
744
745 case AF_INET6:
746 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
747 (struct in6_addr *)daddr);
748 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
749 (struct in6_addr *)saddr);
750 x->sel.prefixlen_d = 128;
751 x->sel.prefixlen_s = 128;
752 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
753 (struct in6_addr *)saddr);
754 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
755 (struct in6_addr *)daddr);
756 break;
757 };
758
759 x->km.state = XFRM_STATE_ACQ;
760 x->id.proto = proto;
761 x->props.family = family;
762 x->props.mode = mode;
763 x->props.reqid = reqid;
764 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
765 xfrm_state_hold(x);
766 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
767 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700768 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700769 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700770 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700771 wake_up(&km_waitq);
David S. Miller918049f2006-10-12 22:03:24 -0700772
773 xfrm_state_num++;
774
775 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -0700776 }
777
778 return x;
779}
780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
782
783int xfrm_state_add(struct xfrm_state *x)
784{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 struct xfrm_state *x1;
786 int family;
787 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700788 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
790 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 spin_lock_bh(&xfrm_state_lock);
793
David S. Milleredcd5822006-08-24 00:42:45 -0700794 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 if (x1) {
796 xfrm_state_put(x1);
797 x1 = NULL;
798 err = -EEXIST;
799 goto out;
800 }
801
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700802 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 x1 = __xfrm_find_acq_byseq(x->km.seq);
804 if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
805 xfrm_state_put(x1);
806 x1 = NULL;
807 }
808 }
809
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700810 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700811 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
812 x->id.proto,
813 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
David S. Millerc7f5ea32006-08-24 03:29:04 -0700815 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 __xfrm_state_insert(x);
817 err = 0;
818
819out:
820 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 if (x1) {
823 xfrm_state_delete(x1);
824 xfrm_state_put(x1);
825 }
826
827 return err;
828}
829EXPORT_SYMBOL(xfrm_state_add);
830
831int xfrm_state_update(struct xfrm_state *x)
832{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 struct xfrm_state *x1;
834 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700835 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700838 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 err = -ESRCH;
841 if (!x1)
842 goto out;
843
844 if (xfrm_state_kern(x1)) {
845 xfrm_state_put(x1);
846 err = -EEXIST;
847 goto out;
848 }
849
850 if (x1->km.state == XFRM_STATE_ACQ) {
851 __xfrm_state_insert(x);
852 x = NULL;
853 }
854 err = 0;
855
856out:
857 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
859 if (err)
860 return err;
861
862 if (!x) {
863 xfrm_state_delete(x1);
864 xfrm_state_put(x1);
865 return 0;
866 }
867
868 err = -EINVAL;
869 spin_lock_bh(&x1->lock);
870 if (likely(x1->km.state == XFRM_STATE_VALID)) {
871 if (x->encap && x1->encap)
872 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700873 if (x->coaddr && x1->coaddr) {
874 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
875 }
876 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
877 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
879 x1->km.dying = 0;
880
David S. Millera47f0ce2006-08-24 03:54:22 -0700881 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 if (x1->curlft.use_time)
883 xfrm_state_check_expire(x1);
884
885 err = 0;
886 }
887 spin_unlock_bh(&x1->lock);
888
889 xfrm_state_put(x1);
890
891 return err;
892}
893EXPORT_SYMBOL(xfrm_state_update);
894
895int xfrm_state_check_expire(struct xfrm_state *x)
896{
897 if (!x->curlft.use_time)
898 x->curlft.use_time = (unsigned long)xtime.tv_sec;
899
900 if (x->km.state != XFRM_STATE_VALID)
901 return -EINVAL;
902
903 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
904 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -0700905 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -0700906 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return -EINVAL;
908 }
909
910 if (!x->km.dying &&
911 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -0700912 x->curlft.packets >= x->lft.soft_packet_limit)) {
913 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800914 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -0700915 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return 0;
917}
918EXPORT_SYMBOL(xfrm_state_check_expire);
919
920static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
921{
922 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
923 - skb_headroom(skb);
924
925 if (nhead > 0)
926 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
927
928 /* Check tail too... */
929 return 0;
930}
931
932int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
933{
934 int err = xfrm_state_check_expire(x);
935 if (err < 0)
936 goto err;
937 err = xfrm_state_check_space(x, skb);
938err:
939 return err;
940}
941EXPORT_SYMBOL(xfrm_state_check);
942
943struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -0700944xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 unsigned short family)
946{
947 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700950 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 return x;
953}
954EXPORT_SYMBOL(xfrm_state_lookup);
955
956struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700957xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
958 u8 proto, unsigned short family)
959{
960 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700961
962 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700963 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700964 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700965 return x;
966}
967EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
968
969struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
971 xfrm_address_t *daddr, xfrm_address_t *saddr,
972 int create, unsigned short family)
973{
974 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -0700977 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -0700979
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 return x;
981}
982EXPORT_SYMBOL(xfrm_find_acq);
983
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -0700984#ifdef CONFIG_XFRM_SUB_POLICY
985int
986xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
987 unsigned short family)
988{
989 int err = 0;
990 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
991 if (!afinfo)
992 return -EAFNOSUPPORT;
993
994 spin_lock_bh(&xfrm_state_lock);
995 if (afinfo->tmpl_sort)
996 err = afinfo->tmpl_sort(dst, src, n);
997 spin_unlock_bh(&xfrm_state_lock);
998 xfrm_state_put_afinfo(afinfo);
999 return err;
1000}
1001EXPORT_SYMBOL(xfrm_tmpl_sort);
1002
1003int
1004xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1005 unsigned short family)
1006{
1007 int err = 0;
1008 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1009 if (!afinfo)
1010 return -EAFNOSUPPORT;
1011
1012 spin_lock_bh(&xfrm_state_lock);
1013 if (afinfo->state_sort)
1014 err = afinfo->state_sort(dst, src, n);
1015 spin_unlock_bh(&xfrm_state_lock);
1016 xfrm_state_put_afinfo(afinfo);
1017 return err;
1018}
1019EXPORT_SYMBOL(xfrm_state_sort);
1020#endif
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022/* Silly enough, but I'm lazy to build resolution list */
1023
1024static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1025{
1026 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
David S. Millerf034b5d2006-08-24 03:08:07 -07001028 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001029 struct hlist_node *entry;
1030 struct xfrm_state *x;
1031
1032 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1033 if (x->km.seq == seq &&
1034 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 xfrm_state_hold(x);
1036 return x;
1037 }
1038 }
1039 }
1040 return NULL;
1041}
1042
1043struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1044{
1045 struct xfrm_state *x;
1046
1047 spin_lock_bh(&xfrm_state_lock);
1048 x = __xfrm_find_acq_byseq(seq);
1049 spin_unlock_bh(&xfrm_state_lock);
1050 return x;
1051}
1052EXPORT_SYMBOL(xfrm_find_acq_byseq);
1053
1054u32 xfrm_get_acqseq(void)
1055{
1056 u32 res;
1057 static u32 acqseq;
1058 static DEFINE_SPINLOCK(acqseq_lock);
1059
1060 spin_lock_bh(&acqseq_lock);
1061 res = (++acqseq ? : ++acqseq);
1062 spin_unlock_bh(&acqseq_lock);
1063 return res;
1064}
1065EXPORT_SYMBOL(xfrm_get_acqseq);
1066
1067void
Al Viro26977b42006-09-27 18:47:05 -07001068xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069{
David S. Millerf034b5d2006-08-24 03:08:07 -07001070 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 struct xfrm_state *x0;
1072
1073 if (x->id.spi)
1074 return;
1075
1076 if (minspi == maxspi) {
1077 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1078 if (x0) {
1079 xfrm_state_put(x0);
1080 return;
1081 }
1082 x->id.spi = minspi;
1083 } else {
1084 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001085 u32 low = ntohl(minspi);
1086 u32 high = ntohl(maxspi);
1087 for (h=0; h<high-low+1; h++) {
1088 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1090 if (x0 == NULL) {
1091 x->id.spi = htonl(spi);
1092 break;
1093 }
1094 xfrm_state_put(x0);
1095 }
1096 }
1097 if (x->id.spi) {
1098 spin_lock_bh(&xfrm_state_lock);
1099 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001100 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 spin_unlock_bh(&xfrm_state_lock);
1102 wake_up(&km_waitq);
1103 }
1104}
1105EXPORT_SYMBOL(xfrm_alloc_spi);
1106
1107int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1108 void *data)
1109{
1110 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001111 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001112 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 int count = 0;
1114 int err = 0;
1115
1116 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001117 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001118 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001119 if (!xfrm_id_proto_match(x->id.proto, proto))
1120 continue;
1121 if (last) {
1122 err = func(last, count, data);
1123 if (err)
1124 goto out;
1125 }
1126 last = x;
1127 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 }
1129 }
1130 if (count == 0) {
1131 err = -ENOENT;
1132 goto out;
1133 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001134 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135out:
1136 spin_unlock_bh(&xfrm_state_lock);
1137 return err;
1138}
1139EXPORT_SYMBOL(xfrm_state_walk);
1140
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001141
1142void xfrm_replay_notify(struct xfrm_state *x, int event)
1143{
1144 struct km_event c;
1145 /* we send notify messages in case
1146 * 1. we updated on of the sequence numbers, and the seqno difference
1147 * is at least x->replay_maxdiff, in this case we also update the
1148 * timeout of our timer function
1149 * 2. if x->replay_maxage has elapsed since last update,
1150 * and there were changes
1151 *
1152 * The state structure must be locked!
1153 */
1154
1155 switch (event) {
1156 case XFRM_REPLAY_UPDATE:
1157 if (x->replay_maxdiff &&
1158 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001159 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1160 if (x->xflags & XFRM_TIME_DEFER)
1161 event = XFRM_REPLAY_TIMEOUT;
1162 else
1163 return;
1164 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001165
1166 break;
1167
1168 case XFRM_REPLAY_TIMEOUT:
1169 if ((x->replay.seq == x->preplay.seq) &&
1170 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001171 (x->replay.oseq == x->preplay.oseq)) {
1172 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001173 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001174 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001175
1176 break;
1177 }
1178
1179 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1180 c.event = XFRM_MSG_NEWAE;
1181 c.data.aevent = event;
1182 km_state_notify(x, &c);
1183
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001184 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001185 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001186 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001187}
David S. Millera70fcb02006-03-20 19:18:52 -08001188EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001189
1190static void xfrm_replay_timer_handler(unsigned long data)
1191{
1192 struct xfrm_state *x = (struct xfrm_state*)data;
1193
1194 spin_lock(&x->lock);
1195
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001196 if (x->km.state == XFRM_STATE_VALID) {
1197 if (xfrm_aevent_is_on())
1198 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1199 else
1200 x->xflags |= XFRM_TIME_DEFER;
1201 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001202
1203 spin_unlock(&x->lock);
1204}
1205
Al Viroa252cc22006-09-27 18:48:18 -07001206int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
1208 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001209 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
1211 if (unlikely(seq == 0))
1212 return -EINVAL;
1213
1214 if (likely(seq > x->replay.seq))
1215 return 0;
1216
1217 diff = x->replay.seq - seq;
1218 if (diff >= x->props.replay_window) {
1219 x->stats.replay_window++;
1220 return -EINVAL;
1221 }
1222
1223 if (x->replay.bitmap & (1U << diff)) {
1224 x->stats.replay++;
1225 return -EINVAL;
1226 }
1227 return 0;
1228}
1229EXPORT_SYMBOL(xfrm_replay_check);
1230
Al Viro61f46272006-09-27 18:48:33 -07001231void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232{
1233 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001234 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
1236 if (seq > x->replay.seq) {
1237 diff = seq - x->replay.seq;
1238 if (diff < x->props.replay_window)
1239 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1240 else
1241 x->replay.bitmap = 1;
1242 x->replay.seq = seq;
1243 } else {
1244 diff = x->replay.seq - seq;
1245 x->replay.bitmap |= (1U << diff);
1246 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001247
1248 if (xfrm_aevent_is_on())
1249 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250}
1251EXPORT_SYMBOL(xfrm_replay_advance);
1252
1253static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1254static DEFINE_RWLOCK(xfrm_km_lock);
1255
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001256void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257{
1258 struct xfrm_mgr *km;
1259
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001260 read_lock(&xfrm_km_lock);
1261 list_for_each_entry(km, &xfrm_km_list, list)
1262 if (km->notify_policy)
1263 km->notify_policy(xp, dir, c);
1264 read_unlock(&xfrm_km_lock);
1265}
1266
1267void km_state_notify(struct xfrm_state *x, struct km_event *c)
1268{
1269 struct xfrm_mgr *km;
1270 read_lock(&xfrm_km_lock);
1271 list_for_each_entry(km, &xfrm_km_list, list)
1272 if (km->notify)
1273 km->notify(x, c);
1274 read_unlock(&xfrm_km_lock);
1275}
1276
1277EXPORT_SYMBOL(km_policy_notify);
1278EXPORT_SYMBOL(km_state_notify);
1279
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001280void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001281{
1282 struct km_event c;
1283
Herbert Xubf088672005-06-18 22:44:00 -07001284 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001285 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001286 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001287 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 if (hard)
1290 wake_up(&km_waitq);
1291}
1292
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001293EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001294/*
1295 * We send to all registered managers regardless of failure
1296 * We are happy with one success
1297*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001298int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001300 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 struct xfrm_mgr *km;
1302
1303 read_lock(&xfrm_km_lock);
1304 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001305 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1306 if (!acqret)
1307 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 }
1309 read_unlock(&xfrm_km_lock);
1310 return err;
1311}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001312EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Al Viro5d36b182006-11-08 00:24:06 -08001314int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315{
1316 int err = -EINVAL;
1317 struct xfrm_mgr *km;
1318
1319 read_lock(&xfrm_km_lock);
1320 list_for_each_entry(km, &xfrm_km_list, list) {
1321 if (km->new_mapping)
1322 err = km->new_mapping(x, ipaddr, sport);
1323 if (!err)
1324 break;
1325 }
1326 read_unlock(&xfrm_km_lock);
1327 return err;
1328}
1329EXPORT_SYMBOL(km_new_mapping);
1330
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001331void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001333 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
Herbert Xubf088672005-06-18 22:44:00 -07001335 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001336 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001337 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001338 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 if (hard)
1341 wake_up(&km_waitq);
1342}
David S. Millera70fcb02006-03-20 19:18:52 -08001343EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001345int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1346{
1347 int err = -EINVAL;
1348 int ret;
1349 struct xfrm_mgr *km;
1350
1351 read_lock(&xfrm_km_lock);
1352 list_for_each_entry(km, &xfrm_km_list, list) {
1353 if (km->report) {
1354 ret = km->report(proto, sel, addr);
1355 if (!ret)
1356 err = ret;
1357 }
1358 }
1359 read_unlock(&xfrm_km_lock);
1360 return err;
1361}
1362EXPORT_SYMBOL(km_report);
1363
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1365{
1366 int err;
1367 u8 *data;
1368 struct xfrm_mgr *km;
1369 struct xfrm_policy *pol = NULL;
1370
1371 if (optlen <= 0 || optlen > PAGE_SIZE)
1372 return -EMSGSIZE;
1373
1374 data = kmalloc(optlen, GFP_KERNEL);
1375 if (!data)
1376 return -ENOMEM;
1377
1378 err = -EFAULT;
1379 if (copy_from_user(data, optval, optlen))
1380 goto out;
1381
1382 err = -EINVAL;
1383 read_lock(&xfrm_km_lock);
1384 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001385 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 optlen, &err);
1387 if (err >= 0)
1388 break;
1389 }
1390 read_unlock(&xfrm_km_lock);
1391
1392 if (err >= 0) {
1393 xfrm_sk_policy_insert(sk, err, pol);
1394 xfrm_pol_put(pol);
1395 err = 0;
1396 }
1397
1398out:
1399 kfree(data);
1400 return err;
1401}
1402EXPORT_SYMBOL(xfrm_user_policy);
1403
1404int xfrm_register_km(struct xfrm_mgr *km)
1405{
1406 write_lock_bh(&xfrm_km_lock);
1407 list_add_tail(&km->list, &xfrm_km_list);
1408 write_unlock_bh(&xfrm_km_lock);
1409 return 0;
1410}
1411EXPORT_SYMBOL(xfrm_register_km);
1412
1413int xfrm_unregister_km(struct xfrm_mgr *km)
1414{
1415 write_lock_bh(&xfrm_km_lock);
1416 list_del(&km->list);
1417 write_unlock_bh(&xfrm_km_lock);
1418 return 0;
1419}
1420EXPORT_SYMBOL(xfrm_unregister_km);
1421
1422int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1423{
1424 int err = 0;
1425 if (unlikely(afinfo == NULL))
1426 return -EINVAL;
1427 if (unlikely(afinfo->family >= NPROTO))
1428 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001429 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1431 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001432 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001434 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 return err;
1436}
1437EXPORT_SYMBOL(xfrm_state_register_afinfo);
1438
1439int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1440{
1441 int err = 0;
1442 if (unlikely(afinfo == NULL))
1443 return -EINVAL;
1444 if (unlikely(afinfo->family >= NPROTO))
1445 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001446 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1448 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1449 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001450 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001453 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 return err;
1455}
1456EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1457
Miika Komucdca7262007-02-06 14:24:56 -08001458struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459{
1460 struct xfrm_state_afinfo *afinfo;
1461 if (unlikely(family >= NPROTO))
1462 return NULL;
1463 read_lock(&xfrm_state_afinfo_lock);
1464 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001465 if (unlikely(!afinfo))
1466 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 return afinfo;
1468}
1469
Miika Komucdca7262007-02-06 14:24:56 -08001470void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471{
Herbert Xu546be242006-05-27 23:03:58 -07001472 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473}
1474
Miika Komucdca7262007-02-06 14:24:56 -08001475EXPORT_SYMBOL(xfrm_state_get_afinfo);
1476EXPORT_SYMBOL(xfrm_state_put_afinfo);
1477
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1479void xfrm_state_delete_tunnel(struct xfrm_state *x)
1480{
1481 if (x->tunnel) {
1482 struct xfrm_state *t = x->tunnel;
1483
1484 if (atomic_read(&t->tunnel_users) == 2)
1485 xfrm_state_delete(t);
1486 atomic_dec(&t->tunnel_users);
1487 xfrm_state_put(t);
1488 x->tunnel = NULL;
1489 }
1490}
1491EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1492
Herbert Xu80b30c12005-10-15 10:58:30 +10001493/*
1494 * This function is NOT optimal. For example, with ESP it will give an
1495 * MTU that's usually two bytes short of being optimal. However, it will
1496 * usually give an answer that's a multiple of 4 provided the input is
1497 * also a multiple of 4.
1498 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1500{
1501 int res = mtu;
1502
1503 res -= x->props.header_len;
1504
1505 for (;;) {
1506 int m = res;
1507
1508 if (m < 68)
1509 return 68;
1510
1511 spin_lock_bh(&x->lock);
1512 if (x->km.state == XFRM_STATE_VALID &&
1513 x->type && x->type->get_max_size)
1514 m = x->type->get_max_size(x, m);
1515 else
1516 m += x->props.header_len;
1517 spin_unlock_bh(&x->lock);
1518
1519 if (m <= mtu)
1520 break;
1521 res -= (m - mtu);
1522 }
1523
1524 return res;
1525}
1526
Herbert Xu72cb6962005-06-20 13:18:08 -07001527int xfrm_init_state(struct xfrm_state *x)
1528{
Herbert Xud094cd82005-06-20 13:19:41 -07001529 struct xfrm_state_afinfo *afinfo;
1530 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001531 int err;
1532
Herbert Xud094cd82005-06-20 13:19:41 -07001533 err = -EAFNOSUPPORT;
1534 afinfo = xfrm_state_get_afinfo(family);
1535 if (!afinfo)
1536 goto error;
1537
1538 err = 0;
1539 if (afinfo->init_flags)
1540 err = afinfo->init_flags(x);
1541
1542 xfrm_state_put_afinfo(afinfo);
1543
1544 if (err)
1545 goto error;
1546
1547 err = -EPROTONOSUPPORT;
1548 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001549 if (x->type == NULL)
1550 goto error;
1551
1552 err = x->type->init_state(x);
1553 if (err)
1554 goto error;
1555
Herbert Xub59f45d2006-05-27 23:05:54 -07001556 x->mode = xfrm_get_mode(x->props.mode, family);
1557 if (x->mode == NULL)
1558 goto error;
1559
Herbert Xu72cb6962005-06-20 13:18:08 -07001560 x->km.state = XFRM_STATE_VALID;
1561
1562error:
1563 return err;
1564}
1565
1566EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568void __init xfrm_state_init(void)
1569{
David S. Millerf034b5d2006-08-24 03:08:07 -07001570 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
David S. Millerf034b5d2006-08-24 03:08:07 -07001572 sz = sizeof(struct hlist_head) * 8;
1573
David S. Miller44e36b42006-08-24 04:50:50 -07001574 xfrm_state_bydst = xfrm_hash_alloc(sz);
1575 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1576 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001577 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1578 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1579 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1580
David Howellsc4028952006-11-22 14:57:56 +00001581 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582}
1583