blob: 372f06eb8bb7e389298c555999f785232a92ff94 [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 Latten161a09e2006-11-27 13:11:54 -0600394void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
396 int i;
Joy Latten161a09e2006-11-27 13:11:54 -0600397 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399 spin_lock_bh(&xfrm_state_lock);
Masahide NAKAMURAa9917c02006-08-31 15:14:32 -0700400 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -0700401 struct hlist_node *entry;
402 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403restart:
David S. Miller8f126e32006-08-24 02:45:07 -0700404 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 if (!xfrm_state_kern(x) &&
Masahide NAKAMURA57947082006-09-22 15:06:24 -0700406 xfrm_id_proto_match(x->id.proto, proto)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 xfrm_state_hold(x);
408 spin_unlock_bh(&xfrm_state_lock);
409
Joy Latten161a09e2006-11-27 13:11:54 -0600410 err = xfrm_state_delete(x);
411 xfrm_audit_log(audit_info->loginuid,
412 audit_info->secid,
413 AUDIT_MAC_IPSEC_DELSA,
414 err ? 0 : 1, NULL, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 xfrm_state_put(x);
416
417 spin_lock_bh(&xfrm_state_lock);
418 goto restart;
419 }
420 }
421 }
422 spin_unlock_bh(&xfrm_state_lock);
423 wake_up(&km_waitq);
424}
425EXPORT_SYMBOL(xfrm_state_flush);
426
Jamal Hadi Salimaf11e312007-05-04 12:55:13 -0700427void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
Jamal Hadi Salim28d89092007-04-26 00:10:29 -0700428{
429 spin_lock_bh(&xfrm_state_lock);
430 si->sadcnt = xfrm_state_num;
431 si->sadhcnt = xfrm_state_hmask;
432 si->sadhmcnt = xfrm_state_hashmax;
433 spin_unlock_bh(&xfrm_state_lock);
434}
435EXPORT_SYMBOL(xfrm_sad_getinfo);
436
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437static int
438xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
439 struct xfrm_tmpl *tmpl,
440 xfrm_address_t *daddr, xfrm_address_t *saddr,
441 unsigned short family)
442{
443 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
444 if (!afinfo)
445 return -1;
446 afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
447 xfrm_state_put_afinfo(afinfo);
448 return 0;
449}
450
Al Viroa94cfd12006-09-27 18:47:24 -0700451static 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 -0700452{
453 unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
454 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700455 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700456
David S. Miller8f126e32006-08-24 02:45:07 -0700457 hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
David S. Milleredcd5822006-08-24 00:42:45 -0700458 if (x->props.family != family ||
459 x->id.spi != spi ||
460 x->id.proto != proto)
461 continue;
462
463 switch (family) {
464 case AF_INET:
465 if (x->id.daddr.a4 != daddr->a4)
466 continue;
467 break;
468 case AF_INET6:
469 if (!ipv6_addr_equal((struct in6_addr *)daddr,
470 (struct in6_addr *)
471 x->id.daddr.a6))
472 continue;
473 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700474 }
David S. Milleredcd5822006-08-24 00:42:45 -0700475
476 xfrm_state_hold(x);
477 return x;
478 }
479
480 return NULL;
481}
482
483static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
484{
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700485 unsigned int h = xfrm_src_hash(daddr, saddr, family);
David S. Milleredcd5822006-08-24 00:42:45 -0700486 struct xfrm_state *x;
David S. Miller8f126e32006-08-24 02:45:07 -0700487 struct hlist_node *entry;
David S. Milleredcd5822006-08-24 00:42:45 -0700488
David S. Miller8f126e32006-08-24 02:45:07 -0700489 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
David S. Milleredcd5822006-08-24 00:42:45 -0700490 if (x->props.family != family ||
491 x->id.proto != proto)
492 continue;
493
494 switch (family) {
495 case AF_INET:
496 if (x->id.daddr.a4 != daddr->a4 ||
497 x->props.saddr.a4 != saddr->a4)
498 continue;
499 break;
500 case AF_INET6:
501 if (!ipv6_addr_equal((struct in6_addr *)daddr,
502 (struct in6_addr *)
503 x->id.daddr.a6) ||
504 !ipv6_addr_equal((struct in6_addr *)saddr,
505 (struct in6_addr *)
506 x->props.saddr.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 inline struct xfrm_state *
519__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
520{
521 if (use_spi)
522 return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
523 x->id.proto, family);
524 else
525 return __xfrm_state_lookup_byaddr(&x->id.daddr,
526 &x->props.saddr,
527 x->id.proto, family);
528}
529
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700530static void xfrm_hash_grow_check(int have_hash_collision)
531{
532 if (have_hash_collision &&
533 (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
534 xfrm_state_num > xfrm_state_hmask)
535 schedule_work(&xfrm_hash_work);
536}
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900539xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 struct flowi *fl, struct xfrm_tmpl *tmpl,
541 struct xfrm_policy *pol, int *err,
542 unsigned short family)
543{
David S. Millerc1969f22006-08-24 04:00:03 -0700544 unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700545 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 struct xfrm_state *x, *x0;
547 int acquire_in_progress = 0;
548 int error = 0;
549 struct xfrm_state *best = NULL;
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 spin_lock_bh(&xfrm_state_lock);
David S. Miller8f126e32006-08-24 02:45:07 -0700552 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 if (x->props.family == family &&
554 x->props.reqid == tmpl->reqid &&
Masahide NAKAMURAfbd9a5b2006-08-23 18:08:21 -0700555 !(x->props.flags & XFRM_STATE_WILDRECV) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 xfrm_state_addr_check(x, daddr, saddr, family) &&
557 tmpl->mode == x->props.mode &&
558 tmpl->id.proto == x->id.proto &&
559 (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
560 /* Resolution logic:
561 1. There is a valid state with matching selector.
562 Done.
563 2. Valid state with inappropriate selector. Skip.
564
565 Entering area of "sysdeps".
566
567 3. If state is not valid, selector is temporary,
568 it selects only session which triggered
569 previous resolution. Key manager will do
570 something to install a state with proper
571 selector.
572 */
573 if (x->km.state == XFRM_STATE_VALID) {
Trent Jaegerdf718372005-12-13 23:12:27 -0800574 if (!xfrm_selector_match(&x->sel, fl, family) ||
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700575 !security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 continue;
577 if (!best ||
578 best->km.dying > x->km.dying ||
579 (best->km.dying == x->km.dying &&
580 best->curlft.add_time < x->curlft.add_time))
581 best = x;
582 } else if (x->km.state == XFRM_STATE_ACQ) {
583 acquire_in_progress = 1;
584 } else if (x->km.state == XFRM_STATE_ERROR ||
585 x->km.state == XFRM_STATE_EXPIRED) {
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900586 if (xfrm_selector_match(&x->sel, fl, family) &&
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700587 security_xfrm_state_pol_flow_match(x, pol, fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 error = -ESRCH;
589 }
590 }
591 }
592
593 x = best;
594 if (!x && !error && !acquire_in_progress) {
Patrick McHardy5c5d2812005-04-21 20:12:32 -0700595 if (tmpl->id.spi &&
David S. Milleredcd5822006-08-24 00:42:45 -0700596 (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
597 tmpl->id.proto, family)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 xfrm_state_put(x0);
599 error = -EEXIST;
600 goto out;
601 }
602 x = xfrm_state_alloc();
603 if (x == NULL) {
604 error = -ENOMEM;
605 goto out;
606 }
607 /* Initialize temporary selector matching only
608 * to current session. */
609 xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
610
Venkat Yekkiralae0d1caa2006-07-24 23:29:07 -0700611 error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
612 if (error) {
613 x->km.state = XFRM_STATE_DEAD;
614 xfrm_state_put(x);
615 x = NULL;
616 goto out;
617 }
618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 if (km_query(x, tmpl, pol) == 0) {
620 x->km.state = XFRM_STATE_ACQ;
David S. Miller8f126e32006-08-24 02:45:07 -0700621 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700622 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700623 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 if (x->id.spi) {
625 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700626 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 }
David S. Miller01e67d02007-05-25 00:41:38 -0700628 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
629 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 add_timer(&x->timer);
Patrick McHardy2fab22f2006-10-24 15:34:00 -0700631 xfrm_state_num++;
632 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 } else {
634 x->km.state = XFRM_STATE_DEAD;
635 xfrm_state_put(x);
636 x = NULL;
637 error = -ESRCH;
638 }
639 }
640out:
641 if (x)
642 xfrm_state_hold(x);
643 else
644 *err = acquire_in_progress ? -EAGAIN : error;
645 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return x;
647}
648
649static void __xfrm_state_insert(struct xfrm_state *x)
650{
David S. Millera624c102006-08-24 03:24:33 -0700651 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
David S. Miller9d4a7062006-08-24 03:18:09 -0700653 x->genid = ++xfrm_state_genid;
654
David S. Millerc1969f22006-08-24 04:00:03 -0700655 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
656 x->props.reqid, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700657 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700659 h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -0700660 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Masahide NAKAMURA7b4dc3602006-09-27 22:21:52 -0700662 if (x->id.spi) {
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700663 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
664 x->props.family);
665
David S. Miller8f126e32006-08-24 02:45:07 -0700666 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Masahide NAKAMURA6c44e6b2006-08-23 17:53:57 -0700667 }
668
David S. Millera47f0ce2006-08-24 03:54:22 -0700669 mod_timer(&x->timer, jiffies + HZ);
670 if (x->replay_maxage)
671 mod_timer(&x->rtimer, jiffies + x->replay_maxage);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -0800672
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 wake_up(&km_waitq);
David S. Millerf034b5d2006-08-24 03:08:07 -0700674
675 xfrm_state_num++;
676
David S. Miller918049f2006-10-12 22:03:24 -0700677 xfrm_hash_grow_check(x->bydst.next != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678}
679
David S. Millerc7f5ea32006-08-24 03:29:04 -0700680/* xfrm_state_lock is held */
681static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
682{
683 unsigned short family = xnew->props.family;
684 u32 reqid = xnew->props.reqid;
685 struct xfrm_state *x;
686 struct hlist_node *entry;
687 unsigned int h;
688
David S. Millerc1969f22006-08-24 04:00:03 -0700689 h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700690 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
691 if (x->props.family == family &&
692 x->props.reqid == reqid &&
David S. Millerc1969f22006-08-24 04:00:03 -0700693 !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
694 !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
David S. Millerc7f5ea32006-08-24 03:29:04 -0700695 x->genid = xfrm_state_genid;
696 }
697}
698
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699void xfrm_state_insert(struct xfrm_state *x)
700{
701 spin_lock_bh(&xfrm_state_lock);
David S. Millerc7f5ea32006-08-24 03:29:04 -0700702 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 __xfrm_state_insert(x);
704 spin_unlock_bh(&xfrm_state_lock);
705}
706EXPORT_SYMBOL(xfrm_state_insert);
707
David S. Miller27708342006-08-24 00:13:10 -0700708/* xfrm_state_lock is held */
709static 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)
710{
David S. Millerc1969f22006-08-24 04:00:03 -0700711 unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700712 struct hlist_node *entry;
David S. Miller27708342006-08-24 00:13:10 -0700713 struct xfrm_state *x;
714
David S. Miller8f126e32006-08-24 02:45:07 -0700715 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
David S. Miller27708342006-08-24 00:13:10 -0700716 if (x->props.reqid != reqid ||
717 x->props.mode != mode ||
718 x->props.family != family ||
719 x->km.state != XFRM_STATE_ACQ ||
Joy Latten75e252d2007-03-12 17:14:07 -0700720 x->id.spi != 0 ||
721 x->id.proto != proto)
David S. Miller27708342006-08-24 00:13:10 -0700722 continue;
723
724 switch (family) {
725 case AF_INET:
726 if (x->id.daddr.a4 != daddr->a4 ||
727 x->props.saddr.a4 != saddr->a4)
728 continue;
729 break;
730 case AF_INET6:
731 if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
732 (struct in6_addr *)daddr) ||
733 !ipv6_addr_equal((struct in6_addr *)
734 x->props.saddr.a6,
735 (struct in6_addr *)saddr))
736 continue;
737 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700738 }
David S. Miller27708342006-08-24 00:13:10 -0700739
740 xfrm_state_hold(x);
741 return x;
742 }
743
744 if (!create)
745 return NULL;
746
747 x = xfrm_state_alloc();
748 if (likely(x)) {
749 switch (family) {
750 case AF_INET:
751 x->sel.daddr.a4 = daddr->a4;
752 x->sel.saddr.a4 = saddr->a4;
753 x->sel.prefixlen_d = 32;
754 x->sel.prefixlen_s = 32;
755 x->props.saddr.a4 = saddr->a4;
756 x->id.daddr.a4 = daddr->a4;
757 break;
758
759 case AF_INET6:
760 ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
761 (struct in6_addr *)daddr);
762 ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
763 (struct in6_addr *)saddr);
764 x->sel.prefixlen_d = 128;
765 x->sel.prefixlen_s = 128;
766 ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
767 (struct in6_addr *)saddr);
768 ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
769 (struct in6_addr *)daddr);
770 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700771 }
David S. Miller27708342006-08-24 00:13:10 -0700772
773 x->km.state = XFRM_STATE_ACQ;
774 x->id.proto = proto;
775 x->props.family = family;
776 x->props.mode = mode;
777 x->props.reqid = reqid;
David S. Miller01e67d02007-05-25 00:41:38 -0700778 x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
David S. Miller27708342006-08-24 00:13:10 -0700779 xfrm_state_hold(x);
David S. Miller01e67d02007-05-25 00:41:38 -0700780 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
David S. Miller27708342006-08-24 00:13:10 -0700781 add_timer(&x->timer);
David S. Miller8f126e32006-08-24 02:45:07 -0700782 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
Masahide NAKAMURA667bbcb2006-10-03 15:56:09 -0700783 h = xfrm_src_hash(daddr, saddr, family);
David S. Miller8f126e32006-08-24 02:45:07 -0700784 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
David S. Miller27708342006-08-24 00:13:10 -0700785 wake_up(&km_waitq);
David S. Miller918049f2006-10-12 22:03:24 -0700786
787 xfrm_state_num++;
788
789 xfrm_hash_grow_check(x->bydst.next != NULL);
David S. Miller27708342006-08-24 00:13:10 -0700790 }
791
792 return x;
793}
794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
796
797int xfrm_state_add(struct xfrm_state *x)
798{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 struct xfrm_state *x1;
800 int family;
801 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700802 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
804 family = x->props.family;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806 spin_lock_bh(&xfrm_state_lock);
807
David S. Milleredcd5822006-08-24 00:42:45 -0700808 x1 = __xfrm_state_locate(x, use_spi, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 if (x1) {
810 xfrm_state_put(x1);
811 x1 = NULL;
812 err = -EEXIST;
813 goto out;
814 }
815
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700816 if (use_spi && x->km.seq) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 x1 = __xfrm_find_acq_byseq(x->km.seq);
Joy Latten75e252d2007-03-12 17:14:07 -0700818 if (x1 && ((x1->id.proto != x->id.proto) ||
819 xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 xfrm_state_put(x1);
821 x1 = NULL;
822 }
823 }
824
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -0700825 if (use_spi && !x1)
David S. Miller27708342006-08-24 00:13:10 -0700826 x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
827 x->id.proto,
828 &x->id.daddr, &x->props.saddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
David S. Millerc7f5ea32006-08-24 03:29:04 -0700830 __xfrm_state_bump_genids(x);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 __xfrm_state_insert(x);
832 err = 0;
833
834out:
835 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 if (x1) {
838 xfrm_state_delete(x1);
839 xfrm_state_put(x1);
840 }
841
842 return err;
843}
844EXPORT_SYMBOL(xfrm_state_add);
845
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800846#ifdef CONFIG_XFRM_MIGRATE
847struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
848{
849 int err = -ENOMEM;
850 struct xfrm_state *x = xfrm_state_alloc();
851 if (!x)
852 goto error;
853
854 memcpy(&x->id, &orig->id, sizeof(x->id));
855 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
856 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
857 x->props.mode = orig->props.mode;
858 x->props.replay_window = orig->props.replay_window;
859 x->props.reqid = orig->props.reqid;
860 x->props.family = orig->props.family;
861 x->props.saddr = orig->props.saddr;
862
863 if (orig->aalg) {
864 x->aalg = xfrm_algo_clone(orig->aalg);
865 if (!x->aalg)
866 goto error;
867 }
868 x->props.aalgo = orig->props.aalgo;
869
870 if (orig->ealg) {
871 x->ealg = xfrm_algo_clone(orig->ealg);
872 if (!x->ealg)
873 goto error;
874 }
875 x->props.ealgo = orig->props.ealgo;
876
877 if (orig->calg) {
878 x->calg = xfrm_algo_clone(orig->calg);
879 if (!x->calg)
880 goto error;
881 }
882 x->props.calgo = orig->props.calgo;
883
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900884 if (orig->encap) {
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800885 x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
886 if (!x->encap)
887 goto error;
888 }
889
890 if (orig->coaddr) {
891 x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
892 GFP_KERNEL);
893 if (!x->coaddr)
894 goto error;
895 }
896
897 err = xfrm_init_state(x);
898 if (err)
899 goto error;
900
901 x->props.flags = orig->props.flags;
902
903 x->curlft.add_time = orig->curlft.add_time;
904 x->km.state = orig->km.state;
905 x->km.seq = orig->km.seq;
906
907 return x;
908
909 error:
910 if (errp)
911 *errp = err;
912 if (x) {
913 kfree(x->aalg);
914 kfree(x->ealg);
915 kfree(x->calg);
916 kfree(x->encap);
917 kfree(x->coaddr);
918 }
919 kfree(x);
920 return NULL;
921}
922EXPORT_SYMBOL(xfrm_state_clone);
923
924/* xfrm_state_lock is held */
925struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
926{
927 unsigned int h;
928 struct xfrm_state *x;
929 struct hlist_node *entry;
930
931 if (m->reqid) {
932 h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
933 m->reqid, m->old_family);
934 hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
935 if (x->props.mode != m->mode ||
936 x->id.proto != m->proto)
937 continue;
938 if (m->reqid && x->props.reqid != m->reqid)
939 continue;
940 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
941 m->old_family) ||
942 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
943 m->old_family))
944 continue;
945 xfrm_state_hold(x);
946 return x;
947 }
948 } else {
949 h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
950 m->old_family);
951 hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
952 if (x->props.mode != m->mode ||
953 x->id.proto != m->proto)
954 continue;
955 if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
956 m->old_family) ||
957 xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
958 m->old_family))
959 continue;
960 xfrm_state_hold(x);
961 return x;
962 }
963 }
964
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +0900965 return NULL;
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -0800966}
967EXPORT_SYMBOL(xfrm_migrate_state_find);
968
969struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
970 struct xfrm_migrate *m)
971{
972 struct xfrm_state *xc;
973 int err;
974
975 xc = xfrm_state_clone(x, &err);
976 if (!xc)
977 return NULL;
978
979 memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
980 memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
981
982 /* add state */
983 if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
984 /* a care is needed when the destination address of the
985 state is to be updated as it is a part of triplet */
986 xfrm_state_insert(xc);
987 } else {
988 if ((err = xfrm_state_add(xc)) < 0)
989 goto error;
990 }
991
992 return xc;
993error:
994 kfree(xc);
995 return NULL;
996}
997EXPORT_SYMBOL(xfrm_state_migrate);
998#endif
999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000int xfrm_state_update(struct xfrm_state *x)
1001{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 struct xfrm_state *x1;
1003 int err;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001004 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001007 x1 = __xfrm_state_locate(x, use_spi, x->props.family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
1009 err = -ESRCH;
1010 if (!x1)
1011 goto out;
1012
1013 if (xfrm_state_kern(x1)) {
1014 xfrm_state_put(x1);
1015 err = -EEXIST;
1016 goto out;
1017 }
1018
1019 if (x1->km.state == XFRM_STATE_ACQ) {
1020 __xfrm_state_insert(x);
1021 x = NULL;
1022 }
1023 err = 0;
1024
1025out:
1026 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
1028 if (err)
1029 return err;
1030
1031 if (!x) {
1032 xfrm_state_delete(x1);
1033 xfrm_state_put(x1);
1034 return 0;
1035 }
1036
1037 err = -EINVAL;
1038 spin_lock_bh(&x1->lock);
1039 if (likely(x1->km.state == XFRM_STATE_VALID)) {
1040 if (x->encap && x1->encap)
1041 memcpy(x1->encap, x->encap, sizeof(*x1->encap));
Noriaki TAKAMIYA060f02a2006-08-23 18:18:55 -07001042 if (x->coaddr && x1->coaddr) {
1043 memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1044 }
1045 if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1046 memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
1048 x1->km.dying = 0;
1049
David S. Millera47f0ce2006-08-24 03:54:22 -07001050 mod_timer(&x1->timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 if (x1->curlft.use_time)
1052 xfrm_state_check_expire(x1);
1053
1054 err = 0;
1055 }
1056 spin_unlock_bh(&x1->lock);
1057
1058 xfrm_state_put(x1);
1059
1060 return err;
1061}
1062EXPORT_SYMBOL(xfrm_state_update);
1063
1064int xfrm_state_check_expire(struct xfrm_state *x)
1065{
1066 if (!x->curlft.use_time)
James Morris9d729f72007-03-04 16:12:44 -08001067 x->curlft.use_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
1069 if (x->km.state != XFRM_STATE_VALID)
1070 return -EINVAL;
1071
1072 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
1073 x->curlft.packets >= x->lft.hard_packet_limit) {
Herbert Xu4666faa2005-06-18 22:43:22 -07001074 x->km.state = XFRM_STATE_EXPIRED;
David S. Millera47f0ce2006-08-24 03:54:22 -07001075 mod_timer(&x->timer, jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return -EINVAL;
1077 }
1078
1079 if (!x->km.dying &&
1080 (x->curlft.bytes >= x->lft.soft_byte_limit ||
Herbert Xu4666faa2005-06-18 22:43:22 -07001081 x->curlft.packets >= x->lft.soft_packet_limit)) {
1082 x->km.dying = 1;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001083 km_state_expired(x, 0, 0);
Herbert Xu4666faa2005-06-18 22:43:22 -07001084 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 return 0;
1086}
1087EXPORT_SYMBOL(xfrm_state_check_expire);
1088
1089static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
1090{
1091 int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
1092 - skb_headroom(skb);
1093
1094 if (nhead > 0)
1095 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
1096
1097 /* Check tail too... */
1098 return 0;
1099}
1100
1101int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
1102{
1103 int err = xfrm_state_check_expire(x);
1104 if (err < 0)
1105 goto err;
1106 err = xfrm_state_check_space(x, skb);
1107err:
1108 return err;
1109}
1110EXPORT_SYMBOL(xfrm_state_check);
1111
1112struct xfrm_state *
Al Viroa94cfd12006-09-27 18:47:24 -07001113xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 unsigned short family)
1115{
1116 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
1118 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001119 x = __xfrm_state_lookup(daddr, spi, proto, family);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 spin_unlock_bh(&xfrm_state_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 return x;
1122}
1123EXPORT_SYMBOL(xfrm_state_lookup);
1124
1125struct xfrm_state *
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001126xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1127 u8 proto, unsigned short family)
1128{
1129 struct xfrm_state *x;
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001130
1131 spin_lock_bh(&xfrm_state_lock);
David S. Milleredcd5822006-08-24 00:42:45 -07001132 x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001133 spin_unlock_bh(&xfrm_state_lock);
Masahide NAKAMURAeb2971b2006-08-23 17:56:04 -07001134 return x;
1135}
1136EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1137
1138struct xfrm_state *
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001139xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
1140 xfrm_address_t *daddr, xfrm_address_t *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 int create, unsigned short family)
1142{
1143 struct xfrm_state *x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
1145 spin_lock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001146 x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 spin_unlock_bh(&xfrm_state_lock);
David S. Miller27708342006-08-24 00:13:10 -07001148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 return x;
1150}
1151EXPORT_SYMBOL(xfrm_find_acq);
1152
Masahide NAKAMURA41a49cc2006-08-23 22:48:31 -07001153#ifdef CONFIG_XFRM_SUB_POLICY
1154int
1155xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
1156 unsigned short family)
1157{
1158 int err = 0;
1159 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1160 if (!afinfo)
1161 return -EAFNOSUPPORT;
1162
1163 spin_lock_bh(&xfrm_state_lock);
1164 if (afinfo->tmpl_sort)
1165 err = afinfo->tmpl_sort(dst, src, n);
1166 spin_unlock_bh(&xfrm_state_lock);
1167 xfrm_state_put_afinfo(afinfo);
1168 return err;
1169}
1170EXPORT_SYMBOL(xfrm_tmpl_sort);
1171
1172int
1173xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
1174 unsigned short family)
1175{
1176 int err = 0;
1177 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
1178 if (!afinfo)
1179 return -EAFNOSUPPORT;
1180
1181 spin_lock_bh(&xfrm_state_lock);
1182 if (afinfo->state_sort)
1183 err = afinfo->state_sort(dst, src, n);
1184 spin_unlock_bh(&xfrm_state_lock);
1185 xfrm_state_put_afinfo(afinfo);
1186 return err;
1187}
1188EXPORT_SYMBOL(xfrm_state_sort);
1189#endif
1190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191/* Silly enough, but I'm lazy to build resolution list */
1192
1193static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
1194{
1195 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
David S. Millerf034b5d2006-08-24 03:08:07 -07001197 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001198 struct hlist_node *entry;
1199 struct xfrm_state *x;
1200
1201 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
1202 if (x->km.seq == seq &&
1203 x->km.state == XFRM_STATE_ACQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 xfrm_state_hold(x);
1205 return x;
1206 }
1207 }
1208 }
1209 return NULL;
1210}
1211
1212struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
1213{
1214 struct xfrm_state *x;
1215
1216 spin_lock_bh(&xfrm_state_lock);
1217 x = __xfrm_find_acq_byseq(seq);
1218 spin_unlock_bh(&xfrm_state_lock);
1219 return x;
1220}
1221EXPORT_SYMBOL(xfrm_find_acq_byseq);
1222
1223u32 xfrm_get_acqseq(void)
1224{
1225 u32 res;
1226 static u32 acqseq;
1227 static DEFINE_SPINLOCK(acqseq_lock);
1228
1229 spin_lock_bh(&acqseq_lock);
1230 res = (++acqseq ? : ++acqseq);
1231 spin_unlock_bh(&acqseq_lock);
1232 return res;
1233}
1234EXPORT_SYMBOL(xfrm_get_acqseq);
1235
1236void
Al Viro26977b42006-09-27 18:47:05 -07001237xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238{
David S. Millerf034b5d2006-08-24 03:08:07 -07001239 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 struct xfrm_state *x0;
1241
1242 if (x->id.spi)
1243 return;
1244
1245 if (minspi == maxspi) {
1246 x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
1247 if (x0) {
1248 xfrm_state_put(x0);
1249 return;
1250 }
1251 x->id.spi = minspi;
1252 } else {
1253 u32 spi = 0;
Al Viro26977b42006-09-27 18:47:05 -07001254 u32 low = ntohl(minspi);
1255 u32 high = ntohl(maxspi);
1256 for (h=0; h<high-low+1; h++) {
1257 spi = low + net_random()%(high-low+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
1259 if (x0 == NULL) {
1260 x->id.spi = htonl(spi);
1261 break;
1262 }
1263 xfrm_state_put(x0);
1264 }
1265 }
1266 if (x->id.spi) {
1267 spin_lock_bh(&xfrm_state_lock);
1268 h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
David S. Miller8f126e32006-08-24 02:45:07 -07001269 hlist_add_head(&x->byspi, xfrm_state_byspi+h);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 spin_unlock_bh(&xfrm_state_lock);
1271 wake_up(&km_waitq);
1272 }
1273}
1274EXPORT_SYMBOL(xfrm_alloc_spi);
1275
1276int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
1277 void *data)
1278{
1279 int i;
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001280 struct xfrm_state *x, *last = NULL;
David S. Miller8f126e32006-08-24 02:45:07 -07001281 struct hlist_node *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 int count = 0;
1283 int err = 0;
1284
1285 spin_lock_bh(&xfrm_state_lock);
David S. Millerf034b5d2006-08-24 03:08:07 -07001286 for (i = 0; i <= xfrm_state_hmask; i++) {
David S. Miller8f126e32006-08-24 02:45:07 -07001287 hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001288 if (!xfrm_id_proto_match(x->id.proto, proto))
1289 continue;
1290 if (last) {
1291 err = func(last, count, data);
1292 if (err)
1293 goto out;
1294 }
1295 last = x;
1296 count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 }
1298 }
1299 if (count == 0) {
1300 err = -ENOENT;
1301 goto out;
1302 }
Jamal Hadi Salim94b9bb52006-12-04 20:03:35 -08001303 err = func(last, 0, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304out:
1305 spin_unlock_bh(&xfrm_state_lock);
1306 return err;
1307}
1308EXPORT_SYMBOL(xfrm_state_walk);
1309
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001310
1311void xfrm_replay_notify(struct xfrm_state *x, int event)
1312{
1313 struct km_event c;
1314 /* we send notify messages in case
1315 * 1. we updated on of the sequence numbers, and the seqno difference
1316 * is at least x->replay_maxdiff, in this case we also update the
1317 * timeout of our timer function
1318 * 2. if x->replay_maxage has elapsed since last update,
1319 * and there were changes
1320 *
1321 * The state structure must be locked!
1322 */
1323
1324 switch (event) {
1325 case XFRM_REPLAY_UPDATE:
1326 if (x->replay_maxdiff &&
1327 (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001328 (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
1329 if (x->xflags & XFRM_TIME_DEFER)
1330 event = XFRM_REPLAY_TIMEOUT;
1331 else
1332 return;
1333 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001334
1335 break;
1336
1337 case XFRM_REPLAY_TIMEOUT:
1338 if ((x->replay.seq == x->preplay.seq) &&
1339 (x->replay.bitmap == x->preplay.bitmap) &&
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001340 (x->replay.oseq == x->preplay.oseq)) {
1341 x->xflags |= XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001342 return;
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001343 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001344
1345 break;
1346 }
1347
1348 memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1349 c.event = XFRM_MSG_NEWAE;
1350 c.data.aevent = event;
1351 km_state_notify(x, &c);
1352
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001353 if (x->replay_maxage &&
David S. Millera47f0ce2006-08-24 03:54:22 -07001354 !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001355 x->xflags &= ~XFRM_TIME_DEFER;
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001356}
David S. Millera70fcb02006-03-20 19:18:52 -08001357EXPORT_SYMBOL(xfrm_replay_notify);
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001358
1359static void xfrm_replay_timer_handler(unsigned long data)
1360{
1361 struct xfrm_state *x = (struct xfrm_state*)data;
1362
1363 spin_lock(&x->lock);
1364
Jamal Hadi Salim27170962006-04-14 15:03:05 -07001365 if (x->km.state == XFRM_STATE_VALID) {
1366 if (xfrm_aevent_is_on())
1367 xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
1368 else
1369 x->xflags |= XFRM_TIME_DEFER;
1370 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001371
1372 spin_unlock(&x->lock);
1373}
1374
Al Viroa252cc22006-09-27 18:48:18 -07001375int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376{
1377 u32 diff;
Al Viroa252cc22006-09-27 18:48:18 -07001378 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
1380 if (unlikely(seq == 0))
1381 return -EINVAL;
1382
1383 if (likely(seq > x->replay.seq))
1384 return 0;
1385
1386 diff = x->replay.seq - seq;
Herbert Xu4c4d51a72007-04-05 00:07:39 -07001387 if (diff >= min_t(unsigned int, x->props.replay_window,
1388 sizeof(x->replay.bitmap) * 8)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 x->stats.replay_window++;
1390 return -EINVAL;
1391 }
1392
1393 if (x->replay.bitmap & (1U << diff)) {
1394 x->stats.replay++;
1395 return -EINVAL;
1396 }
1397 return 0;
1398}
1399EXPORT_SYMBOL(xfrm_replay_check);
1400
Al Viro61f46272006-09-27 18:48:33 -07001401void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402{
1403 u32 diff;
Al Viro61f46272006-09-27 18:48:33 -07001404 u32 seq = ntohl(net_seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
1406 if (seq > x->replay.seq) {
1407 diff = seq - x->replay.seq;
1408 if (diff < x->props.replay_window)
1409 x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1410 else
1411 x->replay.bitmap = 1;
1412 x->replay.seq = seq;
1413 } else {
1414 diff = x->replay.seq - seq;
1415 x->replay.bitmap |= (1U << diff);
1416 }
Jamal Hadi Salimf8cd5482006-03-20 19:15:11 -08001417
1418 if (xfrm_aevent_is_on())
1419 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420}
1421EXPORT_SYMBOL(xfrm_replay_advance);
1422
1423static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
1424static DEFINE_RWLOCK(xfrm_km_lock);
1425
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001426void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427{
1428 struct xfrm_mgr *km;
1429
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001430 read_lock(&xfrm_km_lock);
1431 list_for_each_entry(km, &xfrm_km_list, list)
1432 if (km->notify_policy)
1433 km->notify_policy(xp, dir, c);
1434 read_unlock(&xfrm_km_lock);
1435}
1436
1437void km_state_notify(struct xfrm_state *x, struct km_event *c)
1438{
1439 struct xfrm_mgr *km;
1440 read_lock(&xfrm_km_lock);
1441 list_for_each_entry(km, &xfrm_km_list, list)
1442 if (km->notify)
1443 km->notify(x, c);
1444 read_unlock(&xfrm_km_lock);
1445}
1446
1447EXPORT_SYMBOL(km_policy_notify);
1448EXPORT_SYMBOL(km_state_notify);
1449
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001450void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001451{
1452 struct km_event c;
1453
Herbert Xubf088672005-06-18 22:44:00 -07001454 c.data.hard = hard;
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001455 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001456 c.event = XFRM_MSG_EXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001457 km_state_notify(x, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458
1459 if (hard)
1460 wake_up(&km_waitq);
1461}
1462
Jamal Hadi Salim53bc6b42006-03-20 19:17:03 -08001463EXPORT_SYMBOL(km_state_expired);
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001464/*
1465 * We send to all registered managers regardless of failure
1466 * We are happy with one success
1467*/
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001468int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001470 int err = -EINVAL, acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 struct xfrm_mgr *km;
1472
1473 read_lock(&xfrm_km_lock);
1474 list_for_each_entry(km, &xfrm_km_list, list) {
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001475 acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
1476 if (!acqret)
1477 err = acqret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 }
1479 read_unlock(&xfrm_km_lock);
1480 return err;
1481}
Jamal Hadi Salim980ebd22006-03-20 19:16:40 -08001482EXPORT_SYMBOL(km_query);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
Al Viro5d36b182006-11-08 00:24:06 -08001484int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485{
1486 int err = -EINVAL;
1487 struct xfrm_mgr *km;
1488
1489 read_lock(&xfrm_km_lock);
1490 list_for_each_entry(km, &xfrm_km_list, list) {
1491 if (km->new_mapping)
1492 err = km->new_mapping(x, ipaddr, sport);
1493 if (!err)
1494 break;
1495 }
1496 read_unlock(&xfrm_km_lock);
1497 return err;
1498}
1499EXPORT_SYMBOL(km_new_mapping);
1500
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001501void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502{
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001503 struct km_event c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
Herbert Xubf088672005-06-18 22:44:00 -07001505 c.data.hard = hard;
Jamal Hadi Salim6c5c8ca2006-03-20 19:17:25 -08001506 c.pid = pid;
Herbert Xuf60f6b82005-06-18 22:44:37 -07001507 c.event = XFRM_MSG_POLEXPIRE;
Jamal Hadi Salim26b15da2005-06-18 22:42:13 -07001508 km_policy_notify(pol, dir, &c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 if (hard)
1511 wake_up(&km_waitq);
1512}
David S. Millera70fcb02006-03-20 19:18:52 -08001513EXPORT_SYMBOL(km_policy_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Shinta Sugimoto80c9aba2007-02-08 13:11:42 -08001515int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1516 struct xfrm_migrate *m, int num_migrate)
1517{
1518 int err = -EINVAL;
1519 int ret;
1520 struct xfrm_mgr *km;
1521
1522 read_lock(&xfrm_km_lock);
1523 list_for_each_entry(km, &xfrm_km_list, list) {
1524 if (km->migrate) {
1525 ret = km->migrate(sel, dir, type, m, num_migrate);
1526 if (!ret)
1527 err = ret;
1528 }
1529 }
1530 read_unlock(&xfrm_km_lock);
1531 return err;
1532}
1533EXPORT_SYMBOL(km_migrate);
1534
Masahide NAKAMURA97a64b42006-08-23 20:44:06 -07001535int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
1536{
1537 int err = -EINVAL;
1538 int ret;
1539 struct xfrm_mgr *km;
1540
1541 read_lock(&xfrm_km_lock);
1542 list_for_each_entry(km, &xfrm_km_list, list) {
1543 if (km->report) {
1544 ret = km->report(proto, sel, addr);
1545 if (!ret)
1546 err = ret;
1547 }
1548 }
1549 read_unlock(&xfrm_km_lock);
1550 return err;
1551}
1552EXPORT_SYMBOL(km_report);
1553
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1555{
1556 int err;
1557 u8 *data;
1558 struct xfrm_mgr *km;
1559 struct xfrm_policy *pol = NULL;
1560
1561 if (optlen <= 0 || optlen > PAGE_SIZE)
1562 return -EMSGSIZE;
1563
1564 data = kmalloc(optlen, GFP_KERNEL);
1565 if (!data)
1566 return -ENOMEM;
1567
1568 err = -EFAULT;
1569 if (copy_from_user(data, optval, optlen))
1570 goto out;
1571
1572 err = -EINVAL;
1573 read_lock(&xfrm_km_lock);
1574 list_for_each_entry(km, &xfrm_km_list, list) {
Venkat Yekkiralacb969f02006-07-24 23:32:20 -07001575 pol = km->compile_policy(sk, optname, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 optlen, &err);
1577 if (err >= 0)
1578 break;
1579 }
1580 read_unlock(&xfrm_km_lock);
1581
1582 if (err >= 0) {
1583 xfrm_sk_policy_insert(sk, err, pol);
1584 xfrm_pol_put(pol);
1585 err = 0;
1586 }
1587
1588out:
1589 kfree(data);
1590 return err;
1591}
1592EXPORT_SYMBOL(xfrm_user_policy);
1593
1594int xfrm_register_km(struct xfrm_mgr *km)
1595{
1596 write_lock_bh(&xfrm_km_lock);
1597 list_add_tail(&km->list, &xfrm_km_list);
1598 write_unlock_bh(&xfrm_km_lock);
1599 return 0;
1600}
1601EXPORT_SYMBOL(xfrm_register_km);
1602
1603int xfrm_unregister_km(struct xfrm_mgr *km)
1604{
1605 write_lock_bh(&xfrm_km_lock);
1606 list_del(&km->list);
1607 write_unlock_bh(&xfrm_km_lock);
1608 return 0;
1609}
1610EXPORT_SYMBOL(xfrm_unregister_km);
1611
1612int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
1613{
1614 int err = 0;
1615 if (unlikely(afinfo == NULL))
1616 return -EINVAL;
1617 if (unlikely(afinfo->family >= NPROTO))
1618 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001619 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
1621 err = -ENOBUFS;
David S. Milleredcd5822006-08-24 00:42:45 -07001622 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 xfrm_state_afinfo[afinfo->family] = afinfo;
Ingo Molnarf3111502006-04-28 15:30:03 -07001624 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 return err;
1626}
1627EXPORT_SYMBOL(xfrm_state_register_afinfo);
1628
1629int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
1630{
1631 int err = 0;
1632 if (unlikely(afinfo == NULL))
1633 return -EINVAL;
1634 if (unlikely(afinfo->family >= NPROTO))
1635 return -EAFNOSUPPORT;
Ingo Molnarf3111502006-04-28 15:30:03 -07001636 write_lock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
1638 if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
1639 err = -EINVAL;
David S. Milleredcd5822006-08-24 00:42:45 -07001640 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 xfrm_state_afinfo[afinfo->family] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 }
Ingo Molnarf3111502006-04-28 15:30:03 -07001643 write_unlock_bh(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 return err;
1645}
1646EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
1647
Miika Komucdca7262007-02-06 14:24:56 -08001648struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649{
1650 struct xfrm_state_afinfo *afinfo;
1651 if (unlikely(family >= NPROTO))
1652 return NULL;
1653 read_lock(&xfrm_state_afinfo_lock);
1654 afinfo = xfrm_state_afinfo[family];
Herbert Xu546be242006-05-27 23:03:58 -07001655 if (unlikely(!afinfo))
1656 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 return afinfo;
1658}
1659
Miika Komucdca7262007-02-06 14:24:56 -08001660void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
Herbert Xu546be242006-05-27 23:03:58 -07001662 read_unlock(&xfrm_state_afinfo_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663}
1664
Miika Komucdca7262007-02-06 14:24:56 -08001665EXPORT_SYMBOL(xfrm_state_get_afinfo);
1666EXPORT_SYMBOL(xfrm_state_put_afinfo);
1667
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
1669void xfrm_state_delete_tunnel(struct xfrm_state *x)
1670{
1671 if (x->tunnel) {
1672 struct xfrm_state *t = x->tunnel;
1673
1674 if (atomic_read(&t->tunnel_users) == 2)
1675 xfrm_state_delete(t);
1676 atomic_dec(&t->tunnel_users);
1677 xfrm_state_put(t);
1678 x->tunnel = NULL;
1679 }
1680}
1681EXPORT_SYMBOL(xfrm_state_delete_tunnel);
1682
1683int xfrm_state_mtu(struct xfrm_state *x, int mtu)
1684{
Patrick McHardyc5c25232007-04-09 11:47:18 -07001685 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Patrick McHardyc5c25232007-04-09 11:47:18 -07001687 spin_lock_bh(&x->lock);
1688 if (x->km.state == XFRM_STATE_VALID &&
1689 x->type && x->type->get_mtu)
1690 res = x->type->get_mtu(x, mtu);
1691 else
1692 res = mtu;
1693 spin_unlock_bh(&x->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return res;
1695}
1696
Herbert Xu72cb6962005-06-20 13:18:08 -07001697int xfrm_init_state(struct xfrm_state *x)
1698{
Herbert Xud094cd82005-06-20 13:19:41 -07001699 struct xfrm_state_afinfo *afinfo;
1700 int family = x->props.family;
Herbert Xu72cb6962005-06-20 13:18:08 -07001701 int err;
1702
Herbert Xud094cd82005-06-20 13:19:41 -07001703 err = -EAFNOSUPPORT;
1704 afinfo = xfrm_state_get_afinfo(family);
1705 if (!afinfo)
1706 goto error;
1707
1708 err = 0;
1709 if (afinfo->init_flags)
1710 err = afinfo->init_flags(x);
1711
1712 xfrm_state_put_afinfo(afinfo);
1713
1714 if (err)
1715 goto error;
1716
1717 err = -EPROTONOSUPPORT;
1718 x->type = xfrm_get_type(x->id.proto, family);
Herbert Xu72cb6962005-06-20 13:18:08 -07001719 if (x->type == NULL)
1720 goto error;
1721
1722 err = x->type->init_state(x);
1723 if (err)
1724 goto error;
1725
Herbert Xub59f45d2006-05-27 23:05:54 -07001726 x->mode = xfrm_get_mode(x->props.mode, family);
1727 if (x->mode == NULL)
1728 goto error;
1729
Herbert Xu72cb6962005-06-20 13:18:08 -07001730 x->km.state = XFRM_STATE_VALID;
1731
1732error:
1733 return err;
1734}
1735
1736EXPORT_SYMBOL(xfrm_init_state);
YOSHIFUJI Hideakia716c112007-02-09 23:25:29 +09001737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738void __init xfrm_state_init(void)
1739{
David S. Millerf034b5d2006-08-24 03:08:07 -07001740 unsigned int sz;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
David S. Millerf034b5d2006-08-24 03:08:07 -07001742 sz = sizeof(struct hlist_head) * 8;
1743
David S. Miller44e36b42006-08-24 04:50:50 -07001744 xfrm_state_bydst = xfrm_hash_alloc(sz);
1745 xfrm_state_bysrc = xfrm_hash_alloc(sz);
1746 xfrm_state_byspi = xfrm_hash_alloc(sz);
David S. Millerf034b5d2006-08-24 03:08:07 -07001747 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1748 panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1749 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1750
David Howellsc4028952006-11-22 14:57:56 +00001751 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752}
1753