blob: f3a61ebd8d65bca12d1936943074c1a03e23fd4e [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
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900230 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
233static void xfrm_timer_handler(unsigned long data)
234{
235 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800236 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 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;
James Morris9d729f72007-03-04 16:12:44 -0800329 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 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
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700424void xfrm_sad_getinfo(struct xfrm_sadinfo *si)
425{
426 spin_lock_bh(&xfrm_state_lock);
427 si->sadcnt = xfrm_state_num;
428 si->sadhcnt = xfrm_state_hmask;
429 si->sadhmcnt = xfrm_state_hashmax;
430 spin_unlock_bh(&xfrm_state_lock);
431}
432EXPORT_SYMBOL(xfrm_sad_getinfo);
433
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434static int
435xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
436 struct xfrm_tmpl *tmpl,
437 xfrm_address_t *daddr, xfrm_address_t *saddr,
438 unsigned short family)
439{
440 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
441 if (!afinfo)
442 return -1;
443 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
444 xfrm_state_put_afinfo(afinfo);
445 return 0;
446}
447
Al Viroa94cfd12006-09-27 18:47:24 -0700448static 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 -0700449{
450 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
451 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700452 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700453
David S. Miller8f126e32006-08-24 02:45:07 -0700454 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700455 if (x->props.family != family ||
456 x->id.spi != spi ||
457 x->id.proto != proto)
458 continue;
459
460 switch (family) {
461 case AF_INET:
462 if (x->id.daddr.a4 != daddr->a4)
463 continue;
464 break;
465 case AF_INET6:
466 if (!ipv6_addr_equal((struct in6_addr *)daddr,
467 (struct in6_addr *)
468 x->id.daddr.a6))
469 continue;
470 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700471 }
David S. Milleredcd5822006-08-24 00:42:45 -0700472
473 xfrm_state_hold(x);
474 return x;
475 }
476
477 return NULL;
478}
479
480static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
481{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700482 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700483 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700484 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700485
David S. Miller8f126e32006-08-24 02:45:07 -0700486 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700487 if (x->props.family != family ||
488 x->id.proto != proto)
489 continue;
490
491 switch (family) {
492 case AF_INET:
493 if (x->id.daddr.a4 != daddr->a4 ||
494 x->props.saddr.a4 != saddr->a4)
495 continue;
496 break;
497 case AF_INET6:
498 if (!ipv6_addr_equal((struct in6_addr *)daddr,
499 (struct in6_addr *)
500 x->id.daddr.a6) ||
501 !ipv6_addr_equal((struct in6_addr *)saddr,
502 (struct in6_addr *)
503 x->props.saddr.a6))
504 continue;
505 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700506 }
David S. Milleredcd5822006-08-24 00:42:45 -0700507
508 xfrm_state_hold(x);
509 return x;
510 }
511
512 return NULL;
513}
514
515static inline struct xfrm_state *
516__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
517{
518 if (use_spi)
519 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
520 x->id.proto, family);
521 else
522 return __xfrm_state_lookup_byaddr(&x->id.daddr,
523 &x->props.saddr,
524 x->id.proto, family);
525}
526
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700527static void xfrm_hash_grow_check(int have_hash_collision)
528{
529 if (have_hash_collision &&
530 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
531 xfrm_state_num > xfrm_state_hmask)
532 schedule_work(&xfrm_hash_work);
533}
534
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900536xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 struct flowi *fl, struct xfrm_tmpl *tmpl,
538 struct xfrm_policy *pol, int *err,
539 unsigned short family)
540{
David S. Millerc1969f22006-08-24 04:00:03 -0700541 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700542 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 struct xfrm_state *x, *x0;
544 int acquire_in_progress = 0;
545 int error = 0;
546 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700549 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 if (x->props.family == family &&
551 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700552 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 xfrm_state_addr_check(x, daddr, saddr, family) &&
554 tmpl->mode == x->props.mode &&
555 tmpl->id.proto == x->id.proto &&
556 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
557 /* Resolution logic:
558 1. There is a valid state with matching selector.
559 Done.
560 2. Valid state with inappropriate selector. Skip.
561
562 Entering area of "sysdeps".
563
564 3. If state is not valid, selector is temporary,
565 it selects only session which triggered
566 previous resolution. Key manager will do
567 something to install a state with proper
568 selector.
569 */
570 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800571 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700572 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 continue;
574 if (!best ||
575 best->km.dying > x->km.dying ||
576 (best->km.dying == x->km.dying &&
577 best->curlft.add_time < x->curlft.add_time))
578 best = x;
579 } else if (x->km.state == XFRM_STATE_ACQ) {
580 acquire_in_progress = 1;
581 } else if (x->km.state == XFRM_STATE_ERROR ||
582 x->km.state == XFRM_STATE_EXPIRED) {
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900583 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700584 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 error = -ESRCH;
586 }
587 }
588 }
589
590 x = best;
591 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700592 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700593 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
594 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 xfrm_state_put(x0);
596 error = -EEXIST;
597 goto out;
598 }
599 x = xfrm_state_alloc();
600 if (x == NULL) {
601 error = -ENOMEM;
602 goto out;
603 }
604 /* Initialize temporary selector matching only
605 * to current session. */
606 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
607
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700608 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
609 if (error) {
610 x->km.state = XFRM_STATE_DEAD;
611 xfrm_state_put(x);
612 x = NULL;
613 goto out;
614 }
615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 if (km_query(x, tmpl, pol) == 0) {
617 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700618 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700619 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700620 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 if (x->id.spi) {
622 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700623 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 }
625 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
627 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700628 xfrm_state_num++;
629 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 } else {
631 x->km.state = XFRM_STATE_DEAD;
632 xfrm_state_put(x);
633 x = NULL;
634 error = -ESRCH;
635 }
636 }
637out:
638 if (x)
639 xfrm_state_hold(x);
640 else
641 *err = acquire_in_progress ? -EAGAIN : error;
642 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return x;
644}
645
646static void __xfrm_state_insert(struct xfrm_state *x)
647{
David S. Millera624c102006-08-24 03:24:33 -0700648 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
David S. Miller9d4a7062006-08-24 03:18:09 -0700650 x->genid = ++xfrm_state_genid;
651
David S. Millerc1969f22006-08-24 04:00:03 -0700652 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
653 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700654 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700656 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700657 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700659 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700660 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
661 x->props.family);
662
David S. Miller8f126e32006-08-24 02:45:07 -0700663 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700664 }
665
David S. Millera47f0ce2006-08-24 03:54:22 -0700666 mod_timer(&x->timer, jiffies + HZ);
667 if (x->replay_maxage)
668 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700671
672 xfrm_state_num++;
673
David S. Miller918049f2006-10-12 22:03:24 -0700674 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675}
676
David S. Millerc7f5ea32006-08-24 03:29:04 -0700677/* xfrm_state_lock is held */
678static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
679{
680 unsigned short family = xnew->props.family;
681 u32 reqid = xnew->props.reqid;
682 struct xfrm_state *x;
683 struct hlist_node *entry;
684 unsigned int h;
685
David S. Millerc1969f22006-08-24 04:00:03 -0700686 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700687 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
688 if (x->props.family == family &&
689 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700690 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
691 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700692 x->genid = xfrm_state_genid;
693 }
694}
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696void xfrm_state_insert(struct xfrm_state *x)
697{
698 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700699 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 __xfrm_state_insert(x);
701 spin_unlock_bh(&xfrm_state_lock);
702}
703EXPORT_SYMBOL(xfrm_state_insert);
704
David S. Miller27708342006-08-24 00:13:10 -0700705/* xfrm_state_lock is held */
706static 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)
707{
David S. Millerc1969f22006-08-24 04:00:03 -0700708 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700709 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700710 struct xfrm_state *x;
711
David S. Miller8f126e32006-08-24 02:45:07 -0700712 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700713 if (x->props.reqid != reqid ||
714 x->props.mode != mode ||
715 x->props.family != family ||
716 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700717 x->id.spi != 0 ||
718 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700719 continue;
720
721 switch (family) {
722 case AF_INET:
723 if (x->id.daddr.a4 != daddr->a4 ||
724 x->props.saddr.a4 != saddr->a4)
725 continue;
726 break;
727 case AF_INET6:
728 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
729 (struct in6_addr *)daddr) ||
730 !ipv6_addr_equal((struct in6_addr *)
731 x->props.saddr.a6,
732 (struct in6_addr *)saddr))
733 continue;
734 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700735 }
David S. Miller27708342006-08-24 00:13:10 -0700736
737 xfrm_state_hold(x);
738 return x;
739 }
740
741 if (!create)
742 return NULL;
743
744 x = xfrm_state_alloc();
745 if (likely(x)) {
746 switch (family) {
747 case AF_INET:
748 x->sel.daddr.a4 = daddr->a4;
749 x->sel.saddr.a4 = saddr->a4;
750 x->sel.prefixlen_d = 32;
751 x->sel.prefixlen_s = 32;
752 x->props.saddr.a4 = saddr->a4;
753 x->id.daddr.a4 = daddr->a4;
754 break;
755
756 case AF_INET6:
757 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
758 (struct in6_addr *)daddr);
759 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
760 (struct in6_addr *)saddr);
761 x->sel.prefixlen_d = 128;
762 x->sel.prefixlen_s = 128;
763 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
764 (struct in6_addr *)saddr);
765 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
766 (struct in6_addr *)daddr);
767 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700768 }
David S. Miller27708342006-08-24 00:13:10 -0700769
770 x->km.state = XFRM_STATE_ACQ;
771 x->id.proto = proto;
772 x->props.family = family;
773 x->props.mode = mode;
774 x->props.reqid = reqid;
775 x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
776 xfrm_state_hold(x);
777 x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
778 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700779 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700780 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700781 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700782 wake_up(&km_waitq);
David S. Miller918049f2006-10-12 22:03:24 -0700783
784 xfrm_state_num++;
785
786 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -0700787 }
788
789 return x;
790}
791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
793
794int xfrm_state_add(struct xfrm_state *x)
795{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 struct xfrm_state *x1;
797 int family;
798 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700799 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803 spin_lock_bh(&xfrm_state_lock);
804
David S. Milleredcd5822006-08-24 00:42:45 -0700805 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (x1) {
807 xfrm_state_put(x1);
808 x1 = NULL;
809 err = -EEXIST;
810 goto out;
811 }
812
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700813 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -0700815 if (x1 && ((x1->id.proto != x->id.proto) ||
816 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 xfrm_state_put(x1);
818 x1 = NULL;
819 }
820 }
821
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700822 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700823 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
824 x->id.proto,
825 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
David S. Millerc7f5ea32006-08-24 03:29:04 -0700827 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 __xfrm_state_insert(x);
829 err = 0;
830
831out:
832 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
834 if (x1) {
835 xfrm_state_delete(x1);
836 xfrm_state_put(x1);
837 }
838
839 return err;
840}
841EXPORT_SYMBOL(xfrm_state_add);
842
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800843#ifdef CONFIG_XFRM_MIGRATE
844struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
845{
846 int err = -ENOMEM;
847 struct xfrm_state *x = xfrm_state_alloc();
848 if (!x)
849 goto error;
850
851 memcpy(&x->id, &orig->id, sizeof(x->id));
852 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
853 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
854 x->props.mode = orig->props.mode;
855 x->props.replay_window = orig->props.replay_window;
856 x->props.reqid = orig->props.reqid;
857 x->props.family = orig->props.family;
858 x->props.saddr = orig->props.saddr;
859
860 if (orig->aalg) {
861 x->aalg = xfrm_algo_clone(orig->aalg);
862 if (!x->aalg)
863 goto error;
864 }
865 x->props.aalgo = orig->props.aalgo;
866
867 if (orig->ealg) {
868 x->ealg = xfrm_algo_clone(orig->ealg);
869 if (!x->ealg)
870 goto error;
871 }
872 x->props.ealgo = orig->props.ealgo;
873
874 if (orig->calg) {
875 x->calg = xfrm_algo_clone(orig->calg);
876 if (!x->calg)
877 goto error;
878 }
879 x->props.calgo = orig->props.calgo;
880
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900881 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800882 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
883 if (!x->encap)
884 goto error;
885 }
886
887 if (orig->coaddr) {
888 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
889 GFP_KERNEL);
890 if (!x->coaddr)
891 goto error;
892 }
893
894 err = xfrm_init_state(x);
895 if (err)
896 goto error;
897
898 x->props.flags = orig->props.flags;
899
900 x->curlft.add_time = orig->curlft.add_time;
901 x->km.state = orig->km.state;
902 x->km.seq = orig->km.seq;
903
904 return x;
905
906 error:
907 if (errp)
908 *errp = err;
909 if (x) {
910 kfree(x->aalg);
911 kfree(x->ealg);
912 kfree(x->calg);
913 kfree(x->encap);
914 kfree(x->coaddr);
915 }
916 kfree(x);
917 return NULL;
918}
919EXPORT_SYMBOL(xfrm_state_clone);
920
921/* xfrm_state_lock is held */
922struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
923{
924 unsigned int h;
925 struct xfrm_state *x;
926 struct hlist_node *entry;
927
928 if (m->reqid) {
929 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
930 m->reqid, m->old_family);
931 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
932 if (x->props.mode != m->mode ||
933 x->id.proto != m->proto)
934 continue;
935 if (m->reqid && x->props.reqid != m->reqid)
936 continue;
937 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
938 m->old_family) ||
939 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
940 m->old_family))
941 continue;
942 xfrm_state_hold(x);
943 return x;
944 }
945 } else {
946 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
947 m->old_family);
948 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
949 if (x->props.mode != m->mode ||
950 x->id.proto != m->proto)
951 continue;
952 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
953 m->old_family) ||
954 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
955 m->old_family))
956 continue;
957 xfrm_state_hold(x);
958 return x;
959 }
960 }
961
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900962 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800963}
964EXPORT_SYMBOL(xfrm_migrate_state_find);
965
966struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
967 struct xfrm_migrate *m)
968{
969 struct xfrm_state *xc;
970 int err;
971
972 xc = xfrm_state_clone(x, &err);
973 if (!xc)
974 return NULL;
975
976 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
977 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
978
979 /* add state */
980 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
981 /* a care is needed when the destination address of the
982 state is to be updated as it is a part of triplet */
983 xfrm_state_insert(xc);
984 } else {
985 if ((err = xfrm_state_add(xc)) < 0)
986 goto error;
987 }
988
989 return xc;
990error:
991 kfree(xc);
992 return NULL;
993}
994EXPORT_SYMBOL(xfrm_state_migrate);
995#endif
996
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997int xfrm_state_update(struct xfrm_state *x)
998{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 struct xfrm_state *x1;
1000 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001001 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001004 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
1006 err = -ESRCH;
1007 if (!x1)
1008 goto out;
1009
1010 if (xfrm_state_kern(x1)) {
1011 xfrm_state_put(x1);
1012 err = -EEXIST;
1013 goto out;
1014 }
1015
1016 if (x1->km.state == XFRM_STATE_ACQ) {
1017 __xfrm_state_insert(x);
1018 x = NULL;
1019 }
1020 err = 0;
1021
1022out:
1023 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
1025 if (err)
1026 return err;
1027
1028 if (!x) {
1029 xfrm_state_delete(x1);
1030 xfrm_state_put(x1);
1031 return 0;
1032 }
1033
1034 err = -EINVAL;
1035 spin_lock_bh(&x1->lock);
1036 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1037 if (x->encap && x1->encap)
1038 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001039 if (x->coaddr && x1->coaddr) {
1040 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1041 }
1042 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1043 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1045 x1->km.dying = 0;
1046
David S. Millera47f0ce2006-08-24 03:54:22 -07001047 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 if (x1->curlft.use_time)
1049 xfrm_state_check_expire(x1);
1050
1051 err = 0;
1052 }
1053 spin_unlock_bh(&x1->lock);
1054
1055 xfrm_state_put(x1);
1056
1057 return err;
1058}
1059EXPORT_SYMBOL(xfrm_state_update);
1060
1061int xfrm_state_check_expire(struct xfrm_state *x)
1062{
1063 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001064 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066 if (x->km.state != XFRM_STATE_VALID)
1067 return -EINVAL;
1068
1069 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1070 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001071 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001072 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 return -EINVAL;
1074 }
1075
1076 if (!x->km.dying &&
1077 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001078 x->curlft.packets >= x->lft.soft_packet_limit)) {
1079 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001080 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001081 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return 0;
1083}
1084EXPORT_SYMBOL(xfrm_state_check_expire);
1085
1086static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1087{
1088 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1089 - skb_headroom(skb);
1090
1091 if (nhead > 0)
1092 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1093
1094 /* Check tail too... */
1095 return 0;
1096}
1097
1098int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1099{
1100 int err = xfrm_state_check_expire(x);
1101 if (err < 0)
1102 goto err;
1103 err = xfrm_state_check_space(x, skb);
1104err:
1105 return err;
1106}
1107EXPORT_SYMBOL(xfrm_state_check);
1108
1109struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001110xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 unsigned short family)
1112{
1113 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
1115 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001116 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 return x;
1119}
1120EXPORT_SYMBOL(xfrm_state_lookup);
1121
1122struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001123xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1124 u8 proto, unsigned short family)
1125{
1126 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001127
1128 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001129 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001130 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001131 return x;
1132}
1133EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1134
1135struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001136xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1137 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 int create, unsigned short family)
1139{
1140 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
1142 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001143 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001145
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 return x;
1147}
1148EXPORT_SYMBOL(xfrm_find_acq);
1149
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001150#ifdef CONFIG_XFRM_SUB_POLICY
1151int
1152xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1153 unsigned short family)
1154{
1155 int err = 0;
1156 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1157 if (!afinfo)
1158 return -EAFNOSUPPORT;
1159
1160 spin_lock_bh(&xfrm_state_lock);
1161 if (afinfo->tmpl_sort)
1162 err = afinfo->tmpl_sort(dst, src, n);
1163 spin_unlock_bh(&xfrm_state_lock);
1164 xfrm_state_put_afinfo(afinfo);
1165 return err;
1166}
1167EXPORT_SYMBOL(xfrm_tmpl_sort);
1168
1169int
1170xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1171 unsigned short family)
1172{
1173 int err = 0;
1174 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1175 if (!afinfo)
1176 return -EAFNOSUPPORT;
1177
1178 spin_lock_bh(&xfrm_state_lock);
1179 if (afinfo->state_sort)
1180 err = afinfo->state_sort(dst, src, n);
1181 spin_unlock_bh(&xfrm_state_lock);
1182 xfrm_state_put_afinfo(afinfo);
1183 return err;
1184}
1185EXPORT_SYMBOL(xfrm_state_sort);
1186#endif
1187
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188/* Silly enough, but I'm lazy to build resolution list */
1189
1190static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1191{
1192 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
David S. Millerf034b5d2006-08-24 03:08:07 -07001194 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001195 struct hlist_node *entry;
1196 struct xfrm_state *x;
1197
1198 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1199 if (x->km.seq == seq &&
1200 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 xfrm_state_hold(x);
1202 return x;
1203 }
1204 }
1205 }
1206 return NULL;
1207}
1208
1209struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1210{
1211 struct xfrm_state *x;
1212
1213 spin_lock_bh(&xfrm_state_lock);
1214 x = __xfrm_find_acq_byseq(seq);
1215 spin_unlock_bh(&xfrm_state_lock);
1216 return x;
1217}
1218EXPORT_SYMBOL(xfrm_find_acq_byseq);
1219
1220u32 xfrm_get_acqseq(void)
1221{
1222 u32 res;
1223 static u32 acqseq;
1224 static DEFINE_SPINLOCK(acqseq_lock);
1225
1226 spin_lock_bh(&acqseq_lock);
1227 res = (++acqseq ? : ++acqseq);
1228 spin_unlock_bh(&acqseq_lock);
1229 return res;
1230}
1231EXPORT_SYMBOL(xfrm_get_acqseq);
1232
1233void
Al Viro26977b42006-09-27 18:47:05 -07001234xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235{
David S. Millerf034b5d2006-08-24 03:08:07 -07001236 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 struct xfrm_state *x0;
1238
1239 if (x->id.spi)
1240 return;
1241
1242 if (minspi == maxspi) {
1243 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1244 if (x0) {
1245 xfrm_state_put(x0);
1246 return;
1247 }
1248 x->id.spi = minspi;
1249 } else {
1250 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001251 u32 low = ntohl(minspi);
1252 u32 high = ntohl(maxspi);
1253 for (h=0; h<high-low+1; h++) {
1254 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1256 if (x0 == NULL) {
1257 x->id.spi = htonl(spi);
1258 break;
1259 }
1260 xfrm_state_put(x0);
1261 }
1262 }
1263 if (x->id.spi) {
1264 spin_lock_bh(&xfrm_state_lock);
1265 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001266 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 spin_unlock_bh(&xfrm_state_lock);
1268 wake_up(&km_waitq);
1269 }
1270}
1271EXPORT_SYMBOL(xfrm_alloc_spi);
1272
1273int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1274 void *data)
1275{
1276 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001277 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001278 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 int count = 0;
1280 int err = 0;
1281
1282 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001283 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001284 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001285 if (!xfrm_id_proto_match(x->id.proto, proto))
1286 continue;
1287 if (last) {
1288 err = func(last, count, data);
1289 if (err)
1290 goto out;
1291 }
1292 last = x;
1293 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 }
1295 }
1296 if (count == 0) {
1297 err = -ENOENT;
1298 goto out;
1299 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001300 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301out:
1302 spin_unlock_bh(&xfrm_state_lock);
1303 return err;
1304}
1305EXPORT_SYMBOL(xfrm_state_walk);
1306
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001307
1308void xfrm_replay_notify(struct xfrm_state *x, int event)
1309{
1310 struct km_event c;
1311 /* we send notify messages in case
1312 * 1. we updated on of the sequence numbers, and the seqno difference
1313 * is at least x->replay_maxdiff, in this case we also update the
1314 * timeout of our timer function
1315 * 2. if x->replay_maxage has elapsed since last update,
1316 * and there were changes
1317 *
1318 * The state structure must be locked!
1319 */
1320
1321 switch (event) {
1322 case XFRM_REPLAY_UPDATE:
1323 if (x->replay_maxdiff &&
1324 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001325 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1326 if (x->xflags & XFRM_TIME_DEFER)
1327 event = XFRM_REPLAY_TIMEOUT;
1328 else
1329 return;
1330 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001331
1332 break;
1333
1334 case XFRM_REPLAY_TIMEOUT:
1335 if ((x->replay.seq == x->preplay.seq) &&
1336 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001337 (x->replay.oseq == x->preplay.oseq)) {
1338 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001339 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001340 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001341
1342 break;
1343 }
1344
1345 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1346 c.event = XFRM_MSG_NEWAE;
1347 c.data.aevent = event;
1348 km_state_notify(x, &c);
1349
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001350 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001351 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001352 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001353}
David S. Millera70fcb02006-03-20 19:18:52 -08001354EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001355
1356static void xfrm_replay_timer_handler(unsigned long data)
1357{
1358 struct xfrm_state *x = (struct xfrm_state*)data;
1359
1360 spin_lock(&x->lock);
1361
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001362 if (x->km.state == XFRM_STATE_VALID) {
1363 if (xfrm_aevent_is_on())
1364 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1365 else
1366 x->xflags |= XFRM_TIME_DEFER;
1367 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001368
1369 spin_unlock(&x->lock);
1370}
1371
Al Viroa252cc22006-09-27 18:48:18 -07001372int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373{
1374 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001375 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
1377 if (unlikely(seq == 0))
1378 return -EINVAL;
1379
1380 if (likely(seq > x->replay.seq))
1381 return 0;
1382
1383 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001384 if (diff >= min_t(unsigned int, x->props.replay_window,
1385 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 x->stats.replay_window++;
1387 return -EINVAL;
1388 }
1389
1390 if (x->replay.bitmap & (1U << diff)) {
1391 x->stats.replay++;
1392 return -EINVAL;
1393 }
1394 return 0;
1395}
1396EXPORT_SYMBOL(xfrm_replay_check);
1397
Al Viro61f46272006-09-27 18:48:33 -07001398void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399{
1400 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001401 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 if (seq > x->replay.seq) {
1404 diff = seq - x->replay.seq;
1405 if (diff < x->props.replay_window)
1406 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1407 else
1408 x->replay.bitmap = 1;
1409 x->replay.seq = seq;
1410 } else {
1411 diff = x->replay.seq - seq;
1412 x->replay.bitmap |= (1U << diff);
1413 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001414
1415 if (xfrm_aevent_is_on())
1416 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417}
1418EXPORT_SYMBOL(xfrm_replay_advance);
1419
1420static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1421static DEFINE_RWLOCK(xfrm_km_lock);
1422
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001423void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424{
1425 struct xfrm_mgr *km;
1426
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001427 read_lock(&xfrm_km_lock);
1428 list_for_each_entry(km, &xfrm_km_list, list)
1429 if (km->notify_policy)
1430 km->notify_policy(xp, dir, c);
1431 read_unlock(&xfrm_km_lock);
1432}
1433
1434void km_state_notify(struct xfrm_state *x, struct km_event *c)
1435{
1436 struct xfrm_mgr *km;
1437 read_lock(&xfrm_km_lock);
1438 list_for_each_entry(km, &xfrm_km_list, list)
1439 if (km->notify)
1440 km->notify(x, c);
1441 read_unlock(&xfrm_km_lock);
1442}
1443
1444EXPORT_SYMBOL(km_policy_notify);
1445EXPORT_SYMBOL(km_state_notify);
1446
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001447void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001448{
1449 struct km_event c;
1450
Herbert Xubf088672005-06-18 22:44:00 -07001451 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001452 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001453 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001454 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
1456 if (hard)
1457 wake_up(&km_waitq);
1458}
1459
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001460EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001461/*
1462 * We send to all registered managers regardless of failure
1463 * We are happy with one success
1464*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001465int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001467 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 struct xfrm_mgr *km;
1469
1470 read_lock(&xfrm_km_lock);
1471 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001472 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1473 if (!acqret)
1474 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 }
1476 read_unlock(&xfrm_km_lock);
1477 return err;
1478}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001479EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480
Al Viro5d36b182006-11-08 00:24:06 -08001481int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482{
1483 int err = -EINVAL;
1484 struct xfrm_mgr *km;
1485
1486 read_lock(&xfrm_km_lock);
1487 list_for_each_entry(km, &xfrm_km_list, list) {
1488 if (km->new_mapping)
1489 err = km->new_mapping(x, ipaddr, sport);
1490 if (!err)
1491 break;
1492 }
1493 read_unlock(&xfrm_km_lock);
1494 return err;
1495}
1496EXPORT_SYMBOL(km_new_mapping);
1497
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001498void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001500 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
Herbert Xubf088672005-06-18 22:44:00 -07001502 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001503 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001504 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001505 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 if (hard)
1508 wake_up(&km_waitq);
1509}
David S. Millera70fcb02006-03-20 19:18:52 -08001510EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001512int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1513 struct xfrm_migrate *m, int num_migrate)
1514{
1515 int err = -EINVAL;
1516 int ret;
1517 struct xfrm_mgr *km;
1518
1519 read_lock(&xfrm_km_lock);
1520 list_for_each_entry(km, &xfrm_km_list, list) {
1521 if (km->migrate) {
1522 ret = km->migrate(sel, dir, type, m, num_migrate);
1523 if (!ret)
1524 err = ret;
1525 }
1526 }
1527 read_unlock(&xfrm_km_lock);
1528 return err;
1529}
1530EXPORT_SYMBOL(km_migrate);
1531
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001532int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1533{
1534 int err = -EINVAL;
1535 int ret;
1536 struct xfrm_mgr *km;
1537
1538 read_lock(&xfrm_km_lock);
1539 list_for_each_entry(km, &xfrm_km_list, list) {
1540 if (km->report) {
1541 ret = km->report(proto, sel, addr);
1542 if (!ret)
1543 err = ret;
1544 }
1545 }
1546 read_unlock(&xfrm_km_lock);
1547 return err;
1548}
1549EXPORT_SYMBOL(km_report);
1550
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1552{
1553 int err;
1554 u8 *data;
1555 struct xfrm_mgr *km;
1556 struct xfrm_policy *pol = NULL;
1557
1558 if (optlen <= 0 || optlen > PAGE_SIZE)
1559 return -EMSGSIZE;
1560
1561 data = kmalloc(optlen, GFP_KERNEL);
1562 if (!data)
1563 return -ENOMEM;
1564
1565 err = -EFAULT;
1566 if (copy_from_user(data, optval, optlen))
1567 goto out;
1568
1569 err = -EINVAL;
1570 read_lock(&xfrm_km_lock);
1571 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001572 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 optlen, &err);
1574 if (err >= 0)
1575 break;
1576 }
1577 read_unlock(&xfrm_km_lock);
1578
1579 if (err >= 0) {
1580 xfrm_sk_policy_insert(sk, err, pol);
1581 xfrm_pol_put(pol);
1582 err = 0;
1583 }
1584
1585out:
1586 kfree(data);
1587 return err;
1588}
1589EXPORT_SYMBOL(xfrm_user_policy);
1590
1591int xfrm_register_km(struct xfrm_mgr *km)
1592{
1593 write_lock_bh(&xfrm_km_lock);
1594 list_add_tail(&km->list, &xfrm_km_list);
1595 write_unlock_bh(&xfrm_km_lock);
1596 return 0;
1597}
1598EXPORT_SYMBOL(xfrm_register_km);
1599
1600int xfrm_unregister_km(struct xfrm_mgr *km)
1601{
1602 write_lock_bh(&xfrm_km_lock);
1603 list_del(&km->list);
1604 write_unlock_bh(&xfrm_km_lock);
1605 return 0;
1606}
1607EXPORT_SYMBOL(xfrm_unregister_km);
1608
1609int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1610{
1611 int err = 0;
1612 if (unlikely(afinfo == NULL))
1613 return -EINVAL;
1614 if (unlikely(afinfo->family >= NPROTO))
1615 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001616 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1618 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001619 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001621 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 return err;
1623}
1624EXPORT_SYMBOL(xfrm_state_register_afinfo);
1625
1626int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1627{
1628 int err = 0;
1629 if (unlikely(afinfo == NULL))
1630 return -EINVAL;
1631 if (unlikely(afinfo->family >= NPROTO))
1632 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001633 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1635 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1636 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001637 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001640 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 return err;
1642}
1643EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1644
Miika Komucdca7262007-02-06 14:24:56 -08001645struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646{
1647 struct xfrm_state_afinfo *afinfo;
1648 if (unlikely(family >= NPROTO))
1649 return NULL;
1650 read_lock(&xfrm_state_afinfo_lock);
1651 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001652 if (unlikely(!afinfo))
1653 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 return afinfo;
1655}
1656
Miika Komucdca7262007-02-06 14:24:56 -08001657void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658{
Herbert Xu546be242006-05-27 23:03:58 -07001659 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660}
1661
Miika Komucdca7262007-02-06 14:24:56 -08001662EXPORT_SYMBOL(xfrm_state_get_afinfo);
1663EXPORT_SYMBOL(xfrm_state_put_afinfo);
1664
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1666void xfrm_state_delete_tunnel(struct xfrm_state *x)
1667{
1668 if (x->tunnel) {
1669 struct xfrm_state *t = x->tunnel;
1670
1671 if (atomic_read(&t->tunnel_users) == 2)
1672 xfrm_state_delete(t);
1673 atomic_dec(&t->tunnel_users);
1674 xfrm_state_put(t);
1675 x->tunnel = NULL;
1676 }
1677}
1678EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1679
1680int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1681{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001682 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Patrick McHardyc5c25232007-04-09 11:47:18 -07001684 spin_lock_bh(&x->lock);
1685 if (x->km.state == XFRM_STATE_VALID &&
1686 x->type && x->type->get_mtu)
1687 res = x->type->get_mtu(x, mtu);
1688 else
1689 res = mtu;
1690 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 return res;
1692}
1693
Herbert Xu72cb6962005-06-20 13:18:08 -07001694int xfrm_init_state(struct xfrm_state *x)
1695{
Herbert Xud094cd82005-06-20 13:19:41 -07001696 struct xfrm_state_afinfo *afinfo;
1697 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001698 int err;
1699
Herbert Xud094cd82005-06-20 13:19:41 -07001700 err = -EAFNOSUPPORT;
1701 afinfo = xfrm_state_get_afinfo(family);
1702 if (!afinfo)
1703 goto error;
1704
1705 err = 0;
1706 if (afinfo->init_flags)
1707 err = afinfo->init_flags(x);
1708
1709 xfrm_state_put_afinfo(afinfo);
1710
1711 if (err)
1712 goto error;
1713
1714 err = -EPROTONOSUPPORT;
1715 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001716 if (x->type == NULL)
1717 goto error;
1718
1719 err = x->type->init_state(x);
1720 if (err)
1721 goto error;
1722
Herbert Xub59f45d2006-05-27 23:05:54 -07001723 x->mode = xfrm_get_mode(x->props.mode, family);
1724 if (x->mode == NULL)
1725 goto error;
1726
Herbert Xu72cb6962005-06-20 13:18:08 -07001727 x->km.state = XFRM_STATE_VALID;
1728
1729error:
1730 return err;
1731}
1732
1733EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001734
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735void __init xfrm_state_init(void)
1736{
David S. Millerf034b5d2006-08-24 03:08:07 -07001737 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
David S. Millerf034b5d2006-08-24 03:08:07 -07001739 sz = sizeof(struct hlist_head) * 8;
1740
David S. Miller44e36b42006-08-24 04:50:50 -07001741 xfrm_state_bydst = xfrm_hash_alloc(sz);
1742 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1743 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001744 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1745 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1746 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1747
David Howellsc4028952006-11-22 14:57:56 +00001748 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749}
1750