xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision cdca7265)
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>
23161a09e7SJoy Latten #include <linux/audit.h>
241da177e4SLinus Torvalds 
2544e36b42SDavid S. Miller #include "xfrm_hash.h"
2644e36b42SDavid S. Miller 
27ee857a7dSDavid S. Miller struct sock *xfrm_nl;
28ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl);
29ee857a7dSDavid S. Miller 
30f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
31a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32a70fcb0bSDavid S. Miller 
33f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
34a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35a70fcb0bSDavid S. Miller 
361da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
39a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
401da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint
461da177e4SLinus Torvalds  * of tunnel or destination of transport mode) allowed by selector.
471da177e4SLinus Torvalds  *
481da177e4SLinus Torvalds  * Main use is finding SA after policy selected tunnel or transport mode.
491da177e4SLinus Torvalds  * Also, it can be used by ah/esp icmp error handler to find offending SA.
501da177e4SLinus Torvalds  */
51f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly;
52f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly;
53f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly;
54f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
55f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
56f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
579d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
581da177e4SLinus Torvalds 
59c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
60c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
61c1969f29SDavid S. Miller 					 u32 reqid,
62a624c108SDavid S. Miller 					 unsigned short family)
63a624c108SDavid S. Miller {
64c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
65a624c108SDavid S. Miller }
66a624c108SDavid S. Miller 
67667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
68667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
6944e36b42SDavid S. Miller 					 unsigned short family)
70f034b5d4SDavid S. Miller {
71667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
72f034b5d4SDavid S. Miller }
73f034b5d4SDavid S. Miller 
742575b654SDavid S. Miller static inline unsigned int
758122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
76f034b5d4SDavid S. Miller {
77c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
78f034b5d4SDavid S. Miller }
79f034b5d4SDavid S. Miller 
80f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
81f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
82f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
83f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
84f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
85f034b5d4SDavid S. Miller {
86f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
87f034b5d4SDavid S. Miller 	struct xfrm_state *x;
88f034b5d4SDavid S. Miller 
89f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
90f034b5d4SDavid S. Miller 		unsigned int h;
91f034b5d4SDavid S. Miller 
92c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
93c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
94c1969f29SDavid S. Miller 				    nhashmask);
95f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
96f034b5d4SDavid S. Miller 
97667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
98667bbcb6SMasahide NAKAMURA 				    x->props.family,
99f034b5d4SDavid S. Miller 				    nhashmask);
100f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
101f034b5d4SDavid S. Miller 
1027b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1037b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1047b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1057b4dc360SMasahide NAKAMURA 					    nhashmask);
106f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
107f034b5d4SDavid S. Miller 		}
108f034b5d4SDavid S. Miller 	}
1097b4dc360SMasahide NAKAMURA }
110f034b5d4SDavid S. Miller 
111f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
112f034b5d4SDavid S. Miller {
113f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
114f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
115f034b5d4SDavid S. Miller }
116f034b5d4SDavid S. Miller 
117f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
118f034b5d4SDavid S. Miller 
119c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
120f034b5d4SDavid S. Miller {
121f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
122f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
123f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
124f034b5d4SDavid S. Miller 	int i;
125f034b5d4SDavid S. Miller 
126f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
127f034b5d4SDavid S. Miller 
128f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
12944e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
130f034b5d4SDavid S. Miller 	if (!ndst)
131f034b5d4SDavid S. Miller 		goto out_unlock;
13244e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
133f034b5d4SDavid S. Miller 	if (!nsrc) {
13444e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
135f034b5d4SDavid S. Miller 		goto out_unlock;
136f034b5d4SDavid S. Miller 	}
13744e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
138f034b5d4SDavid S. Miller 	if (!nspi) {
13944e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
14044e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
141f034b5d4SDavid S. Miller 		goto out_unlock;
142f034b5d4SDavid S. Miller 	}
143f034b5d4SDavid S. Miller 
144f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
145f034b5d4SDavid S. Miller 
146f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
147f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
148f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
149f034b5d4SDavid S. Miller 				   nhashmask);
150f034b5d4SDavid S. Miller 
151f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
152f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
153f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
154f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
155f034b5d4SDavid S. Miller 
156f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
157f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
158f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
159f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
160f034b5d4SDavid S. Miller 
161f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
162f034b5d4SDavid S. Miller 
163f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
16444e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
16544e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
16644e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
167f034b5d4SDavid S. Miller 
168f034b5d4SDavid S. Miller out_unlock:
169f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
170f034b5d4SDavid S. Miller }
171f034b5d4SDavid S. Miller 
172c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
173f034b5d4SDavid S. Miller 
1741da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1751da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1781da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1818f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1821da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1831da177e4SLinus Torvalds 
18453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1851da177e4SLinus Torvalds 
186980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
18753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
1901da177e4SLinus Torvalds {
191a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
192a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
1931da177e4SLinus Torvalds 	kfree(x->aalg);
1941da177e4SLinus Torvalds 	kfree(x->ealg);
1951da177e4SLinus Torvalds 	kfree(x->calg);
1961da177e4SLinus Torvalds 	kfree(x->encap);
197060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
198b59f45d0SHerbert Xu 	if (x->mode)
199b59f45d0SHerbert Xu 		xfrm_put_mode(x->mode);
2001da177e4SLinus Torvalds 	if (x->type) {
2011da177e4SLinus Torvalds 		x->type->destructor(x);
2021da177e4SLinus Torvalds 		xfrm_put_type(x->type);
2031da177e4SLinus Torvalds 	}
204df71837dSTrent Jaeger 	security_xfrm_state_free(x);
2051da177e4SLinus Torvalds 	kfree(x);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
208c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds 	struct xfrm_state *x;
2118f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
2128f126e37SDavid S. Miller 	struct hlist_head gc_list;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
2158f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
2168f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
2171da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
2181da177e4SLinus Torvalds 
2198f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
2201da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
2218f126e37SDavid S. Miller 
2221da177e4SLinus Torvalds 	wake_up(&km_waitq);
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
2261da177e4SLinus Torvalds {
2271da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
2281da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
2291da177e4SLinus Torvalds 	else
2301da177e4SLinus Torvalds 	        return secs*HZ;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
2361da177e4SLinus Torvalds 	unsigned long now = (unsigned long)xtime.tv_sec;
2371da177e4SLinus Torvalds 	long next = LONG_MAX;
2381da177e4SLinus Torvalds 	int warn = 0;
239161a09e7SJoy Latten 	int err = 0;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	spin_lock(&x->lock);
2421da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
2431da177e4SLinus Torvalds 		goto out;
2441da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
2451da177e4SLinus Torvalds 		goto expired;
2461da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
2471da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
2481da177e4SLinus Torvalds 			x->curlft.add_time - now;
2491da177e4SLinus Torvalds 		if (tmo <= 0)
2501da177e4SLinus Torvalds 			goto expired;
2511da177e4SLinus Torvalds 		if (tmo < next)
2521da177e4SLinus Torvalds 			next = tmo;
2531da177e4SLinus Torvalds 	}
2541da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
2551da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
2561da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2571da177e4SLinus Torvalds 		if (tmo <= 0)
2581da177e4SLinus Torvalds 			goto expired;
2591da177e4SLinus Torvalds 		if (tmo < next)
2601da177e4SLinus Torvalds 			next = tmo;
2611da177e4SLinus Torvalds 	}
2621da177e4SLinus Torvalds 	if (x->km.dying)
2631da177e4SLinus Torvalds 		goto resched;
2641da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
2651da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
2661da177e4SLinus Torvalds 			x->curlft.add_time - now;
2671da177e4SLinus Torvalds 		if (tmo <= 0)
2681da177e4SLinus Torvalds 			warn = 1;
2691da177e4SLinus Torvalds 		else if (tmo < next)
2701da177e4SLinus Torvalds 			next = tmo;
2711da177e4SLinus Torvalds 	}
2721da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
2731da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
2741da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
2751da177e4SLinus Torvalds 		if (tmo <= 0)
2761da177e4SLinus Torvalds 			warn = 1;
2771da177e4SLinus Torvalds 		else if (tmo < next)
2781da177e4SLinus Torvalds 			next = tmo;
2791da177e4SLinus Torvalds 	}
2801da177e4SLinus Torvalds 
2814666faabSHerbert Xu 	x->km.dying = warn;
2821da177e4SLinus Torvalds 	if (warn)
28353bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
2841da177e4SLinus Torvalds resched:
285a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
286a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
287a47f0ce0SDavid S. Miller 
2881da177e4SLinus Torvalds 	goto out;
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds expired:
2911da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
2921da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
2931da177e4SLinus Torvalds 		wake_up(&km_waitq);
2941da177e4SLinus Torvalds 		next = 2;
2951da177e4SLinus Torvalds 		goto resched;
2961da177e4SLinus Torvalds 	}
297161a09e7SJoy Latten 
298161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
299161a09e7SJoy Latten 	if (!err && x->id.spi)
30053bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
3011da177e4SLinus Torvalds 
302161a09e7SJoy Latten 	xfrm_audit_log(audit_get_loginuid(current->audit_context), 0,
303161a09e7SJoy Latten 		       AUDIT_MAC_IPSEC_DELSA, err ? 0 : 1, NULL, x);
304161a09e7SJoy Latten 
3051da177e4SLinus Torvalds out:
3061da177e4SLinus Torvalds 	spin_unlock(&x->lock);
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
3090ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
3100ac84752SDavid S. Miller 
3111da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
3121da177e4SLinus Torvalds {
3131da177e4SLinus Torvalds 	struct xfrm_state *x;
3141da177e4SLinus Torvalds 
3150da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (x) {
3181da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
3191da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
3208f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
3218f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
3228f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
3231da177e4SLinus Torvalds 		init_timer(&x->timer);
3241da177e4SLinus Torvalds 		x->timer.function = xfrm_timer_handler;
3251da177e4SLinus Torvalds 		x->timer.data	  = (unsigned long)x;
326f8cd5488SJamal Hadi Salim 		init_timer(&x->rtimer);
327f8cd5488SJamal Hadi Salim 		x->rtimer.function = xfrm_replay_timer_handler;
328f8cd5488SJamal Hadi Salim 		x->rtimer.data     = (unsigned long)x;
3291da177e4SLinus Torvalds 		x->curlft.add_time = (unsigned long)xtime.tv_sec;
3301da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
3311da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
3321da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
3331da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
334f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
335f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
3361da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 	return x;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
3431da177e4SLinus Torvalds {
3441da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
3478f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
3481da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
3491da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
3521da177e4SLinus Torvalds 
35353bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
3541da177e4SLinus Torvalds {
35526b15dadSJamal Hadi Salim 	int err = -ESRCH;
35626b15dadSJamal Hadi Salim 
3571da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
3581da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
3591da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
3608f126e37SDavid S. Miller 		hlist_del(&x->bydst);
3618f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
362a47f0ce0SDavid S. Miller 		if (x->id.spi)
3638f126e37SDavid S. Miller 			hlist_del(&x->byspi);
364f034b5d4SDavid S. Miller 		xfrm_state_num--;
3651da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
3681da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
3691da177e4SLinus Torvalds 		 * is what we are dropping here.
3701da177e4SLinus Torvalds 		 */
37121380b81SHerbert Xu 		__xfrm_state_put(x);
37226b15dadSJamal Hadi Salim 		err = 0;
3731da177e4SLinus Torvalds 	}
3741da177e4SLinus Torvalds 
37526b15dadSJamal Hadi Salim 	return err;
37626b15dadSJamal Hadi Salim }
37753bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
37826b15dadSJamal Hadi Salim 
37926b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
3801da177e4SLinus Torvalds {
38126b15dadSJamal Hadi Salim 	int err;
38226b15dadSJamal Hadi Salim 
3831da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
38426b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
3851da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
38626b15dadSJamal Hadi Salim 
38726b15dadSJamal Hadi Salim 	return err;
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
3901da177e4SLinus Torvalds 
391161a09e7SJoy Latten void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
3921da177e4SLinus Torvalds {
3931da177e4SLinus Torvalds 	int i;
394161a09e7SJoy Latten 	int err = 0;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
397a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
3988f126e37SDavid S. Miller 		struct hlist_node *entry;
3998f126e37SDavid S. Miller 		struct xfrm_state *x;
4001da177e4SLinus Torvalds restart:
4018f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
4021da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
4035794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
4041da177e4SLinus Torvalds 				xfrm_state_hold(x);
4051da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
4061da177e4SLinus Torvalds 
407161a09e7SJoy Latten 				err = xfrm_state_delete(x);
408161a09e7SJoy Latten 				xfrm_audit_log(audit_info->loginuid,
409161a09e7SJoy Latten 					       audit_info->secid,
410161a09e7SJoy Latten 					       AUDIT_MAC_IPSEC_DELSA,
411161a09e7SJoy Latten 					       err ? 0 : 1, NULL, x);
4121da177e4SLinus Torvalds 				xfrm_state_put(x);
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
4151da177e4SLinus Torvalds 				goto restart;
4161da177e4SLinus Torvalds 			}
4171da177e4SLinus Torvalds 		}
4181da177e4SLinus Torvalds 	}
4191da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
4201da177e4SLinus Torvalds 	wake_up(&km_waitq);
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds static int
4251da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
4261da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
4271da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
4281da177e4SLinus Torvalds 		  unsigned short family)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
4311da177e4SLinus Torvalds 	if (!afinfo)
4321da177e4SLinus Torvalds 		return -1;
4331da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
4341da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
4351da177e4SLinus Torvalds 	return 0;
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds 
438a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
439edcd5821SDavid S. Miller {
440edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
441edcd5821SDavid S. Miller 	struct xfrm_state *x;
4428f126e37SDavid S. Miller 	struct hlist_node *entry;
443edcd5821SDavid S. Miller 
4448f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
445edcd5821SDavid S. Miller 		if (x->props.family != family ||
446edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
447edcd5821SDavid S. Miller 		    x->id.proto     != proto)
448edcd5821SDavid S. Miller 			continue;
449edcd5821SDavid S. Miller 
450edcd5821SDavid S. Miller 		switch (family) {
451edcd5821SDavid S. Miller 		case AF_INET:
452edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
453edcd5821SDavid S. Miller 				continue;
454edcd5821SDavid S. Miller 			break;
455edcd5821SDavid S. Miller 		case AF_INET6:
456edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
457edcd5821SDavid S. Miller 					     (struct in6_addr *)
458edcd5821SDavid S. Miller 					     x->id.daddr.a6))
459edcd5821SDavid S. Miller 				continue;
460edcd5821SDavid S. Miller 			break;
461edcd5821SDavid S. Miller 		};
462edcd5821SDavid S. Miller 
463edcd5821SDavid S. Miller 		xfrm_state_hold(x);
464edcd5821SDavid S. Miller 		return x;
465edcd5821SDavid S. Miller 	}
466edcd5821SDavid S. Miller 
467edcd5821SDavid S. Miller 	return NULL;
468edcd5821SDavid S. Miller }
469edcd5821SDavid S. Miller 
470edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
471edcd5821SDavid S. Miller {
472667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
473edcd5821SDavid S. Miller 	struct xfrm_state *x;
4748f126e37SDavid S. Miller 	struct hlist_node *entry;
475edcd5821SDavid S. Miller 
4768f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
477edcd5821SDavid S. Miller 		if (x->props.family != family ||
478edcd5821SDavid S. Miller 		    x->id.proto     != proto)
479edcd5821SDavid S. Miller 			continue;
480edcd5821SDavid S. Miller 
481edcd5821SDavid S. Miller 		switch (family) {
482edcd5821SDavid S. Miller 		case AF_INET:
483edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
484edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
485edcd5821SDavid S. Miller 				continue;
486edcd5821SDavid S. Miller 			break;
487edcd5821SDavid S. Miller 		case AF_INET6:
488edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
489edcd5821SDavid S. Miller 					     (struct in6_addr *)
490edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
491edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
492edcd5821SDavid S. Miller 					     (struct in6_addr *)
493edcd5821SDavid S. Miller 					     x->props.saddr.a6))
494edcd5821SDavid S. Miller 				continue;
495edcd5821SDavid S. Miller 			break;
496edcd5821SDavid S. Miller 		};
497edcd5821SDavid S. Miller 
498edcd5821SDavid S. Miller 		xfrm_state_hold(x);
499edcd5821SDavid S. Miller 		return x;
500edcd5821SDavid S. Miller 	}
501edcd5821SDavid S. Miller 
502edcd5821SDavid S. Miller 	return NULL;
503edcd5821SDavid S. Miller }
504edcd5821SDavid S. Miller 
505edcd5821SDavid S. Miller static inline struct xfrm_state *
506edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
507edcd5821SDavid S. Miller {
508edcd5821SDavid S. Miller 	if (use_spi)
509edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
510edcd5821SDavid S. Miller 					   x->id.proto, family);
511edcd5821SDavid S. Miller 	else
512edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
513edcd5821SDavid S. Miller 						  &x->props.saddr,
514edcd5821SDavid S. Miller 						  x->id.proto, family);
515edcd5821SDavid S. Miller }
516edcd5821SDavid S. Miller 
5172fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
5182fab22f2SPatrick McHardy {
5192fab22f2SPatrick McHardy 	if (have_hash_collision &&
5202fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
5212fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
5222fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
5232fab22f2SPatrick McHardy }
5242fab22f2SPatrick McHardy 
5251da177e4SLinus Torvalds struct xfrm_state *
5261da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
5271da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
5281da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
5291da177e4SLinus Torvalds 		unsigned short family)
5301da177e4SLinus Torvalds {
531c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
5328f126e37SDavid S. Miller 	struct hlist_node *entry;
5331da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
5341da177e4SLinus Torvalds 	int acquire_in_progress = 0;
5351da177e4SLinus Torvalds 	int error = 0;
5361da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
5398f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
5401da177e4SLinus Torvalds 		if (x->props.family == family &&
5411da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
542fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
5431da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
5441da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
5451da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
5461da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
5471da177e4SLinus Torvalds 			/* Resolution logic:
5481da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
5491da177e4SLinus Torvalds 			      Done.
5501da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 			   Entering area of "sysdeps".
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
5551da177e4SLinus Torvalds 			      it selects only session which triggered
5561da177e4SLinus Torvalds 			      previous resolution. Key manager will do
5571da177e4SLinus Torvalds 			      something to install a state with proper
5581da177e4SLinus Torvalds 			      selector.
5591da177e4SLinus Torvalds 			 */
5601da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
561df71837dSTrent Jaeger 				if (!xfrm_selector_match(&x->sel, fl, family) ||
562e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
5631da177e4SLinus Torvalds 					continue;
5641da177e4SLinus Torvalds 				if (!best ||
5651da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
5661da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
5671da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
5681da177e4SLinus Torvalds 					best = x;
5691da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
5701da177e4SLinus Torvalds 				acquire_in_progress = 1;
5711da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
5721da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
573df71837dSTrent Jaeger  				if (xfrm_selector_match(&x->sel, fl, family) &&
574e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
5751da177e4SLinus Torvalds 					error = -ESRCH;
5761da177e4SLinus Torvalds 			}
5771da177e4SLinus Torvalds 		}
5781da177e4SLinus Torvalds 	}
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 	x = best;
5811da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
5825c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
583edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
584edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
5851da177e4SLinus Torvalds 			xfrm_state_put(x0);
5861da177e4SLinus Torvalds 			error = -EEXIST;
5871da177e4SLinus Torvalds 			goto out;
5881da177e4SLinus Torvalds 		}
5891da177e4SLinus Torvalds 		x = xfrm_state_alloc();
5901da177e4SLinus Torvalds 		if (x == NULL) {
5911da177e4SLinus Torvalds 			error = -ENOMEM;
5921da177e4SLinus Torvalds 			goto out;
5931da177e4SLinus Torvalds 		}
5941da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
5951da177e4SLinus Torvalds 		 * to current session. */
5961da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
5971da177e4SLinus Torvalds 
598e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
599e0d1caa7SVenkat Yekkirala 		if (error) {
600e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
601e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
602e0d1caa7SVenkat Yekkirala 			x = NULL;
603e0d1caa7SVenkat Yekkirala 			goto out;
604e0d1caa7SVenkat Yekkirala 		}
605e0d1caa7SVenkat Yekkirala 
6061da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
6071da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
6088f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
609667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
6108f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
6111da177e4SLinus Torvalds 			if (x->id.spi) {
6121da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
6138f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
6141da177e4SLinus Torvalds 			}
6151da177e4SLinus Torvalds 			x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
6161da177e4SLinus Torvalds 			x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
6171da177e4SLinus Torvalds 			add_timer(&x->timer);
6182fab22f2SPatrick McHardy 			xfrm_state_num++;
6192fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
6201da177e4SLinus Torvalds 		} else {
6211da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
6221da177e4SLinus Torvalds 			xfrm_state_put(x);
6231da177e4SLinus Torvalds 			x = NULL;
6241da177e4SLinus Torvalds 			error = -ESRCH;
6251da177e4SLinus Torvalds 		}
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds out:
6281da177e4SLinus Torvalds 	if (x)
6291da177e4SLinus Torvalds 		xfrm_state_hold(x);
6301da177e4SLinus Torvalds 	else
6311da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
6321da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6331da177e4SLinus Torvalds 	return x;
6341da177e4SLinus Torvalds }
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
6371da177e4SLinus Torvalds {
638a624c108SDavid S. Miller 	unsigned int h;
6391da177e4SLinus Torvalds 
6409d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
6419d4a706dSDavid S. Miller 
642c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
643c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
6448f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
6451da177e4SLinus Torvalds 
646667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
6478f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
6486c44e6b7SMasahide NAKAMURA 
6497b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
6506c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
6516c44e6b7SMasahide NAKAMURA 				  x->props.family);
6521da177e4SLinus Torvalds 
6538f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
6546c44e6b7SMasahide NAKAMURA 	}
6551da177e4SLinus Torvalds 
656a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
657a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
658a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
659f8cd5488SJamal Hadi Salim 
6601da177e4SLinus Torvalds 	wake_up(&km_waitq);
661f034b5d4SDavid S. Miller 
662f034b5d4SDavid S. Miller 	xfrm_state_num++;
663f034b5d4SDavid S. Miller 
664918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds 
667c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
668c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
669c7f5ea3aSDavid S. Miller {
670c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
671c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
672c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
673c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
674c7f5ea3aSDavid S. Miller 	unsigned int h;
675c7f5ea3aSDavid S. Miller 
676c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
677c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
678c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
679c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
680c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
681c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
682c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
683c7f5ea3aSDavid S. Miller 	}
684c7f5ea3aSDavid S. Miller }
685c7f5ea3aSDavid S. Miller 
6861da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
6871da177e4SLinus Torvalds {
6881da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
689c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
6901da177e4SLinus Torvalds 	__xfrm_state_insert(x);
6911da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
6941da177e4SLinus Torvalds 
6952770834cSDavid S. Miller /* xfrm_state_lock is held */
6962770834cSDavid 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)
6972770834cSDavid S. Miller {
698c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
6998f126e37SDavid S. Miller 	struct hlist_node *entry;
7002770834cSDavid S. Miller 	struct xfrm_state *x;
7012770834cSDavid S. Miller 
7028f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
7032770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
7042770834cSDavid S. Miller 		    x->props.mode   != mode ||
7052770834cSDavid S. Miller 		    x->props.family != family ||
7062770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
7072770834cSDavid S. Miller 		    x->id.spi       != 0)
7082770834cSDavid S. Miller 			continue;
7092770834cSDavid S. Miller 
7102770834cSDavid S. Miller 		switch (family) {
7112770834cSDavid S. Miller 		case AF_INET:
7122770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
7132770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
7142770834cSDavid S. Miller 				continue;
7152770834cSDavid S. Miller 			break;
7162770834cSDavid S. Miller 		case AF_INET6:
7172770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
7182770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
7192770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
7202770834cSDavid S. Miller 					     x->props.saddr.a6,
7212770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
7222770834cSDavid S. Miller 				continue;
7232770834cSDavid S. Miller 			break;
7242770834cSDavid S. Miller 		};
7252770834cSDavid S. Miller 
7262770834cSDavid S. Miller 		xfrm_state_hold(x);
7272770834cSDavid S. Miller 		return x;
7282770834cSDavid S. Miller 	}
7292770834cSDavid S. Miller 
7302770834cSDavid S. Miller 	if (!create)
7312770834cSDavid S. Miller 		return NULL;
7322770834cSDavid S. Miller 
7332770834cSDavid S. Miller 	x = xfrm_state_alloc();
7342770834cSDavid S. Miller 	if (likely(x)) {
7352770834cSDavid S. Miller 		switch (family) {
7362770834cSDavid S. Miller 		case AF_INET:
7372770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
7382770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
7392770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
7402770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
7412770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
7422770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
7432770834cSDavid S. Miller 			break;
7442770834cSDavid S. Miller 
7452770834cSDavid S. Miller 		case AF_INET6:
7462770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
7472770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7482770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
7492770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7502770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
7512770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
7522770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
7532770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7542770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
7552770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7562770834cSDavid S. Miller 			break;
7572770834cSDavid S. Miller 		};
7582770834cSDavid S. Miller 
7592770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
7602770834cSDavid S. Miller 		x->id.proto = proto;
7612770834cSDavid S. Miller 		x->props.family = family;
7622770834cSDavid S. Miller 		x->props.mode = mode;
7632770834cSDavid S. Miller 		x->props.reqid = reqid;
7642770834cSDavid S. Miller 		x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
7652770834cSDavid S. Miller 		xfrm_state_hold(x);
7662770834cSDavid S. Miller 		x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
7672770834cSDavid S. Miller 		add_timer(&x->timer);
7688f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
769667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
7708f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
7712770834cSDavid S. Miller 		wake_up(&km_waitq);
772918049f0SDavid S. Miller 
773918049f0SDavid S. Miller 		xfrm_state_num++;
774918049f0SDavid S. Miller 
775918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
7762770834cSDavid S. Miller 	}
7772770834cSDavid S. Miller 
7782770834cSDavid S. Miller 	return x;
7792770834cSDavid S. Miller }
7802770834cSDavid S. Miller 
7811da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
7841da177e4SLinus Torvalds {
7851da177e4SLinus Torvalds 	struct xfrm_state *x1;
7861da177e4SLinus Torvalds 	int family;
7871da177e4SLinus Torvalds 	int err;
788eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 	family = x->props.family;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7931da177e4SLinus Torvalds 
794edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
7951da177e4SLinus Torvalds 	if (x1) {
7961da177e4SLinus Torvalds 		xfrm_state_put(x1);
7971da177e4SLinus Torvalds 		x1 = NULL;
7981da177e4SLinus Torvalds 		err = -EEXIST;
7991da177e4SLinus Torvalds 		goto out;
8001da177e4SLinus Torvalds 	}
8011da177e4SLinus Torvalds 
802eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
8031da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
8041da177e4SLinus Torvalds 		if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
8051da177e4SLinus Torvalds 			xfrm_state_put(x1);
8061da177e4SLinus Torvalds 			x1 = NULL;
8071da177e4SLinus Torvalds 		}
8081da177e4SLinus Torvalds 	}
8091da177e4SLinus Torvalds 
810eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
8112770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
8122770834cSDavid S. Miller 				     x->id.proto,
8131da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
8141da177e4SLinus Torvalds 
815c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
8161da177e4SLinus Torvalds 	__xfrm_state_insert(x);
8171da177e4SLinus Torvalds 	err = 0;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds out:
8201da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	if (x1) {
8231da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8241da177e4SLinus Torvalds 		xfrm_state_put(x1);
8251da177e4SLinus Torvalds 	}
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	return err;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
8321da177e4SLinus Torvalds {
8331da177e4SLinus Torvalds 	struct xfrm_state *x1;
8341da177e4SLinus Torvalds 	int err;
835eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
8361da177e4SLinus Torvalds 
8371da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
838edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	err = -ESRCH;
8411da177e4SLinus Torvalds 	if (!x1)
8421da177e4SLinus Torvalds 		goto out;
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
8451da177e4SLinus Torvalds 		xfrm_state_put(x1);
8461da177e4SLinus Torvalds 		err = -EEXIST;
8471da177e4SLinus Torvalds 		goto out;
8481da177e4SLinus Torvalds 	}
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
8511da177e4SLinus Torvalds 		__xfrm_state_insert(x);
8521da177e4SLinus Torvalds 		x = NULL;
8531da177e4SLinus Torvalds 	}
8541da177e4SLinus Torvalds 	err = 0;
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds out:
8571da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 	if (err)
8601da177e4SLinus Torvalds 		return err;
8611da177e4SLinus Torvalds 
8621da177e4SLinus Torvalds 	if (!x) {
8631da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8641da177e4SLinus Torvalds 		xfrm_state_put(x1);
8651da177e4SLinus Torvalds 		return 0;
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	err = -EINVAL;
8691da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
8701da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
8711da177e4SLinus Torvalds 		if (x->encap && x1->encap)
8721da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
873060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
874060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
875060f02a3SNoriaki TAKAMIYA 		}
876060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
877060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
8781da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
8791da177e4SLinus Torvalds 		x1->km.dying = 0;
8801da177e4SLinus Torvalds 
881a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
8821da177e4SLinus Torvalds 		if (x1->curlft.use_time)
8831da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 		err = 0;
8861da177e4SLinus Torvalds 	}
8871da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds 	xfrm_state_put(x1);
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	return err;
8921da177e4SLinus Torvalds }
8931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds 	if (!x->curlft.use_time)
8981da177e4SLinus Torvalds 		x->curlft.use_time = (unsigned long)xtime.tv_sec;
8991da177e4SLinus Torvalds 
9001da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
9011da177e4SLinus Torvalds 		return -EINVAL;
9021da177e4SLinus Torvalds 
9031da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
9041da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
9054666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
906a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
9071da177e4SLinus Torvalds 		return -EINVAL;
9081da177e4SLinus Torvalds 	}
9091da177e4SLinus Torvalds 
9101da177e4SLinus Torvalds 	if (!x->km.dying &&
9111da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
9124666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
9134666faabSHerbert Xu 		x->km.dying = 1;
91453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
9154666faabSHerbert Xu 	}
9161da177e4SLinus Torvalds 	return 0;
9171da177e4SLinus Torvalds }
9181da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
9191da177e4SLinus Torvalds 
9201da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
9211da177e4SLinus Torvalds {
9221da177e4SLinus Torvalds 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
9231da177e4SLinus Torvalds 		- skb_headroom(skb);
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds 	if (nhead > 0)
9261da177e4SLinus Torvalds 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	/* Check tail too... */
9291da177e4SLinus Torvalds 	return 0;
9301da177e4SLinus Torvalds }
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
9331da177e4SLinus Torvalds {
9341da177e4SLinus Torvalds 	int err = xfrm_state_check_expire(x);
9351da177e4SLinus Torvalds 	if (err < 0)
9361da177e4SLinus Torvalds 		goto err;
9371da177e4SLinus Torvalds 	err = xfrm_state_check_space(x, skb);
9381da177e4SLinus Torvalds err:
9391da177e4SLinus Torvalds 	return err;
9401da177e4SLinus Torvalds }
9411da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check);
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds struct xfrm_state *
944a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
9451da177e4SLinus Torvalds 		  unsigned short family)
9461da177e4SLinus Torvalds {
9471da177e4SLinus Torvalds 	struct xfrm_state *x;
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
950edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
9511da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9521da177e4SLinus Torvalds 	return x;
9531da177e4SLinus Torvalds }
9541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds struct xfrm_state *
957eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
958eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
959eb2971b6SMasahide NAKAMURA {
960eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
961eb2971b6SMasahide NAKAMURA 
962eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
963edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
964eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
965eb2971b6SMasahide NAKAMURA 	return x;
966eb2971b6SMasahide NAKAMURA }
967eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
968eb2971b6SMasahide NAKAMURA 
969eb2971b6SMasahide NAKAMURA struct xfrm_state *
9701da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
9711da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
9721da177e4SLinus Torvalds 	      int create, unsigned short family)
9731da177e4SLinus Torvalds {
9741da177e4SLinus Torvalds 	struct xfrm_state *x;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
9772770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
9781da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9792770834cSDavid S. Miller 
9801da177e4SLinus Torvalds 	return x;
9811da177e4SLinus Torvalds }
9821da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
9831da177e4SLinus Torvalds 
98441a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
98541a49cc3SMasahide NAKAMURA int
98641a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
98741a49cc3SMasahide NAKAMURA 	       unsigned short family)
98841a49cc3SMasahide NAKAMURA {
98941a49cc3SMasahide NAKAMURA 	int err = 0;
99041a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
99141a49cc3SMasahide NAKAMURA 	if (!afinfo)
99241a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
99341a49cc3SMasahide NAKAMURA 
99441a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
99541a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
99641a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
99741a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
99841a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
99941a49cc3SMasahide NAKAMURA 	return err;
100041a49cc3SMasahide NAKAMURA }
100141a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
100241a49cc3SMasahide NAKAMURA 
100341a49cc3SMasahide NAKAMURA int
100441a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
100541a49cc3SMasahide NAKAMURA 		unsigned short family)
100641a49cc3SMasahide NAKAMURA {
100741a49cc3SMasahide NAKAMURA 	int err = 0;
100841a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
100941a49cc3SMasahide NAKAMURA 	if (!afinfo)
101041a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
101141a49cc3SMasahide NAKAMURA 
101241a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
101341a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
101441a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
101541a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
101641a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
101741a49cc3SMasahide NAKAMURA 	return err;
101841a49cc3SMasahide NAKAMURA }
101941a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
102041a49cc3SMasahide NAKAMURA #endif
102141a49cc3SMasahide NAKAMURA 
10221da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
10251da177e4SLinus Torvalds {
10261da177e4SLinus Torvalds 	int i;
10271da177e4SLinus Torvalds 
1028f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
10298f126e37SDavid S. Miller 		struct hlist_node *entry;
10308f126e37SDavid S. Miller 		struct xfrm_state *x;
10318f126e37SDavid S. Miller 
10328f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
10338f126e37SDavid S. Miller 			if (x->km.seq == seq &&
10348f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
10351da177e4SLinus Torvalds 				xfrm_state_hold(x);
10361da177e4SLinus Torvalds 				return x;
10371da177e4SLinus Torvalds 			}
10381da177e4SLinus Torvalds 		}
10391da177e4SLinus Torvalds 	}
10401da177e4SLinus Torvalds 	return NULL;
10411da177e4SLinus Torvalds }
10421da177e4SLinus Torvalds 
10431da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
10441da177e4SLinus Torvalds {
10451da177e4SLinus Torvalds 	struct xfrm_state *x;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10481da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
10491da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10501da177e4SLinus Torvalds 	return x;
10511da177e4SLinus Torvalds }
10521da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
10531da177e4SLinus Torvalds 
10541da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
10551da177e4SLinus Torvalds {
10561da177e4SLinus Torvalds 	u32 res;
10571da177e4SLinus Torvalds 	static u32 acqseq;
10581da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
10591da177e4SLinus Torvalds 
10601da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
10611da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
10621da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
10631da177e4SLinus Torvalds 	return res;
10641da177e4SLinus Torvalds }
10651da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds void
106826977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
10691da177e4SLinus Torvalds {
1070f034b5d4SDavid S. Miller 	unsigned int h;
10711da177e4SLinus Torvalds 	struct xfrm_state *x0;
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	if (x->id.spi)
10741da177e4SLinus Torvalds 		return;
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	if (minspi == maxspi) {
10771da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
10781da177e4SLinus Torvalds 		if (x0) {
10791da177e4SLinus Torvalds 			xfrm_state_put(x0);
10801da177e4SLinus Torvalds 			return;
10811da177e4SLinus Torvalds 		}
10821da177e4SLinus Torvalds 		x->id.spi = minspi;
10831da177e4SLinus Torvalds 	} else {
10841da177e4SLinus Torvalds 		u32 spi = 0;
108526977b4eSAl Viro 		u32 low = ntohl(minspi);
108626977b4eSAl Viro 		u32 high = ntohl(maxspi);
108726977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
108826977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
10891da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
10901da177e4SLinus Torvalds 			if (x0 == NULL) {
10911da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
10921da177e4SLinus Torvalds 				break;
10931da177e4SLinus Torvalds 			}
10941da177e4SLinus Torvalds 			xfrm_state_put(x0);
10951da177e4SLinus Torvalds 		}
10961da177e4SLinus Torvalds 	}
10971da177e4SLinus Torvalds 	if (x->id.spi) {
10981da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
10991da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
11008f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
11011da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
11021da177e4SLinus Torvalds 		wake_up(&km_waitq);
11031da177e4SLinus Torvalds 	}
11041da177e4SLinus Torvalds }
11051da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
11081da177e4SLinus Torvalds 		    void *data)
11091da177e4SLinus Torvalds {
11101da177e4SLinus Torvalds 	int i;
111194b9bb54SJamal Hadi Salim 	struct xfrm_state *x, *last = NULL;
11128f126e37SDavid S. Miller 	struct hlist_node *entry;
11131da177e4SLinus Torvalds 	int count = 0;
11141da177e4SLinus Torvalds 	int err = 0;
11151da177e4SLinus Torvalds 
11161da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1117f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
11188f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
111994b9bb54SJamal Hadi Salim 			if (!xfrm_id_proto_match(x->id.proto, proto))
112094b9bb54SJamal Hadi Salim 				continue;
112194b9bb54SJamal Hadi Salim 			if (last) {
112294b9bb54SJamal Hadi Salim 				err = func(last, count, data);
112394b9bb54SJamal Hadi Salim 				if (err)
112494b9bb54SJamal Hadi Salim 					goto out;
112594b9bb54SJamal Hadi Salim 			}
112694b9bb54SJamal Hadi Salim 			last = x;
11271da177e4SLinus Torvalds 			count++;
11281da177e4SLinus Torvalds 		}
11291da177e4SLinus Torvalds 	}
11301da177e4SLinus Torvalds 	if (count == 0) {
11311da177e4SLinus Torvalds 		err = -ENOENT;
11321da177e4SLinus Torvalds 		goto out;
11331da177e4SLinus Torvalds 	}
113494b9bb54SJamal Hadi Salim 	err = func(last, 0, data);
11351da177e4SLinus Torvalds out:
11361da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11371da177e4SLinus Torvalds 	return err;
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
11401da177e4SLinus Torvalds 
1141f8cd5488SJamal Hadi Salim 
1142f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1143f8cd5488SJamal Hadi Salim {
1144f8cd5488SJamal Hadi Salim 	struct km_event c;
1145f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1146f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1147f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1148f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1149f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1150f8cd5488SJamal Hadi Salim 	 *     and there were changes
1151f8cd5488SJamal Hadi Salim 	 *
1152f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1153f8cd5488SJamal Hadi Salim 	 */
1154f8cd5488SJamal Hadi Salim 
1155f8cd5488SJamal Hadi Salim 	switch (event) {
1156f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1157f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1158f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
11592717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
11602717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
11612717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
11622717096aSJamal Hadi Salim 			else
1163f8cd5488SJamal Hadi Salim 				return;
11642717096aSJamal Hadi Salim 		}
1165f8cd5488SJamal Hadi Salim 
1166f8cd5488SJamal Hadi Salim 		break;
1167f8cd5488SJamal Hadi Salim 
1168f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1169f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1170f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
11712717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
11722717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1173f8cd5488SJamal Hadi Salim 			return;
11742717096aSJamal Hadi Salim 		}
1175f8cd5488SJamal Hadi Salim 
1176f8cd5488SJamal Hadi Salim 		break;
1177f8cd5488SJamal Hadi Salim 	}
1178f8cd5488SJamal Hadi Salim 
1179f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1180f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1181f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1182f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1183f8cd5488SJamal Hadi Salim 
1184f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1185a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
11862717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
11872717096aSJamal Hadi Salim }
1188a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify);
1189f8cd5488SJamal Hadi Salim 
1190f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1191f8cd5488SJamal Hadi Salim {
1192f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1193f8cd5488SJamal Hadi Salim 
1194f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1195f8cd5488SJamal Hadi Salim 
11962717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
11972717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1198f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
11992717096aSJamal Hadi Salim 		else
12002717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
12012717096aSJamal Hadi Salim 	}
1202f8cd5488SJamal Hadi Salim 
1203f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1204f8cd5488SJamal Hadi Salim }
1205f8cd5488SJamal Hadi Salim 
1206a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
12071da177e4SLinus Torvalds {
12081da177e4SLinus Torvalds 	u32 diff;
1209a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds 	if (unlikely(seq == 0))
12121da177e4SLinus Torvalds 		return -EINVAL;
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
12151da177e4SLinus Torvalds 		return 0;
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
12181da177e4SLinus Torvalds 	if (diff >= x->props.replay_window) {
12191da177e4SLinus Torvalds 		x->stats.replay_window++;
12201da177e4SLinus Torvalds 		return -EINVAL;
12211da177e4SLinus Torvalds 	}
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
12241da177e4SLinus Torvalds 		x->stats.replay++;
12251da177e4SLinus Torvalds 		return -EINVAL;
12261da177e4SLinus Torvalds 	}
12271da177e4SLinus Torvalds 	return 0;
12281da177e4SLinus Torvalds }
12291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
12301da177e4SLinus Torvalds 
123161f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
12321da177e4SLinus Torvalds {
12331da177e4SLinus Torvalds 	u32 diff;
123461f4627bSAl Viro 	u32 seq = ntohl(net_seq);
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
12371da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
12381da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
12391da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
12401da177e4SLinus Torvalds 		else
12411da177e4SLinus Torvalds 			x->replay.bitmap = 1;
12421da177e4SLinus Torvalds 		x->replay.seq = seq;
12431da177e4SLinus Torvalds 	} else {
12441da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
12451da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
12461da177e4SLinus Torvalds 	}
1247f8cd5488SJamal Hadi Salim 
1248f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1249f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
12501da177e4SLinus Torvalds }
12511da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
12521da177e4SLinus Torvalds 
12531da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
12541da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
12551da177e4SLinus Torvalds 
125626b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
12571da177e4SLinus Torvalds {
12581da177e4SLinus Torvalds 	struct xfrm_mgr *km;
12591da177e4SLinus Torvalds 
126026b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
126126b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
126226b15dadSJamal Hadi Salim 		if (km->notify_policy)
126326b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
126426b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
126526b15dadSJamal Hadi Salim }
126626b15dadSJamal Hadi Salim 
126726b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
126826b15dadSJamal Hadi Salim {
126926b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
127026b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
127126b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
127226b15dadSJamal Hadi Salim 		if (km->notify)
127326b15dadSJamal Hadi Salim 			km->notify(x, c);
127426b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
127526b15dadSJamal Hadi Salim }
127626b15dadSJamal Hadi Salim 
127726b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
127826b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
127926b15dadSJamal Hadi Salim 
128053bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
128126b15dadSJamal Hadi Salim {
128226b15dadSJamal Hadi Salim 	struct km_event c;
128326b15dadSJamal Hadi Salim 
1284bf08867fSHerbert Xu 	c.data.hard = hard;
128553bc6b4dSJamal Hadi Salim 	c.pid = pid;
1286f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
128726b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 	if (hard)
12901da177e4SLinus Torvalds 		wake_up(&km_waitq);
12911da177e4SLinus Torvalds }
12921da177e4SLinus Torvalds 
129353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
129426b15dadSJamal Hadi Salim /*
129526b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
129626b15dadSJamal Hadi Salim  * We are happy with one success
129726b15dadSJamal Hadi Salim */
1298980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
12991da177e4SLinus Torvalds {
130026b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
13011da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13021da177e4SLinus Torvalds 
13031da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13041da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
130526b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
130626b15dadSJamal Hadi Salim 		if (!acqret)
130726b15dadSJamal Hadi Salim 			err = acqret;
13081da177e4SLinus Torvalds 	}
13091da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13101da177e4SLinus Torvalds 	return err;
13111da177e4SLinus Torvalds }
1312980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
13131da177e4SLinus Torvalds 
13145d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
13151da177e4SLinus Torvalds {
13161da177e4SLinus Torvalds 	int err = -EINVAL;
13171da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13181da177e4SLinus Torvalds 
13191da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13201da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
13211da177e4SLinus Torvalds 		if (km->new_mapping)
13221da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
13231da177e4SLinus Torvalds 		if (!err)
13241da177e4SLinus Torvalds 			break;
13251da177e4SLinus Torvalds 	}
13261da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13271da177e4SLinus Torvalds 	return err;
13281da177e4SLinus Torvalds }
13291da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
13301da177e4SLinus Torvalds 
13316c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
13321da177e4SLinus Torvalds {
133326b15dadSJamal Hadi Salim 	struct km_event c;
13341da177e4SLinus Torvalds 
1335bf08867fSHerbert Xu 	c.data.hard = hard;
13366c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1337f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
133826b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds 	if (hard)
13411da177e4SLinus Torvalds 		wake_up(&km_waitq);
13421da177e4SLinus Torvalds }
1343a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
13441da177e4SLinus Torvalds 
134597a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
134697a64b45SMasahide NAKAMURA {
134797a64b45SMasahide NAKAMURA 	int err = -EINVAL;
134897a64b45SMasahide NAKAMURA 	int ret;
134997a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
135097a64b45SMasahide NAKAMURA 
135197a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
135297a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
135397a64b45SMasahide NAKAMURA 		if (km->report) {
135497a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
135597a64b45SMasahide NAKAMURA 			if (!ret)
135697a64b45SMasahide NAKAMURA 				err = ret;
135797a64b45SMasahide NAKAMURA 		}
135897a64b45SMasahide NAKAMURA 	}
135997a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
136097a64b45SMasahide NAKAMURA 	return err;
136197a64b45SMasahide NAKAMURA }
136297a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
136397a64b45SMasahide NAKAMURA 
13641da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
13651da177e4SLinus Torvalds {
13661da177e4SLinus Torvalds 	int err;
13671da177e4SLinus Torvalds 	u8 *data;
13681da177e4SLinus Torvalds 	struct xfrm_mgr *km;
13691da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
13721da177e4SLinus Torvalds 		return -EMSGSIZE;
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
13751da177e4SLinus Torvalds 	if (!data)
13761da177e4SLinus Torvalds 		return -ENOMEM;
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds 	err = -EFAULT;
13791da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
13801da177e4SLinus Torvalds 		goto out;
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds 	err = -EINVAL;
13831da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
13841da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1385cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
13861da177e4SLinus Torvalds 					 optlen, &err);
13871da177e4SLinus Torvalds 		if (err >= 0)
13881da177e4SLinus Torvalds 			break;
13891da177e4SLinus Torvalds 	}
13901da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	if (err >= 0) {
13931da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
13941da177e4SLinus Torvalds 		xfrm_pol_put(pol);
13951da177e4SLinus Torvalds 		err = 0;
13961da177e4SLinus Torvalds 	}
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds out:
13991da177e4SLinus Torvalds 	kfree(data);
14001da177e4SLinus Torvalds 	return err;
14011da177e4SLinus Torvalds }
14021da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
14031da177e4SLinus Torvalds 
14041da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
14051da177e4SLinus Torvalds {
14061da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
14071da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
14081da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
14091da177e4SLinus Torvalds 	return 0;
14101da177e4SLinus Torvalds }
14111da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
14121da177e4SLinus Torvalds 
14131da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
14141da177e4SLinus Torvalds {
14151da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
14161da177e4SLinus Torvalds 	list_del(&km->list);
14171da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
14181da177e4SLinus Torvalds 	return 0;
14191da177e4SLinus Torvalds }
14201da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
14211da177e4SLinus Torvalds 
14221da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
14231da177e4SLinus Torvalds {
14241da177e4SLinus Torvalds 	int err = 0;
14251da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14261da177e4SLinus Torvalds 		return -EINVAL;
14271da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14281da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1429f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14301da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
14311da177e4SLinus Torvalds 		err = -ENOBUFS;
1432edcd5821SDavid S. Miller 	else
14331da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1434f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14351da177e4SLinus Torvalds 	return err;
14361da177e4SLinus Torvalds }
14371da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
14401da177e4SLinus Torvalds {
14411da177e4SLinus Torvalds 	int err = 0;
14421da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
14431da177e4SLinus Torvalds 		return -EINVAL;
14441da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
14451da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1446f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
14471da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
14481da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
14491da177e4SLinus Torvalds 			err = -EINVAL;
1450edcd5821SDavid S. Miller 		else
14511da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
14521da177e4SLinus Torvalds 	}
1453f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
14541da177e4SLinus Torvalds 	return err;
14551da177e4SLinus Torvalds }
14561da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
14571da177e4SLinus Torvalds 
1458cdca7265SMiika Komu struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
14591da177e4SLinus Torvalds {
14601da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
14611da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
14621da177e4SLinus Torvalds 		return NULL;
14631da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
14641da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1465546be240SHerbert Xu 	if (unlikely(!afinfo))
14661da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
14671da177e4SLinus Torvalds 	return afinfo;
14681da177e4SLinus Torvalds }
14691da177e4SLinus Torvalds 
1470cdca7265SMiika Komu void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
14711da177e4SLinus Torvalds {
1472546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
14731da177e4SLinus Torvalds }
14741da177e4SLinus Torvalds 
1475cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_get_afinfo);
1476cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_put_afinfo);
1477cdca7265SMiika Komu 
14781da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
14791da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
14801da177e4SLinus Torvalds {
14811da177e4SLinus Torvalds 	if (x->tunnel) {
14821da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
14831da177e4SLinus Torvalds 
14841da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
14851da177e4SLinus Torvalds 			xfrm_state_delete(t);
14861da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
14871da177e4SLinus Torvalds 		xfrm_state_put(t);
14881da177e4SLinus Torvalds 		x->tunnel = NULL;
14891da177e4SLinus Torvalds 	}
14901da177e4SLinus Torvalds }
14911da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
14921da177e4SLinus Torvalds 
149380b30c10SHerbert Xu /*
149480b30c10SHerbert Xu  * This function is NOT optimal.  For example, with ESP it will give an
149580b30c10SHerbert Xu  * MTU that's usually two bytes short of being optimal.  However, it will
149680b30c10SHerbert Xu  * usually give an answer that's a multiple of 4 provided the input is
149780b30c10SHerbert Xu  * also a multiple of 4.
149880b30c10SHerbert Xu  */
14991da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
15001da177e4SLinus Torvalds {
15011da177e4SLinus Torvalds 	int res = mtu;
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	res -= x->props.header_len;
15041da177e4SLinus Torvalds 
15051da177e4SLinus Torvalds 	for (;;) {
15061da177e4SLinus Torvalds 		int m = res;
15071da177e4SLinus Torvalds 
15081da177e4SLinus Torvalds 		if (m < 68)
15091da177e4SLinus Torvalds 			return 68;
15101da177e4SLinus Torvalds 
15111da177e4SLinus Torvalds 		spin_lock_bh(&x->lock);
15121da177e4SLinus Torvalds 		if (x->km.state == XFRM_STATE_VALID &&
15131da177e4SLinus Torvalds 		    x->type && x->type->get_max_size)
15141da177e4SLinus Torvalds 			m = x->type->get_max_size(x, m);
15151da177e4SLinus Torvalds 		else
15161da177e4SLinus Torvalds 			m += x->props.header_len;
15171da177e4SLinus Torvalds 		spin_unlock_bh(&x->lock);
15181da177e4SLinus Torvalds 
15191da177e4SLinus Torvalds 		if (m <= mtu)
15201da177e4SLinus Torvalds 			break;
15211da177e4SLinus Torvalds 		res -= (m - mtu);
15221da177e4SLinus Torvalds 	}
15231da177e4SLinus Torvalds 
15241da177e4SLinus Torvalds 	return res;
15251da177e4SLinus Torvalds }
15261da177e4SLinus Torvalds 
152772cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
152872cb6962SHerbert Xu {
1529d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1530d094cd83SHerbert Xu 	int family = x->props.family;
153172cb6962SHerbert Xu 	int err;
153272cb6962SHerbert Xu 
1533d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1534d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1535d094cd83SHerbert Xu 	if (!afinfo)
1536d094cd83SHerbert Xu 		goto error;
1537d094cd83SHerbert Xu 
1538d094cd83SHerbert Xu 	err = 0;
1539d094cd83SHerbert Xu 	if (afinfo->init_flags)
1540d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1541d094cd83SHerbert Xu 
1542d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1543d094cd83SHerbert Xu 
1544d094cd83SHerbert Xu 	if (err)
1545d094cd83SHerbert Xu 		goto error;
1546d094cd83SHerbert Xu 
1547d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1548d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
154972cb6962SHerbert Xu 	if (x->type == NULL)
155072cb6962SHerbert Xu 		goto error;
155172cb6962SHerbert Xu 
155272cb6962SHerbert Xu 	err = x->type->init_state(x);
155372cb6962SHerbert Xu 	if (err)
155472cb6962SHerbert Xu 		goto error;
155572cb6962SHerbert Xu 
1556b59f45d0SHerbert Xu 	x->mode = xfrm_get_mode(x->props.mode, family);
1557b59f45d0SHerbert Xu 	if (x->mode == NULL)
1558b59f45d0SHerbert Xu 		goto error;
1559b59f45d0SHerbert Xu 
156072cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
156172cb6962SHerbert Xu 
156272cb6962SHerbert Xu error:
156372cb6962SHerbert Xu 	return err;
156472cb6962SHerbert Xu }
156572cb6962SHerbert Xu 
156672cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
15671da177e4SLinus Torvalds 
15681da177e4SLinus Torvalds void __init xfrm_state_init(void)
15691da177e4SLinus Torvalds {
1570f034b5d4SDavid S. Miller 	unsigned int sz;
15711da177e4SLinus Torvalds 
1572f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
1573f034b5d4SDavid S. Miller 
157444e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
157544e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
157644e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
1577f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1578f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1579f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1580f034b5d4SDavid S. Miller 
1581c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds 
1584