blob: a00745abaad3d7db30691e4b70a5937288988881 [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>
Jesper Juhlb5890d82007-08-10 15:20:21 -070022#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
David S. Miller44e36b42006-08-24 04:50:50 -070024#include "xfrm_hash.h"
25
David S. Milleree857a72006-03-20 19:18:37 -080026struct sock *xfrm_nl;
27EXPORT_SYMBOL(xfrm_nl);
28
David S. Miller01e67d02007-05-25 00:41:38 -070029u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080030EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
31
David S. Miller01e67d02007-05-25 00:41:38 -070032u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080033EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
34
David S. Miller01e67d02007-05-25 00:41:38 -070035u32 sysctl_xfrm_acq_expires __read_mostly = 30;
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037/* Each xfrm_state may be linked to two tables:
38
39 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070040 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 destination/tunnel endpoint. (output)
42 */
43
44static DEFINE_SPINLOCK(xfrm_state_lock);
45
46/* Hash table to find appropriate SA towards given target (endpoint
47 * of tunnel or destination of transport mode) allowed by selector.
48 *
49 * Main use is finding SA after policy selected tunnel or transport mode.
50 * Also, it can be used by ah/esp icmp error handler to find offending SA.
51 */
David S. Millerf034b5d2006-08-24 03:08:07 -070052static struct hlist_head *xfrm_state_bydst __read_mostly;
53static struct hlist_head *xfrm_state_bysrc __read_mostly;
54static struct hlist_head *xfrm_state_byspi __read_mostly;
55static unsigned int xfrm_state_hmask __read_mostly;
56static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
57static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070058static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
David S. Millerc1969f22006-08-24 04:00:03 -070060static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
61 xfrm_address_t *saddr,
62 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070063 unsigned short family)
64{
David S. Millerc1969f22006-08-24 04:00:03 -070065 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070066}
67
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070068static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
69 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070070 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070071{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070072 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070073}
74
David S. Miller2575b652006-08-24 03:26:44 -070075static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070076xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070077{
David S. Millerc1969f22006-08-24 04:00:03 -070078 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070079}
80
David S. Millerf034b5d2006-08-24 03:08:07 -070081static void xfrm_hash_transfer(struct hlist_head *list,
82 struct hlist_head *ndsttable,
83 struct hlist_head *nsrctable,
84 struct hlist_head *nspitable,
85 unsigned int nhashmask)
86{
87 struct hlist_node *entry, *tmp;
88 struct xfrm_state *x;
89
90 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
91 unsigned int h;
92
David S. Millerc1969f22006-08-24 04:00:03 -070093 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
94 x->props.reqid, x->props.family,
95 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070096 hlist_add_head(&x->bydst, ndsttable+h);
97
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070098 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
99 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700100 nhashmask);
101 hlist_add_head(&x->bysrc, nsrctable+h);
102
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700103 if (x->id.spi) {
104 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
105 x->id.proto, x->props.family,
106 nhashmask);
107 hlist_add_head(&x->byspi, nspitable+h);
108 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700109 }
110}
111
112static unsigned long xfrm_hash_new_size(void)
113{
114 return ((xfrm_state_hmask + 1) << 1) *
115 sizeof(struct hlist_head);
116}
117
118static DEFINE_MUTEX(hash_resize_mutex);
119
David Howellsc4028952006-11-22 14:57:56 +0000120static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700121{
122 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
123 unsigned long nsize, osize;
124 unsigned int nhashmask, ohashmask;
125 int i;
126
127 mutex_lock(&hash_resize_mutex);
128
129 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700130 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700131 if (!ndst)
132 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700133 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700134 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700135 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700136 goto out_unlock;
137 }
David S. Miller44e36b42006-08-24 04:50:50 -0700138 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700139 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700140 xfrm_hash_free(ndst, nsize);
141 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700142 goto out_unlock;
143 }
144
145 spin_lock_bh(&xfrm_state_lock);
146
147 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
148 for (i = xfrm_state_hmask; i >= 0; i--)
149 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
150 nhashmask);
151
152 odst = xfrm_state_bydst;
153 osrc = xfrm_state_bysrc;
154 ospi = xfrm_state_byspi;
155 ohashmask = xfrm_state_hmask;
156
157 xfrm_state_bydst = ndst;
158 xfrm_state_bysrc = nsrc;
159 xfrm_state_byspi = nspi;
160 xfrm_state_hmask = nhashmask;
161
162 spin_unlock_bh(&xfrm_state_lock);
163
164 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700165 xfrm_hash_free(odst, osize);
166 xfrm_hash_free(osrc, osize);
167 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700168
169out_unlock:
170 mutex_unlock(&hash_resize_mutex);
171}
172
David Howellsc4028952006-11-22 14:57:56 +0000173static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175DECLARE_WAIT_QUEUE_HEAD(km_waitq);
176EXPORT_SYMBOL(km_waitq);
177
178static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
179static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
180
181static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700182static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183static DEFINE_SPINLOCK(xfrm_state_gc_lock);
184
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800185int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800187int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800188void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
190static void xfrm_state_gc_destroy(struct xfrm_state *x)
191{
David S. Millera47f0ce2006-08-24 03:54:22 -0700192 del_timer_sync(&x->timer);
193 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800194 kfree(x->aalg);
195 kfree(x->ealg);
196 kfree(x->calg);
197 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700198 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700199 if (x->mode)
200 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 if (x->type) {
202 x->type->destructor(x);
203 xfrm_put_type(x->type);
204 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800205 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 kfree(x);
207}
208
David Howellsc4028952006-11-22 14:57:56 +0000209static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
211 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700212 struct hlist_node *entry, *tmp;
213 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700216 gc_list.first = xfrm_state_gc_list.first;
217 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 spin_unlock_bh(&xfrm_state_gc_lock);
219
David S. Miller8f126e32006-08-24 02:45:07 -0700220 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 wake_up(&km_waitq);
224}
225
226static inline unsigned long make_jiffies(long secs)
227{
228 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
229 return MAX_SCHEDULE_TIMEOUT-1;
230 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900231 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232}
233
234static void xfrm_timer_handler(unsigned long data)
235{
236 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800237 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 long next = LONG_MAX;
239 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600240 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
242 spin_lock(&x->lock);
243 if (x->km.state == XFRM_STATE_DEAD)
244 goto out;
245 if (x->km.state == XFRM_STATE_EXPIRED)
246 goto expired;
247 if (x->lft.hard_add_expires_seconds) {
248 long tmo = x->lft.hard_add_expires_seconds +
249 x->curlft.add_time - now;
250 if (tmo <= 0)
251 goto expired;
252 if (tmo < next)
253 next = tmo;
254 }
255 if (x->lft.hard_use_expires_seconds) {
256 long tmo = x->lft.hard_use_expires_seconds +
257 (x->curlft.use_time ? : now) - now;
258 if (tmo <= 0)
259 goto expired;
260 if (tmo < next)
261 next = tmo;
262 }
263 if (x->km.dying)
264 goto resched;
265 if (x->lft.soft_add_expires_seconds) {
266 long tmo = x->lft.soft_add_expires_seconds +
267 x->curlft.add_time - now;
268 if (tmo <= 0)
269 warn = 1;
270 else if (tmo < next)
271 next = tmo;
272 }
273 if (x->lft.soft_use_expires_seconds) {
274 long tmo = x->lft.soft_use_expires_seconds +
275 (x->curlft.use_time ? : now) - now;
276 if (tmo <= 0)
277 warn = 1;
278 else if (tmo < next)
279 next = tmo;
280 }
281
Herbert Xu4666faa2005-06-18 22:43:22 -0700282 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800284 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700286 if (next != LONG_MAX)
287 mod_timer(&x->timer, jiffies + make_jiffies(next));
288
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 goto out;
290
291expired:
292 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
293 x->km.state = XFRM_STATE_EXPIRED;
294 wake_up(&km_waitq);
295 next = 2;
296 goto resched;
297 }
Joy Latten161a09e2006-11-27 13:11:54 -0600298
299 err = __xfrm_state_delete(x);
300 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800301 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Joy Lattenab5f5e82007-09-17 11:51:22 -0700303 xfrm_audit_state_delete(x, err ? 0 : 1,
304 audit_get_loginuid(current->audit_context), 0);
Joy Latten161a09e2006-11-27 13:11:54 -0600305
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306out:
307 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308}
309
David S. Miller0ac84752006-03-20 19:18:23 -0800310static void xfrm_replay_timer_handler(unsigned long data);
311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312struct xfrm_state *xfrm_state_alloc(void)
313{
314 struct xfrm_state *x;
315
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700316 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 atomic_set(&x->refcnt, 1);
320 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700321 INIT_HLIST_NODE(&x->bydst);
322 INIT_HLIST_NODE(&x->bysrc);
323 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 init_timer(&x->timer);
325 x->timer.function = xfrm_timer_handler;
326 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800327 init_timer(&x->rtimer);
328 x->rtimer.function = xfrm_replay_timer_handler;
329 x->rtimer.data = (unsigned long)x;
James Morris9d729f72007-03-04 16:12:44 -0800330 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 x->lft.soft_byte_limit = XFRM_INF;
332 x->lft.soft_packet_limit = XFRM_INF;
333 x->lft.hard_byte_limit = XFRM_INF;
334 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800335 x->replay_maxage = 0;
336 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 spin_lock_init(&x->lock);
338 }
339 return x;
340}
341EXPORT_SYMBOL(xfrm_state_alloc);
342
343void __xfrm_state_destroy(struct xfrm_state *x)
344{
345 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
346
347 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700348 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 spin_unlock_bh(&xfrm_state_gc_lock);
350 schedule_work(&xfrm_state_gc_work);
351}
352EXPORT_SYMBOL(__xfrm_state_destroy);
353
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800354int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700356 int err = -ESRCH;
357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 if (x->km.state != XFRM_STATE_DEAD) {
359 x->km.state = XFRM_STATE_DEAD;
360 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700361 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700362 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700363 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700364 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700365 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 /* All xfrm_state objects are created by xfrm_state_alloc.
369 * The xfrm_state_alloc call gives a reference, and that
370 * is what we are dropping here.
371 */
Herbert Xu21380b82006-02-22 14:47:13 -0800372 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700373 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700375
376 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800378EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700380int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700382 int err;
383
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700385 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700387
388 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389}
390EXPORT_SYMBOL(xfrm_state_delete);
391
Joy Latten4aa2e622007-06-04 19:05:57 -0400392#ifdef CONFIG_SECURITY_NETWORK_XFRM
393static inline int
394xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
Joy Latten4aa2e622007-06-04 19:05:57 -0400396 int i, err = 0;
397
398 for (i = 0; i <= xfrm_state_hmask; i++) {
399 struct hlist_node *entry;
400 struct xfrm_state *x;
401
402 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
403 if (xfrm_id_proto_match(x->id.proto, proto) &&
404 (err = security_xfrm_state_delete(x)) != 0) {
Joy Lattenab5f5e82007-09-17 11:51:22 -0700405 xfrm_audit_state_delete(x, 0,
406 audit_info->loginuid,
407 audit_info->secid);
Joy Latten4aa2e622007-06-04 19:05:57 -0400408 return err;
409 }
410 }
411 }
412
413 return err;
414}
415#else
416static inline int
417xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
418{
419 return 0;
420}
421#endif
422
423int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
424{
425 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
427 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400428 err = xfrm_state_flush_secctx_check(proto, audit_info);
429 if (err)
430 goto out;
431
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700432 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700433 struct hlist_node *entry;
434 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700436 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700438 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 xfrm_state_hold(x);
440 spin_unlock_bh(&xfrm_state_lock);
441
Joy Latten161a09e2006-11-27 13:11:54 -0600442 err = xfrm_state_delete(x);
Joy Lattenab5f5e82007-09-17 11:51:22 -0700443 xfrm_audit_state_delete(x, err ? 0 : 1,
444 audit_info->loginuid,
445 audit_info->secid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 xfrm_state_put(x);
447
448 spin_lock_bh(&xfrm_state_lock);
449 goto restart;
450 }
451 }
452 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400453 err = 0;
454
455out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 spin_unlock_bh(&xfrm_state_lock);
457 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400458 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460EXPORT_SYMBOL(xfrm_state_flush);
461
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700462void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700463{
464 spin_lock_bh(&xfrm_state_lock);
465 si->sadcnt = xfrm_state_num;
466 si->sadhcnt = xfrm_state_hmask;
467 si->sadhmcnt = xfrm_state_hashmax;
468 spin_unlock_bh(&xfrm_state_lock);
469}
470EXPORT_SYMBOL(xfrm_sad_getinfo);
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472static int
473xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
474 struct xfrm_tmpl *tmpl,
475 xfrm_address_t *daddr, xfrm_address_t *saddr,
476 unsigned short family)
477{
478 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
479 if (!afinfo)
480 return -1;
481 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
482 xfrm_state_put_afinfo(afinfo);
483 return 0;
484}
485
Al Viroa94cfd12006-09-27 18:47:24 -0700486static 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 -0700487{
488 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
489 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700490 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700491
David S. Miller8f126e32006-08-24 02:45:07 -0700492 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700493 if (x->props.family != family ||
494 x->id.spi != spi ||
495 x->id.proto != proto)
496 continue;
497
498 switch (family) {
499 case AF_INET:
500 if (x->id.daddr.a4 != daddr->a4)
501 continue;
502 break;
503 case AF_INET6:
504 if (!ipv6_addr_equal((struct in6_addr *)daddr,
505 (struct in6_addr *)
506 x->id.daddr.a6))
507 continue;
508 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700509 }
David S. Milleredcd5822006-08-24 00:42:45 -0700510
511 xfrm_state_hold(x);
512 return x;
513 }
514
515 return NULL;
516}
517
518static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
519{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700520 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700521 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700522 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700523
David S. Miller8f126e32006-08-24 02:45:07 -0700524 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700525 if (x->props.family != family ||
526 x->id.proto != proto)
527 continue;
528
529 switch (family) {
530 case AF_INET:
531 if (x->id.daddr.a4 != daddr->a4 ||
532 x->props.saddr.a4 != saddr->a4)
533 continue;
534 break;
535 case AF_INET6:
536 if (!ipv6_addr_equal((struct in6_addr *)daddr,
537 (struct in6_addr *)
538 x->id.daddr.a6) ||
539 !ipv6_addr_equal((struct in6_addr *)saddr,
540 (struct in6_addr *)
541 x->props.saddr.a6))
542 continue;
543 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700544 }
David S. Milleredcd5822006-08-24 00:42:45 -0700545
546 xfrm_state_hold(x);
547 return x;
548 }
549
550 return NULL;
551}
552
553static inline struct xfrm_state *
554__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
555{
556 if (use_spi)
557 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
558 x->id.proto, family);
559 else
560 return __xfrm_state_lookup_byaddr(&x->id.daddr,
561 &x->props.saddr,
562 x->id.proto, family);
563}
564
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700565static void xfrm_hash_grow_check(int have_hash_collision)
566{
567 if (have_hash_collision &&
568 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
569 xfrm_state_num > xfrm_state_hmask)
570 schedule_work(&xfrm_hash_work);
571}
572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900574xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 struct flowi *fl, struct xfrm_tmpl *tmpl,
576 struct xfrm_policy *pol, int *err,
577 unsigned short family)
578{
David S. Millerc1969f22006-08-24 04:00:03 -0700579 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700580 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 struct xfrm_state *x, *x0;
582 int acquire_in_progress = 0;
583 int error = 0;
584 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700587 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 if (x->props.family == family &&
589 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700590 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 xfrm_state_addr_check(x, daddr, saddr, family) &&
592 tmpl->mode == x->props.mode &&
593 tmpl->id.proto == x->id.proto &&
594 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
595 /* Resolution logic:
596 1. There is a valid state with matching selector.
597 Done.
598 2. Valid state with inappropriate selector. Skip.
599
600 Entering area of "sysdeps".
601
602 3. If state is not valid, selector is temporary,
603 it selects only session which triggered
604 previous resolution. Key manager will do
605 something to install a state with proper
606 selector.
607 */
608 if (x->km.state == XFRM_STATE_VALID) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700609 if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700610 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 continue;
612 if (!best ||
613 best->km.dying > x->km.dying ||
614 (best->km.dying == x->km.dying &&
615 best->curlft.add_time < x->curlft.add_time))
616 best = x;
617 } else if (x->km.state == XFRM_STATE_ACQ) {
618 acquire_in_progress = 1;
619 } else if (x->km.state == XFRM_STATE_ERROR ||
620 x->km.state == XFRM_STATE_EXPIRED) {
Joakim Koskela48b8d782007-07-26 00:08:42 -0700621 if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700622 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 error = -ESRCH;
624 }
625 }
626 }
627
628 x = best;
629 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700630 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700631 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
632 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 xfrm_state_put(x0);
634 error = -EEXIST;
635 goto out;
636 }
637 x = xfrm_state_alloc();
638 if (x == NULL) {
639 error = -ENOMEM;
640 goto out;
641 }
642 /* Initialize temporary selector matching only
643 * to current session. */
644 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
645
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700646 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
647 if (error) {
648 x->km.state = XFRM_STATE_DEAD;
649 xfrm_state_put(x);
650 x = NULL;
651 goto out;
652 }
653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 if (km_query(x, tmpl, pol) == 0) {
655 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700656 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700657 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700658 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 if (x->id.spi) {
660 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700661 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
David S. Miller01e67d02007-05-25 00:41:38 -0700663 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
664 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700666 xfrm_state_num++;
667 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 } else {
669 x->km.state = XFRM_STATE_DEAD;
670 xfrm_state_put(x);
671 x = NULL;
672 error = -ESRCH;
673 }
674 }
675out:
676 if (x)
677 xfrm_state_hold(x);
678 else
679 *err = acquire_in_progress ? -EAGAIN : error;
680 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return x;
682}
683
Jamal Hadi Salim628529b2007-07-02 22:41:14 -0700684struct xfrm_state *
685xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
686 unsigned short family, u8 mode, u8 proto, u32 reqid)
687{
688 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
689 struct xfrm_state *rx = NULL, *x = NULL;
690 struct hlist_node *entry;
691
692 spin_lock(&xfrm_state_lock);
693 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
694 if (x->props.family == family &&
695 x->props.reqid == reqid &&
696 !(x->props.flags & XFRM_STATE_WILDRECV) &&
697 xfrm_state_addr_check(x, daddr, saddr, family) &&
698 mode == x->props.mode &&
699 proto == x->id.proto &&
700 x->km.state == XFRM_STATE_VALID) {
701 rx = x;
702 break;
703 }
704 }
705
706 if (rx)
707 xfrm_state_hold(rx);
708 spin_unlock(&xfrm_state_lock);
709
710
711 return rx;
712}
713EXPORT_SYMBOL(xfrm_stateonly_find);
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715static void __xfrm_state_insert(struct xfrm_state *x)
716{
David S. Millera624c102006-08-24 03:24:33 -0700717 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
David S. Miller9d4a7062006-08-24 03:18:09 -0700719 x->genid = ++xfrm_state_genid;
720
David S. Millerc1969f22006-08-24 04:00:03 -0700721 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
722 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700723 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700725 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700726 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700728 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700729 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
730 x->props.family);
731
David S. Miller8f126e32006-08-24 02:45:07 -0700732 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700733 }
734
David S. Millera47f0ce2006-08-24 03:54:22 -0700735 mod_timer(&x->timer, jiffies + HZ);
736 if (x->replay_maxage)
737 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700740
741 xfrm_state_num++;
742
David S. Miller918049f2006-10-12 22:03:24 -0700743 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744}
745
David S. Millerc7f5ea32006-08-24 03:29:04 -0700746/* xfrm_state_lock is held */
747static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
748{
749 unsigned short family = xnew->props.family;
750 u32 reqid = xnew->props.reqid;
751 struct xfrm_state *x;
752 struct hlist_node *entry;
753 unsigned int h;
754
David S. Millerc1969f22006-08-24 04:00:03 -0700755 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700756 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
757 if (x->props.family == family &&
758 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700759 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
760 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700761 x->genid = xfrm_state_genid;
762 }
763}
764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765void xfrm_state_insert(struct xfrm_state *x)
766{
767 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700768 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 __xfrm_state_insert(x);
770 spin_unlock_bh(&xfrm_state_lock);
771}
772EXPORT_SYMBOL(xfrm_state_insert);
773
David S. Miller27708342006-08-24 00:13:10 -0700774/* xfrm_state_lock is held */
775static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
776{
David S. Millerc1969f22006-08-24 04:00:03 -0700777 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700778 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700779 struct xfrm_state *x;
780
David S. Miller8f126e32006-08-24 02:45:07 -0700781 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700782 if (x->props.reqid != reqid ||
783 x->props.mode != mode ||
784 x->props.family != family ||
785 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700786 x->id.spi != 0 ||
787 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700788 continue;
789
790 switch (family) {
791 case AF_INET:
792 if (x->id.daddr.a4 != daddr->a4 ||
793 x->props.saddr.a4 != saddr->a4)
794 continue;
795 break;
796 case AF_INET6:
797 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
798 (struct in6_addr *)daddr) ||
799 !ipv6_addr_equal((struct in6_addr *)
800 x->props.saddr.a6,
801 (struct in6_addr *)saddr))
802 continue;
803 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700804 }
David S. Miller27708342006-08-24 00:13:10 -0700805
806 xfrm_state_hold(x);
807 return x;
808 }
809
810 if (!create)
811 return NULL;
812
813 x = xfrm_state_alloc();
814 if (likely(x)) {
815 switch (family) {
816 case AF_INET:
817 x->sel.daddr.a4 = daddr->a4;
818 x->sel.saddr.a4 = saddr->a4;
819 x->sel.prefixlen_d = 32;
820 x->sel.prefixlen_s = 32;
821 x->props.saddr.a4 = saddr->a4;
822 x->id.daddr.a4 = daddr->a4;
823 break;
824
825 case AF_INET6:
826 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
827 (struct in6_addr *)daddr);
828 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
829 (struct in6_addr *)saddr);
830 x->sel.prefixlen_d = 128;
831 x->sel.prefixlen_s = 128;
832 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
833 (struct in6_addr *)saddr);
834 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
835 (struct in6_addr *)daddr);
836 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700837 }
David S. Miller27708342006-08-24 00:13:10 -0700838
839 x->km.state = XFRM_STATE_ACQ;
840 x->id.proto = proto;
841 x->props.family = family;
842 x->props.mode = mode;
843 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -0700844 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -0700845 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -0700846 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -0700847 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700848 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700849 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700850 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700851 wake_up(&km_waitq);
David S. Miller918049f2006-10-12 22:03:24 -0700852
853 xfrm_state_num++;
854
855 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -0700856 }
857
858 return x;
859}
860
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
862
863int xfrm_state_add(struct xfrm_state *x)
864{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 struct xfrm_state *x1;
866 int family;
867 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700868 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
870 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
872 spin_lock_bh(&xfrm_state_lock);
873
David S. Milleredcd5822006-08-24 00:42:45 -0700874 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 if (x1) {
876 xfrm_state_put(x1);
877 x1 = NULL;
878 err = -EEXIST;
879 goto out;
880 }
881
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700882 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -0700884 if (x1 && ((x1->id.proto != x->id.proto) ||
885 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 xfrm_state_put(x1);
887 x1 = NULL;
888 }
889 }
890
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700891 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700892 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
893 x->id.proto,
894 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
David S. Millerc7f5ea32006-08-24 03:29:04 -0700896 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 __xfrm_state_insert(x);
898 err = 0;
899
900out:
901 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
903 if (x1) {
904 xfrm_state_delete(x1);
905 xfrm_state_put(x1);
906 }
907
908 return err;
909}
910EXPORT_SYMBOL(xfrm_state_add);
911
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800912#ifdef CONFIG_XFRM_MIGRATE
913struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
914{
915 int err = -ENOMEM;
916 struct xfrm_state *x = xfrm_state_alloc();
917 if (!x)
918 goto error;
919
920 memcpy(&x->id, &orig->id, sizeof(x->id));
921 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
922 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
923 x->props.mode = orig->props.mode;
924 x->props.replay_window = orig->props.replay_window;
925 x->props.reqid = orig->props.reqid;
926 x->props.family = orig->props.family;
927 x->props.saddr = orig->props.saddr;
928
929 if (orig->aalg) {
930 x->aalg = xfrm_algo_clone(orig->aalg);
931 if (!x->aalg)
932 goto error;
933 }
934 x->props.aalgo = orig->props.aalgo;
935
936 if (orig->ealg) {
937 x->ealg = xfrm_algo_clone(orig->ealg);
938 if (!x->ealg)
939 goto error;
940 }
941 x->props.ealgo = orig->props.ealgo;
942
943 if (orig->calg) {
944 x->calg = xfrm_algo_clone(orig->calg);
945 if (!x->calg)
946 goto error;
947 }
948 x->props.calgo = orig->props.calgo;
949
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900950 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800951 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
952 if (!x->encap)
953 goto error;
954 }
955
956 if (orig->coaddr) {
957 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
958 GFP_KERNEL);
959 if (!x->coaddr)
960 goto error;
961 }
962
963 err = xfrm_init_state(x);
964 if (err)
965 goto error;
966
967 x->props.flags = orig->props.flags;
968
969 x->curlft.add_time = orig->curlft.add_time;
970 x->km.state = orig->km.state;
971 x->km.seq = orig->km.seq;
972
973 return x;
974
975 error:
976 if (errp)
977 *errp = err;
978 if (x) {
979 kfree(x->aalg);
980 kfree(x->ealg);
981 kfree(x->calg);
982 kfree(x->encap);
983 kfree(x->coaddr);
984 }
985 kfree(x);
986 return NULL;
987}
988EXPORT_SYMBOL(xfrm_state_clone);
989
990/* xfrm_state_lock is held */
991struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
992{
993 unsigned int h;
994 struct xfrm_state *x;
995 struct hlist_node *entry;
996
997 if (m->reqid) {
998 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
999 m->reqid, m->old_family);
1000 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
1001 if (x->props.mode != m->mode ||
1002 x->id.proto != m->proto)
1003 continue;
1004 if (m->reqid && x->props.reqid != m->reqid)
1005 continue;
1006 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1007 m->old_family) ||
1008 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1009 m->old_family))
1010 continue;
1011 xfrm_state_hold(x);
1012 return x;
1013 }
1014 } else {
1015 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
1016 m->old_family);
1017 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
1018 if (x->props.mode != m->mode ||
1019 x->id.proto != m->proto)
1020 continue;
1021 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
1022 m->old_family) ||
1023 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
1024 m->old_family))
1025 continue;
1026 xfrm_state_hold(x);
1027 return x;
1028 }
1029 }
1030
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001031 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001032}
1033EXPORT_SYMBOL(xfrm_migrate_state_find);
1034
1035struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1036 struct xfrm_migrate *m)
1037{
1038 struct xfrm_state *xc;
1039 int err;
1040
1041 xc = xfrm_state_clone(x, &err);
1042 if (!xc)
1043 return NULL;
1044
1045 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1046 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1047
1048 /* add state */
1049 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1050 /* a care is needed when the destination address of the
1051 state is to be updated as it is a part of triplet */
1052 xfrm_state_insert(xc);
1053 } else {
1054 if ((err = xfrm_state_add(xc)) < 0)
1055 goto error;
1056 }
1057
1058 return xc;
1059error:
1060 kfree(xc);
1061 return NULL;
1062}
1063EXPORT_SYMBOL(xfrm_state_migrate);
1064#endif
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066int xfrm_state_update(struct xfrm_state *x)
1067{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 struct xfrm_state *x1;
1069 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001070 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001073 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075 err = -ESRCH;
1076 if (!x1)
1077 goto out;
1078
1079 if (xfrm_state_kern(x1)) {
1080 xfrm_state_put(x1);
1081 err = -EEXIST;
1082 goto out;
1083 }
1084
1085 if (x1->km.state == XFRM_STATE_ACQ) {
1086 __xfrm_state_insert(x);
1087 x = NULL;
1088 }
1089 err = 0;
1090
1091out:
1092 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
1094 if (err)
1095 return err;
1096
1097 if (!x) {
1098 xfrm_state_delete(x1);
1099 xfrm_state_put(x1);
1100 return 0;
1101 }
1102
1103 err = -EINVAL;
1104 spin_lock_bh(&x1->lock);
1105 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1106 if (x->encap && x1->encap)
1107 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001108 if (x->coaddr && x1->coaddr) {
1109 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1110 }
1111 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1112 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1114 x1->km.dying = 0;
1115
David S. Millera47f0ce2006-08-24 03:54:22 -07001116 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 if (x1->curlft.use_time)
1118 xfrm_state_check_expire(x1);
1119
1120 err = 0;
1121 }
1122 spin_unlock_bh(&x1->lock);
1123
1124 xfrm_state_put(x1);
1125
1126 return err;
1127}
1128EXPORT_SYMBOL(xfrm_state_update);
1129
1130int xfrm_state_check_expire(struct xfrm_state *x)
1131{
1132 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001133 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134
1135 if (x->km.state != XFRM_STATE_VALID)
1136 return -EINVAL;
1137
1138 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1139 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001140 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001141 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return -EINVAL;
1143 }
1144
1145 if (!x->km.dying &&
1146 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001147 x->curlft.packets >= x->lft.soft_packet_limit)) {
1148 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001149 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001150 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 return 0;
1152}
1153EXPORT_SYMBOL(xfrm_state_check_expire);
1154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001156xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 unsigned short family)
1158{
1159 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001162 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 return x;
1165}
1166EXPORT_SYMBOL(xfrm_state_lookup);
1167
1168struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001169xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1170 u8 proto, unsigned short family)
1171{
1172 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001173
1174 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001175 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001176 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001177 return x;
1178}
1179EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1180
1181struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001182xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1183 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 int create, unsigned short family)
1185{
1186 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187
1188 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001189 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 return x;
1193}
1194EXPORT_SYMBOL(xfrm_find_acq);
1195
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001196#ifdef CONFIG_XFRM_SUB_POLICY
1197int
1198xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1199 unsigned short family)
1200{
1201 int err = 0;
1202 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1203 if (!afinfo)
1204 return -EAFNOSUPPORT;
1205
1206 spin_lock_bh(&xfrm_state_lock);
1207 if (afinfo->tmpl_sort)
1208 err = afinfo->tmpl_sort(dst, src, n);
1209 spin_unlock_bh(&xfrm_state_lock);
1210 xfrm_state_put_afinfo(afinfo);
1211 return err;
1212}
1213EXPORT_SYMBOL(xfrm_tmpl_sort);
1214
1215int
1216xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1217 unsigned short family)
1218{
1219 int err = 0;
1220 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1221 if (!afinfo)
1222 return -EAFNOSUPPORT;
1223
1224 spin_lock_bh(&xfrm_state_lock);
1225 if (afinfo->state_sort)
1226 err = afinfo->state_sort(dst, src, n);
1227 spin_unlock_bh(&xfrm_state_lock);
1228 xfrm_state_put_afinfo(afinfo);
1229 return err;
1230}
1231EXPORT_SYMBOL(xfrm_state_sort);
1232#endif
1233
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234/* Silly enough, but I'm lazy to build resolution list */
1235
1236static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1237{
1238 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
David S. Millerf034b5d2006-08-24 03:08:07 -07001240 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001241 struct hlist_node *entry;
1242 struct xfrm_state *x;
1243
1244 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1245 if (x->km.seq == seq &&
1246 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 xfrm_state_hold(x);
1248 return x;
1249 }
1250 }
1251 }
1252 return NULL;
1253}
1254
1255struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1256{
1257 struct xfrm_state *x;
1258
1259 spin_lock_bh(&xfrm_state_lock);
1260 x = __xfrm_find_acq_byseq(seq);
1261 spin_unlock_bh(&xfrm_state_lock);
1262 return x;
1263}
1264EXPORT_SYMBOL(xfrm_find_acq_byseq);
1265
1266u32 xfrm_get_acqseq(void)
1267{
1268 u32 res;
1269 static u32 acqseq;
1270 static DEFINE_SPINLOCK(acqseq_lock);
1271
1272 spin_lock_bh(&acqseq_lock);
1273 res = (++acqseq ? : ++acqseq);
1274 spin_unlock_bh(&acqseq_lock);
1275 return res;
1276}
1277EXPORT_SYMBOL(xfrm_get_acqseq);
1278
1279void
Al Viro26977b42006-09-27 18:47:05 -07001280xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281{
David S. Millerf034b5d2006-08-24 03:08:07 -07001282 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 struct xfrm_state *x0;
1284
1285 if (x->id.spi)
1286 return;
1287
1288 if (minspi == maxspi) {
1289 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1290 if (x0) {
1291 xfrm_state_put(x0);
1292 return;
1293 }
1294 x->id.spi = minspi;
1295 } else {
1296 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001297 u32 low = ntohl(minspi);
1298 u32 high = ntohl(maxspi);
1299 for (h=0; h<high-low+1; h++) {
1300 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1302 if (x0 == NULL) {
1303 x->id.spi = htonl(spi);
1304 break;
1305 }
1306 xfrm_state_put(x0);
1307 }
1308 }
1309 if (x->id.spi) {
1310 spin_lock_bh(&xfrm_state_lock);
1311 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001312 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 spin_unlock_bh(&xfrm_state_lock);
1314 wake_up(&km_waitq);
1315 }
1316}
1317EXPORT_SYMBOL(xfrm_alloc_spi);
1318
1319int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1320 void *data)
1321{
1322 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001323 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001324 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 int count = 0;
1326 int err = 0;
1327
1328 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001329 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001330 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001331 if (!xfrm_id_proto_match(x->id.proto, proto))
1332 continue;
1333 if (last) {
1334 err = func(last, count, data);
1335 if (err)
1336 goto out;
1337 }
1338 last = x;
1339 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 }
1341 }
1342 if (count == 0) {
1343 err = -ENOENT;
1344 goto out;
1345 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001346 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347out:
1348 spin_unlock_bh(&xfrm_state_lock);
1349 return err;
1350}
1351EXPORT_SYMBOL(xfrm_state_walk);
1352
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001353
1354void xfrm_replay_notify(struct xfrm_state *x, int event)
1355{
1356 struct km_event c;
1357 /* we send notify messages in case
1358 * 1. we updated on of the sequence numbers, and the seqno difference
1359 * is at least x->replay_maxdiff, in this case we also update the
1360 * timeout of our timer function
1361 * 2. if x->replay_maxage has elapsed since last update,
1362 * and there were changes
1363 *
1364 * The state structure must be locked!
1365 */
1366
1367 switch (event) {
1368 case XFRM_REPLAY_UPDATE:
1369 if (x->replay_maxdiff &&
1370 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001371 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1372 if (x->xflags & XFRM_TIME_DEFER)
1373 event = XFRM_REPLAY_TIMEOUT;
1374 else
1375 return;
1376 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001377
1378 break;
1379
1380 case XFRM_REPLAY_TIMEOUT:
1381 if ((x->replay.seq == x->preplay.seq) &&
1382 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001383 (x->replay.oseq == x->preplay.oseq)) {
1384 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001385 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001386 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001387
1388 break;
1389 }
1390
1391 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1392 c.event = XFRM_MSG_NEWAE;
1393 c.data.aevent = event;
1394 km_state_notify(x, &c);
1395
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001396 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001397 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001398 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001399}
1400
1401static void xfrm_replay_timer_handler(unsigned long data)
1402{
1403 struct xfrm_state *x = (struct xfrm_state*)data;
1404
1405 spin_lock(&x->lock);
1406
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001407 if (x->km.state == XFRM_STATE_VALID) {
1408 if (xfrm_aevent_is_on())
1409 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1410 else
1411 x->xflags |= XFRM_TIME_DEFER;
1412 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001413
1414 spin_unlock(&x->lock);
1415}
1416
Al Viroa252cc22006-09-27 18:48:18 -07001417int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418{
1419 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001420 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 if (unlikely(seq == 0))
1423 return -EINVAL;
1424
1425 if (likely(seq > x->replay.seq))
1426 return 0;
1427
1428 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001429 if (diff >= min_t(unsigned int, x->props.replay_window,
1430 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 x->stats.replay_window++;
1432 return -EINVAL;
1433 }
1434
1435 if (x->replay.bitmap & (1U << diff)) {
1436 x->stats.replay++;
1437 return -EINVAL;
1438 }
1439 return 0;
1440}
1441EXPORT_SYMBOL(xfrm_replay_check);
1442
Al Viro61f46272006-09-27 18:48:33 -07001443void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444{
1445 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001446 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
1448 if (seq > x->replay.seq) {
1449 diff = seq - x->replay.seq;
1450 if (diff < x->props.replay_window)
1451 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1452 else
1453 x->replay.bitmap = 1;
1454 x->replay.seq = seq;
1455 } else {
1456 diff = x->replay.seq - seq;
1457 x->replay.bitmap |= (1U << diff);
1458 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001459
1460 if (xfrm_aevent_is_on())
1461 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462}
1463EXPORT_SYMBOL(xfrm_replay_advance);
1464
1465static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1466static DEFINE_RWLOCK(xfrm_km_lock);
1467
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001468void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
1470 struct xfrm_mgr *km;
1471
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001472 read_lock(&xfrm_km_lock);
1473 list_for_each_entry(km, &xfrm_km_list, list)
1474 if (km->notify_policy)
1475 km->notify_policy(xp, dir, c);
1476 read_unlock(&xfrm_km_lock);
1477}
1478
1479void km_state_notify(struct xfrm_state *x, struct km_event *c)
1480{
1481 struct xfrm_mgr *km;
1482 read_lock(&xfrm_km_lock);
1483 list_for_each_entry(km, &xfrm_km_list, list)
1484 if (km->notify)
1485 km->notify(x, c);
1486 read_unlock(&xfrm_km_lock);
1487}
1488
1489EXPORT_SYMBOL(km_policy_notify);
1490EXPORT_SYMBOL(km_state_notify);
1491
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001492void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001493{
1494 struct km_event c;
1495
Herbert Xubf088672005-06-18 22:44:00 -07001496 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001497 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001498 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001499 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 if (hard)
1502 wake_up(&km_waitq);
1503}
1504
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001505EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001506/*
1507 * We send to all registered managers regardless of failure
1508 * We are happy with one success
1509*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001510int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001512 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 struct xfrm_mgr *km;
1514
1515 read_lock(&xfrm_km_lock);
1516 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001517 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1518 if (!acqret)
1519 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 }
1521 read_unlock(&xfrm_km_lock);
1522 return err;
1523}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001524EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Al Viro5d36b182006-11-08 00:24:06 -08001526int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527{
1528 int err = -EINVAL;
1529 struct xfrm_mgr *km;
1530
1531 read_lock(&xfrm_km_lock);
1532 list_for_each_entry(km, &xfrm_km_list, list) {
1533 if (km->new_mapping)
1534 err = km->new_mapping(x, ipaddr, sport);
1535 if (!err)
1536 break;
1537 }
1538 read_unlock(&xfrm_km_lock);
1539 return err;
1540}
1541EXPORT_SYMBOL(km_new_mapping);
1542
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001543void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001545 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
Herbert Xubf088672005-06-18 22:44:00 -07001547 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001548 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001549 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001550 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
1552 if (hard)
1553 wake_up(&km_waitq);
1554}
David S. Millera70fcb02006-03-20 19:18:52 -08001555EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001557int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1558 struct xfrm_migrate *m, int num_migrate)
1559{
1560 int err = -EINVAL;
1561 int ret;
1562 struct xfrm_mgr *km;
1563
1564 read_lock(&xfrm_km_lock);
1565 list_for_each_entry(km, &xfrm_km_list, list) {
1566 if (km->migrate) {
1567 ret = km->migrate(sel, dir, type, m, num_migrate);
1568 if (!ret)
1569 err = ret;
1570 }
1571 }
1572 read_unlock(&xfrm_km_lock);
1573 return err;
1574}
1575EXPORT_SYMBOL(km_migrate);
1576
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001577int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1578{
1579 int err = -EINVAL;
1580 int ret;
1581 struct xfrm_mgr *km;
1582
1583 read_lock(&xfrm_km_lock);
1584 list_for_each_entry(km, &xfrm_km_list, list) {
1585 if (km->report) {
1586 ret = km->report(proto, sel, addr);
1587 if (!ret)
1588 err = ret;
1589 }
1590 }
1591 read_unlock(&xfrm_km_lock);
1592 return err;
1593}
1594EXPORT_SYMBOL(km_report);
1595
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1597{
1598 int err;
1599 u8 *data;
1600 struct xfrm_mgr *km;
1601 struct xfrm_policy *pol = NULL;
1602
1603 if (optlen <= 0 || optlen > PAGE_SIZE)
1604 return -EMSGSIZE;
1605
1606 data = kmalloc(optlen, GFP_KERNEL);
1607 if (!data)
1608 return -ENOMEM;
1609
1610 err = -EFAULT;
1611 if (copy_from_user(data, optval, optlen))
1612 goto out;
1613
1614 err = -EINVAL;
1615 read_lock(&xfrm_km_lock);
1616 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001617 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 optlen, &err);
1619 if (err >= 0)
1620 break;
1621 }
1622 read_unlock(&xfrm_km_lock);
1623
1624 if (err >= 0) {
1625 xfrm_sk_policy_insert(sk, err, pol);
1626 xfrm_pol_put(pol);
1627 err = 0;
1628 }
1629
1630out:
1631 kfree(data);
1632 return err;
1633}
1634EXPORT_SYMBOL(xfrm_user_policy);
1635
1636int xfrm_register_km(struct xfrm_mgr *km)
1637{
1638 write_lock_bh(&xfrm_km_lock);
1639 list_add_tail(&km->list, &xfrm_km_list);
1640 write_unlock_bh(&xfrm_km_lock);
1641 return 0;
1642}
1643EXPORT_SYMBOL(xfrm_register_km);
1644
1645int xfrm_unregister_km(struct xfrm_mgr *km)
1646{
1647 write_lock_bh(&xfrm_km_lock);
1648 list_del(&km->list);
1649 write_unlock_bh(&xfrm_km_lock);
1650 return 0;
1651}
1652EXPORT_SYMBOL(xfrm_unregister_km);
1653
1654int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1655{
1656 int err = 0;
1657 if (unlikely(afinfo == NULL))
1658 return -EINVAL;
1659 if (unlikely(afinfo->family >= NPROTO))
1660 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001661 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1663 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001664 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001666 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 return err;
1668}
1669EXPORT_SYMBOL(xfrm_state_register_afinfo);
1670
1671int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1672{
1673 int err = 0;
1674 if (unlikely(afinfo == NULL))
1675 return -EINVAL;
1676 if (unlikely(afinfo->family >= NPROTO))
1677 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001678 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1680 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1681 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001682 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001685 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 return err;
1687}
1688EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1689
Miika Komucdca7262007-02-06 14:24:56 -08001690struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691{
1692 struct xfrm_state_afinfo *afinfo;
1693 if (unlikely(family >= NPROTO))
1694 return NULL;
1695 read_lock(&xfrm_state_afinfo_lock);
1696 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001697 if (unlikely(!afinfo))
1698 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 return afinfo;
1700}
1701
Miika Komucdca7262007-02-06 14:24:56 -08001702void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Herbert Xu546be242006-05-27 23:03:58 -07001704 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705}
1706
Miika Komucdca7262007-02-06 14:24:56 -08001707EXPORT_SYMBOL(xfrm_state_get_afinfo);
1708EXPORT_SYMBOL(xfrm_state_put_afinfo);
1709
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1711void xfrm_state_delete_tunnel(struct xfrm_state *x)
1712{
1713 if (x->tunnel) {
1714 struct xfrm_state *t = x->tunnel;
1715
1716 if (atomic_read(&t->tunnel_users) == 2)
1717 xfrm_state_delete(t);
1718 atomic_dec(&t->tunnel_users);
1719 xfrm_state_put(t);
1720 x->tunnel = NULL;
1721 }
1722}
1723EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1724
1725int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1726{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001727 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
Patrick McHardyc5c25232007-04-09 11:47:18 -07001729 spin_lock_bh(&x->lock);
1730 if (x->km.state == XFRM_STATE_VALID &&
1731 x->type && x->type->get_mtu)
1732 res = x->type->get_mtu(x, mtu);
1733 else
Patrick McHardy28121612007-06-18 22:30:15 -07001734 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001735 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 return res;
1737}
1738
Herbert Xu72cb6962005-06-20 13:18:08 -07001739int xfrm_init_state(struct xfrm_state *x)
1740{
Herbert Xud094cd82005-06-20 13:19:41 -07001741 struct xfrm_state_afinfo *afinfo;
1742 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001743 int err;
1744
Herbert Xud094cd82005-06-20 13:19:41 -07001745 err = -EAFNOSUPPORT;
1746 afinfo = xfrm_state_get_afinfo(family);
1747 if (!afinfo)
1748 goto error;
1749
1750 err = 0;
1751 if (afinfo->init_flags)
1752 err = afinfo->init_flags(x);
1753
1754 xfrm_state_put_afinfo(afinfo);
1755
1756 if (err)
1757 goto error;
1758
1759 err = -EPROTONOSUPPORT;
1760 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001761 if (x->type == NULL)
1762 goto error;
1763
1764 err = x->type->init_state(x);
1765 if (err)
1766 goto error;
1767
Herbert Xub59f45d2006-05-27 23:05:54 -07001768 x->mode = xfrm_get_mode(x->props.mode, family);
1769 if (x->mode == NULL)
1770 goto error;
1771
Herbert Xu72cb6962005-06-20 13:18:08 -07001772 x->km.state = XFRM_STATE_VALID;
1773
1774error:
1775 return err;
1776}
1777
1778EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001779
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780void __init xfrm_state_init(void)
1781{
David S. Millerf034b5d2006-08-24 03:08:07 -07001782 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783
David S. Millerf034b5d2006-08-24 03:08:07 -07001784 sz = sizeof(struct hlist_head) * 8;
1785
David S. Miller44e36b42006-08-24 04:50:50 -07001786 xfrm_state_bydst = xfrm_hash_alloc(sz);
1787 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1788 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001789 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1790 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1791 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1792
David Howellsc4028952006-11-22 14:57:56 +00001793 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794}
1795
Joy Lattenab5f5e82007-09-17 11:51:22 -07001796#ifdef CONFIG_AUDITSYSCALL
1797static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
1798 struct audit_buffer *audit_buf)
1799{
1800 if (x->security)
1801 audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
1802 x->security->ctx_alg, x->security->ctx_doi,
1803 x->security->ctx_str);
1804
1805 switch(x->props.family) {
1806 case AF_INET:
1807 audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
1808 NIPQUAD(x->props.saddr.a4),
1809 NIPQUAD(x->id.daddr.a4));
1810 break;
1811 case AF_INET6:
1812 {
1813 struct in6_addr saddr6, daddr6;
1814
1815 memcpy(&saddr6, x->props.saddr.a6,
1816 sizeof(struct in6_addr));
1817 memcpy(&daddr6, x->id.daddr.a6,
1818 sizeof(struct in6_addr));
1819 audit_log_format(audit_buf,
1820 " src=" NIP6_FMT " dst=" NIP6_FMT,
1821 NIP6(saddr6), NIP6(daddr6));
1822 }
1823 break;
1824 }
1825}
1826
1827void
1828xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
1829{
1830 struct audit_buffer *audit_buf;
1831 extern int audit_enabled;
1832
1833 if (audit_enabled == 0)
1834 return;
1835 audit_buf = xfrm_audit_start(sid, auid);
1836 if (audit_buf == NULL)
1837 return;
1838 audit_log_format(audit_buf, " op=SAD-add res=%u",result);
1839 xfrm_audit_common_stateinfo(x, audit_buf);
1840 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
1841 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
1842 audit_log_end(audit_buf);
1843}
1844EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
1845
1846void
1847xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid)
1848{
1849 struct audit_buffer *audit_buf;
1850 extern int audit_enabled;
1851
1852 if (audit_enabled == 0)
1853 return;
1854 audit_buf = xfrm_audit_start(sid, auid);
1855 if (audit_buf == NULL)
1856 return;
1857 audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
1858 xfrm_audit_common_stateinfo(x, audit_buf);
1859 audit_log_format(audit_buf, " spi=%lu(0x%lx)",
1860 (unsigned long)x->id.spi, (unsigned long)x->id.spi);
1861 audit_log_end(audit_buf);
1862}
1863EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
1864#endif /* CONFIG_AUDITSYSCALL */