blob: 91b02687db52a62ea60370e8c4066459e0033ac7 [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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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 Salim53bc6b4d2006-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
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800831#ifdef CONFIG_XFRM_MIGRATE
832struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
833{
834 int err = -ENOMEM;
835 struct xfrm_state *x = xfrm_state_alloc();
836 if (!x)
837 goto error;
838
839 memcpy(&x->id, &orig->id, sizeof(x->id));
840 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
841 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
842 x->props.mode = orig->props.mode;
843 x->props.replay_window = orig->props.replay_window;
844 x->props.reqid = orig->props.reqid;
845 x->props.family = orig->props.family;
846 x->props.saddr = orig->props.saddr;
847
848 if (orig->aalg) {
849 x->aalg = xfrm_algo_clone(orig->aalg);
850 if (!x->aalg)
851 goto error;
852 }
853 x->props.aalgo = orig->props.aalgo;
854
855 if (orig->ealg) {
856 x->ealg = xfrm_algo_clone(orig->ealg);
857 if (!x->ealg)
858 goto error;
859 }
860 x->props.ealgo = orig->props.ealgo;
861
862 if (orig->calg) {
863 x->calg = xfrm_algo_clone(orig->calg);
864 if (!x->calg)
865 goto error;
866 }
867 x->props.calgo = orig->props.calgo;
868
869 if (orig->encap) {
870 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
871 if (!x->encap)
872 goto error;
873 }
874
875 if (orig->coaddr) {
876 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
877 GFP_KERNEL);
878 if (!x->coaddr)
879 goto error;
880 }
881
882 err = xfrm_init_state(x);
883 if (err)
884 goto error;
885
886 x->props.flags = orig->props.flags;
887
888 x->curlft.add_time = orig->curlft.add_time;
889 x->km.state = orig->km.state;
890 x->km.seq = orig->km.seq;
891
892 return x;
893
894 error:
895 if (errp)
896 *errp = err;
897 if (x) {
898 kfree(x->aalg);
899 kfree(x->ealg);
900 kfree(x->calg);
901 kfree(x->encap);
902 kfree(x->coaddr);
903 }
904 kfree(x);
905 return NULL;
906}
907EXPORT_SYMBOL(xfrm_state_clone);
908
909/* xfrm_state_lock is held */
910struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
911{
912 unsigned int h;
913 struct xfrm_state *x;
914 struct hlist_node *entry;
915
916 if (m->reqid) {
917 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
918 m->reqid, m->old_family);
919 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
920 if (x->props.mode != m->mode ||
921 x->id.proto != m->proto)
922 continue;
923 if (m->reqid && x->props.reqid != m->reqid)
924 continue;
925 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
926 m->old_family) ||
927 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
928 m->old_family))
929 continue;
930 xfrm_state_hold(x);
931 return x;
932 }
933 } else {
934 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
935 m->old_family);
936 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
937 if (x->props.mode != m->mode ||
938 x->id.proto != m->proto)
939 continue;
940 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
941 m->old_family) ||
942 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
943 m->old_family))
944 continue;
945 xfrm_state_hold(x);
946 return x;
947 }
948 }
949
950 return NULL;
951}
952EXPORT_SYMBOL(xfrm_migrate_state_find);
953
954struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
955 struct xfrm_migrate *m)
956{
957 struct xfrm_state *xc;
958 int err;
959
960 xc = xfrm_state_clone(x, &err);
961 if (!xc)
962 return NULL;
963
964 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
965 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
966
967 /* add state */
968 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
969 /* a care is needed when the destination address of the
970 state is to be updated as it is a part of triplet */
971 xfrm_state_insert(xc);
972 } else {
973 if ((err = xfrm_state_add(xc)) < 0)
974 goto error;
975 }
976
977 return xc;
978error:
979 kfree(xc);
980 return NULL;
981}
982EXPORT_SYMBOL(xfrm_state_migrate);
983#endif
984
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985int xfrm_state_update(struct xfrm_state *x)
986{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 struct xfrm_state *x1;
988 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700989 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -0700992 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993
994 err = -ESRCH;
995 if (!x1)
996 goto out;
997
998 if (xfrm_state_kern(x1)) {
999 xfrm_state_put(x1);
1000 err = -EEXIST;
1001 goto out;
1002 }
1003
1004 if (x1->km.state == XFRM_STATE_ACQ) {
1005 __xfrm_state_insert(x);
1006 x = NULL;
1007 }
1008 err = 0;
1009
1010out:
1011 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012
1013 if (err)
1014 return err;
1015
1016 if (!x) {
1017 xfrm_state_delete(x1);
1018 xfrm_state_put(x1);
1019 return 0;
1020 }
1021
1022 err = -EINVAL;
1023 spin_lock_bh(&x1->lock);
1024 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1025 if (x->encap && x1->encap)
1026 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001027 if (x->coaddr && x1->coaddr) {
1028 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1029 }
1030 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1031 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1033 x1->km.dying = 0;
1034
David S. Millera47f0ce2006-08-24 03:54:22 -07001035 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 if (x1->curlft.use_time)
1037 xfrm_state_check_expire(x1);
1038
1039 err = 0;
1040 }
1041 spin_unlock_bh(&x1->lock);
1042
1043 xfrm_state_put(x1);
1044
1045 return err;
1046}
1047EXPORT_SYMBOL(xfrm_state_update);
1048
1049int xfrm_state_check_expire(struct xfrm_state *x)
1050{
1051 if (!x->curlft.use_time)
1052 x->curlft.use_time = (unsigned long)xtime.tv_sec;
1053
1054 if (x->km.state != XFRM_STATE_VALID)
1055 return -EINVAL;
1056
1057 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1058 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001059 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001060 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return -EINVAL;
1062 }
1063
1064 if (!x->km.dying &&
1065 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001066 x->curlft.packets >= x->lft.soft_packet_limit)) {
1067 x->km.dying = 1;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001068 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return 0;
1071}
1072EXPORT_SYMBOL(xfrm_state_check_expire);
1073
1074static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1075{
1076 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1077 - skb_headroom(skb);
1078
1079 if (nhead > 0)
1080 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1081
1082 /* Check tail too... */
1083 return 0;
1084}
1085
1086int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1087{
1088 int err = xfrm_state_check_expire(x);
1089 if (err < 0)
1090 goto err;
1091 err = xfrm_state_check_space(x, skb);
1092err:
1093 return err;
1094}
1095EXPORT_SYMBOL(xfrm_state_check);
1096
1097struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001098xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 unsigned short family)
1100{
1101 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
1103 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001104 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 return x;
1107}
1108EXPORT_SYMBOL(xfrm_state_lookup);
1109
1110struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001111xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1112 u8 proto, unsigned short family)
1113{
1114 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001115
1116 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001117 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001118 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001119 return x;
1120}
1121EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1122
1123struct xfrm_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1125 xfrm_address_t *daddr, xfrm_address_t *saddr,
1126 int create, unsigned short family)
1127{
1128 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
1130 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001131 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 return x;
1135}
1136EXPORT_SYMBOL(xfrm_find_acq);
1137
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001138#ifdef CONFIG_XFRM_SUB_POLICY
1139int
1140xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1141 unsigned short family)
1142{
1143 int err = 0;
1144 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1145 if (!afinfo)
1146 return -EAFNOSUPPORT;
1147
1148 spin_lock_bh(&xfrm_state_lock);
1149 if (afinfo->tmpl_sort)
1150 err = afinfo->tmpl_sort(dst, src, n);
1151 spin_unlock_bh(&xfrm_state_lock);
1152 xfrm_state_put_afinfo(afinfo);
1153 return err;
1154}
1155EXPORT_SYMBOL(xfrm_tmpl_sort);
1156
1157int
1158xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1159 unsigned short family)
1160{
1161 int err = 0;
1162 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1163 if (!afinfo)
1164 return -EAFNOSUPPORT;
1165
1166 spin_lock_bh(&xfrm_state_lock);
1167 if (afinfo->state_sort)
1168 err = afinfo->state_sort(dst, src, n);
1169 spin_unlock_bh(&xfrm_state_lock);
1170 xfrm_state_put_afinfo(afinfo);
1171 return err;
1172}
1173EXPORT_SYMBOL(xfrm_state_sort);
1174#endif
1175
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176/* Silly enough, but I'm lazy to build resolution list */
1177
1178static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1179{
1180 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181
David S. Millerf034b5d2006-08-24 03:08:07 -07001182 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001183 struct hlist_node *entry;
1184 struct xfrm_state *x;
1185
1186 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1187 if (x->km.seq == seq &&
1188 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 xfrm_state_hold(x);
1190 return x;
1191 }
1192 }
1193 }
1194 return NULL;
1195}
1196
1197struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1198{
1199 struct xfrm_state *x;
1200
1201 spin_lock_bh(&xfrm_state_lock);
1202 x = __xfrm_find_acq_byseq(seq);
1203 spin_unlock_bh(&xfrm_state_lock);
1204 return x;
1205}
1206EXPORT_SYMBOL(xfrm_find_acq_byseq);
1207
1208u32 xfrm_get_acqseq(void)
1209{
1210 u32 res;
1211 static u32 acqseq;
1212 static DEFINE_SPINLOCK(acqseq_lock);
1213
1214 spin_lock_bh(&acqseq_lock);
1215 res = (++acqseq ? : ++acqseq);
1216 spin_unlock_bh(&acqseq_lock);
1217 return res;
1218}
1219EXPORT_SYMBOL(xfrm_get_acqseq);
1220
1221void
Al Viro26977b42006-09-27 18:47:05 -07001222xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223{
David S. Millerf034b5d2006-08-24 03:08:07 -07001224 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 struct xfrm_state *x0;
1226
1227 if (x->id.spi)
1228 return;
1229
1230 if (minspi == maxspi) {
1231 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1232 if (x0) {
1233 xfrm_state_put(x0);
1234 return;
1235 }
1236 x->id.spi = minspi;
1237 } else {
1238 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001239 u32 low = ntohl(minspi);
1240 u32 high = ntohl(maxspi);
1241 for (h=0; h<high-low+1; h++) {
1242 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1244 if (x0 == NULL) {
1245 x->id.spi = htonl(spi);
1246 break;
1247 }
1248 xfrm_state_put(x0);
1249 }
1250 }
1251 if (x->id.spi) {
1252 spin_lock_bh(&xfrm_state_lock);
1253 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001254 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 spin_unlock_bh(&xfrm_state_lock);
1256 wake_up(&km_waitq);
1257 }
1258}
1259EXPORT_SYMBOL(xfrm_alloc_spi);
1260
1261int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1262 void *data)
1263{
1264 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001265 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001266 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 int count = 0;
1268 int err = 0;
1269
1270 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001271 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001272 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001273 if (!xfrm_id_proto_match(x->id.proto, proto))
1274 continue;
1275 if (last) {
1276 err = func(last, count, data);
1277 if (err)
1278 goto out;
1279 }
1280 last = x;
1281 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 }
1283 }
1284 if (count == 0) {
1285 err = -ENOENT;
1286 goto out;
1287 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001288 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289out:
1290 spin_unlock_bh(&xfrm_state_lock);
1291 return err;
1292}
1293EXPORT_SYMBOL(xfrm_state_walk);
1294
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001295
1296void xfrm_replay_notify(struct xfrm_state *x, int event)
1297{
1298 struct km_event c;
1299 /* we send notify messages in case
1300 * 1. we updated on of the sequence numbers, and the seqno difference
1301 * is at least x->replay_maxdiff, in this case we also update the
1302 * timeout of our timer function
1303 * 2. if x->replay_maxage has elapsed since last update,
1304 * and there were changes
1305 *
1306 * The state structure must be locked!
1307 */
1308
1309 switch (event) {
1310 case XFRM_REPLAY_UPDATE:
1311 if (x->replay_maxdiff &&
1312 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001313 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1314 if (x->xflags & XFRM_TIME_DEFER)
1315 event = XFRM_REPLAY_TIMEOUT;
1316 else
1317 return;
1318 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001319
1320 break;
1321
1322 case XFRM_REPLAY_TIMEOUT:
1323 if ((x->replay.seq == x->preplay.seq) &&
1324 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001325 (x->replay.oseq == x->preplay.oseq)) {
1326 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001327 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001328 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001329
1330 break;
1331 }
1332
1333 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1334 c.event = XFRM_MSG_NEWAE;
1335 c.data.aevent = event;
1336 km_state_notify(x, &c);
1337
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001338 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001339 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001340 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001341}
David S. Millera70fcb02006-03-20 19:18:52 -08001342EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001343
1344static void xfrm_replay_timer_handler(unsigned long data)
1345{
1346 struct xfrm_state *x = (struct xfrm_state*)data;
1347
1348 spin_lock(&x->lock);
1349
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001350 if (x->km.state == XFRM_STATE_VALID) {
1351 if (xfrm_aevent_is_on())
1352 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1353 else
1354 x->xflags |= XFRM_TIME_DEFER;
1355 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001356
1357 spin_unlock(&x->lock);
1358}
1359
Al Viroa252cc22006-09-27 18:48:18 -07001360int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361{
1362 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001363 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 if (unlikely(seq == 0))
1366 return -EINVAL;
1367
1368 if (likely(seq > x->replay.seq))
1369 return 0;
1370
1371 diff = x->replay.seq - seq;
1372 if (diff >= x->props.replay_window) {
1373 x->stats.replay_window++;
1374 return -EINVAL;
1375 }
1376
1377 if (x->replay.bitmap & (1U << diff)) {
1378 x->stats.replay++;
1379 return -EINVAL;
1380 }
1381 return 0;
1382}
1383EXPORT_SYMBOL(xfrm_replay_check);
1384
Al Viro61f46272006-09-27 18:48:33 -07001385void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386{
1387 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001388 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
1390 if (seq > x->replay.seq) {
1391 diff = seq - x->replay.seq;
1392 if (diff < x->props.replay_window)
1393 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1394 else
1395 x->replay.bitmap = 1;
1396 x->replay.seq = seq;
1397 } else {
1398 diff = x->replay.seq - seq;
1399 x->replay.bitmap |= (1U << diff);
1400 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001401
1402 if (xfrm_aevent_is_on())
1403 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404}
1405EXPORT_SYMBOL(xfrm_replay_advance);
1406
1407static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1408static DEFINE_RWLOCK(xfrm_km_lock);
1409
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001410void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411{
1412 struct xfrm_mgr *km;
1413
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001414 read_lock(&xfrm_km_lock);
1415 list_for_each_entry(km, &xfrm_km_list, list)
1416 if (km->notify_policy)
1417 km->notify_policy(xp, dir, c);
1418 read_unlock(&xfrm_km_lock);
1419}
1420
1421void km_state_notify(struct xfrm_state *x, struct km_event *c)
1422{
1423 struct xfrm_mgr *km;
1424 read_lock(&xfrm_km_lock);
1425 list_for_each_entry(km, &xfrm_km_list, list)
1426 if (km->notify)
1427 km->notify(x, c);
1428 read_unlock(&xfrm_km_lock);
1429}
1430
1431EXPORT_SYMBOL(km_policy_notify);
1432EXPORT_SYMBOL(km_state_notify);
1433
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001434void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001435{
1436 struct km_event c;
1437
Herbert Xubf08867f92005-06-18 22:44:00 -07001438 c.data.hard = hard;
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001439 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001440 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001441 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 if (hard)
1444 wake_up(&km_waitq);
1445}
1446
Jamal Hadi Salim53bc6b4d2006-03-20 19:17:03 -08001447EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001448/*
1449 * We send to all registered managers regardless of failure
1450 * We are happy with one success
1451*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001452int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001454 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 struct xfrm_mgr *km;
1456
1457 read_lock(&xfrm_km_lock);
1458 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001459 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1460 if (!acqret)
1461 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 }
1463 read_unlock(&xfrm_km_lock);
1464 return err;
1465}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001466EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467
Al Viro5d36b182006-11-08 00:24:06 -08001468int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
1470 int err = -EINVAL;
1471 struct xfrm_mgr *km;
1472
1473 read_lock(&xfrm_km_lock);
1474 list_for_each_entry(km, &xfrm_km_list, list) {
1475 if (km->new_mapping)
1476 err = km->new_mapping(x, ipaddr, sport);
1477 if (!err)
1478 break;
1479 }
1480 read_unlock(&xfrm_km_lock);
1481 return err;
1482}
1483EXPORT_SYMBOL(km_new_mapping);
1484
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001485void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001487 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Herbert Xubf08867f92005-06-18 22:44:00 -07001489 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001490 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001491 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001492 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
1494 if (hard)
1495 wake_up(&km_waitq);
1496}
David S. Millera70fcb02006-03-20 19:18:52 -08001497EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001499int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1500 struct xfrm_migrate *m, int num_migrate)
1501{
1502 int err = -EINVAL;
1503 int ret;
1504 struct xfrm_mgr *km;
1505
1506 read_lock(&xfrm_km_lock);
1507 list_for_each_entry(km, &xfrm_km_list, list) {
1508 if (km->migrate) {
1509 ret = km->migrate(sel, dir, type, m, num_migrate);
1510 if (!ret)
1511 err = ret;
1512 }
1513 }
1514 read_unlock(&xfrm_km_lock);
1515 return err;
1516}
1517EXPORT_SYMBOL(km_migrate);
1518
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001519int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1520{
1521 int err = -EINVAL;
1522 int ret;
1523 struct xfrm_mgr *km;
1524
1525 read_lock(&xfrm_km_lock);
1526 list_for_each_entry(km, &xfrm_km_list, list) {
1527 if (km->report) {
1528 ret = km->report(proto, sel, addr);
1529 if (!ret)
1530 err = ret;
1531 }
1532 }
1533 read_unlock(&xfrm_km_lock);
1534 return err;
1535}
1536EXPORT_SYMBOL(km_report);
1537
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1539{
1540 int err;
1541 u8 *data;
1542 struct xfrm_mgr *km;
1543 struct xfrm_policy *pol = NULL;
1544
1545 if (optlen <= 0 || optlen > PAGE_SIZE)
1546 return -EMSGSIZE;
1547
1548 data = kmalloc(optlen, GFP_KERNEL);
1549 if (!data)
1550 return -ENOMEM;
1551
1552 err = -EFAULT;
1553 if (copy_from_user(data, optval, optlen))
1554 goto out;
1555
1556 err = -EINVAL;
1557 read_lock(&xfrm_km_lock);
1558 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001559 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 optlen, &err);
1561 if (err >= 0)
1562 break;
1563 }
1564 read_unlock(&xfrm_km_lock);
1565
1566 if (err >= 0) {
1567 xfrm_sk_policy_insert(sk, err, pol);
1568 xfrm_pol_put(pol);
1569 err = 0;
1570 }
1571
1572out:
1573 kfree(data);
1574 return err;
1575}
1576EXPORT_SYMBOL(xfrm_user_policy);
1577
1578int xfrm_register_km(struct xfrm_mgr *km)
1579{
1580 write_lock_bh(&xfrm_km_lock);
1581 list_add_tail(&km->list, &xfrm_km_list);
1582 write_unlock_bh(&xfrm_km_lock);
1583 return 0;
1584}
1585EXPORT_SYMBOL(xfrm_register_km);
1586
1587int xfrm_unregister_km(struct xfrm_mgr *km)
1588{
1589 write_lock_bh(&xfrm_km_lock);
1590 list_del(&km->list);
1591 write_unlock_bh(&xfrm_km_lock);
1592 return 0;
1593}
1594EXPORT_SYMBOL(xfrm_unregister_km);
1595
1596int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1597{
1598 int err = 0;
1599 if (unlikely(afinfo == NULL))
1600 return -EINVAL;
1601 if (unlikely(afinfo->family >= NPROTO))
1602 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001603 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1605 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001606 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001608 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 return err;
1610}
1611EXPORT_SYMBOL(xfrm_state_register_afinfo);
1612
1613int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1614{
1615 int err = 0;
1616 if (unlikely(afinfo == NULL))
1617 return -EINVAL;
1618 if (unlikely(afinfo->family >= NPROTO))
1619 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001620 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1622 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1623 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001624 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001627 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 return err;
1629}
1630EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1631
Miika Komucdca7262007-02-06 14:24:56 -08001632struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633{
1634 struct xfrm_state_afinfo *afinfo;
1635 if (unlikely(family >= NPROTO))
1636 return NULL;
1637 read_lock(&xfrm_state_afinfo_lock);
1638 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001639 if (unlikely(!afinfo))
1640 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 return afinfo;
1642}
1643
Miika Komucdca7262007-02-06 14:24:56 -08001644void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645{
Herbert Xu546be242006-05-27 23:03:58 -07001646 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647}
1648
Miika Komucdca7262007-02-06 14:24:56 -08001649EXPORT_SYMBOL(xfrm_state_get_afinfo);
1650EXPORT_SYMBOL(xfrm_state_put_afinfo);
1651
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1653void xfrm_state_delete_tunnel(struct xfrm_state *x)
1654{
1655 if (x->tunnel) {
1656 struct xfrm_state *t = x->tunnel;
1657
1658 if (atomic_read(&t->tunnel_users) == 2)
1659 xfrm_state_delete(t);
1660 atomic_dec(&t->tunnel_users);
1661 xfrm_state_put(t);
1662 x->tunnel = NULL;
1663 }
1664}
1665EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1666
Herbert Xu80b30c12005-10-15 10:58:30 +10001667/*
1668 * This function is NOT optimal. For example, with ESP it will give an
1669 * MTU that's usually two bytes short of being optimal. However, it will
1670 * usually give an answer that's a multiple of 4 provided the input is
1671 * also a multiple of 4.
1672 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1674{
1675 int res = mtu;
1676
1677 res -= x->props.header_len;
1678
1679 for (;;) {
1680 int m = res;
1681
1682 if (m < 68)
1683 return 68;
1684
1685 spin_lock_bh(&x->lock);
1686 if (x->km.state == XFRM_STATE_VALID &&
1687 x->type && x->type->get_max_size)
1688 m = x->type->get_max_size(x, m);
1689 else
1690 m += x->props.header_len;
1691 spin_unlock_bh(&x->lock);
1692
1693 if (m <= mtu)
1694 break;
1695 res -= (m - mtu);
1696 }
1697
1698 return res;
1699}
1700
Herbert Xu72cb6962005-06-20 13:18:08 -07001701int xfrm_init_state(struct xfrm_state *x)
1702{
Herbert Xud094cd82005-06-20 13:19:41 -07001703 struct xfrm_state_afinfo *afinfo;
1704 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001705 int err;
1706
Herbert Xud094cd82005-06-20 13:19:41 -07001707 err = -EAFNOSUPPORT;
1708 afinfo = xfrm_state_get_afinfo(family);
1709 if (!afinfo)
1710 goto error;
1711
1712 err = 0;
1713 if (afinfo->init_flags)
1714 err = afinfo->init_flags(x);
1715
1716 xfrm_state_put_afinfo(afinfo);
1717
1718 if (err)
1719 goto error;
1720
1721 err = -EPROTONOSUPPORT;
1722 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001723 if (x->type == NULL)
1724 goto error;
1725
1726 err = x->type->init_state(x);
1727 if (err)
1728 goto error;
1729
Herbert Xub59f45d2006-05-27 23:05:54 -07001730 x->mode = xfrm_get_mode(x->props.mode, family);
1731 if (x->mode == NULL)
1732 goto error;
1733
Herbert Xu72cb6962005-06-20 13:18:08 -07001734 x->km.state = XFRM_STATE_VALID;
1735
1736error:
1737 return err;
1738}
1739
1740EXPORT_SYMBOL(xfrm_init_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742void __init xfrm_state_init(void)
1743{
David S. Millerf034b5d2006-08-24 03:08:07 -07001744 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
David S. Millerf034b5d2006-08-24 03:08:07 -07001746 sz = sizeof(struct hlist_head) * 8;
1747
David S. Miller44e36b42006-08-24 04:50:50 -07001748 xfrm_state_bydst = xfrm_hash_alloc(sz);
1749 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1750 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001751 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1752 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1753 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1754
David Howellsc4028952006-11-22 14:57:56 +00001755 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756}
1757