xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision c4028958)
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 
66667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
67667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
6844e36b42SDavid S. Miller 					 unsigned short family)
69f034b5d4SDavid S. Miller {
70667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
71f034b5d4SDavid S. Miller }
72f034b5d4SDavid S. Miller 
732575b654SDavid S. Miller static inline unsigned int
748122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
75f034b5d4SDavid S. Miller {
76c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
77f034b5d4SDavid S. Miller }
78f034b5d4SDavid S. Miller 
79f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
80f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
81f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
82f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
83f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
84f034b5d4SDavid S. Miller {
85f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
86f034b5d4SDavid S. Miller 	struct xfrm_state *x;
87f034b5d4SDavid S. Miller 
88f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
89f034b5d4SDavid S. Miller 		unsigned int h;
90f034b5d4SDavid S. Miller 
91c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
92c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
93c1969f29SDavid S. Miller 				    nhashmask);
94f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
95f034b5d4SDavid S. Miller 
96667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
97667bbcb6SMasahide NAKAMURA 				    x->props.family,
98f034b5d4SDavid S. Miller 				    nhashmask);
99f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
100f034b5d4SDavid S. Miller 
1017b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1027b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1037b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1047b4dc360SMasahide NAKAMURA 					    nhashmask);
105f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
106f034b5d4SDavid S. Miller 		}
107f034b5d4SDavid S. Miller 	}
1087b4dc360SMasahide NAKAMURA }
109f034b5d4SDavid S. Miller 
110f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
111f034b5d4SDavid S. Miller {
112f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
113f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
114f034b5d4SDavid S. Miller }
115f034b5d4SDavid S. Miller 
116f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
117f034b5d4SDavid S. Miller 
118c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
119f034b5d4SDavid S. Miller {
120f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
121f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
122f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
123f034b5d4SDavid S. Miller 	int i;
124f034b5d4SDavid S. Miller 
125f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
126f034b5d4SDavid S. Miller 
127f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
12844e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
129f034b5d4SDavid S. Miller 	if (!ndst)
130f034b5d4SDavid S. Miller 		goto out_unlock;
13144e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
132f034b5d4SDavid S. Miller 	if (!nsrc) {
13344e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
134f034b5d4SDavid S. Miller 		goto out_unlock;
135f034b5d4SDavid S. Miller 	}
13644e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
137f034b5d4SDavid S. Miller 	if (!nspi) {
13844e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
13944e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
140f034b5d4SDavid S. Miller 		goto out_unlock;
141f034b5d4SDavid S. Miller 	}
142f034b5d4SDavid S. Miller 
143f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
144f034b5d4SDavid S. Miller 
145f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
146f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
147f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
148f034b5d4SDavid S. Miller 				   nhashmask);
149f034b5d4SDavid S. Miller 
150f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
151f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
152f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
153f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
154f034b5d4SDavid S. Miller 
155f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
156f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
157f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
158f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
159f034b5d4SDavid S. Miller 
160f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
161f034b5d4SDavid S. Miller 
162f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
16344e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
16444e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
16544e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
166f034b5d4SDavid S. Miller 
167f034b5d4SDavid S. Miller out_unlock:
168f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
169f034b5d4SDavid S. Miller }
170f034b5d4SDavid S. Miller 
171c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
172f034b5d4SDavid S. Miller 
1731da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1741da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1771da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1808f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1811da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1821da177e4SLinus Torvalds 
18353bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
1861da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
1871da177e4SLinus Torvalds 
188980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
18953bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
1921da177e4SLinus Torvalds {
193a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
194a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
1951da177e4SLinus Torvalds 	kfree(x->aalg);
1961da177e4SLinus Torvalds 	kfree(x->ealg);
1971da177e4SLinus Torvalds 	kfree(x->calg);
1981da177e4SLinus Torvalds 	kfree(x->encap);
199060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
200b59f45d0SHerbert Xu 	if (x->mode)
201b59f45d0SHerbert Xu 		xfrm_put_mode(x->mode);
2021da177e4SLinus Torvalds 	if (x->type) {
2031da177e4SLinus Torvalds 		x->type->destructor(x);
2041da177e4SLinus Torvalds 		xfrm_put_type(x->type);
2051da177e4SLinus Torvalds 	}
206df71837dSTrent Jaeger 	security_xfrm_state_free(x);
2071da177e4SLinus Torvalds 	kfree(x);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
210c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	struct xfrm_state *x;
2138f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
2148f126e37SDavid S. Miller 	struct hlist_head gc_list;
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
2178f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
2188f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
2191da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
2201da177e4SLinus Torvalds 
2218f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
2221da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
2238f126e37SDavid S. Miller 
2241da177e4SLinus Torvalds 	wake_up(&km_waitq);
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
2301da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
2311da177e4SLinus Torvalds 	else
2321da177e4SLinus Torvalds 	        return secs*HZ;
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
2381da177e4SLinus Torvalds 	unsigned long now = (unsigned long)xtime.tv_sec;
2391da177e4SLinus Torvalds 	long next = LONG_MAX;
2401da177e4SLinus Torvalds 	int warn = 0;
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	spin_lock(&x->lock);
2431da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
2441da177e4SLinus Torvalds 		goto out;
2451da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
2461da177e4SLinus Torvalds 		goto expired;
2471da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
2481da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
2491da177e4SLinus Torvalds 			x->curlft.add_time - now;
2501da177e4SLinus Torvalds 		if (tmo <= 0)
2511da177e4SLinus Torvalds 			goto expired;
2521da177e4SLinus Torvalds 		if (tmo < next)
2531da177e4SLinus Torvalds 			next = tmo;
2541da177e4SLinus Torvalds 	}
2551da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
2561da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
2571da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2581da177e4SLinus Torvalds 		if (tmo <= 0)
2591da177e4SLinus Torvalds 			goto expired;
2601da177e4SLinus Torvalds 		if (tmo < next)
2611da177e4SLinus Torvalds 			next = tmo;
2621da177e4SLinus Torvalds 	}
2631da177e4SLinus Torvalds 	if (x->km.dying)
2641da177e4SLinus Torvalds 		goto resched;
2651da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
2661da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
2671da177e4SLinus Torvalds 			x->curlft.add_time - now;
2681da177e4SLinus Torvalds 		if (tmo <= 0)
2691da177e4SLinus Torvalds 			warn = 1;
2701da177e4SLinus Torvalds 		else if (tmo < next)
2711da177e4SLinus Torvalds 			next = tmo;
2721da177e4SLinus Torvalds 	}
2731da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
2741da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
2751da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2761da177e4SLinus Torvalds 		if (tmo <= 0)
2771da177e4SLinus Torvalds 			warn = 1;
2781da177e4SLinus Torvalds 		else if (tmo < next)
2791da177e4SLinus Torvalds 			next = tmo;
2801da177e4SLinus Torvalds 	}
2811da177e4SLinus Torvalds 
2824666faabSHerbert Xu 	x->km.dying = warn;
2831da177e4SLinus Torvalds 	if (warn)
28453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
2851da177e4SLinus Torvalds resched:
286a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
287a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
288a47f0ce0SDavid S. Miller 
2891da177e4SLinus Torvalds 	goto out;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds expired:
2921da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
2931da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
2941da177e4SLinus Torvalds 		wake_up(&km_waitq);
2951da177e4SLinus Torvalds 		next = 2;
2961da177e4SLinus Torvalds 		goto resched;
2971da177e4SLinus Torvalds 	}
2984666faabSHerbert Xu 	if (!__xfrm_state_delete(x) && x->id.spi)
29953bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds out:
3021da177e4SLinus Torvalds 	spin_unlock(&x->lock);
3031da177e4SLinus Torvalds }
3041da177e4SLinus Torvalds 
3050ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
3060ac84752SDavid S. Miller 
3071da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	struct xfrm_state *x;
3101da177e4SLinus Torvalds 
3110da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	if (x) {
3141da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
3151da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
3168f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
3178f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
3188f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
3191da177e4SLinus Torvalds 		init_timer(&x->timer);
3201da177e4SLinus Torvalds 		x->timer.function = xfrm_timer_handler;
3211da177e4SLinus Torvalds 		x->timer.data	  = (unsigned long)x;
322f8cd5488SJamal Hadi Salim 		init_timer(&x->rtimer);
323f8cd5488SJamal Hadi Salim 		x->rtimer.function = xfrm_replay_timer_handler;
324f8cd5488SJamal Hadi Salim 		x->rtimer.data     = (unsigned long)x;
3251da177e4SLinus Torvalds 		x->curlft.add_time = (unsigned long)xtime.tv_sec;
3261da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
3271da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
3281da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
3291da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
330f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
331f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
3321da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	return x;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
3438f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
3441da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
3451da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
3481da177e4SLinus Torvalds 
34953bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
3501da177e4SLinus Torvalds {
35126b15dadSJamal Hadi Salim 	int err = -ESRCH;
35226b15dadSJamal Hadi Salim 
3531da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
3541da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
3551da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
3568f126e37SDavid S. Miller 		hlist_del(&x->bydst);
3578f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
358a47f0ce0SDavid S. Miller 		if (x->id.spi)
3598f126e37SDavid S. Miller 			hlist_del(&x->byspi);
360f034b5d4SDavid S. Miller 		xfrm_state_num--;
3611da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
3641da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
3651da177e4SLinus Torvalds 		 * is what we are dropping here.
3661da177e4SLinus Torvalds 		 */
36721380b81SHerbert Xu 		__xfrm_state_put(x);
36826b15dadSJamal Hadi Salim 		err = 0;
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
37126b15dadSJamal Hadi Salim 	return err;
37226b15dadSJamal Hadi Salim }
37353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
37426b15dadSJamal Hadi Salim 
37526b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
3761da177e4SLinus Torvalds {
37726b15dadSJamal Hadi Salim 	int err;
37826b15dadSJamal Hadi Salim 
3791da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
38026b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
3811da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
38226b15dadSJamal Hadi Salim 
38326b15dadSJamal Hadi Salim 	return err;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds void xfrm_state_flush(u8 proto)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds 	int i;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
392a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
3938f126e37SDavid S. Miller 		struct hlist_node *entry;
3948f126e37SDavid S. Miller 		struct xfrm_state *x;
3951da177e4SLinus Torvalds restart:
3968f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
3971da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
3985794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
3991da177e4SLinus Torvalds 				xfrm_state_hold(x);
4001da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 				xfrm_state_delete(x);
4031da177e4SLinus Torvalds 				xfrm_state_put(x);
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
4061da177e4SLinus Torvalds 				goto restart;
4071da177e4SLinus Torvalds 			}
4081da177e4SLinus Torvalds 		}
4091da177e4SLinus Torvalds 	}
4101da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
4111da177e4SLinus Torvalds 	wake_up(&km_waitq);
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds static int
4161da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
4171da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
4181da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
4191da177e4SLinus Torvalds 		  unsigned short family)
4201da177e4SLinus Torvalds {
4211da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
4221da177e4SLinus Torvalds 	if (!afinfo)
4231da177e4SLinus Torvalds 		return -1;
4241da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
4251da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
4261da177e4SLinus Torvalds 	return 0;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
429a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
430edcd5821SDavid S. Miller {
431edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
432edcd5821SDavid S. Miller 	struct xfrm_state *x;
4338f126e37SDavid S. Miller 	struct hlist_node *entry;
434edcd5821SDavid S. Miller 
4358f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
436edcd5821SDavid S. Miller 		if (x->props.family != family ||
437edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
438edcd5821SDavid S. Miller 		    x->id.proto     != proto)
439edcd5821SDavid S. Miller 			continue;
440edcd5821SDavid S. Miller 
441edcd5821SDavid S. Miller 		switch (family) {
442edcd5821SDavid S. Miller 		case AF_INET:
443edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
444edcd5821SDavid S. Miller 				continue;
445edcd5821SDavid S. Miller 			break;
446edcd5821SDavid S. Miller 		case AF_INET6:
447edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
448edcd5821SDavid S. Miller 					     (struct in6_addr *)
449edcd5821SDavid S. Miller 					     x->id.daddr.a6))
450edcd5821SDavid S. Miller 				continue;
451edcd5821SDavid S. Miller 			break;
452edcd5821SDavid S. Miller 		};
453edcd5821SDavid S. Miller 
454edcd5821SDavid S. Miller 		xfrm_state_hold(x);
455edcd5821SDavid S. Miller 		return x;
456edcd5821SDavid S. Miller 	}
457edcd5821SDavid S. Miller 
458edcd5821SDavid S. Miller 	return NULL;
459edcd5821SDavid S. Miller }
460edcd5821SDavid S. Miller 
461edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
462edcd5821SDavid S. Miller {
463667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
464edcd5821SDavid S. Miller 	struct xfrm_state *x;
4658f126e37SDavid S. Miller 	struct hlist_node *entry;
466edcd5821SDavid S. Miller 
4678f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
468edcd5821SDavid S. Miller 		if (x->props.family != family ||
469edcd5821SDavid S. Miller 		    x->id.proto     != proto)
470edcd5821SDavid S. Miller 			continue;
471edcd5821SDavid S. Miller 
472edcd5821SDavid S. Miller 		switch (family) {
473edcd5821SDavid S. Miller 		case AF_INET:
474edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
475edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
476edcd5821SDavid S. Miller 				continue;
477edcd5821SDavid S. Miller 			break;
478edcd5821SDavid S. Miller 		case AF_INET6:
479edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
480edcd5821SDavid S. Miller 					     (struct in6_addr *)
481edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
482edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
483edcd5821SDavid S. Miller 					     (struct in6_addr *)
484edcd5821SDavid S. Miller 					     x->props.saddr.a6))
485edcd5821SDavid S. Miller 				continue;
486edcd5821SDavid S. Miller 			break;
487edcd5821SDavid S. Miller 		};
488edcd5821SDavid S. Miller 
489edcd5821SDavid S. Miller 		xfrm_state_hold(x);
490edcd5821SDavid S. Miller 		return x;
491edcd5821SDavid S. Miller 	}
492edcd5821SDavid S. Miller 
493edcd5821SDavid S. Miller 	return NULL;
494edcd5821SDavid S. Miller }
495edcd5821SDavid S. Miller 
496edcd5821SDavid S. Miller static inline struct xfrm_state *
497edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
498edcd5821SDavid S. Miller {
499edcd5821SDavid S. Miller 	if (use_spi)
500edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
501edcd5821SDavid S. Miller 					   x->id.proto, family);
502edcd5821SDavid S. Miller 	else
503edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
504edcd5821SDavid S. Miller 						  &x->props.saddr,
505edcd5821SDavid S. Miller 						  x->id.proto, family);
506edcd5821SDavid S. Miller }
507edcd5821SDavid S. Miller 
5082fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
5092fab22f2SPatrick McHardy {
5102fab22f2SPatrick McHardy 	if (have_hash_collision &&
5112fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
5122fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
5132fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
5142fab22f2SPatrick McHardy }
5152fab22f2SPatrick McHardy 
5161da177e4SLinus Torvalds struct xfrm_state *
5171da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
5181da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
5191da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
5201da177e4SLinus Torvalds 		unsigned short family)
5211da177e4SLinus Torvalds {
522c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
5238f126e37SDavid S. Miller 	struct hlist_node *entry;
5241da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
5251da177e4SLinus Torvalds 	int acquire_in_progress = 0;
5261da177e4SLinus Torvalds 	int error = 0;
5271da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
5308f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
5311da177e4SLinus Torvalds 		if (x->props.family == family &&
5321da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
533fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
5341da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
5351da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
5361da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
5371da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
5381da177e4SLinus Torvalds 			/* Resolution logic:
5391da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
5401da177e4SLinus Torvalds 			      Done.
5411da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 			   Entering area of "sysdeps".
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
5461da177e4SLinus Torvalds 			      it selects only session which triggered
5471da177e4SLinus Torvalds 			      previous resolution. Key manager will do
5481da177e4SLinus Torvalds 			      something to install a state with proper
5491da177e4SLinus Torvalds 			      selector.
5501da177e4SLinus Torvalds 			 */
5511da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
552df71837dSTrent Jaeger 				if (!xfrm_selector_match(&x->sel, fl, family) ||
553e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
5541da177e4SLinus Torvalds 					continue;
5551da177e4SLinus Torvalds 				if (!best ||
5561da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
5571da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
5581da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
5591da177e4SLinus Torvalds 					best = x;
5601da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
5611da177e4SLinus Torvalds 				acquire_in_progress = 1;
5621da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
5631da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
564df71837dSTrent Jaeger  				if (xfrm_selector_match(&x->sel, fl, family) &&
565e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
5661da177e4SLinus Torvalds 					error = -ESRCH;
5671da177e4SLinus Torvalds 			}
5681da177e4SLinus Torvalds 		}
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	x = best;
5721da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
5735c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
574edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
575edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
5761da177e4SLinus Torvalds 			xfrm_state_put(x0);
5771da177e4SLinus Torvalds 			error = -EEXIST;
5781da177e4SLinus Torvalds 			goto out;
5791da177e4SLinus Torvalds 		}
5801da177e4SLinus Torvalds 		x = xfrm_state_alloc();
5811da177e4SLinus Torvalds 		if (x == NULL) {
5821da177e4SLinus Torvalds 			error = -ENOMEM;
5831da177e4SLinus Torvalds 			goto out;
5841da177e4SLinus Torvalds 		}
5851da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
5861da177e4SLinus Torvalds 		 * to current session. */
5871da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
5881da177e4SLinus Torvalds 
589e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
590e0d1caa7SVenkat Yekkirala 		if (error) {
591e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
592e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
593e0d1caa7SVenkat Yekkirala 			x = NULL;
594e0d1caa7SVenkat Yekkirala 			goto out;
595e0d1caa7SVenkat Yekkirala 		}
596e0d1caa7SVenkat Yekkirala 
5971da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
5981da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
5998f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
600667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
6018f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
6021da177e4SLinus Torvalds 			if (x->id.spi) {
6031da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
6048f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
6051da177e4SLinus Torvalds 			}
6061da177e4SLinus Torvalds 			x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
6071da177e4SLinus Torvalds 			x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
6081da177e4SLinus Torvalds 			add_timer(&x->timer);
6092fab22f2SPatrick McHardy 			xfrm_state_num++;
6102fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
6111da177e4SLinus Torvalds 		} else {
6121da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
6131da177e4SLinus Torvalds 			xfrm_state_put(x);
6141da177e4SLinus Torvalds 			x = NULL;
6151da177e4SLinus Torvalds 			error = -ESRCH;
6161da177e4SLinus Torvalds 		}
6171da177e4SLinus Torvalds 	}
6181da177e4SLinus Torvalds out:
6191da177e4SLinus Torvalds 	if (x)
6201da177e4SLinus Torvalds 		xfrm_state_hold(x);
6211da177e4SLinus Torvalds 	else
6221da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
6231da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6241da177e4SLinus Torvalds 	return x;
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
6281da177e4SLinus Torvalds {
629a624c108SDavid S. Miller 	unsigned int h;
6301da177e4SLinus Torvalds 
6319d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
6329d4a706dSDavid S. Miller 
633c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
634c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
6358f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
6361da177e4SLinus Torvalds 
637667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
6388f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
6396c44e6b7SMasahide NAKAMURA 
6407b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
6416c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
6426c44e6b7SMasahide NAKAMURA 				  x->props.family);
6431da177e4SLinus Torvalds 
6448f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
6456c44e6b7SMasahide NAKAMURA 	}
6461da177e4SLinus Torvalds 
647a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
648a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
649a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
650f8cd5488SJamal Hadi Salim 
6511da177e4SLinus Torvalds 	wake_up(&km_waitq);
652f034b5d4SDavid S. Miller 
653f034b5d4SDavid S. Miller 	xfrm_state_num++;
654f034b5d4SDavid S. Miller 
655918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
6561da177e4SLinus Torvalds }
6571da177e4SLinus Torvalds 
658c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
659c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
660c7f5ea3aSDavid S. Miller {
661c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
662c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
663c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
664c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
665c7f5ea3aSDavid S. Miller 	unsigned int h;
666c7f5ea3aSDavid S. Miller 
667c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
668c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
669c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
670c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
671c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
672c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
673c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
674c7f5ea3aSDavid S. Miller 	}
675c7f5ea3aSDavid S. Miller }
676c7f5ea3aSDavid S. Miller 
6771da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
6781da177e4SLinus Torvalds {
6791da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
680c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
6811da177e4SLinus Torvalds 	__xfrm_state_insert(x);
6821da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
6851da177e4SLinus Torvalds 
6862770834cSDavid S. Miller /* xfrm_state_lock is held */
6872770834cSDavid 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)
6882770834cSDavid S. Miller {
689c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
6908f126e37SDavid S. Miller 	struct hlist_node *entry;
6912770834cSDavid S. Miller 	struct xfrm_state *x;
6922770834cSDavid S. Miller 
6938f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
6942770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
6952770834cSDavid S. Miller 		    x->props.mode   != mode ||
6962770834cSDavid S. Miller 		    x->props.family != family ||
6972770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
6982770834cSDavid S. Miller 		    x->id.spi       != 0)
6992770834cSDavid S. Miller 			continue;
7002770834cSDavid S. Miller 
7012770834cSDavid S. Miller 		switch (family) {
7022770834cSDavid S. Miller 		case AF_INET:
7032770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
7042770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
7052770834cSDavid S. Miller 				continue;
7062770834cSDavid S. Miller 			break;
7072770834cSDavid S. Miller 		case AF_INET6:
7082770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
7092770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
7102770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
7112770834cSDavid S. Miller 					     x->props.saddr.a6,
7122770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
7132770834cSDavid S. Miller 				continue;
7142770834cSDavid S. Miller 			break;
7152770834cSDavid S. Miller 		};
7162770834cSDavid S. Miller 
7172770834cSDavid S. Miller 		xfrm_state_hold(x);
7182770834cSDavid S. Miller 		return x;
7192770834cSDavid S. Miller 	}
7202770834cSDavid S. Miller 
7212770834cSDavid S. Miller 	if (!create)
7222770834cSDavid S. Miller 		return NULL;
7232770834cSDavid S. Miller 
7242770834cSDavid S. Miller 	x = xfrm_state_alloc();
7252770834cSDavid S. Miller 	if (likely(x)) {
7262770834cSDavid S. Miller 		switch (family) {
7272770834cSDavid S. Miller 		case AF_INET:
7282770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
7292770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
7302770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
7312770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
7322770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
7332770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
7342770834cSDavid S. Miller 			break;
7352770834cSDavid S. Miller 
7362770834cSDavid S. Miller 		case AF_INET6:
7372770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
7382770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7392770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
7402770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7412770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
7422770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
7432770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
7442770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7452770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
7462770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7472770834cSDavid S. Miller 			break;
7482770834cSDavid S. Miller 		};
7492770834cSDavid S. Miller 
7502770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
7512770834cSDavid S. Miller 		x->id.proto = proto;
7522770834cSDavid S. Miller 		x->props.family = family;
7532770834cSDavid S. Miller 		x->props.mode = mode;
7542770834cSDavid S. Miller 		x->props.reqid = reqid;
7552770834cSDavid S. Miller 		x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
7562770834cSDavid S. Miller 		xfrm_state_hold(x);
7572770834cSDavid S. Miller 		x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
7582770834cSDavid S. Miller 		add_timer(&x->timer);
7598f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
760667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
7618f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
7622770834cSDavid S. Miller 		wake_up(&km_waitq);
763918049f0SDavid S. Miller 
764918049f0SDavid S. Miller 		xfrm_state_num++;
765918049f0SDavid S. Miller 
766918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
7672770834cSDavid S. Miller 	}
7682770834cSDavid S. Miller 
7692770834cSDavid S. Miller 	return x;
7702770834cSDavid S. Miller }
7712770834cSDavid S. Miller 
7721da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
7731da177e4SLinus Torvalds 
7741da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
7751da177e4SLinus Torvalds {
7761da177e4SLinus Torvalds 	struct xfrm_state *x1;
7771da177e4SLinus Torvalds 	int family;
7781da177e4SLinus Torvalds 	int err;
779eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	family = x->props.family;
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7841da177e4SLinus Torvalds 
785edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
7861da177e4SLinus Torvalds 	if (x1) {
7871da177e4SLinus Torvalds 		xfrm_state_put(x1);
7881da177e4SLinus Torvalds 		x1 = NULL;
7891da177e4SLinus Torvalds 		err = -EEXIST;
7901da177e4SLinus Torvalds 		goto out;
7911da177e4SLinus Torvalds 	}
7921da177e4SLinus Torvalds 
793eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
7941da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
7951da177e4SLinus Torvalds 		if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
7961da177e4SLinus Torvalds 			xfrm_state_put(x1);
7971da177e4SLinus Torvalds 			x1 = NULL;
7981da177e4SLinus Torvalds 		}
7991da177e4SLinus Torvalds 	}
8001da177e4SLinus Torvalds 
801eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
8022770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
8032770834cSDavid S. Miller 				     x->id.proto,
8041da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
8051da177e4SLinus Torvalds 
806c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
8071da177e4SLinus Torvalds 	__xfrm_state_insert(x);
8081da177e4SLinus Torvalds 	err = 0;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds out:
8111da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds 	if (x1) {
8141da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8151da177e4SLinus Torvalds 		xfrm_state_put(x1);
8161da177e4SLinus Torvalds 	}
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds 	return err;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
8231da177e4SLinus Torvalds {
8241da177e4SLinus Torvalds 	struct xfrm_state *x1;
8251da177e4SLinus Torvalds 	int err;
826eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
829edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 	err = -ESRCH;
8321da177e4SLinus Torvalds 	if (!x1)
8331da177e4SLinus Torvalds 		goto out;
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
8361da177e4SLinus Torvalds 		xfrm_state_put(x1);
8371da177e4SLinus Torvalds 		err = -EEXIST;
8381da177e4SLinus Torvalds 		goto out;
8391da177e4SLinus Torvalds 	}
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
8421da177e4SLinus Torvalds 		__xfrm_state_insert(x);
8431da177e4SLinus Torvalds 		x = NULL;
8441da177e4SLinus Torvalds 	}
8451da177e4SLinus Torvalds 	err = 0;
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds out:
8481da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 	if (err)
8511da177e4SLinus Torvalds 		return err;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	if (!x) {
8541da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8551da177e4SLinus Torvalds 		xfrm_state_put(x1);
8561da177e4SLinus Torvalds 		return 0;
8571da177e4SLinus Torvalds 	}
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 	err = -EINVAL;
8601da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
8611da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
8621da177e4SLinus Torvalds 		if (x->encap && x1->encap)
8631da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
864060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
865060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
866060f02a3SNoriaki TAKAMIYA 		}
867060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
868060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
8691da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
8701da177e4SLinus Torvalds 		x1->km.dying = 0;
8711da177e4SLinus Torvalds 
872a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
8731da177e4SLinus Torvalds 		if (x1->curlft.use_time)
8741da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 		err = 0;
8771da177e4SLinus Torvalds 	}
8781da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	xfrm_state_put(x1);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	return err;
8831da177e4SLinus Torvalds }
8841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
8871da177e4SLinus Torvalds {
8881da177e4SLinus Torvalds 	if (!x->curlft.use_time)
8891da177e4SLinus Torvalds 		x->curlft.use_time = (unsigned long)xtime.tv_sec;
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
8921da177e4SLinus Torvalds 		return -EINVAL;
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
8951da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
8964666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
897a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
8981da177e4SLinus Torvalds 		return -EINVAL;
8991da177e4SLinus Torvalds 	}
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	if (!x->km.dying &&
9021da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
9034666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
9044666faabSHerbert Xu 		x->km.dying = 1;
90553bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
9064666faabSHerbert Xu 	}
9071da177e4SLinus Torvalds 	return 0;
9081da177e4SLinus Torvalds }
9091da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
9121da177e4SLinus Torvalds {
9131da177e4SLinus Torvalds 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
9141da177e4SLinus Torvalds 		- skb_headroom(skb);
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	if (nhead > 0)
9171da177e4SLinus Torvalds 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds 	/* Check tail too... */
9201da177e4SLinus Torvalds 	return 0;
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
9241da177e4SLinus Torvalds {
9251da177e4SLinus Torvalds 	int err = xfrm_state_check_expire(x);
9261da177e4SLinus Torvalds 	if (err < 0)
9271da177e4SLinus Torvalds 		goto err;
9281da177e4SLinus Torvalds 	err = xfrm_state_check_space(x, skb);
9291da177e4SLinus Torvalds err:
9301da177e4SLinus Torvalds 	return err;
9311da177e4SLinus Torvalds }
9321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check);
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds struct xfrm_state *
935a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
9361da177e4SLinus Torvalds 		  unsigned short family)
9371da177e4SLinus Torvalds {
9381da177e4SLinus Torvalds 	struct xfrm_state *x;
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
941edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
9421da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9431da177e4SLinus Torvalds 	return x;
9441da177e4SLinus Torvalds }
9451da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds struct xfrm_state *
948eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
949eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
950eb2971b6SMasahide NAKAMURA {
951eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
952eb2971b6SMasahide NAKAMURA 
953eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
954edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
955eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
956eb2971b6SMasahide NAKAMURA 	return x;
957eb2971b6SMasahide NAKAMURA }
958eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
959eb2971b6SMasahide NAKAMURA 
960eb2971b6SMasahide NAKAMURA struct xfrm_state *
9611da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
9621da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
9631da177e4SLinus Torvalds 	      int create, unsigned short family)
9641da177e4SLinus Torvalds {
9651da177e4SLinus Torvalds 	struct xfrm_state *x;
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
9682770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
9691da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9702770834cSDavid S. Miller 
9711da177e4SLinus Torvalds 	return x;
9721da177e4SLinus Torvalds }
9731da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
9741da177e4SLinus Torvalds 
97541a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
97641a49cc3SMasahide NAKAMURA int
97741a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
97841a49cc3SMasahide NAKAMURA 	       unsigned short family)
97941a49cc3SMasahide NAKAMURA {
98041a49cc3SMasahide NAKAMURA 	int err = 0;
98141a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
98241a49cc3SMasahide NAKAMURA 	if (!afinfo)
98341a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
98441a49cc3SMasahide NAKAMURA 
98541a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
98641a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
98741a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
98841a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
98941a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
99041a49cc3SMasahide NAKAMURA 	return err;
99141a49cc3SMasahide NAKAMURA }
99241a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
99341a49cc3SMasahide NAKAMURA 
99441a49cc3SMasahide NAKAMURA int
99541a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
99641a49cc3SMasahide NAKAMURA 		unsigned short family)
99741a49cc3SMasahide NAKAMURA {
99841a49cc3SMasahide NAKAMURA 	int err = 0;
99941a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
100041a49cc3SMasahide NAKAMURA 	if (!afinfo)
100141a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
100241a49cc3SMasahide NAKAMURA 
100341a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
100441a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
100541a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
100641a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
100741a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
100841a49cc3SMasahide NAKAMURA 	return err;
100941a49cc3SMasahide NAKAMURA }
101041a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
101141a49cc3SMasahide NAKAMURA #endif
101241a49cc3SMasahide NAKAMURA 
10131da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
10161da177e4SLinus Torvalds {
10171da177e4SLinus Torvalds 	int i;
10181da177e4SLinus Torvalds 
1019f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
10208f126e37SDavid S. Miller 		struct hlist_node *entry;
10218f126e37SDavid S. Miller 		struct xfrm_state *x;
10228f126e37SDavid S. Miller 
10238f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
10248f126e37SDavid S. Miller 			if (x->km.seq == seq &&
10258f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
10261da177e4SLinus Torvalds 				xfrm_state_hold(x);
10271da177e4SLinus Torvalds 				return x;
10281da177e4SLinus Torvalds 			}
10291da177e4SLinus Torvalds 		}
10301da177e4SLinus Torvalds 	}
10311da177e4SLinus Torvalds 	return NULL;
10321da177e4SLinus Torvalds }
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
10351da177e4SLinus Torvalds {
10361da177e4SLinus Torvalds 	struct xfrm_state *x;
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10391da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
10401da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10411da177e4SLinus Torvalds 	return x;
10421da177e4SLinus Torvalds }
10431da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
10441da177e4SLinus Torvalds 
10451da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
10461da177e4SLinus Torvalds {
10471da177e4SLinus Torvalds 	u32 res;
10481da177e4SLinus Torvalds 	static u32 acqseq;
10491da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
10521da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
10531da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
10541da177e4SLinus Torvalds 	return res;
10551da177e4SLinus Torvalds }
10561da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
10571da177e4SLinus Torvalds 
10581da177e4SLinus Torvalds void
105926977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
10601da177e4SLinus Torvalds {
1061f034b5d4SDavid S. Miller 	unsigned int h;
10621da177e4SLinus Torvalds 	struct xfrm_state *x0;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	if (x->id.spi)
10651da177e4SLinus Torvalds 		return;
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 	if (minspi == maxspi) {
10681da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
10691da177e4SLinus Torvalds 		if (x0) {
10701da177e4SLinus Torvalds 			xfrm_state_put(x0);
10711da177e4SLinus Torvalds 			return;
10721da177e4SLinus Torvalds 		}
10731da177e4SLinus Torvalds 		x->id.spi = minspi;
10741da177e4SLinus Torvalds 	} else {
10751da177e4SLinus Torvalds 		u32 spi = 0;
107626977b4eSAl Viro 		u32 low = ntohl(minspi);
107726977b4eSAl Viro 		u32 high = ntohl(maxspi);
107826977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
107926977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
10801da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
10811da177e4SLinus Torvalds 			if (x0 == NULL) {
10821da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
10831da177e4SLinus Torvalds 				break;
10841da177e4SLinus Torvalds 			}
10851da177e4SLinus Torvalds 			xfrm_state_put(x0);
10861da177e4SLinus Torvalds 		}
10871da177e4SLinus Torvalds 	}
10881da177e4SLinus Torvalds 	if (x->id.spi) {
10891da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
10901da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
10918f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
10921da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
10931da177e4SLinus Torvalds 		wake_up(&km_waitq);
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds }
10961da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
10991da177e4SLinus Torvalds 		    void *data)
11001da177e4SLinus Torvalds {
11011da177e4SLinus Torvalds 	int i;
11021da177e4SLinus Torvalds 	struct xfrm_state *x;
11038f126e37SDavid S. Miller 	struct hlist_node *entry;
11041da177e4SLinus Torvalds 	int count = 0;
11051da177e4SLinus Torvalds 	int err = 0;
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1108f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
11098f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
11105794708fSMasahide NAKAMURA 			if (xfrm_id_proto_match(x->id.proto, proto))
11111da177e4SLinus Torvalds 				count++;
11121da177e4SLinus Torvalds 		}
11131da177e4SLinus Torvalds 	}
11141da177e4SLinus Torvalds 	if (count == 0) {
11151da177e4SLinus Torvalds 		err = -ENOENT;
11161da177e4SLinus Torvalds 		goto out;
11171da177e4SLinus Torvalds 	}
11181da177e4SLinus Torvalds 
1119f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
11208f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
11215794708fSMasahide NAKAMURA 			if (!xfrm_id_proto_match(x->id.proto, proto))
11221da177e4SLinus Torvalds 				continue;
11231da177e4SLinus Torvalds 			err = func(x, --count, data);
11241da177e4SLinus Torvalds 			if (err)
11251da177e4SLinus Torvalds 				goto out;
11261da177e4SLinus Torvalds 		}
11271da177e4SLinus Torvalds 	}
11281da177e4SLinus Torvalds out:
11291da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11301da177e4SLinus Torvalds 	return err;
11311da177e4SLinus Torvalds }
11321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
11331da177e4SLinus Torvalds 
1134f8cd5488SJamal Hadi Salim 
1135f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1136f8cd5488SJamal Hadi Salim {
1137f8cd5488SJamal Hadi Salim 	struct km_event c;
1138f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1139f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1140f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1141f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1142f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1143f8cd5488SJamal Hadi Salim 	 *     and there were changes
1144f8cd5488SJamal Hadi Salim 	 *
1145f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1146f8cd5488SJamal Hadi Salim 	 */
1147f8cd5488SJamal Hadi Salim 
1148f8cd5488SJamal Hadi Salim 	switch (event) {
1149f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1150f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1151f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
11522717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
11532717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
11542717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
11552717096aSJamal Hadi Salim 			else
1156f8cd5488SJamal Hadi Salim 				return;
11572717096aSJamal Hadi Salim 		}
1158f8cd5488SJamal Hadi Salim 
1159f8cd5488SJamal Hadi Salim 		break;
1160f8cd5488SJamal Hadi Salim 
1161f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1162f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1163f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
11642717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
11652717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1166f8cd5488SJamal Hadi Salim 			return;
11672717096aSJamal Hadi Salim 		}
1168f8cd5488SJamal Hadi Salim 
1169f8cd5488SJamal Hadi Salim 		break;
1170f8cd5488SJamal Hadi Salim 	}
1171f8cd5488SJamal Hadi Salim 
1172f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1173f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1174f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1175f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1176f8cd5488SJamal Hadi Salim 
1177f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1178a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
11792717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
11802717096aSJamal Hadi Salim }
1181a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify);
1182f8cd5488SJamal Hadi Salim 
1183f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1184f8cd5488SJamal Hadi Salim {
1185f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1186f8cd5488SJamal Hadi Salim 
1187f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1188f8cd5488SJamal Hadi Salim 
11892717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
11902717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1191f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
11922717096aSJamal Hadi Salim 		else
11932717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
11942717096aSJamal Hadi Salim 	}
1195f8cd5488SJamal Hadi Salim 
1196f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1197f8cd5488SJamal Hadi Salim }
1198f8cd5488SJamal Hadi Salim 
1199a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
12001da177e4SLinus Torvalds {
12011da177e4SLinus Torvalds 	u32 diff;
1202a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds 	if (unlikely(seq == 0))
12051da177e4SLinus Torvalds 		return -EINVAL;
12061da177e4SLinus Torvalds 
12071da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
12081da177e4SLinus Torvalds 		return 0;
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
12111da177e4SLinus Torvalds 	if (diff >= x->props.replay_window) {
12121da177e4SLinus Torvalds 		x->stats.replay_window++;
12131da177e4SLinus Torvalds 		return -EINVAL;
12141da177e4SLinus Torvalds 	}
12151da177e4SLinus Torvalds 
12161da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
12171da177e4SLinus Torvalds 		x->stats.replay++;
12181da177e4SLinus Torvalds 		return -EINVAL;
12191da177e4SLinus Torvalds 	}
12201da177e4SLinus Torvalds 	return 0;
12211da177e4SLinus Torvalds }
12221da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
12231da177e4SLinus Torvalds 
122461f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
12251da177e4SLinus Torvalds {
12261da177e4SLinus Torvalds 	u32 diff;
122761f4627bSAl Viro 	u32 seq = ntohl(net_seq);
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
12301da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
12311da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
12321da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
12331da177e4SLinus Torvalds 		else
12341da177e4SLinus Torvalds 			x->replay.bitmap = 1;
12351da177e4SLinus Torvalds 		x->replay.seq = seq;
12361da177e4SLinus Torvalds 	} else {
12371da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
12381da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
12391da177e4SLinus Torvalds 	}
1240f8cd5488SJamal Hadi Salim 
1241f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1242f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
12431da177e4SLinus Torvalds }
12441da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
12471da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
12481da177e4SLinus Torvalds 
124926b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
12501da177e4SLinus Torvalds {
12511da177e4SLinus Torvalds 	struct xfrm_mgr *km;
12521da177e4SLinus Torvalds 
125326b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
125426b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
125526b15dadSJamal Hadi Salim 		if (km->notify_policy)
125626b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
125726b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
125826b15dadSJamal Hadi Salim }
125926b15dadSJamal Hadi Salim 
126026b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
126126b15dadSJamal Hadi Salim {
126226b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
126326b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
126426b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
126526b15dadSJamal Hadi Salim 		if (km->notify)
126626b15dadSJamal Hadi Salim 			km->notify(x, c);
126726b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
126826b15dadSJamal Hadi Salim }
126926b15dadSJamal Hadi Salim 
127026b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
127126b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
127226b15dadSJamal Hadi Salim 
127353bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
127426b15dadSJamal Hadi Salim {
127526b15dadSJamal Hadi Salim 	struct km_event c;
127626b15dadSJamal Hadi Salim 
1277bf08867fSHerbert Xu 	c.data.hard = hard;
127853bc6b4dSJamal Hadi Salim 	c.pid = pid;
1279f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
128026b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds 	if (hard)
12831da177e4SLinus Torvalds 		wake_up(&km_waitq);
12841da177e4SLinus Torvalds }
12851da177e4SLinus Torvalds 
128653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
128726b15dadSJamal Hadi Salim /*
128826b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
128926b15dadSJamal Hadi Salim  * We are happy with one success
129026b15dadSJamal Hadi Salim */
1291980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
12921da177e4SLinus Torvalds {
129326b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
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) {
129826b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
129926b15dadSJamal Hadi Salim 		if (!acqret)
130026b15dadSJamal Hadi Salim 			err = acqret;
13011da177e4SLinus Torvalds 	}
13021da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13031da177e4SLinus Torvalds 	return err;
13041da177e4SLinus Torvalds }
1305980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
13081da177e4SLinus Torvalds {
13091da177e4SLinus Torvalds 	int err = -EINVAL;
13101da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13111da177e4SLinus Torvalds 
13121da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13131da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
13141da177e4SLinus Torvalds 		if (km->new_mapping)
13151da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
13161da177e4SLinus Torvalds 		if (!err)
13171da177e4SLinus Torvalds 			break;
13181da177e4SLinus Torvalds 	}
13191da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13201da177e4SLinus Torvalds 	return err;
13211da177e4SLinus Torvalds }
13221da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
13231da177e4SLinus Torvalds 
13246c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
13251da177e4SLinus Torvalds {
132626b15dadSJamal Hadi Salim 	struct km_event c;
13271da177e4SLinus Torvalds 
1328bf08867fSHerbert Xu 	c.data.hard = hard;
13296c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1330f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
133126b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	if (hard)
13341da177e4SLinus Torvalds 		wake_up(&km_waitq);
13351da177e4SLinus Torvalds }
1336a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
13371da177e4SLinus Torvalds 
133897a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
133997a64b45SMasahide NAKAMURA {
134097a64b45SMasahide NAKAMURA 	int err = -EINVAL;
134197a64b45SMasahide NAKAMURA 	int ret;
134297a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
134397a64b45SMasahide NAKAMURA 
134497a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
134597a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
134697a64b45SMasahide NAKAMURA 		if (km->report) {
134797a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
134897a64b45SMasahide NAKAMURA 			if (!ret)
134997a64b45SMasahide NAKAMURA 				err = ret;
135097a64b45SMasahide NAKAMURA 		}
135197a64b45SMasahide NAKAMURA 	}
135297a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
135397a64b45SMasahide NAKAMURA 	return err;
135497a64b45SMasahide NAKAMURA }
135597a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
135697a64b45SMasahide NAKAMURA 
13571da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
13581da177e4SLinus Torvalds {
13591da177e4SLinus Torvalds 	int err;
13601da177e4SLinus Torvalds 	u8 *data;
13611da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13621da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
13631da177e4SLinus Torvalds 
13641da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
13651da177e4SLinus Torvalds 		return -EMSGSIZE;
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
13681da177e4SLinus Torvalds 	if (!data)
13691da177e4SLinus Torvalds 		return -ENOMEM;
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 	err = -EFAULT;
13721da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
13731da177e4SLinus Torvalds 		goto out;
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds 	err = -EINVAL;
13761da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13771da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1378cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
13791da177e4SLinus Torvalds 					 optlen, &err);
13801da177e4SLinus Torvalds 		if (err >= 0)
13811da177e4SLinus Torvalds 			break;
13821da177e4SLinus Torvalds 	}
13831da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13841da177e4SLinus Torvalds 
13851da177e4SLinus Torvalds 	if (err >= 0) {
13861da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
13871da177e4SLinus Torvalds 		xfrm_pol_put(pol);
13881da177e4SLinus Torvalds 		err = 0;
13891da177e4SLinus Torvalds 	}
13901da177e4SLinus Torvalds 
13911da177e4SLinus Torvalds out:
13921da177e4SLinus Torvalds 	kfree(data);
13931da177e4SLinus Torvalds 	return err;
13941da177e4SLinus Torvalds }
13951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
13981da177e4SLinus Torvalds {
13991da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
14001da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
14011da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
14021da177e4SLinus Torvalds 	return 0;
14031da177e4SLinus Torvalds }
14041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
14071da177e4SLinus Torvalds {
14081da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
14091da177e4SLinus Torvalds 	list_del(&km->list);
14101da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
14111da177e4SLinus Torvalds 	return 0;
14121da177e4SLinus Torvalds }
14131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
14161da177e4SLinus Torvalds {
14171da177e4SLinus Torvalds 	int err = 0;
14181da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14191da177e4SLinus Torvalds 		return -EINVAL;
14201da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14211da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1422f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14231da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
14241da177e4SLinus Torvalds 		err = -ENOBUFS;
1425edcd5821SDavid S. Miller 	else
14261da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1427f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14281da177e4SLinus Torvalds 	return err;
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
14311da177e4SLinus Torvalds 
14321da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
14331da177e4SLinus Torvalds {
14341da177e4SLinus Torvalds 	int err = 0;
14351da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14361da177e4SLinus Torvalds 		return -EINVAL;
14371da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14381da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1439f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14401da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
14411da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
14421da177e4SLinus Torvalds 			err = -EINVAL;
1443edcd5821SDavid S. Miller 		else
14441da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
14451da177e4SLinus Torvalds 	}
1446f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14471da177e4SLinus Torvalds 	return err;
14481da177e4SLinus Torvalds }
14491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
14501da177e4SLinus Torvalds 
14511da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
14521da177e4SLinus Torvalds {
14531da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
14541da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
14551da177e4SLinus Torvalds 		return NULL;
14561da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
14571da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1458546be240SHerbert Xu 	if (unlikely(!afinfo))
14591da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
14601da177e4SLinus Torvalds 	return afinfo;
14611da177e4SLinus Torvalds }
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
14641da177e4SLinus Torvalds {
1465546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
14661da177e4SLinus Torvalds }
14671da177e4SLinus Torvalds 
14681da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
14691da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
14701da177e4SLinus Torvalds {
14711da177e4SLinus Torvalds 	if (x->tunnel) {
14721da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
14751da177e4SLinus Torvalds 			xfrm_state_delete(t);
14761da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
14771da177e4SLinus Torvalds 		xfrm_state_put(t);
14781da177e4SLinus Torvalds 		x->tunnel = NULL;
14791da177e4SLinus Torvalds 	}
14801da177e4SLinus Torvalds }
14811da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
14821da177e4SLinus Torvalds 
148380b30c10SHerbert Xu /*
148480b30c10SHerbert Xu  * This function is NOT optimal.  For example, with ESP it will give an
148580b30c10SHerbert Xu  * MTU that's usually two bytes short of being optimal.  However, it will
148680b30c10SHerbert Xu  * usually give an answer that's a multiple of 4 provided the input is
148780b30c10SHerbert Xu  * also a multiple of 4.
148880b30c10SHerbert Xu  */
14891da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
14901da177e4SLinus Torvalds {
14911da177e4SLinus Torvalds 	int res = mtu;
14921da177e4SLinus Torvalds 
14931da177e4SLinus Torvalds 	res -= x->props.header_len;
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 	for (;;) {
14961da177e4SLinus Torvalds 		int m = res;
14971da177e4SLinus Torvalds 
14981da177e4SLinus Torvalds 		if (m < 68)
14991da177e4SLinus Torvalds 			return 68;
15001da177e4SLinus Torvalds 
15011da177e4SLinus Torvalds 		spin_lock_bh(&x->lock);
15021da177e4SLinus Torvalds 		if (x->km.state == XFRM_STATE_VALID &&
15031da177e4SLinus Torvalds 		    x->type && x->type->get_max_size)
15041da177e4SLinus Torvalds 			m = x->type->get_max_size(x, m);
15051da177e4SLinus Torvalds 		else
15061da177e4SLinus Torvalds 			m += x->props.header_len;
15071da177e4SLinus Torvalds 		spin_unlock_bh(&x->lock);
15081da177e4SLinus Torvalds 
15091da177e4SLinus Torvalds 		if (m <= mtu)
15101da177e4SLinus Torvalds 			break;
15111da177e4SLinus Torvalds 		res -= (m - mtu);
15121da177e4SLinus Torvalds 	}
15131da177e4SLinus Torvalds 
15141da177e4SLinus Torvalds 	return res;
15151da177e4SLinus Torvalds }
15161da177e4SLinus Torvalds 
151772cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
151872cb6962SHerbert Xu {
1519d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1520d094cd83SHerbert Xu 	int family = x->props.family;
152172cb6962SHerbert Xu 	int err;
152272cb6962SHerbert Xu 
1523d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1524d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1525d094cd83SHerbert Xu 	if (!afinfo)
1526d094cd83SHerbert Xu 		goto error;
1527d094cd83SHerbert Xu 
1528d094cd83SHerbert Xu 	err = 0;
1529d094cd83SHerbert Xu 	if (afinfo->init_flags)
1530d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1531d094cd83SHerbert Xu 
1532d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1533d094cd83SHerbert Xu 
1534d094cd83SHerbert Xu 	if (err)
1535d094cd83SHerbert Xu 		goto error;
1536d094cd83SHerbert Xu 
1537d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1538d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
153972cb6962SHerbert Xu 	if (x->type == NULL)
154072cb6962SHerbert Xu 		goto error;
154172cb6962SHerbert Xu 
154272cb6962SHerbert Xu 	err = x->type->init_state(x);
154372cb6962SHerbert Xu 	if (err)
154472cb6962SHerbert Xu 		goto error;
154572cb6962SHerbert Xu 
1546b59f45d0SHerbert Xu 	x->mode = xfrm_get_mode(x->props.mode, family);
1547b59f45d0SHerbert Xu 	if (x->mode == NULL)
1548b59f45d0SHerbert Xu 		goto error;
1549b59f45d0SHerbert Xu 
155072cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
155172cb6962SHerbert Xu 
155272cb6962SHerbert Xu error:
155372cb6962SHerbert Xu 	return err;
155472cb6962SHerbert Xu }
155572cb6962SHerbert Xu 
155672cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
15571da177e4SLinus Torvalds 
15581da177e4SLinus Torvalds void __init xfrm_state_init(void)
15591da177e4SLinus Torvalds {
1560f034b5d4SDavid S. Miller 	unsigned int sz;
15611da177e4SLinus Torvalds 
1562f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
1563f034b5d4SDavid S. Miller 
156444e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
156544e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
156644e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
1567f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1568f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1569f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1570f034b5d4SDavid S. Miller 
1571c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
15721da177e4SLinus Torvalds }
15731da177e4SLinus Torvalds 
1574