xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision 8122adf0)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * xfrm_state.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Changes:
51da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI
61da177e4SLinus Torvalds  * 	Kazunori MIYAZAWA @USAGI
71da177e4SLinus Torvalds  * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
81da177e4SLinus Torvalds  * 		IPv6 support
91da177e4SLinus Torvalds  * 	YOSHIFUJI Hideaki @USAGI
101da177e4SLinus Torvalds  * 		Split up af-specific functions
111da177e4SLinus Torvalds  *	Derek Atkins <derek@ihtfp.com>
121da177e4SLinus Torvalds  *		Add UDP Encapsulation
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/workqueue.h>
171da177e4SLinus Torvalds #include <net/xfrm.h>
181da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
191da177e4SLinus Torvalds #include <linux/ipsec.h>
201da177e4SLinus Torvalds #include <linux/module.h>
21f034b5d4SDavid S. Miller #include <linux/cache.h>
221da177e4SLinus Torvalds #include <asm/uaccess.h>
231da177e4SLinus Torvalds 
2444e36b42SDavid S. Miller #include "xfrm_hash.h"
2544e36b42SDavid S. Miller 
26ee857a7dSDavid S. Miller struct sock *xfrm_nl;
27ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl);
28ee857a7dSDavid S. Miller 
29f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
30a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
31a70fcb0bSDavid S. Miller 
32f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
33a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
34a70fcb0bSDavid S. Miller 
351da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
38a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
391da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
401da177e4SLinus Torvalds  */
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint
451da177e4SLinus Torvalds  * of tunnel or destination of transport mode) allowed by selector.
461da177e4SLinus Torvalds  *
471da177e4SLinus Torvalds  * Main use is finding SA after policy selected tunnel or transport mode.
481da177e4SLinus Torvalds  * Also, it can be used by ah/esp icmp error handler to find offending SA.
491da177e4SLinus Torvalds  */
50f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly;
51f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly;
52f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly;
53f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
54f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
55f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
569d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
571da177e4SLinus Torvalds 
58c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
59c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
60c1969f29SDavid S. Miller 					 u32 reqid,
61a624c108SDavid S. Miller 					 unsigned short family)
62a624c108SDavid S. Miller {
63c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
64a624c108SDavid S. Miller }
65a624c108SDavid S. Miller 
6644e36b42SDavid S. Miller static inline unsigned int xfrm_src_hash(xfrm_address_t *addr,
6744e36b42SDavid S. Miller 					 unsigned short family)
68f034b5d4SDavid S. Miller {
69f034b5d4SDavid S. Miller 	return __xfrm_src_hash(addr, family, xfrm_state_hmask);
70f034b5d4SDavid S. Miller }
71f034b5d4SDavid S. Miller 
722575b654SDavid S. Miller static inline unsigned int
738122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
74f034b5d4SDavid S. Miller {
75c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
76f034b5d4SDavid S. Miller }
77f034b5d4SDavid S. Miller 
78f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
79f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
80f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
81f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
82f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
83f034b5d4SDavid S. Miller {
84f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
85f034b5d4SDavid S. Miller 	struct xfrm_state *x;
86f034b5d4SDavid S. Miller 
87f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
88f034b5d4SDavid S. Miller 		unsigned int h;
89f034b5d4SDavid S. Miller 
90c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
91c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
92c1969f29SDavid S. Miller 				    nhashmask);
93f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
94f034b5d4SDavid S. Miller 
95f034b5d4SDavid S. Miller 		h = __xfrm_src_hash(&x->props.saddr, x->props.family,
96f034b5d4SDavid S. Miller 				    nhashmask);
97f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
98f034b5d4SDavid S. Miller 
99f034b5d4SDavid S. Miller 		h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
100f034b5d4SDavid S. Miller 				    x->props.family, nhashmask);
101f034b5d4SDavid S. Miller 		hlist_add_head(&x->byspi, nspitable+h);
102f034b5d4SDavid S. Miller 	}
103f034b5d4SDavid S. Miller }
104f034b5d4SDavid S. Miller 
105f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
106f034b5d4SDavid S. Miller {
107f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
108f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
109f034b5d4SDavid S. Miller }
110f034b5d4SDavid S. Miller 
111f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
112f034b5d4SDavid S. Miller 
113f034b5d4SDavid S. Miller static void xfrm_hash_resize(void *__unused)
114f034b5d4SDavid S. Miller {
115f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
116f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
117f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
118f034b5d4SDavid S. Miller 	int i;
119f034b5d4SDavid S. Miller 
120f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
121f034b5d4SDavid S. Miller 
122f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
12344e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
124f034b5d4SDavid S. Miller 	if (!ndst)
125f034b5d4SDavid S. Miller 		goto out_unlock;
12644e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
127f034b5d4SDavid S. Miller 	if (!nsrc) {
12844e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
129f034b5d4SDavid S. Miller 		goto out_unlock;
130f034b5d4SDavid S. Miller 	}
13144e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
132f034b5d4SDavid S. Miller 	if (!nspi) {
13344e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
13444e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
135f034b5d4SDavid S. Miller 		goto out_unlock;
136f034b5d4SDavid S. Miller 	}
137f034b5d4SDavid S. Miller 
138f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
139f034b5d4SDavid S. Miller 
140f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
141f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
142f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
143f034b5d4SDavid S. Miller 				   nhashmask);
144f034b5d4SDavid S. Miller 
145f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
146f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
147f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
148f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
149f034b5d4SDavid S. Miller 
150f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
151f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
152f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
153f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
154f034b5d4SDavid S. Miller 
155f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
156f034b5d4SDavid S. Miller 
157f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
15844e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
15944e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
16044e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
161f034b5d4SDavid S. Miller 
162f034b5d4SDavid S. Miller out_unlock:
163f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
164f034b5d4SDavid S. Miller }
165f034b5d4SDavid S. Miller 
166f034b5d4SDavid S. Miller static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
167f034b5d4SDavid S. Miller 
1681da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1691da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1721da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1758f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1761da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1771da177e4SLinus Torvalds 
17853bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
1811da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
1821da177e4SLinus Torvalds 
183980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
18453bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
1871da177e4SLinus Torvalds {
188a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
189a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
1901da177e4SLinus Torvalds 	kfree(x->aalg);
1911da177e4SLinus Torvalds 	kfree(x->ealg);
1921da177e4SLinus Torvalds 	kfree(x->calg);
1931da177e4SLinus Torvalds 	kfree(x->encap);
194060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
195b59f45d0SHerbert Xu 	if (x->mode)
196b59f45d0SHerbert Xu 		xfrm_put_mode(x->mode);
1971da177e4SLinus Torvalds 	if (x->type) {
1981da177e4SLinus Torvalds 		x->type->destructor(x);
1991da177e4SLinus Torvalds 		xfrm_put_type(x->type);
2001da177e4SLinus Torvalds 	}
201df71837dSTrent Jaeger 	security_xfrm_state_free(x);
2021da177e4SLinus Torvalds 	kfree(x);
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds static void xfrm_state_gc_task(void *data)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds 	struct xfrm_state *x;
2088f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
2098f126e37SDavid S. Miller 	struct hlist_head gc_list;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
2128f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
2138f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
2141da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
2151da177e4SLinus Torvalds 
2168f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
2171da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
2188f126e37SDavid S. Miller 
2191da177e4SLinus Torvalds 	wake_up(&km_waitq);
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
2231da177e4SLinus Torvalds {
2241da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
2251da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
2261da177e4SLinus Torvalds 	else
2271da177e4SLinus Torvalds 	        return secs*HZ;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
2331da177e4SLinus Torvalds 	unsigned long now = (unsigned long)xtime.tv_sec;
2341da177e4SLinus Torvalds 	long next = LONG_MAX;
2351da177e4SLinus Torvalds 	int warn = 0;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	spin_lock(&x->lock);
2381da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
2391da177e4SLinus Torvalds 		goto out;
2401da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
2411da177e4SLinus Torvalds 		goto expired;
2421da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
2431da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
2441da177e4SLinus Torvalds 			x->curlft.add_time - now;
2451da177e4SLinus Torvalds 		if (tmo <= 0)
2461da177e4SLinus Torvalds 			goto expired;
2471da177e4SLinus Torvalds 		if (tmo < next)
2481da177e4SLinus Torvalds 			next = tmo;
2491da177e4SLinus Torvalds 	}
2501da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
2511da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
2521da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2531da177e4SLinus Torvalds 		if (tmo <= 0)
2541da177e4SLinus Torvalds 			goto expired;
2551da177e4SLinus Torvalds 		if (tmo < next)
2561da177e4SLinus Torvalds 			next = tmo;
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds 	if (x->km.dying)
2591da177e4SLinus Torvalds 		goto resched;
2601da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
2611da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
2621da177e4SLinus Torvalds 			x->curlft.add_time - now;
2631da177e4SLinus Torvalds 		if (tmo <= 0)
2641da177e4SLinus Torvalds 			warn = 1;
2651da177e4SLinus Torvalds 		else if (tmo < next)
2661da177e4SLinus Torvalds 			next = tmo;
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
2691da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
2701da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2711da177e4SLinus Torvalds 		if (tmo <= 0)
2721da177e4SLinus Torvalds 			warn = 1;
2731da177e4SLinus Torvalds 		else if (tmo < next)
2741da177e4SLinus Torvalds 			next = tmo;
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds 
2774666faabSHerbert Xu 	x->km.dying = warn;
2781da177e4SLinus Torvalds 	if (warn)
27953bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
2801da177e4SLinus Torvalds resched:
281a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
282a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
283a47f0ce0SDavid S. Miller 
2841da177e4SLinus Torvalds 	goto out;
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds expired:
2871da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
2881da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
2891da177e4SLinus Torvalds 		wake_up(&km_waitq);
2901da177e4SLinus Torvalds 		next = 2;
2911da177e4SLinus Torvalds 		goto resched;
2921da177e4SLinus Torvalds 	}
2934666faabSHerbert Xu 	if (!__xfrm_state_delete(x) && x->id.spi)
29453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds out:
2971da177e4SLinus Torvalds 	spin_unlock(&x->lock);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3000ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
3010ac84752SDavid S. Miller 
3021da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct xfrm_state *x;
3051da177e4SLinus Torvalds 
3060da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	if (x) {
3091da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
3101da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
3118f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
3128f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
3138f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
3141da177e4SLinus Torvalds 		init_timer(&x->timer);
3151da177e4SLinus Torvalds 		x->timer.function = xfrm_timer_handler;
3161da177e4SLinus Torvalds 		x->timer.data	  = (unsigned long)x;
317f8cd5488SJamal Hadi Salim 		init_timer(&x->rtimer);
318f8cd5488SJamal Hadi Salim 		x->rtimer.function = xfrm_replay_timer_handler;
319f8cd5488SJamal Hadi Salim 		x->rtimer.data     = (unsigned long)x;
3201da177e4SLinus Torvalds 		x->curlft.add_time = (unsigned long)xtime.tv_sec;
3211da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
3221da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
3231da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
3241da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
325f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
326f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
3271da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 	return x;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
3388f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
3391da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
3401da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
3431da177e4SLinus Torvalds 
34453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
3451da177e4SLinus Torvalds {
34626b15dadSJamal Hadi Salim 	int err = -ESRCH;
34726b15dadSJamal Hadi Salim 
3481da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
3491da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
3501da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
3518f126e37SDavid S. Miller 		hlist_del(&x->bydst);
3528f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
353a47f0ce0SDavid S. Miller 		if (x->id.spi)
3548f126e37SDavid S. Miller 			hlist_del(&x->byspi);
355f034b5d4SDavid S. Miller 		xfrm_state_num--;
3561da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
3591da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
3601da177e4SLinus Torvalds 		 * is what we are dropping here.
3611da177e4SLinus Torvalds 		 */
36221380b81SHerbert Xu 		__xfrm_state_put(x);
36326b15dadSJamal Hadi Salim 		err = 0;
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds 
36626b15dadSJamal Hadi Salim 	return err;
36726b15dadSJamal Hadi Salim }
36853bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
36926b15dadSJamal Hadi Salim 
37026b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
3711da177e4SLinus Torvalds {
37226b15dadSJamal Hadi Salim 	int err;
37326b15dadSJamal Hadi Salim 
3741da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
37526b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
3761da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
37726b15dadSJamal Hadi Salim 
37826b15dadSJamal Hadi Salim 	return err;
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds void xfrm_state_flush(u8 proto)
3831da177e4SLinus Torvalds {
3841da177e4SLinus Torvalds 	int i;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
387a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
3888f126e37SDavid S. Miller 		struct hlist_node *entry;
3898f126e37SDavid S. Miller 		struct xfrm_state *x;
3901da177e4SLinus Torvalds restart:
3918f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
3921da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
3935794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
3941da177e4SLinus Torvalds 				xfrm_state_hold(x);
3951da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 				xfrm_state_delete(x);
3981da177e4SLinus Torvalds 				xfrm_state_put(x);
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
4011da177e4SLinus Torvalds 				goto restart;
4021da177e4SLinus Torvalds 			}
4031da177e4SLinus Torvalds 		}
4041da177e4SLinus Torvalds 	}
4051da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
4061da177e4SLinus Torvalds 	wake_up(&km_waitq);
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds static int
4111da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
4121da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
4131da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
4141da177e4SLinus Torvalds 		  unsigned short family)
4151da177e4SLinus Torvalds {
4161da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
4171da177e4SLinus Torvalds 	if (!afinfo)
4181da177e4SLinus Torvalds 		return -1;
4191da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
4201da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
4211da177e4SLinus Torvalds 	return 0;
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
424a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
425edcd5821SDavid S. Miller {
426edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
427edcd5821SDavid S. Miller 	struct xfrm_state *x;
4288f126e37SDavid S. Miller 	struct hlist_node *entry;
429edcd5821SDavid S. Miller 
4308f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
431edcd5821SDavid S. Miller 		if (x->props.family != family ||
432edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
433edcd5821SDavid S. Miller 		    x->id.proto     != proto)
434edcd5821SDavid S. Miller 			continue;
435edcd5821SDavid S. Miller 
436edcd5821SDavid S. Miller 		switch (family) {
437edcd5821SDavid S. Miller 		case AF_INET:
438edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
439edcd5821SDavid S. Miller 				continue;
440edcd5821SDavid S. Miller 			break;
441edcd5821SDavid S. Miller 		case AF_INET6:
442edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
443edcd5821SDavid S. Miller 					     (struct in6_addr *)
444edcd5821SDavid S. Miller 					     x->id.daddr.a6))
445edcd5821SDavid S. Miller 				continue;
446edcd5821SDavid S. Miller 			break;
447edcd5821SDavid S. Miller 		};
448edcd5821SDavid S. Miller 
449edcd5821SDavid S. Miller 		xfrm_state_hold(x);
450edcd5821SDavid S. Miller 		return x;
451edcd5821SDavid S. Miller 	}
452edcd5821SDavid S. Miller 
453edcd5821SDavid S. Miller 	return NULL;
454edcd5821SDavid S. Miller }
455edcd5821SDavid S. Miller 
456edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
457edcd5821SDavid S. Miller {
458edcd5821SDavid S. Miller 	unsigned int h = xfrm_src_hash(saddr, family);
459edcd5821SDavid S. Miller 	struct xfrm_state *x;
4608f126e37SDavid S. Miller 	struct hlist_node *entry;
461edcd5821SDavid S. Miller 
4628f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
463edcd5821SDavid S. Miller 		if (x->props.family != family ||
464edcd5821SDavid S. Miller 		    x->id.proto     != proto)
465edcd5821SDavid S. Miller 			continue;
466edcd5821SDavid S. Miller 
467edcd5821SDavid S. Miller 		switch (family) {
468edcd5821SDavid S. Miller 		case AF_INET:
469edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
470edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
471edcd5821SDavid S. Miller 				continue;
472edcd5821SDavid S. Miller 			break;
473edcd5821SDavid S. Miller 		case AF_INET6:
474edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
475edcd5821SDavid S. Miller 					     (struct in6_addr *)
476edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
477edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
478edcd5821SDavid S. Miller 					     (struct in6_addr *)
479edcd5821SDavid S. Miller 					     x->props.saddr.a6))
480edcd5821SDavid S. Miller 				continue;
481edcd5821SDavid S. Miller 			break;
482edcd5821SDavid S. Miller 		};
483edcd5821SDavid S. Miller 
484edcd5821SDavid S. Miller 		xfrm_state_hold(x);
485edcd5821SDavid S. Miller 		return x;
486edcd5821SDavid S. Miller 	}
487edcd5821SDavid S. Miller 
488edcd5821SDavid S. Miller 	return NULL;
489edcd5821SDavid S. Miller }
490edcd5821SDavid S. Miller 
491edcd5821SDavid S. Miller static inline struct xfrm_state *
492edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
493edcd5821SDavid S. Miller {
494edcd5821SDavid S. Miller 	if (use_spi)
495edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
496edcd5821SDavid S. Miller 					   x->id.proto, family);
497edcd5821SDavid S. Miller 	else
498edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
499edcd5821SDavid S. Miller 						  &x->props.saddr,
500edcd5821SDavid S. Miller 						  x->id.proto, family);
501edcd5821SDavid S. Miller }
502edcd5821SDavid S. Miller 
5031da177e4SLinus Torvalds struct xfrm_state *
5041da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
5051da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
5061da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
5071da177e4SLinus Torvalds 		unsigned short family)
5081da177e4SLinus Torvalds {
509c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
5108f126e37SDavid S. Miller 	struct hlist_node *entry;
5111da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
5121da177e4SLinus Torvalds 	int acquire_in_progress = 0;
5131da177e4SLinus Torvalds 	int error = 0;
5141da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
5178f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
5181da177e4SLinus Torvalds 		if (x->props.family == family &&
5191da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
520fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
5211da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
5221da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
5231da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
5241da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
5251da177e4SLinus Torvalds 			/* Resolution logic:
5261da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
5271da177e4SLinus Torvalds 			      Done.
5281da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 			   Entering area of "sysdeps".
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
5331da177e4SLinus Torvalds 			      it selects only session which triggered
5341da177e4SLinus Torvalds 			      previous resolution. Key manager will do
5351da177e4SLinus Torvalds 			      something to install a state with proper
5361da177e4SLinus Torvalds 			      selector.
5371da177e4SLinus Torvalds 			 */
5381da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
539df71837dSTrent Jaeger 				if (!xfrm_selector_match(&x->sel, fl, family) ||
540e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
5411da177e4SLinus Torvalds 					continue;
5421da177e4SLinus Torvalds 				if (!best ||
5431da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
5441da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
5451da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
5461da177e4SLinus Torvalds 					best = x;
5471da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
5481da177e4SLinus Torvalds 				acquire_in_progress = 1;
5491da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
5501da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
551df71837dSTrent Jaeger  				if (xfrm_selector_match(&x->sel, fl, family) &&
552e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
5531da177e4SLinus Torvalds 					error = -ESRCH;
5541da177e4SLinus Torvalds 			}
5551da177e4SLinus Torvalds 		}
5561da177e4SLinus Torvalds 	}
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	x = best;
5591da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
5605c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
561edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
562edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
5631da177e4SLinus Torvalds 			xfrm_state_put(x0);
5641da177e4SLinus Torvalds 			error = -EEXIST;
5651da177e4SLinus Torvalds 			goto out;
5661da177e4SLinus Torvalds 		}
5671da177e4SLinus Torvalds 		x = xfrm_state_alloc();
5681da177e4SLinus Torvalds 		if (x == NULL) {
5691da177e4SLinus Torvalds 			error = -ENOMEM;
5701da177e4SLinus Torvalds 			goto out;
5711da177e4SLinus Torvalds 		}
5721da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
5731da177e4SLinus Torvalds 		 * to current session. */
5741da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
5751da177e4SLinus Torvalds 
576e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
577e0d1caa7SVenkat Yekkirala 		if (error) {
578e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
579e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
580e0d1caa7SVenkat Yekkirala 			x = NULL;
581e0d1caa7SVenkat Yekkirala 			goto out;
582e0d1caa7SVenkat Yekkirala 		}
583e0d1caa7SVenkat Yekkirala 
5841da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
5851da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
5868f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
5878f126e37SDavid S. Miller 			h = xfrm_src_hash(saddr, family);
5888f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
5891da177e4SLinus Torvalds 			if (x->id.spi) {
5901da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
5918f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
5921da177e4SLinus Torvalds 			}
5931da177e4SLinus Torvalds 			x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
5941da177e4SLinus Torvalds 			x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
5951da177e4SLinus Torvalds 			add_timer(&x->timer);
5961da177e4SLinus Torvalds 		} else {
5971da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
5981da177e4SLinus Torvalds 			xfrm_state_put(x);
5991da177e4SLinus Torvalds 			x = NULL;
6001da177e4SLinus Torvalds 			error = -ESRCH;
6011da177e4SLinus Torvalds 		}
6021da177e4SLinus Torvalds 	}
6031da177e4SLinus Torvalds out:
6041da177e4SLinus Torvalds 	if (x)
6051da177e4SLinus Torvalds 		xfrm_state_hold(x);
6061da177e4SLinus Torvalds 	else
6071da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
6081da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6091da177e4SLinus Torvalds 	return x;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
6131da177e4SLinus Torvalds {
614a624c108SDavid S. Miller 	unsigned int h;
6151da177e4SLinus Torvalds 
6169d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
6179d4a706dSDavid S. Miller 
618c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
619c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
6208f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
6211da177e4SLinus Torvalds 
6226c44e6b7SMasahide NAKAMURA 	h = xfrm_src_hash(&x->props.saddr, x->props.family);
6238f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
6246c44e6b7SMasahide NAKAMURA 
6256c44e6b7SMasahide NAKAMURA 	if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) {
6266c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
6276c44e6b7SMasahide NAKAMURA 				  x->props.family);
6281da177e4SLinus Torvalds 
6298f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
6306c44e6b7SMasahide NAKAMURA 	}
6311da177e4SLinus Torvalds 
632a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
633a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
634a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
635f8cd5488SJamal Hadi Salim 
6361da177e4SLinus Torvalds 	wake_up(&km_waitq);
637f034b5d4SDavid S. Miller 
638f034b5d4SDavid S. Miller 	xfrm_state_num++;
639f034b5d4SDavid S. Miller 
640f034b5d4SDavid S. Miller 	if (x->bydst.next != NULL &&
641f034b5d4SDavid S. Miller 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
642f034b5d4SDavid S. Miller 	    xfrm_state_num > xfrm_state_hmask)
643f034b5d4SDavid S. Miller 		schedule_work(&xfrm_hash_work);
6441da177e4SLinus Torvalds }
6451da177e4SLinus Torvalds 
646c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
647c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
648c7f5ea3aSDavid S. Miller {
649c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
650c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
651c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
652c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
653c7f5ea3aSDavid S. Miller 	unsigned int h;
654c7f5ea3aSDavid S. Miller 
655c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
656c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
657c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
658c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
659c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
660c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
661c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
662c7f5ea3aSDavid S. Miller 	}
663c7f5ea3aSDavid S. Miller }
664c7f5ea3aSDavid S. Miller 
6651da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
6661da177e4SLinus Torvalds {
6671da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
668c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
6691da177e4SLinus Torvalds 	__xfrm_state_insert(x);
6701da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
6731da177e4SLinus Torvalds 
6742770834cSDavid S. Miller /* xfrm_state_lock is held */
6752770834cSDavid S. Miller static 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)
6762770834cSDavid S. Miller {
677c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
6788f126e37SDavid S. Miller 	struct hlist_node *entry;
6792770834cSDavid S. Miller 	struct xfrm_state *x;
6802770834cSDavid S. Miller 
6818f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
6822770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
6832770834cSDavid S. Miller 		    x->props.mode   != mode ||
6842770834cSDavid S. Miller 		    x->props.family != family ||
6852770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
6862770834cSDavid S. Miller 		    x->id.spi       != 0)
6872770834cSDavid S. Miller 			continue;
6882770834cSDavid S. Miller 
6892770834cSDavid S. Miller 		switch (family) {
6902770834cSDavid S. Miller 		case AF_INET:
6912770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
6922770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
6932770834cSDavid S. Miller 				continue;
6942770834cSDavid S. Miller 			break;
6952770834cSDavid S. Miller 		case AF_INET6:
6962770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
6972770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
6982770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
6992770834cSDavid S. Miller 					     x->props.saddr.a6,
7002770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
7012770834cSDavid S. Miller 				continue;
7022770834cSDavid S. Miller 			break;
7032770834cSDavid S. Miller 		};
7042770834cSDavid S. Miller 
7052770834cSDavid S. Miller 		xfrm_state_hold(x);
7062770834cSDavid S. Miller 		return x;
7072770834cSDavid S. Miller 	}
7082770834cSDavid S. Miller 
7092770834cSDavid S. Miller 	if (!create)
7102770834cSDavid S. Miller 		return NULL;
7112770834cSDavid S. Miller 
7122770834cSDavid S. Miller 	x = xfrm_state_alloc();
7132770834cSDavid S. Miller 	if (likely(x)) {
7142770834cSDavid S. Miller 		switch (family) {
7152770834cSDavid S. Miller 		case AF_INET:
7162770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
7172770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
7182770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
7192770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
7202770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
7212770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
7222770834cSDavid S. Miller 			break;
7232770834cSDavid S. Miller 
7242770834cSDavid S. Miller 		case AF_INET6:
7252770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
7262770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7272770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
7282770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7292770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
7302770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
7312770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
7322770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7332770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
7342770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7352770834cSDavid S. Miller 			break;
7362770834cSDavid S. Miller 		};
7372770834cSDavid S. Miller 
7382770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
7392770834cSDavid S. Miller 		x->id.proto = proto;
7402770834cSDavid S. Miller 		x->props.family = family;
7412770834cSDavid S. Miller 		x->props.mode = mode;
7422770834cSDavid S. Miller 		x->props.reqid = reqid;
7432770834cSDavid S. Miller 		x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
7442770834cSDavid S. Miller 		xfrm_state_hold(x);
7452770834cSDavid S. Miller 		x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
7462770834cSDavid S. Miller 		add_timer(&x->timer);
7478f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
7482770834cSDavid S. Miller 		h = xfrm_src_hash(saddr, family);
7498f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
7502770834cSDavid S. Miller 		wake_up(&km_waitq);
7512770834cSDavid S. Miller 	}
7522770834cSDavid S. Miller 
7532770834cSDavid S. Miller 	return x;
7542770834cSDavid S. Miller }
7552770834cSDavid S. Miller 
7561da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
7591da177e4SLinus Torvalds {
7601da177e4SLinus Torvalds 	struct xfrm_state *x1;
7611da177e4SLinus Torvalds 	int family;
7621da177e4SLinus Torvalds 	int err;
763eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	family = x->props.family;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7681da177e4SLinus Torvalds 
769edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
7701da177e4SLinus Torvalds 	if (x1) {
7711da177e4SLinus Torvalds 		xfrm_state_put(x1);
7721da177e4SLinus Torvalds 		x1 = NULL;
7731da177e4SLinus Torvalds 		err = -EEXIST;
7741da177e4SLinus Torvalds 		goto out;
7751da177e4SLinus Torvalds 	}
7761da177e4SLinus Torvalds 
777eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
7781da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
7791da177e4SLinus Torvalds 		if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
7801da177e4SLinus Torvalds 			xfrm_state_put(x1);
7811da177e4SLinus Torvalds 			x1 = NULL;
7821da177e4SLinus Torvalds 		}
7831da177e4SLinus Torvalds 	}
7841da177e4SLinus Torvalds 
785eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
7862770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
7872770834cSDavid S. Miller 				     x->id.proto,
7881da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
7891da177e4SLinus Torvalds 
790c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
7911da177e4SLinus Torvalds 	__xfrm_state_insert(x);
7921da177e4SLinus Torvalds 	err = 0;
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds out:
7951da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	if (x1) {
7981da177e4SLinus Torvalds 		xfrm_state_delete(x1);
7991da177e4SLinus Torvalds 		xfrm_state_put(x1);
8001da177e4SLinus Torvalds 	}
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	return err;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
8071da177e4SLinus Torvalds {
8081da177e4SLinus Torvalds 	struct xfrm_state *x1;
8091da177e4SLinus Torvalds 	int err;
810eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
813edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds 	err = -ESRCH;
8161da177e4SLinus Torvalds 	if (!x1)
8171da177e4SLinus Torvalds 		goto out;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
8201da177e4SLinus Torvalds 		xfrm_state_put(x1);
8211da177e4SLinus Torvalds 		err = -EEXIST;
8221da177e4SLinus Torvalds 		goto out;
8231da177e4SLinus Torvalds 	}
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
8261da177e4SLinus Torvalds 		__xfrm_state_insert(x);
8271da177e4SLinus Torvalds 		x = NULL;
8281da177e4SLinus Torvalds 	}
8291da177e4SLinus Torvalds 	err = 0;
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds out:
8321da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8331da177e4SLinus Torvalds 
8341da177e4SLinus Torvalds 	if (err)
8351da177e4SLinus Torvalds 		return err;
8361da177e4SLinus Torvalds 
8371da177e4SLinus Torvalds 	if (!x) {
8381da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8391da177e4SLinus Torvalds 		xfrm_state_put(x1);
8401da177e4SLinus Torvalds 		return 0;
8411da177e4SLinus Torvalds 	}
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds 	err = -EINVAL;
8441da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
8451da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
8461da177e4SLinus Torvalds 		if (x->encap && x1->encap)
8471da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
848060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
849060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
850060f02a3SNoriaki TAKAMIYA 		}
851060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
852060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
8531da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
8541da177e4SLinus Torvalds 		x1->km.dying = 0;
8551da177e4SLinus Torvalds 
856a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
8571da177e4SLinus Torvalds 		if (x1->curlft.use_time)
8581da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 		err = 0;
8611da177e4SLinus Torvalds 	}
8621da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	xfrm_state_put(x1);
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 	return err;
8671da177e4SLinus Torvalds }
8681da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
8711da177e4SLinus Torvalds {
8721da177e4SLinus Torvalds 	if (!x->curlft.use_time)
8731da177e4SLinus Torvalds 		x->curlft.use_time = (unsigned long)xtime.tv_sec;
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
8761da177e4SLinus Torvalds 		return -EINVAL;
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
8791da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
8804666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
881a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
8821da177e4SLinus Torvalds 		return -EINVAL;
8831da177e4SLinus Torvalds 	}
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	if (!x->km.dying &&
8861da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
8874666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
8884666faabSHerbert Xu 		x->km.dying = 1;
88953bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
8904666faabSHerbert Xu 	}
8911da177e4SLinus Torvalds 	return 0;
8921da177e4SLinus Torvalds }
8931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
8981da177e4SLinus Torvalds 		- skb_headroom(skb);
8991da177e4SLinus Torvalds 
9001da177e4SLinus Torvalds 	if (nhead > 0)
9011da177e4SLinus Torvalds 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
9021da177e4SLinus Torvalds 
9031da177e4SLinus Torvalds 	/* Check tail too... */
9041da177e4SLinus Torvalds 	return 0;
9051da177e4SLinus Torvalds }
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
9081da177e4SLinus Torvalds {
9091da177e4SLinus Torvalds 	int err = xfrm_state_check_expire(x);
9101da177e4SLinus Torvalds 	if (err < 0)
9111da177e4SLinus Torvalds 		goto err;
9121da177e4SLinus Torvalds 	err = xfrm_state_check_space(x, skb);
9131da177e4SLinus Torvalds err:
9141da177e4SLinus Torvalds 	return err;
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check);
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds struct xfrm_state *
919a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
9201da177e4SLinus Torvalds 		  unsigned short family)
9211da177e4SLinus Torvalds {
9221da177e4SLinus Torvalds 	struct xfrm_state *x;
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
925edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
9261da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9271da177e4SLinus Torvalds 	return x;
9281da177e4SLinus Torvalds }
9291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds struct xfrm_state *
932eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
933eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
934eb2971b6SMasahide NAKAMURA {
935eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
936eb2971b6SMasahide NAKAMURA 
937eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
938edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
939eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
940eb2971b6SMasahide NAKAMURA 	return x;
941eb2971b6SMasahide NAKAMURA }
942eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
943eb2971b6SMasahide NAKAMURA 
944eb2971b6SMasahide NAKAMURA struct xfrm_state *
9451da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
9461da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
9471da177e4SLinus Torvalds 	      int create, unsigned short family)
9481da177e4SLinus Torvalds {
9491da177e4SLinus Torvalds 	struct xfrm_state *x;
9501da177e4SLinus Torvalds 
9511da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
9522770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
9531da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9542770834cSDavid S. Miller 
9551da177e4SLinus Torvalds 	return x;
9561da177e4SLinus Torvalds }
9571da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
9581da177e4SLinus Torvalds 
95941a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
96041a49cc3SMasahide NAKAMURA int
96141a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
96241a49cc3SMasahide NAKAMURA 	       unsigned short family)
96341a49cc3SMasahide NAKAMURA {
96441a49cc3SMasahide NAKAMURA 	int err = 0;
96541a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
96641a49cc3SMasahide NAKAMURA 	if (!afinfo)
96741a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
96841a49cc3SMasahide NAKAMURA 
96941a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
97041a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
97141a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
97241a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
97341a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
97441a49cc3SMasahide NAKAMURA 	return err;
97541a49cc3SMasahide NAKAMURA }
97641a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
97741a49cc3SMasahide NAKAMURA 
97841a49cc3SMasahide NAKAMURA int
97941a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
98041a49cc3SMasahide NAKAMURA 		unsigned short family)
98141a49cc3SMasahide NAKAMURA {
98241a49cc3SMasahide NAKAMURA 	int err = 0;
98341a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
98441a49cc3SMasahide NAKAMURA 	if (!afinfo)
98541a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
98641a49cc3SMasahide NAKAMURA 
98741a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
98841a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
98941a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
99041a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
99141a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
99241a49cc3SMasahide NAKAMURA 	return err;
99341a49cc3SMasahide NAKAMURA }
99441a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
99541a49cc3SMasahide NAKAMURA #endif
99641a49cc3SMasahide NAKAMURA 
9971da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
9981da177e4SLinus Torvalds 
9991da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
10001da177e4SLinus Torvalds {
10011da177e4SLinus Torvalds 	int i;
10021da177e4SLinus Torvalds 
1003f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
10048f126e37SDavid S. Miller 		struct hlist_node *entry;
10058f126e37SDavid S. Miller 		struct xfrm_state *x;
10068f126e37SDavid S. Miller 
10078f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
10088f126e37SDavid S. Miller 			if (x->km.seq == seq &&
10098f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
10101da177e4SLinus Torvalds 				xfrm_state_hold(x);
10111da177e4SLinus Torvalds 				return x;
10121da177e4SLinus Torvalds 			}
10131da177e4SLinus Torvalds 		}
10141da177e4SLinus Torvalds 	}
10151da177e4SLinus Torvalds 	return NULL;
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
10191da177e4SLinus Torvalds {
10201da177e4SLinus Torvalds 	struct xfrm_state *x;
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10231da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
10241da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10251da177e4SLinus Torvalds 	return x;
10261da177e4SLinus Torvalds }
10271da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
10301da177e4SLinus Torvalds {
10311da177e4SLinus Torvalds 	u32 res;
10321da177e4SLinus Torvalds 	static u32 acqseq;
10331da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
10361da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
10371da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
10381da177e4SLinus Torvalds 	return res;
10391da177e4SLinus Torvalds }
10401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds void
104326977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
10441da177e4SLinus Torvalds {
1045f034b5d4SDavid S. Miller 	unsigned int h;
10461da177e4SLinus Torvalds 	struct xfrm_state *x0;
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	if (x->id.spi)
10491da177e4SLinus Torvalds 		return;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	if (minspi == maxspi) {
10521da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
10531da177e4SLinus Torvalds 		if (x0) {
10541da177e4SLinus Torvalds 			xfrm_state_put(x0);
10551da177e4SLinus Torvalds 			return;
10561da177e4SLinus Torvalds 		}
10571da177e4SLinus Torvalds 		x->id.spi = minspi;
10581da177e4SLinus Torvalds 	} else {
10591da177e4SLinus Torvalds 		u32 spi = 0;
106026977b4eSAl Viro 		u32 low = ntohl(minspi);
106126977b4eSAl Viro 		u32 high = ntohl(maxspi);
106226977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
106326977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
10641da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
10651da177e4SLinus Torvalds 			if (x0 == NULL) {
10661da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
10671da177e4SLinus Torvalds 				break;
10681da177e4SLinus Torvalds 			}
10691da177e4SLinus Torvalds 			xfrm_state_put(x0);
10701da177e4SLinus Torvalds 		}
10711da177e4SLinus Torvalds 	}
10721da177e4SLinus Torvalds 	if (x->id.spi) {
10731da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
10741da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
10758f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
10761da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
10771da177e4SLinus Torvalds 		wake_up(&km_waitq);
10781da177e4SLinus Torvalds 	}
10791da177e4SLinus Torvalds }
10801da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
10831da177e4SLinus Torvalds 		    void *data)
10841da177e4SLinus Torvalds {
10851da177e4SLinus Torvalds 	int i;
10861da177e4SLinus Torvalds 	struct xfrm_state *x;
10878f126e37SDavid S. Miller 	struct hlist_node *entry;
10881da177e4SLinus Torvalds 	int count = 0;
10891da177e4SLinus Torvalds 	int err = 0;
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1092f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
10938f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
10945794708fSMasahide NAKAMURA 			if (xfrm_id_proto_match(x->id.proto, proto))
10951da177e4SLinus Torvalds 				count++;
10961da177e4SLinus Torvalds 		}
10971da177e4SLinus Torvalds 	}
10981da177e4SLinus Torvalds 	if (count == 0) {
10991da177e4SLinus Torvalds 		err = -ENOENT;
11001da177e4SLinus Torvalds 		goto out;
11011da177e4SLinus Torvalds 	}
11021da177e4SLinus Torvalds 
1103f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
11048f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
11055794708fSMasahide NAKAMURA 			if (!xfrm_id_proto_match(x->id.proto, proto))
11061da177e4SLinus Torvalds 				continue;
11071da177e4SLinus Torvalds 			err = func(x, --count, data);
11081da177e4SLinus Torvalds 			if (err)
11091da177e4SLinus Torvalds 				goto out;
11101da177e4SLinus Torvalds 		}
11111da177e4SLinus Torvalds 	}
11121da177e4SLinus Torvalds out:
11131da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11141da177e4SLinus Torvalds 	return err;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
11171da177e4SLinus Torvalds 
1118f8cd5488SJamal Hadi Salim 
1119f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1120f8cd5488SJamal Hadi Salim {
1121f8cd5488SJamal Hadi Salim 	struct km_event c;
1122f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1123f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1124f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1125f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1126f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1127f8cd5488SJamal Hadi Salim 	 *     and there were changes
1128f8cd5488SJamal Hadi Salim 	 *
1129f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1130f8cd5488SJamal Hadi Salim 	 */
1131f8cd5488SJamal Hadi Salim 
1132f8cd5488SJamal Hadi Salim 	switch (event) {
1133f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1134f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1135f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
11362717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
11372717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
11382717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
11392717096aSJamal Hadi Salim 			else
1140f8cd5488SJamal Hadi Salim 				return;
11412717096aSJamal Hadi Salim 		}
1142f8cd5488SJamal Hadi Salim 
1143f8cd5488SJamal Hadi Salim 		break;
1144f8cd5488SJamal Hadi Salim 
1145f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1146f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1147f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
11482717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
11492717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1150f8cd5488SJamal Hadi Salim 			return;
11512717096aSJamal Hadi Salim 		}
1152f8cd5488SJamal Hadi Salim 
1153f8cd5488SJamal Hadi Salim 		break;
1154f8cd5488SJamal Hadi Salim 	}
1155f8cd5488SJamal Hadi Salim 
1156f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1157f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1158f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1159f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1160f8cd5488SJamal Hadi Salim 
1161f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1162a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
11632717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
11642717096aSJamal Hadi Salim }
1165a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify);
1166f8cd5488SJamal Hadi Salim 
1167f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1168f8cd5488SJamal Hadi Salim {
1169f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1170f8cd5488SJamal Hadi Salim 
1171f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1172f8cd5488SJamal Hadi Salim 
11732717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
11742717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1175f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
11762717096aSJamal Hadi Salim 		else
11772717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
11782717096aSJamal Hadi Salim 	}
1179f8cd5488SJamal Hadi Salim 
1180f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1181f8cd5488SJamal Hadi Salim }
1182f8cd5488SJamal Hadi Salim 
1183a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
11841da177e4SLinus Torvalds {
11851da177e4SLinus Torvalds 	u32 diff;
1186a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
11871da177e4SLinus Torvalds 
11881da177e4SLinus Torvalds 	if (unlikely(seq == 0))
11891da177e4SLinus Torvalds 		return -EINVAL;
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
11921da177e4SLinus Torvalds 		return 0;
11931da177e4SLinus Torvalds 
11941da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
11951da177e4SLinus Torvalds 	if (diff >= x->props.replay_window) {
11961da177e4SLinus Torvalds 		x->stats.replay_window++;
11971da177e4SLinus Torvalds 		return -EINVAL;
11981da177e4SLinus Torvalds 	}
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
12011da177e4SLinus Torvalds 		x->stats.replay++;
12021da177e4SLinus Torvalds 		return -EINVAL;
12031da177e4SLinus Torvalds 	}
12041da177e4SLinus Torvalds 	return 0;
12051da177e4SLinus Torvalds }
12061da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
12071da177e4SLinus Torvalds 
120861f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
12091da177e4SLinus Torvalds {
12101da177e4SLinus Torvalds 	u32 diff;
121161f4627bSAl Viro 	u32 seq = ntohl(net_seq);
12121da177e4SLinus Torvalds 
12131da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
12141da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
12151da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
12161da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
12171da177e4SLinus Torvalds 		else
12181da177e4SLinus Torvalds 			x->replay.bitmap = 1;
12191da177e4SLinus Torvalds 		x->replay.seq = seq;
12201da177e4SLinus Torvalds 	} else {
12211da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
12221da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
12231da177e4SLinus Torvalds 	}
1224f8cd5488SJamal Hadi Salim 
1225f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1226f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
12311da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
12321da177e4SLinus Torvalds 
123326b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
12341da177e4SLinus Torvalds {
12351da177e4SLinus Torvalds 	struct xfrm_mgr *km;
12361da177e4SLinus Torvalds 
123726b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
123826b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
123926b15dadSJamal Hadi Salim 		if (km->notify_policy)
124026b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
124126b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
124226b15dadSJamal Hadi Salim }
124326b15dadSJamal Hadi Salim 
124426b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
124526b15dadSJamal Hadi Salim {
124626b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
124726b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
124826b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
124926b15dadSJamal Hadi Salim 		if (km->notify)
125026b15dadSJamal Hadi Salim 			km->notify(x, c);
125126b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
125226b15dadSJamal Hadi Salim }
125326b15dadSJamal Hadi Salim 
125426b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
125526b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
125626b15dadSJamal Hadi Salim 
125753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
125826b15dadSJamal Hadi Salim {
125926b15dadSJamal Hadi Salim 	struct km_event c;
126026b15dadSJamal Hadi Salim 
1261bf08867fSHerbert Xu 	c.data.hard = hard;
126253bc6b4dSJamal Hadi Salim 	c.pid = pid;
1263f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
126426b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
12651da177e4SLinus Torvalds 
12661da177e4SLinus Torvalds 	if (hard)
12671da177e4SLinus Torvalds 		wake_up(&km_waitq);
12681da177e4SLinus Torvalds }
12691da177e4SLinus Torvalds 
127053bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
127126b15dadSJamal Hadi Salim /*
127226b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
127326b15dadSJamal Hadi Salim  * We are happy with one success
127426b15dadSJamal Hadi Salim */
1275980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
12761da177e4SLinus Torvalds {
127726b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
12781da177e4SLinus Torvalds 	struct xfrm_mgr *km;
12791da177e4SLinus Torvalds 
12801da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
12811da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
128226b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
128326b15dadSJamal Hadi Salim 		if (!acqret)
128426b15dadSJamal Hadi Salim 			err = acqret;
12851da177e4SLinus Torvalds 	}
12861da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
12871da177e4SLinus Torvalds 	return err;
12881da177e4SLinus Torvalds }
1289980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
12921da177e4SLinus Torvalds {
12931da177e4SLinus Torvalds 	int err = -EINVAL;
12941da177e4SLinus Torvalds 	struct xfrm_mgr *km;
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
12971da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
12981da177e4SLinus Torvalds 		if (km->new_mapping)
12991da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
13001da177e4SLinus Torvalds 		if (!err)
13011da177e4SLinus Torvalds 			break;
13021da177e4SLinus Torvalds 	}
13031da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13041da177e4SLinus Torvalds 	return err;
13051da177e4SLinus Torvalds }
13061da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
13071da177e4SLinus Torvalds 
13086c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
13091da177e4SLinus Torvalds {
131026b15dadSJamal Hadi Salim 	struct km_event c;
13111da177e4SLinus Torvalds 
1312bf08867fSHerbert Xu 	c.data.hard = hard;
13136c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1314f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
131526b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 	if (hard)
13181da177e4SLinus Torvalds 		wake_up(&km_waitq);
13191da177e4SLinus Torvalds }
1320a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
13211da177e4SLinus Torvalds 
132297a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
132397a64b45SMasahide NAKAMURA {
132497a64b45SMasahide NAKAMURA 	int err = -EINVAL;
132597a64b45SMasahide NAKAMURA 	int ret;
132697a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
132797a64b45SMasahide NAKAMURA 
132897a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
132997a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
133097a64b45SMasahide NAKAMURA 		if (km->report) {
133197a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
133297a64b45SMasahide NAKAMURA 			if (!ret)
133397a64b45SMasahide NAKAMURA 				err = ret;
133497a64b45SMasahide NAKAMURA 		}
133597a64b45SMasahide NAKAMURA 	}
133697a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
133797a64b45SMasahide NAKAMURA 	return err;
133897a64b45SMasahide NAKAMURA }
133997a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
134097a64b45SMasahide NAKAMURA 
13411da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
13421da177e4SLinus Torvalds {
13431da177e4SLinus Torvalds 	int err;
13441da177e4SLinus Torvalds 	u8 *data;
13451da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13461da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
13491da177e4SLinus Torvalds 		return -EMSGSIZE;
13501da177e4SLinus Torvalds 
13511da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
13521da177e4SLinus Torvalds 	if (!data)
13531da177e4SLinus Torvalds 		return -ENOMEM;
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	err = -EFAULT;
13561da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
13571da177e4SLinus Torvalds 		goto out;
13581da177e4SLinus Torvalds 
13591da177e4SLinus Torvalds 	err = -EINVAL;
13601da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13611da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1362cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
13631da177e4SLinus Torvalds 					 optlen, &err);
13641da177e4SLinus Torvalds 		if (err >= 0)
13651da177e4SLinus Torvalds 			break;
13661da177e4SLinus Torvalds 	}
13671da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds 	if (err >= 0) {
13701da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
13711da177e4SLinus Torvalds 		xfrm_pol_put(pol);
13721da177e4SLinus Torvalds 		err = 0;
13731da177e4SLinus Torvalds 	}
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds out:
13761da177e4SLinus Torvalds 	kfree(data);
13771da177e4SLinus Torvalds 	return err;
13781da177e4SLinus Torvalds }
13791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
13801da177e4SLinus Torvalds 
13811da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
13821da177e4SLinus Torvalds {
13831da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
13841da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
13851da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
13861da177e4SLinus Torvalds 	return 0;
13871da177e4SLinus Torvalds }
13881da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
13891da177e4SLinus Torvalds 
13901da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
13911da177e4SLinus Torvalds {
13921da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
13931da177e4SLinus Torvalds 	list_del(&km->list);
13941da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
13951da177e4SLinus Torvalds 	return 0;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
13981da177e4SLinus Torvalds 
13991da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
14001da177e4SLinus Torvalds {
14011da177e4SLinus Torvalds 	int err = 0;
14021da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14031da177e4SLinus Torvalds 		return -EINVAL;
14041da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14051da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1406f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14071da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
14081da177e4SLinus Torvalds 		err = -ENOBUFS;
1409edcd5821SDavid S. Miller 	else
14101da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1411f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14121da177e4SLinus Torvalds 	return err;
14131da177e4SLinus Torvalds }
14141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
14151da177e4SLinus Torvalds 
14161da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
14171da177e4SLinus Torvalds {
14181da177e4SLinus Torvalds 	int err = 0;
14191da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14201da177e4SLinus Torvalds 		return -EINVAL;
14211da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14221da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1423f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14241da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
14251da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
14261da177e4SLinus Torvalds 			err = -EINVAL;
1427edcd5821SDavid S. Miller 		else
14281da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
14291da177e4SLinus Torvalds 	}
1430f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14311da177e4SLinus Torvalds 	return err;
14321da177e4SLinus Torvalds }
14331da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
14361da177e4SLinus Torvalds {
14371da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
14381da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
14391da177e4SLinus Torvalds 		return NULL;
14401da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
14411da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1442546be240SHerbert Xu 	if (unlikely(!afinfo))
14431da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
14441da177e4SLinus Torvalds 	return afinfo;
14451da177e4SLinus Torvalds }
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
14481da177e4SLinus Torvalds {
1449546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
14501da177e4SLinus Torvalds }
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
14531da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
14541da177e4SLinus Torvalds {
14551da177e4SLinus Torvalds 	if (x->tunnel) {
14561da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
14571da177e4SLinus Torvalds 
14581da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
14591da177e4SLinus Torvalds 			xfrm_state_delete(t);
14601da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
14611da177e4SLinus Torvalds 		xfrm_state_put(t);
14621da177e4SLinus Torvalds 		x->tunnel = NULL;
14631da177e4SLinus Torvalds 	}
14641da177e4SLinus Torvalds }
14651da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
14661da177e4SLinus Torvalds 
146780b30c10SHerbert Xu /*
146880b30c10SHerbert Xu  * This function is NOT optimal.  For example, with ESP it will give an
146980b30c10SHerbert Xu  * MTU that's usually two bytes short of being optimal.  However, it will
147080b30c10SHerbert Xu  * usually give an answer that's a multiple of 4 provided the input is
147180b30c10SHerbert Xu  * also a multiple of 4.
147280b30c10SHerbert Xu  */
14731da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
14741da177e4SLinus Torvalds {
14751da177e4SLinus Torvalds 	int res = mtu;
14761da177e4SLinus Torvalds 
14771da177e4SLinus Torvalds 	res -= x->props.header_len;
14781da177e4SLinus Torvalds 
14791da177e4SLinus Torvalds 	for (;;) {
14801da177e4SLinus Torvalds 		int m = res;
14811da177e4SLinus Torvalds 
14821da177e4SLinus Torvalds 		if (m < 68)
14831da177e4SLinus Torvalds 			return 68;
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds 		spin_lock_bh(&x->lock);
14861da177e4SLinus Torvalds 		if (x->km.state == XFRM_STATE_VALID &&
14871da177e4SLinus Torvalds 		    x->type && x->type->get_max_size)
14881da177e4SLinus Torvalds 			m = x->type->get_max_size(x, m);
14891da177e4SLinus Torvalds 		else
14901da177e4SLinus Torvalds 			m += x->props.header_len;
14911da177e4SLinus Torvalds 		spin_unlock_bh(&x->lock);
14921da177e4SLinus Torvalds 
14931da177e4SLinus Torvalds 		if (m <= mtu)
14941da177e4SLinus Torvalds 			break;
14951da177e4SLinus Torvalds 		res -= (m - mtu);
14961da177e4SLinus Torvalds 	}
14971da177e4SLinus Torvalds 
14981da177e4SLinus Torvalds 	return res;
14991da177e4SLinus Torvalds }
15001da177e4SLinus Torvalds 
150172cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
150272cb6962SHerbert Xu {
1503d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1504d094cd83SHerbert Xu 	int family = x->props.family;
150572cb6962SHerbert Xu 	int err;
150672cb6962SHerbert Xu 
1507d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1508d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1509d094cd83SHerbert Xu 	if (!afinfo)
1510d094cd83SHerbert Xu 		goto error;
1511d094cd83SHerbert Xu 
1512d094cd83SHerbert Xu 	err = 0;
1513d094cd83SHerbert Xu 	if (afinfo->init_flags)
1514d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1515d094cd83SHerbert Xu 
1516d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1517d094cd83SHerbert Xu 
1518d094cd83SHerbert Xu 	if (err)
1519d094cd83SHerbert Xu 		goto error;
1520d094cd83SHerbert Xu 
1521d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1522d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
152372cb6962SHerbert Xu 	if (x->type == NULL)
152472cb6962SHerbert Xu 		goto error;
152572cb6962SHerbert Xu 
152672cb6962SHerbert Xu 	err = x->type->init_state(x);
152772cb6962SHerbert Xu 	if (err)
152872cb6962SHerbert Xu 		goto error;
152972cb6962SHerbert Xu 
1530b59f45d0SHerbert Xu 	x->mode = xfrm_get_mode(x->props.mode, family);
1531b59f45d0SHerbert Xu 	if (x->mode == NULL)
1532b59f45d0SHerbert Xu 		goto error;
1533b59f45d0SHerbert Xu 
153472cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
153572cb6962SHerbert Xu 
153672cb6962SHerbert Xu error:
153772cb6962SHerbert Xu 	return err;
153872cb6962SHerbert Xu }
153972cb6962SHerbert Xu 
154072cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
15411da177e4SLinus Torvalds 
15421da177e4SLinus Torvalds void __init xfrm_state_init(void)
15431da177e4SLinus Torvalds {
1544f034b5d4SDavid S. Miller 	unsigned int sz;
15451da177e4SLinus Torvalds 
1546f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
1547f034b5d4SDavid S. Miller 
154844e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
154944e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
155044e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
1551f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1552f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1553f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1554f034b5d4SDavid S. Miller 
15551da177e4SLinus Torvalds 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
15561da177e4SLinus Torvalds }
15571da177e4SLinus Torvalds 
1558