blob: dfacb9c2a6e3861837b1a25b2d919bcc19b88d5a [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>
David S. Miller01e67d02007-05-25 00:41:38 -070024#include <linux/cache.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
David S. Miller44e36b42006-08-24 04:50:50 -070026#include "xfrm_hash.h"
27
David S. Milleree857a72006-03-20 19:18:37 -080028struct sock *xfrm_nl;
29EXPORT_SYMBOL(xfrm_nl);
30
David S. Miller01e67d02007-05-25 00:41:38 -070031u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
David S. Millera70fcb02006-03-20 19:18:52 -080032EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
33
David S. Miller01e67d02007-05-25 00:41:38 -070034u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
David S. Millera70fcb02006-03-20 19:18:52 -080035EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
36
David S. Miller01e67d02007-05-25 00:41:38 -070037u32 sysctl_xfrm_acq_expires __read_mostly = 30;
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039/* Each xfrm_state may be linked to two tables:
40
41 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
David S. Millera624c102006-08-24 03:24:33 -070042 2. Hash table by (daddr,family,reqid) to find what SAs exist for given
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 destination/tunnel endpoint. (output)
44 */
45
46static DEFINE_SPINLOCK(xfrm_state_lock);
47
48/* Hash table to find appropriate SA towards given target (endpoint
49 * of tunnel or destination of transport mode) allowed by selector.
50 *
51 * Main use is finding SA after policy selected tunnel or transport mode.
52 * Also, it can be used by ah/esp icmp error handler to find offending SA.
53 */
David S. Millerf034b5d2006-08-24 03:08:07 -070054static struct hlist_head *xfrm_state_bydst __read_mostly;
55static struct hlist_head *xfrm_state_bysrc __read_mostly;
56static struct hlist_head *xfrm_state_byspi __read_mostly;
57static unsigned int xfrm_state_hmask __read_mostly;
58static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
59static unsigned int xfrm_state_num;
David S. Miller9d4a7062006-08-24 03:18:09 -070060static unsigned int xfrm_state_genid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
David S. Millerc1969f22006-08-24 04:00:03 -070062static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
63 xfrm_address_t *saddr,
64 u32 reqid,
David S. Millera624c102006-08-24 03:24:33 -070065 unsigned short family)
66{
David S. Millerc1969f22006-08-24 04:00:03 -070067 return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
David S. Millera624c102006-08-24 03:24:33 -070068}
69
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070070static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
71 xfrm_address_t *saddr,
David S. Miller44e36b42006-08-24 04:50:50 -070072 unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070073{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -070074 return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070075}
76
David S. Miller2575b652006-08-24 03:26:44 -070077static inline unsigned int
Al Viro8122adf2006-09-27 18:49:35 -070078xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
David S. Millerf034b5d2006-08-24 03:08:07 -070079{
David S. Millerc1969f22006-08-24 04:00:03 -070080 return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070081}
82
David S. Millerf034b5d2006-08-24 03:08:07 -070083static void xfrm_hash_transfer(struct hlist_head *list,
84 struct hlist_head *ndsttable,
85 struct hlist_head *nsrctable,
86 struct hlist_head *nspitable,
87 unsigned int nhashmask)
88{
89 struct hlist_node *entry, *tmp;
90 struct xfrm_state *x;
91
92 hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
93 unsigned int h;
94
David S. Millerc1969f22006-08-24 04:00:03 -070095 h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
96 x->props.reqid, x->props.family,
97 nhashmask);
David S. Millerf034b5d2006-08-24 03:08:07 -070098 hlist_add_head(&x->bydst, ndsttable+h);
99
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700100 h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
101 x->props.family,
David S. Millerf034b5d2006-08-24 03:08:07 -0700102 nhashmask);
103 hlist_add_head(&x->bysrc, nsrctable+h);
104
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700105 if (x->id.spi) {
106 h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
107 x->id.proto, x->props.family,
108 nhashmask);
109 hlist_add_head(&x->byspi, nspitable+h);
110 }
David S. Millerf034b5d2006-08-24 03:08:07 -0700111 }
112}
113
114static unsigned long xfrm_hash_new_size(void)
115{
116 return ((xfrm_state_hmask + 1) << 1) *
117 sizeof(struct hlist_head);
118}
119
120static DEFINE_MUTEX(hash_resize_mutex);
121
David Howellsc4028952006-11-22 14:57:56 +0000122static void xfrm_hash_resize(struct work_struct *__unused)
David S. Millerf034b5d2006-08-24 03:08:07 -0700123{
124 struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
125 unsigned long nsize, osize;
126 unsigned int nhashmask, ohashmask;
127 int i;
128
129 mutex_lock(&hash_resize_mutex);
130
131 nsize = xfrm_hash_new_size();
David S. Miller44e36b42006-08-24 04:50:50 -0700132 ndst = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700133 if (!ndst)
134 goto out_unlock;
David S. Miller44e36b42006-08-24 04:50:50 -0700135 nsrc = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700136 if (!nsrc) {
David S. Miller44e36b42006-08-24 04:50:50 -0700137 xfrm_hash_free(ndst, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700138 goto out_unlock;
139 }
David S. Miller44e36b42006-08-24 04:50:50 -0700140 nspi = xfrm_hash_alloc(nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700141 if (!nspi) {
David S. Miller44e36b42006-08-24 04:50:50 -0700142 xfrm_hash_free(ndst, nsize);
143 xfrm_hash_free(nsrc, nsize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700144 goto out_unlock;
145 }
146
147 spin_lock_bh(&xfrm_state_lock);
148
149 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
150 for (i = xfrm_state_hmask; i >= 0; i--)
151 xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
152 nhashmask);
153
154 odst = xfrm_state_bydst;
155 osrc = xfrm_state_bysrc;
156 ospi = xfrm_state_byspi;
157 ohashmask = xfrm_state_hmask;
158
159 xfrm_state_bydst = ndst;
160 xfrm_state_bysrc = nsrc;
161 xfrm_state_byspi = nspi;
162 xfrm_state_hmask = nhashmask;
163
164 spin_unlock_bh(&xfrm_state_lock);
165
166 osize = (ohashmask + 1) * sizeof(struct hlist_head);
David S. Miller44e36b42006-08-24 04:50:50 -0700167 xfrm_hash_free(odst, osize);
168 xfrm_hash_free(osrc, osize);
169 xfrm_hash_free(ospi, osize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700170
171out_unlock:
172 mutex_unlock(&hash_resize_mutex);
173}
174
David Howellsc4028952006-11-22 14:57:56 +0000175static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
David S. Millerf034b5d2006-08-24 03:08:07 -0700176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177DECLARE_WAIT_QUEUE_HEAD(km_waitq);
178EXPORT_SYMBOL(km_waitq);
179
180static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
181static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
182
183static struct work_struct xfrm_state_gc_work;
David S. Miller8f126e32006-08-24 02:45:07 -0700184static HLIST_HEAD(xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185static DEFINE_SPINLOCK(xfrm_state_gc_lock);
186
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800187int __xfrm_state_delete(struct xfrm_state *x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -0800189int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800190void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192static void xfrm_state_gc_destroy(struct xfrm_state *x)
193{
David S. Millera47f0ce2006-08-24 03:54:22 -0700194 del_timer_sync(&x->timer);
195 del_timer_sync(&x->rtimer);
Jesper Juhla51482b2005-11-08 09:41:34 -0800196 kfree(x->aalg);
197 kfree(x->ealg);
198 kfree(x->calg);
199 kfree(x->encap);
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -0700200 kfree(x->coaddr);
Herbert Xub59f45d2006-05-27 23:05:54 -0700201 if (x->mode)
202 xfrm_put_mode(x->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 if (x->type) {
204 x->type->destructor(x);
205 xfrm_put_type(x->type);
206 }
Trent Jaegerdf718372005-12-13 23:12:27 -0800207 security_xfrm_state_free(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 kfree(x);
209}
210
David Howellsc4028952006-11-22 14:57:56 +0000211static void xfrm_state_gc_task(struct work_struct *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
213 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700214 struct hlist_node *entry, *tmp;
215 struct hlist_head gc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700218 gc_list.first = xfrm_state_gc_list.first;
219 INIT_HLIST_HEAD(&xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 spin_unlock_bh(&xfrm_state_gc_lock);
221
David S. Miller8f126e32006-08-24 02:45:07 -0700222 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 xfrm_state_gc_destroy(x);
David S. Miller8f126e32006-08-24 02:45:07 -0700224
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 wake_up(&km_waitq);
226}
227
228static inline unsigned long make_jiffies(long secs)
229{
230 if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
231 return MAX_SCHEDULE_TIMEOUT-1;
232 else
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900233 return secs*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234}
235
236static void xfrm_timer_handler(unsigned long data)
237{
238 struct xfrm_state *x = (struct xfrm_state*)data;
James Morris9d729f72007-03-04 16:12:44 -0800239 unsigned long now = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 long next = LONG_MAX;
241 int warn = 0;
Joy Latten161a09e2006-11-27 13:11:54 -0600242 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
244 spin_lock(&x->lock);
245 if (x->km.state == XFRM_STATE_DEAD)
246 goto out;
247 if (x->km.state == XFRM_STATE_EXPIRED)
248 goto expired;
249 if (x->lft.hard_add_expires_seconds) {
250 long tmo = x->lft.hard_add_expires_seconds +
251 x->curlft.add_time - now;
252 if (tmo <= 0)
253 goto expired;
254 if (tmo < next)
255 next = tmo;
256 }
257 if (x->lft.hard_use_expires_seconds) {
258 long tmo = x->lft.hard_use_expires_seconds +
259 (x->curlft.use_time ? : now) - now;
260 if (tmo <= 0)
261 goto expired;
262 if (tmo < next)
263 next = tmo;
264 }
265 if (x->km.dying)
266 goto resched;
267 if (x->lft.soft_add_expires_seconds) {
268 long tmo = x->lft.soft_add_expires_seconds +
269 x->curlft.add_time - now;
270 if (tmo <= 0)
271 warn = 1;
272 else if (tmo < next)
273 next = tmo;
274 }
275 if (x->lft.soft_use_expires_seconds) {
276 long tmo = x->lft.soft_use_expires_seconds +
277 (x->curlft.use_time ? : now) - now;
278 if (tmo <= 0)
279 warn = 1;
280 else if (tmo < next)
281 next = tmo;
282 }
283
Herbert Xu4666faa2005-06-18 22:43:22 -0700284 x->km.dying = warn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 if (warn)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800286 km_state_expired(x, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287resched:
David S. Millera47f0ce2006-08-24 03:54:22 -0700288 if (next != LONG_MAX)
289 mod_timer(&x->timer, jiffies + make_jiffies(next));
290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 goto out;
292
293expired:
294 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
295 x->km.state = XFRM_STATE_EXPIRED;
296 wake_up(&km_waitq);
297 next = 2;
298 goto resched;
299 }
Joy Latten161a09e2006-11-27 13:11:54 -0600300
301 err = __xfrm_state_delete(x);
302 if (!err && x->id.spi)
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800303 km_state_expired(x, 1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
Joy Latten161a09e2006-11-27 13:11:54 -0600305 xfrm_audit_log(audit_get_loginuid(current->audit_context), 0,
306 AUDIT_MAC_IPSEC_DELSA, err ? 0 : 1, NULL, x);
307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308out:
309 spin_unlock(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
David S. Miller0ac84752006-03-20 19:18:23 -0800312static void xfrm_replay_timer_handler(unsigned long data);
313
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314struct xfrm_state *xfrm_state_alloc(void)
315{
316 struct xfrm_state *x;
317
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700318 x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 if (x) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 atomic_set(&x->refcnt, 1);
322 atomic_set(&x->tunnel_users, 0);
David S. Miller8f126e32006-08-24 02:45:07 -0700323 INIT_HLIST_NODE(&x->bydst);
324 INIT_HLIST_NODE(&x->bysrc);
325 INIT_HLIST_NODE(&x->byspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 init_timer(&x->timer);
327 x->timer.function = xfrm_timer_handler;
328 x->timer.data = (unsigned long)x;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800329 init_timer(&x->rtimer);
330 x->rtimer.function = xfrm_replay_timer_handler;
331 x->rtimer.data = (unsigned long)x;
James Morris9d729f72007-03-04 16:12:44 -0800332 x->curlft.add_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 x->lft.soft_byte_limit = XFRM_INF;
334 x->lft.soft_packet_limit = XFRM_INF;
335 x->lft.hard_byte_limit = XFRM_INF;
336 x->lft.hard_packet_limit = XFRM_INF;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800337 x->replay_maxage = 0;
338 x->replay_maxdiff = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 spin_lock_init(&x->lock);
340 }
341 return x;
342}
343EXPORT_SYMBOL(xfrm_state_alloc);
344
345void __xfrm_state_destroy(struct xfrm_state *x)
346{
347 BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
348
349 spin_lock_bh(&xfrm_state_gc_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700350 hlist_add_head(&x->bydst, &xfrm_state_gc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 spin_unlock_bh(&xfrm_state_gc_lock);
352 schedule_work(&xfrm_state_gc_work);
353}
354EXPORT_SYMBOL(__xfrm_state_destroy);
355
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800356int __xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700358 int err = -ESRCH;
359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 if (x->km.state != XFRM_STATE_DEAD) {
361 x->km.state = XFRM_STATE_DEAD;
362 spin_lock(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700363 hlist_del(&x->bydst);
David S. Miller8f126e32006-08-24 02:45:07 -0700364 hlist_del(&x->bysrc);
David S. Millera47f0ce2006-08-24 03:54:22 -0700365 if (x->id.spi)
David S. Miller8f126e32006-08-24 02:45:07 -0700366 hlist_del(&x->byspi);
David S. Millerf034b5d2006-08-24 03:08:07 -0700367 xfrm_state_num--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 spin_unlock(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 /* All xfrm_state objects are created by xfrm_state_alloc.
371 * The xfrm_state_alloc call gives a reference, and that
372 * is what we are dropping here.
373 */
Herbert Xu21380b82006-02-22 14:47:13 -0800374 __xfrm_state_put(x);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700375 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700377
378 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -0800380EXPORT_SYMBOL(__xfrm_state_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700382int xfrm_state_delete(struct xfrm_state *x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700384 int err;
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 spin_lock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700387 err = __xfrm_state_delete(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 spin_unlock_bh(&x->lock);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -0700389
390 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391}
392EXPORT_SYMBOL(xfrm_state_delete);
393
Joy Latten4aa2e622007-06-04 19:05:57 -0400394#ifdef CONFIG_SECURITY_NETWORK_XFRM
395static inline int
396xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
Joy Latten4aa2e622007-06-04 19:05:57 -0400398 int i, err = 0;
399
400 for (i = 0; i <= xfrm_state_hmask; i++) {
401 struct hlist_node *entry;
402 struct xfrm_state *x;
403
404 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
405 if (xfrm_id_proto_match(x->id.proto, proto) &&
406 (err = security_xfrm_state_delete(x)) != 0) {
407 xfrm_audit_log(audit_info->loginuid,
408 audit_info->secid,
409 AUDIT_MAC_IPSEC_DELSA,
410 0, NULL, x);
411
412 return err;
413 }
414 }
415 }
416
417 return err;
418}
419#else
420static inline int
421xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
422{
423 return 0;
424}
425#endif
426
427int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
428{
429 int i, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
431 spin_lock_bh(&xfrm_state_lock);
Joy Latten4aa2e622007-06-04 19:05:57 -0400432 err = xfrm_state_flush_secctx_check(proto, audit_info);
433 if (err)
434 goto out;
435
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700436 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700437 struct hlist_node *entry;
438 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700440 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700442 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 xfrm_state_hold(x);
444 spin_unlock_bh(&xfrm_state_lock);
445
Joy Latten161a09e2006-11-27 13:11:54 -0600446 err = xfrm_state_delete(x);
447 xfrm_audit_log(audit_info->loginuid,
448 audit_info->secid,
449 AUDIT_MAC_IPSEC_DELSA,
450 err ? 0 : 1, NULL, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 xfrm_state_put(x);
452
453 spin_lock_bh(&xfrm_state_lock);
454 goto restart;
455 }
456 }
457 }
Joy Latten4aa2e622007-06-04 19:05:57 -0400458 err = 0;
459
460out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 spin_unlock_bh(&xfrm_state_lock);
462 wake_up(&km_waitq);
Joy Latten4aa2e622007-06-04 19:05:57 -0400463 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465EXPORT_SYMBOL(xfrm_state_flush);
466
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700467void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700468{
469 spin_lock_bh(&xfrm_state_lock);
470 si->sadcnt = xfrm_state_num;
471 si->sadhcnt = xfrm_state_hmask;
472 si->sadhmcnt = xfrm_state_hashmax;
473 spin_unlock_bh(&xfrm_state_lock);
474}
475EXPORT_SYMBOL(xfrm_sad_getinfo);
476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477static int
478xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
479 struct xfrm_tmpl *tmpl,
480 xfrm_address_t *daddr, xfrm_address_t *saddr,
481 unsigned short family)
482{
483 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
484 if (!afinfo)
485 return -1;
486 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
487 xfrm_state_put_afinfo(afinfo);
488 return 0;
489}
490
Al Viroa94cfd12006-09-27 18:47:24 -0700491static 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 -0700492{
493 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
494 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700495 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700496
David S. Miller8f126e32006-08-24 02:45:07 -0700497 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700498 if (x->props.family != family ||
499 x->id.spi != spi ||
500 x->id.proto != proto)
501 continue;
502
503 switch (family) {
504 case AF_INET:
505 if (x->id.daddr.a4 != daddr->a4)
506 continue;
507 break;
508 case AF_INET6:
509 if (!ipv6_addr_equal((struct in6_addr *)daddr,
510 (struct in6_addr *)
511 x->id.daddr.a6))
512 continue;
513 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700514 }
David S. Milleredcd5822006-08-24 00:42:45 -0700515
516 xfrm_state_hold(x);
517 return x;
518 }
519
520 return NULL;
521}
522
523static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
524{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700525 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700526 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700527 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700528
David S. Miller8f126e32006-08-24 02:45:07 -0700529 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700530 if (x->props.family != family ||
531 x->id.proto != proto)
532 continue;
533
534 switch (family) {
535 case AF_INET:
536 if (x->id.daddr.a4 != daddr->a4 ||
537 x->props.saddr.a4 != saddr->a4)
538 continue;
539 break;
540 case AF_INET6:
541 if (!ipv6_addr_equal((struct in6_addr *)daddr,
542 (struct in6_addr *)
543 x->id.daddr.a6) ||
544 !ipv6_addr_equal((struct in6_addr *)saddr,
545 (struct in6_addr *)
546 x->props.saddr.a6))
547 continue;
548 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700549 }
David S. Milleredcd5822006-08-24 00:42:45 -0700550
551 xfrm_state_hold(x);
552 return x;
553 }
554
555 return NULL;
556}
557
558static inline struct xfrm_state *
559__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
560{
561 if (use_spi)
562 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
563 x->id.proto, family);
564 else
565 return __xfrm_state_lookup_byaddr(&x->id.daddr,
566 &x->props.saddr,
567 x->id.proto, family);
568}
569
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700570static void xfrm_hash_grow_check(int have_hash_collision)
571{
572 if (have_hash_collision &&
573 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
574 xfrm_state_num > xfrm_state_hmask)
575 schedule_work(&xfrm_hash_work);
576}
577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900579xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 struct flowi *fl, struct xfrm_tmpl *tmpl,
581 struct xfrm_policy *pol, int *err,
582 unsigned short family)
583{
David S. Millerc1969f22006-08-24 04:00:03 -0700584 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700585 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 struct xfrm_state *x, *x0;
587 int acquire_in_progress = 0;
588 int error = 0;
589 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700592 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 if (x->props.family == family &&
594 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700595 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 xfrm_state_addr_check(x, daddr, saddr, family) &&
597 tmpl->mode == x->props.mode &&
598 tmpl->id.proto == x->id.proto &&
599 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
600 /* Resolution logic:
601 1. There is a valid state with matching selector.
602 Done.
603 2. Valid state with inappropriate selector. Skip.
604
605 Entering area of "sysdeps".
606
607 3. If state is not valid, selector is temporary,
608 it selects only session which triggered
609 previous resolution. Key manager will do
610 something to install a state with proper
611 selector.
612 */
613 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800614 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700615 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 continue;
617 if (!best ||
618 best->km.dying > x->km.dying ||
619 (best->km.dying == x->km.dying &&
620 best->curlft.add_time < x->curlft.add_time))
621 best = x;
622 } else if (x->km.state == XFRM_STATE_ACQ) {
623 acquire_in_progress = 1;
624 } else if (x->km.state == XFRM_STATE_ERROR ||
625 x->km.state == XFRM_STATE_EXPIRED) {
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900626 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700627 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 error = -ESRCH;
629 }
630 }
631 }
632
633 x = best;
634 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700635 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700636 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
637 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 xfrm_state_put(x0);
639 error = -EEXIST;
640 goto out;
641 }
642 x = xfrm_state_alloc();
643 if (x == NULL) {
644 error = -ENOMEM;
645 goto out;
646 }
647 /* Initialize temporary selector matching only
648 * to current session. */
649 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
650
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700651 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
652 if (error) {
653 x->km.state = XFRM_STATE_DEAD;
654 xfrm_state_put(x);
655 x = NULL;
656 goto out;
657 }
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 if (km_query(x, tmpl, pol) == 0) {
660 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700661 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700662 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700663 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (x->id.spi) {
665 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700666 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 }
David S. Miller01e67d02007-05-25 00:41:38 -0700668 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
669 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700671 xfrm_state_num++;
672 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 } else {
674 x->km.state = XFRM_STATE_DEAD;
675 xfrm_state_put(x);
676 x = NULL;
677 error = -ESRCH;
678 }
679 }
680out:
681 if (x)
682 xfrm_state_hold(x);
683 else
684 *err = acquire_in_progress ? -EAGAIN : error;
685 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 return x;
687}
688
689static void __xfrm_state_insert(struct xfrm_state *x)
690{
David S. Millera624c102006-08-24 03:24:33 -0700691 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
David S. Miller9d4a7062006-08-24 03:18:09 -0700693 x->genid = ++xfrm_state_genid;
694
David S. Millerc1969f22006-08-24 04:00:03 -0700695 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
696 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700697 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700699 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700700 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700702 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700703 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
704 x->props.family);
705
David S. Miller8f126e32006-08-24 02:45:07 -0700706 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700707 }
708
David S. Millera47f0ce2006-08-24 03:54:22 -0700709 mod_timer(&x->timer, jiffies + HZ);
710 if (x->replay_maxage)
711 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800712
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700714
715 xfrm_state_num++;
716
David S. Miller918049f2006-10-12 22:03:24 -0700717 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718}
719
David S. Millerc7f5ea32006-08-24 03:29:04 -0700720/* xfrm_state_lock is held */
721static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
722{
723 unsigned short family = xnew->props.family;
724 u32 reqid = xnew->props.reqid;
725 struct xfrm_state *x;
726 struct hlist_node *entry;
727 unsigned int h;
728
David S. Millerc1969f22006-08-24 04:00:03 -0700729 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700730 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
731 if (x->props.family == family &&
732 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700733 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
734 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700735 x->genid = xfrm_state_genid;
736 }
737}
738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739void xfrm_state_insert(struct xfrm_state *x)
740{
741 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700742 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 __xfrm_state_insert(x);
744 spin_unlock_bh(&xfrm_state_lock);
745}
746EXPORT_SYMBOL(xfrm_state_insert);
747
David S. Miller27708342006-08-24 00:13:10 -0700748/* xfrm_state_lock is held */
749static 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)
750{
David S. Millerc1969f22006-08-24 04:00:03 -0700751 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700752 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700753 struct xfrm_state *x;
754
David S. Miller8f126e32006-08-24 02:45:07 -0700755 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700756 if (x->props.reqid != reqid ||
757 x->props.mode != mode ||
758 x->props.family != family ||
759 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700760 x->id.spi != 0 ||
761 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700762 continue;
763
764 switch (family) {
765 case AF_INET:
766 if (x->id.daddr.a4 != daddr->a4 ||
767 x->props.saddr.a4 != saddr->a4)
768 continue;
769 break;
770 case AF_INET6:
771 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
772 (struct in6_addr *)daddr) ||
773 !ipv6_addr_equal((struct in6_addr *)
774 x->props.saddr.a6,
775 (struct in6_addr *)saddr))
776 continue;
777 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700778 }
David S. Miller27708342006-08-24 00:13:10 -0700779
780 xfrm_state_hold(x);
781 return x;
782 }
783
784 if (!create)
785 return NULL;
786
787 x = xfrm_state_alloc();
788 if (likely(x)) {
789 switch (family) {
790 case AF_INET:
791 x->sel.daddr.a4 = daddr->a4;
792 x->sel.saddr.a4 = saddr->a4;
793 x->sel.prefixlen_d = 32;
794 x->sel.prefixlen_s = 32;
795 x->props.saddr.a4 = saddr->a4;
796 x->id.daddr.a4 = daddr->a4;
797 break;
798
799 case AF_INET6:
800 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
801 (struct in6_addr *)daddr);
802 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
803 (struct in6_addr *)saddr);
804 x->sel.prefixlen_d = 128;
805 x->sel.prefixlen_s = 128;
806 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
807 (struct in6_addr *)saddr);
808 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
809 (struct in6_addr *)daddr);
810 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700811 }
David S. Miller27708342006-08-24 00:13:10 -0700812
813 x->km.state = XFRM_STATE_ACQ;
814 x->id.proto = proto;
815 x->props.family = family;
816 x->props.mode = mode;
817 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -0700818 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -0700819 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -0700820 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -0700821 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700822 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700823 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700824 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700825 wake_up(&km_waitq);
David S. Miller918049f2006-10-12 22:03:24 -0700826
827 xfrm_state_num++;
828
829 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -0700830 }
831
832 return x;
833}
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
836
837int xfrm_state_add(struct xfrm_state *x)
838{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 struct xfrm_state *x1;
840 int family;
841 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700842 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
846 spin_lock_bh(&xfrm_state_lock);
847
David S. Milleredcd5822006-08-24 00:42:45 -0700848 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 if (x1) {
850 xfrm_state_put(x1);
851 x1 = NULL;
852 err = -EEXIST;
853 goto out;
854 }
855
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700856 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -0700858 if (x1 && ((x1->id.proto != x->id.proto) ||
859 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 xfrm_state_put(x1);
861 x1 = NULL;
862 }
863 }
864
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700865 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700866 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
867 x->id.proto,
868 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
David S. Millerc7f5ea32006-08-24 03:29:04 -0700870 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 __xfrm_state_insert(x);
872 err = 0;
873
874out:
875 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
877 if (x1) {
878 xfrm_state_delete(x1);
879 xfrm_state_put(x1);
880 }
881
882 return err;
883}
884EXPORT_SYMBOL(xfrm_state_add);
885
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800886#ifdef CONFIG_XFRM_MIGRATE
887struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
888{
889 int err = -ENOMEM;
890 struct xfrm_state *x = xfrm_state_alloc();
891 if (!x)
892 goto error;
893
894 memcpy(&x->id, &orig->id, sizeof(x->id));
895 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
896 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
897 x->props.mode = orig->props.mode;
898 x->props.replay_window = orig->props.replay_window;
899 x->props.reqid = orig->props.reqid;
900 x->props.family = orig->props.family;
901 x->props.saddr = orig->props.saddr;
902
903 if (orig->aalg) {
904 x->aalg = xfrm_algo_clone(orig->aalg);
905 if (!x->aalg)
906 goto error;
907 }
908 x->props.aalgo = orig->props.aalgo;
909
910 if (orig->ealg) {
911 x->ealg = xfrm_algo_clone(orig->ealg);
912 if (!x->ealg)
913 goto error;
914 }
915 x->props.ealgo = orig->props.ealgo;
916
917 if (orig->calg) {
918 x->calg = xfrm_algo_clone(orig->calg);
919 if (!x->calg)
920 goto error;
921 }
922 x->props.calgo = orig->props.calgo;
923
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900924 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800925 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
926 if (!x->encap)
927 goto error;
928 }
929
930 if (orig->coaddr) {
931 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
932 GFP_KERNEL);
933 if (!x->coaddr)
934 goto error;
935 }
936
937 err = xfrm_init_state(x);
938 if (err)
939 goto error;
940
941 x->props.flags = orig->props.flags;
942
943 x->curlft.add_time = orig->curlft.add_time;
944 x->km.state = orig->km.state;
945 x->km.seq = orig->km.seq;
946
947 return x;
948
949 error:
950 if (errp)
951 *errp = err;
952 if (x) {
953 kfree(x->aalg);
954 kfree(x->ealg);
955 kfree(x->calg);
956 kfree(x->encap);
957 kfree(x->coaddr);
958 }
959 kfree(x);
960 return NULL;
961}
962EXPORT_SYMBOL(xfrm_state_clone);
963
964/* xfrm_state_lock is held */
965struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
966{
967 unsigned int h;
968 struct xfrm_state *x;
969 struct hlist_node *entry;
970
971 if (m->reqid) {
972 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
973 m->reqid, m->old_family);
974 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
975 if (x->props.mode != m->mode ||
976 x->id.proto != m->proto)
977 continue;
978 if (m->reqid && x->props.reqid != m->reqid)
979 continue;
980 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
981 m->old_family) ||
982 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
983 m->old_family))
984 continue;
985 xfrm_state_hold(x);
986 return x;
987 }
988 } else {
989 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
990 m->old_family);
991 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
992 if (x->props.mode != m->mode ||
993 x->id.proto != m->proto)
994 continue;
995 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
996 m->old_family) ||
997 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
998 m->old_family))
999 continue;
1000 xfrm_state_hold(x);
1001 return x;
1002 }
1003 }
1004
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001005 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001006}
1007EXPORT_SYMBOL(xfrm_migrate_state_find);
1008
1009struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1010 struct xfrm_migrate *m)
1011{
1012 struct xfrm_state *xc;
1013 int err;
1014
1015 xc = xfrm_state_clone(x, &err);
1016 if (!xc)
1017 return NULL;
1018
1019 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
1020 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
1021
1022 /* add state */
1023 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
1024 /* a care is needed when the destination address of the
1025 state is to be updated as it is a part of triplet */
1026 xfrm_state_insert(xc);
1027 } else {
1028 if ((err = xfrm_state_add(xc)) < 0)
1029 goto error;
1030 }
1031
1032 return xc;
1033error:
1034 kfree(xc);
1035 return NULL;
1036}
1037EXPORT_SYMBOL(xfrm_state_migrate);
1038#endif
1039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040int xfrm_state_update(struct xfrm_state *x)
1041{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 struct xfrm_state *x1;
1043 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001044 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001047 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
1049 err = -ESRCH;
1050 if (!x1)
1051 goto out;
1052
1053 if (xfrm_state_kern(x1)) {
1054 xfrm_state_put(x1);
1055 err = -EEXIST;
1056 goto out;
1057 }
1058
1059 if (x1->km.state == XFRM_STATE_ACQ) {
1060 __xfrm_state_insert(x);
1061 x = NULL;
1062 }
1063 err = 0;
1064
1065out:
1066 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 if (err)
1069 return err;
1070
1071 if (!x) {
1072 xfrm_state_delete(x1);
1073 xfrm_state_put(x1);
1074 return 0;
1075 }
1076
1077 err = -EINVAL;
1078 spin_lock_bh(&x1->lock);
1079 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1080 if (x->encap && x1->encap)
1081 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001082 if (x->coaddr && x1->coaddr) {
1083 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1084 }
1085 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1086 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1088 x1->km.dying = 0;
1089
David S. Millera47f0ce2006-08-24 03:54:22 -07001090 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 if (x1->curlft.use_time)
1092 xfrm_state_check_expire(x1);
1093
1094 err = 0;
1095 }
1096 spin_unlock_bh(&x1->lock);
1097
1098 xfrm_state_put(x1);
1099
1100 return err;
1101}
1102EXPORT_SYMBOL(xfrm_state_update);
1103
1104int xfrm_state_check_expire(struct xfrm_state *x)
1105{
1106 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001107 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
1109 if (x->km.state != XFRM_STATE_VALID)
1110 return -EINVAL;
1111
1112 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1113 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001114 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001115 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 return -EINVAL;
1117 }
1118
1119 if (!x->km.dying &&
1120 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001121 x->curlft.packets >= x->lft.soft_packet_limit)) {
1122 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001123 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001124 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 return 0;
1126}
1127EXPORT_SYMBOL(xfrm_state_check_expire);
1128
1129static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1130{
1131 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1132 - skb_headroom(skb);
1133
1134 if (nhead > 0)
1135 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1136
1137 /* Check tail too... */
1138 return 0;
1139}
1140
1141int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1142{
1143 int err = xfrm_state_check_expire(x);
1144 if (err < 0)
1145 goto err;
1146 err = xfrm_state_check_space(x, skb);
1147err:
1148 return err;
1149}
1150EXPORT_SYMBOL(xfrm_state_check);
1151
1152struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001153xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 unsigned short family)
1155{
1156 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
1158 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001159 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 return x;
1162}
1163EXPORT_SYMBOL(xfrm_state_lookup);
1164
1165struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001166xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1167 u8 proto, unsigned short family)
1168{
1169 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001170
1171 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001172 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001173 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001174 return x;
1175}
1176EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1177
1178struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001179xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1180 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 int create, unsigned short family)
1182{
1183 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184
1185 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001186 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001188
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 return x;
1190}
1191EXPORT_SYMBOL(xfrm_find_acq);
1192
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001193#ifdef CONFIG_XFRM_SUB_POLICY
1194int
1195xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1196 unsigned short family)
1197{
1198 int err = 0;
1199 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1200 if (!afinfo)
1201 return -EAFNOSUPPORT;
1202
1203 spin_lock_bh(&xfrm_state_lock);
1204 if (afinfo->tmpl_sort)
1205 err = afinfo->tmpl_sort(dst, src, n);
1206 spin_unlock_bh(&xfrm_state_lock);
1207 xfrm_state_put_afinfo(afinfo);
1208 return err;
1209}
1210EXPORT_SYMBOL(xfrm_tmpl_sort);
1211
1212int
1213xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1214 unsigned short family)
1215{
1216 int err = 0;
1217 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1218 if (!afinfo)
1219 return -EAFNOSUPPORT;
1220
1221 spin_lock_bh(&xfrm_state_lock);
1222 if (afinfo->state_sort)
1223 err = afinfo->state_sort(dst, src, n);
1224 spin_unlock_bh(&xfrm_state_lock);
1225 xfrm_state_put_afinfo(afinfo);
1226 return err;
1227}
1228EXPORT_SYMBOL(xfrm_state_sort);
1229#endif
1230
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231/* Silly enough, but I'm lazy to build resolution list */
1232
1233static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1234{
1235 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
David S. Millerf034b5d2006-08-24 03:08:07 -07001237 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001238 struct hlist_node *entry;
1239 struct xfrm_state *x;
1240
1241 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1242 if (x->km.seq == seq &&
1243 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 xfrm_state_hold(x);
1245 return x;
1246 }
1247 }
1248 }
1249 return NULL;
1250}
1251
1252struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1253{
1254 struct xfrm_state *x;
1255
1256 spin_lock_bh(&xfrm_state_lock);
1257 x = __xfrm_find_acq_byseq(seq);
1258 spin_unlock_bh(&xfrm_state_lock);
1259 return x;
1260}
1261EXPORT_SYMBOL(xfrm_find_acq_byseq);
1262
1263u32 xfrm_get_acqseq(void)
1264{
1265 u32 res;
1266 static u32 acqseq;
1267 static DEFINE_SPINLOCK(acqseq_lock);
1268
1269 spin_lock_bh(&acqseq_lock);
1270 res = (++acqseq ? : ++acqseq);
1271 spin_unlock_bh(&acqseq_lock);
1272 return res;
1273}
1274EXPORT_SYMBOL(xfrm_get_acqseq);
1275
1276void
Al Viro26977b42006-09-27 18:47:05 -07001277xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278{
David S. Millerf034b5d2006-08-24 03:08:07 -07001279 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 struct xfrm_state *x0;
1281
1282 if (x->id.spi)
1283 return;
1284
1285 if (minspi == maxspi) {
1286 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1287 if (x0) {
1288 xfrm_state_put(x0);
1289 return;
1290 }
1291 x->id.spi = minspi;
1292 } else {
1293 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001294 u32 low = ntohl(minspi);
1295 u32 high = ntohl(maxspi);
1296 for (h=0; h<high-low+1; h++) {
1297 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1299 if (x0 == NULL) {
1300 x->id.spi = htonl(spi);
1301 break;
1302 }
1303 xfrm_state_put(x0);
1304 }
1305 }
1306 if (x->id.spi) {
1307 spin_lock_bh(&xfrm_state_lock);
1308 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001309 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 spin_unlock_bh(&xfrm_state_lock);
1311 wake_up(&km_waitq);
1312 }
1313}
1314EXPORT_SYMBOL(xfrm_alloc_spi);
1315
1316int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1317 void *data)
1318{
1319 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001320 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001321 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 int count = 0;
1323 int err = 0;
1324
1325 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001326 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001327 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001328 if (!xfrm_id_proto_match(x->id.proto, proto))
1329 continue;
1330 if (last) {
1331 err = func(last, count, data);
1332 if (err)
1333 goto out;
1334 }
1335 last = x;
1336 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 }
1338 }
1339 if (count == 0) {
1340 err = -ENOENT;
1341 goto out;
1342 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001343 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344out:
1345 spin_unlock_bh(&xfrm_state_lock);
1346 return err;
1347}
1348EXPORT_SYMBOL(xfrm_state_walk);
1349
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001350
1351void xfrm_replay_notify(struct xfrm_state *x, int event)
1352{
1353 struct km_event c;
1354 /* we send notify messages in case
1355 * 1. we updated on of the sequence numbers, and the seqno difference
1356 * is at least x->replay_maxdiff, in this case we also update the
1357 * timeout of our timer function
1358 * 2. if x->replay_maxage has elapsed since last update,
1359 * and there were changes
1360 *
1361 * The state structure must be locked!
1362 */
1363
1364 switch (event) {
1365 case XFRM_REPLAY_UPDATE:
1366 if (x->replay_maxdiff &&
1367 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001368 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1369 if (x->xflags & XFRM_TIME_DEFER)
1370 event = XFRM_REPLAY_TIMEOUT;
1371 else
1372 return;
1373 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001374
1375 break;
1376
1377 case XFRM_REPLAY_TIMEOUT:
1378 if ((x->replay.seq == x->preplay.seq) &&
1379 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001380 (x->replay.oseq == x->preplay.oseq)) {
1381 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001382 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001383 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001384
1385 break;
1386 }
1387
1388 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1389 c.event = XFRM_MSG_NEWAE;
1390 c.data.aevent = event;
1391 km_state_notify(x, &c);
1392
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001393 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001394 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001395 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001396}
David S. Millera70fcb02006-03-20 19:18:52 -08001397EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001398
1399static void xfrm_replay_timer_handler(unsigned long data)
1400{
1401 struct xfrm_state *x = (struct xfrm_state*)data;
1402
1403 spin_lock(&x->lock);
1404
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001405 if (x->km.state == XFRM_STATE_VALID) {
1406 if (xfrm_aevent_is_on())
1407 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1408 else
1409 x->xflags |= XFRM_TIME_DEFER;
1410 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001411
1412 spin_unlock(&x->lock);
1413}
1414
Al Viroa252cc22006-09-27 18:48:18 -07001415int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416{
1417 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001418 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
1420 if (unlikely(seq == 0))
1421 return -EINVAL;
1422
1423 if (likely(seq > x->replay.seq))
1424 return 0;
1425
1426 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001427 if (diff >= min_t(unsigned int, x->props.replay_window,
1428 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 x->stats.replay_window++;
1430 return -EINVAL;
1431 }
1432
1433 if (x->replay.bitmap & (1U << diff)) {
1434 x->stats.replay++;
1435 return -EINVAL;
1436 }
1437 return 0;
1438}
1439EXPORT_SYMBOL(xfrm_replay_check);
1440
Al Viro61f46272006-09-27 18:48:33 -07001441void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
1443 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001444 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
1446 if (seq > x->replay.seq) {
1447 diff = seq - x->replay.seq;
1448 if (diff < x->props.replay_window)
1449 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1450 else
1451 x->replay.bitmap = 1;
1452 x->replay.seq = seq;
1453 } else {
1454 diff = x->replay.seq - seq;
1455 x->replay.bitmap |= (1U << diff);
1456 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001457
1458 if (xfrm_aevent_is_on())
1459 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460}
1461EXPORT_SYMBOL(xfrm_replay_advance);
1462
1463static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1464static DEFINE_RWLOCK(xfrm_km_lock);
1465
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001466void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467{
1468 struct xfrm_mgr *km;
1469
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001470 read_lock(&xfrm_km_lock);
1471 list_for_each_entry(km, &xfrm_km_list, list)
1472 if (km->notify_policy)
1473 km->notify_policy(xp, dir, c);
1474 read_unlock(&xfrm_km_lock);
1475}
1476
1477void km_state_notify(struct xfrm_state *x, struct km_event *c)
1478{
1479 struct xfrm_mgr *km;
1480 read_lock(&xfrm_km_lock);
1481 list_for_each_entry(km, &xfrm_km_list, list)
1482 if (km->notify)
1483 km->notify(x, c);
1484 read_unlock(&xfrm_km_lock);
1485}
1486
1487EXPORT_SYMBOL(km_policy_notify);
1488EXPORT_SYMBOL(km_state_notify);
1489
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001490void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001491{
1492 struct km_event c;
1493
Herbert Xubf088672005-06-18 22:44:00 -07001494 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001495 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001496 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001497 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
1499 if (hard)
1500 wake_up(&km_waitq);
1501}
1502
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001503EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001504/*
1505 * We send to all registered managers regardless of failure
1506 * We are happy with one success
1507*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001508int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001510 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 struct xfrm_mgr *km;
1512
1513 read_lock(&xfrm_km_lock);
1514 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001515 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1516 if (!acqret)
1517 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 }
1519 read_unlock(&xfrm_km_lock);
1520 return err;
1521}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001522EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Al Viro5d36b182006-11-08 00:24:06 -08001524int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525{
1526 int err = -EINVAL;
1527 struct xfrm_mgr *km;
1528
1529 read_lock(&xfrm_km_lock);
1530 list_for_each_entry(km, &xfrm_km_list, list) {
1531 if (km->new_mapping)
1532 err = km->new_mapping(x, ipaddr, sport);
1533 if (!err)
1534 break;
1535 }
1536 read_unlock(&xfrm_km_lock);
1537 return err;
1538}
1539EXPORT_SYMBOL(km_new_mapping);
1540
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001541void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001543 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Herbert Xubf088672005-06-18 22:44:00 -07001545 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001546 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001547 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001548 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
1550 if (hard)
1551 wake_up(&km_waitq);
1552}
David S. Millera70fcb02006-03-20 19:18:52 -08001553EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001555int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1556 struct xfrm_migrate *m, int num_migrate)
1557{
1558 int err = -EINVAL;
1559 int ret;
1560 struct xfrm_mgr *km;
1561
1562 read_lock(&xfrm_km_lock);
1563 list_for_each_entry(km, &xfrm_km_list, list) {
1564 if (km->migrate) {
1565 ret = km->migrate(sel, dir, type, m, num_migrate);
1566 if (!ret)
1567 err = ret;
1568 }
1569 }
1570 read_unlock(&xfrm_km_lock);
1571 return err;
1572}
1573EXPORT_SYMBOL(km_migrate);
1574
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001575int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1576{
1577 int err = -EINVAL;
1578 int ret;
1579 struct xfrm_mgr *km;
1580
1581 read_lock(&xfrm_km_lock);
1582 list_for_each_entry(km, &xfrm_km_list, list) {
1583 if (km->report) {
1584 ret = km->report(proto, sel, addr);
1585 if (!ret)
1586 err = ret;
1587 }
1588 }
1589 read_unlock(&xfrm_km_lock);
1590 return err;
1591}
1592EXPORT_SYMBOL(km_report);
1593
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1595{
1596 int err;
1597 u8 *data;
1598 struct xfrm_mgr *km;
1599 struct xfrm_policy *pol = NULL;
1600
1601 if (optlen <= 0 || optlen > PAGE_SIZE)
1602 return -EMSGSIZE;
1603
1604 data = kmalloc(optlen, GFP_KERNEL);
1605 if (!data)
1606 return -ENOMEM;
1607
1608 err = -EFAULT;
1609 if (copy_from_user(data, optval, optlen))
1610 goto out;
1611
1612 err = -EINVAL;
1613 read_lock(&xfrm_km_lock);
1614 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001615 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 optlen, &err);
1617 if (err >= 0)
1618 break;
1619 }
1620 read_unlock(&xfrm_km_lock);
1621
1622 if (err >= 0) {
1623 xfrm_sk_policy_insert(sk, err, pol);
1624 xfrm_pol_put(pol);
1625 err = 0;
1626 }
1627
1628out:
1629 kfree(data);
1630 return err;
1631}
1632EXPORT_SYMBOL(xfrm_user_policy);
1633
1634int xfrm_register_km(struct xfrm_mgr *km)
1635{
1636 write_lock_bh(&xfrm_km_lock);
1637 list_add_tail(&km->list, &xfrm_km_list);
1638 write_unlock_bh(&xfrm_km_lock);
1639 return 0;
1640}
1641EXPORT_SYMBOL(xfrm_register_km);
1642
1643int xfrm_unregister_km(struct xfrm_mgr *km)
1644{
1645 write_lock_bh(&xfrm_km_lock);
1646 list_del(&km->list);
1647 write_unlock_bh(&xfrm_km_lock);
1648 return 0;
1649}
1650EXPORT_SYMBOL(xfrm_unregister_km);
1651
1652int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1653{
1654 int err = 0;
1655 if (unlikely(afinfo == NULL))
1656 return -EINVAL;
1657 if (unlikely(afinfo->family >= NPROTO))
1658 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001659 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1661 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001662 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001664 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return err;
1666}
1667EXPORT_SYMBOL(xfrm_state_register_afinfo);
1668
1669int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1670{
1671 int err = 0;
1672 if (unlikely(afinfo == NULL))
1673 return -EINVAL;
1674 if (unlikely(afinfo->family >= NPROTO))
1675 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001676 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1678 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1679 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001680 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001683 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 return err;
1685}
1686EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1687
Miika Komucdca7262007-02-06 14:24:56 -08001688struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689{
1690 struct xfrm_state_afinfo *afinfo;
1691 if (unlikely(family >= NPROTO))
1692 return NULL;
1693 read_lock(&xfrm_state_afinfo_lock);
1694 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001695 if (unlikely(!afinfo))
1696 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 return afinfo;
1698}
1699
Miika Komucdca7262007-02-06 14:24:56 -08001700void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701{
Herbert Xu546be242006-05-27 23:03:58 -07001702 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703}
1704
Miika Komucdca7262007-02-06 14:24:56 -08001705EXPORT_SYMBOL(xfrm_state_get_afinfo);
1706EXPORT_SYMBOL(xfrm_state_put_afinfo);
1707
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1709void xfrm_state_delete_tunnel(struct xfrm_state *x)
1710{
1711 if (x->tunnel) {
1712 struct xfrm_state *t = x->tunnel;
1713
1714 if (atomic_read(&t->tunnel_users) == 2)
1715 xfrm_state_delete(t);
1716 atomic_dec(&t->tunnel_users);
1717 xfrm_state_put(t);
1718 x->tunnel = NULL;
1719 }
1720}
1721EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1722
1723int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1724{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001725 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Patrick McHardyc5c25232007-04-09 11:47:18 -07001727 spin_lock_bh(&x->lock);
1728 if (x->km.state == XFRM_STATE_VALID &&
1729 x->type && x->type->get_mtu)
1730 res = x->type->get_mtu(x, mtu);
1731 else
Patrick McHardy28121612007-06-18 22:30:15 -07001732 res = mtu - x->props.header_len;
Patrick McHardyc5c25232007-04-09 11:47:18 -07001733 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 return res;
1735}
1736
Herbert Xu72cb6962005-06-20 13:18:08 -07001737int xfrm_init_state(struct xfrm_state *x)
1738{
Herbert Xud094cd82005-06-20 13:19:41 -07001739 struct xfrm_state_afinfo *afinfo;
1740 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001741 int err;
1742
Herbert Xud094cd82005-06-20 13:19:41 -07001743 err = -EAFNOSUPPORT;
1744 afinfo = xfrm_state_get_afinfo(family);
1745 if (!afinfo)
1746 goto error;
1747
1748 err = 0;
1749 if (afinfo->init_flags)
1750 err = afinfo->init_flags(x);
1751
1752 xfrm_state_put_afinfo(afinfo);
1753
1754 if (err)
1755 goto error;
1756
1757 err = -EPROTONOSUPPORT;
1758 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001759 if (x->type == NULL)
1760 goto error;
1761
1762 err = x->type->init_state(x);
1763 if (err)
1764 goto error;
1765
Herbert Xub59f45d2006-05-27 23:05:54 -07001766 x->mode = xfrm_get_mode(x->props.mode, family);
1767 if (x->mode == NULL)
1768 goto error;
1769
Herbert Xu72cb6962005-06-20 13:18:08 -07001770 x->km.state = XFRM_STATE_VALID;
1771
1772error:
1773 return err;
1774}
1775
1776EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001777
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778void __init xfrm_state_init(void)
1779{
David S. Millerf034b5d2006-08-24 03:08:07 -07001780 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
David S. Millerf034b5d2006-08-24 03:08:07 -07001782 sz = sizeof(struct hlist_head) * 8;
1783
David S. Miller44e36b42006-08-24 04:50:50 -07001784 xfrm_state_bydst = xfrm_hash_alloc(sz);
1785 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1786 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001787 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1788 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1789 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1790
David Howellsc4028952006-11-22 14:57:56 +00001791 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792}
1793