xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision 4c4d51a7)
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 ||
70775e252d9SJoy Latten 		    x->id.spi       != 0 ||
70875e252d9SJoy Latten 		    x->id.proto	    != proto)
7092770834cSDavid S. Miller 			continue;
7102770834cSDavid S. Miller 
7112770834cSDavid S. Miller 		switch (family) {
7122770834cSDavid S. Miller 		case AF_INET:
7132770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
7142770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
7152770834cSDavid S. Miller 				continue;
7162770834cSDavid S. Miller 			break;
7172770834cSDavid S. Miller 		case AF_INET6:
7182770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
7192770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
7202770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
7212770834cSDavid S. Miller 					     x->props.saddr.a6,
7222770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
7232770834cSDavid S. Miller 				continue;
7242770834cSDavid S. Miller 			break;
7252770834cSDavid S. Miller 		};
7262770834cSDavid S. Miller 
7272770834cSDavid S. Miller 		xfrm_state_hold(x);
7282770834cSDavid S. Miller 		return x;
7292770834cSDavid S. Miller 	}
7302770834cSDavid S. Miller 
7312770834cSDavid S. Miller 	if (!create)
7322770834cSDavid S. Miller 		return NULL;
7332770834cSDavid S. Miller 
7342770834cSDavid S. Miller 	x = xfrm_state_alloc();
7352770834cSDavid S. Miller 	if (likely(x)) {
7362770834cSDavid S. Miller 		switch (family) {
7372770834cSDavid S. Miller 		case AF_INET:
7382770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
7392770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
7402770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
7412770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
7422770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
7432770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
7442770834cSDavid S. Miller 			break;
7452770834cSDavid S. Miller 
7462770834cSDavid S. Miller 		case AF_INET6:
7472770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
7482770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7492770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
7502770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7512770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
7522770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
7532770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
7542770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
7552770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
7562770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
7572770834cSDavid S. Miller 			break;
7582770834cSDavid S. Miller 		};
7592770834cSDavid S. Miller 
7602770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
7612770834cSDavid S. Miller 		x->id.proto = proto;
7622770834cSDavid S. Miller 		x->props.family = family;
7632770834cSDavid S. Miller 		x->props.mode = mode;
7642770834cSDavid S. Miller 		x->props.reqid = reqid;
7652770834cSDavid S. Miller 		x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
7662770834cSDavid S. Miller 		xfrm_state_hold(x);
7672770834cSDavid S. Miller 		x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
7682770834cSDavid S. Miller 		add_timer(&x->timer);
7698f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
770667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
7718f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
7722770834cSDavid S. Miller 		wake_up(&km_waitq);
773918049f0SDavid S. Miller 
774918049f0SDavid S. Miller 		xfrm_state_num++;
775918049f0SDavid S. Miller 
776918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
7772770834cSDavid S. Miller 	}
7782770834cSDavid S. Miller 
7792770834cSDavid S. Miller 	return x;
7802770834cSDavid S. Miller }
7812770834cSDavid S. Miller 
7821da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
7851da177e4SLinus Torvalds {
7861da177e4SLinus Torvalds 	struct xfrm_state *x1;
7871da177e4SLinus Torvalds 	int family;
7881da177e4SLinus Torvalds 	int err;
789eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds 	family = x->props.family;
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7941da177e4SLinus Torvalds 
795edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
7961da177e4SLinus Torvalds 	if (x1) {
7971da177e4SLinus Torvalds 		xfrm_state_put(x1);
7981da177e4SLinus Torvalds 		x1 = NULL;
7991da177e4SLinus Torvalds 		err = -EEXIST;
8001da177e4SLinus Torvalds 		goto out;
8011da177e4SLinus Torvalds 	}
8021da177e4SLinus Torvalds 
803eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
8041da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
80575e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
80675e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
8071da177e4SLinus Torvalds 			xfrm_state_put(x1);
8081da177e4SLinus Torvalds 			x1 = NULL;
8091da177e4SLinus Torvalds 		}
8101da177e4SLinus Torvalds 	}
8111da177e4SLinus Torvalds 
812eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
8132770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
8142770834cSDavid S. Miller 				     x->id.proto,
8151da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
8161da177e4SLinus Torvalds 
817c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
8181da177e4SLinus Torvalds 	__xfrm_state_insert(x);
8191da177e4SLinus Torvalds 	err = 0;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds out:
8221da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8231da177e4SLinus Torvalds 
8241da177e4SLinus Torvalds 	if (x1) {
8251da177e4SLinus Torvalds 		xfrm_state_delete(x1);
8261da177e4SLinus Torvalds 		xfrm_state_put(x1);
8271da177e4SLinus Torvalds 	}
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	return err;
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
8321da177e4SLinus Torvalds 
83380c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
83480c9abaaSShinta Sugimoto struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
83580c9abaaSShinta Sugimoto {
83680c9abaaSShinta Sugimoto 	int err = -ENOMEM;
83780c9abaaSShinta Sugimoto 	struct xfrm_state *x = xfrm_state_alloc();
83880c9abaaSShinta Sugimoto 	if (!x)
83980c9abaaSShinta Sugimoto 		goto error;
84080c9abaaSShinta Sugimoto 
84180c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
84280c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
84380c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
84480c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
84580c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
84680c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
84780c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
84880c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
84980c9abaaSShinta Sugimoto 
85080c9abaaSShinta Sugimoto 	if (orig->aalg) {
85180c9abaaSShinta Sugimoto 		x->aalg = xfrm_algo_clone(orig->aalg);
85280c9abaaSShinta Sugimoto 		if (!x->aalg)
85380c9abaaSShinta Sugimoto 			goto error;
85480c9abaaSShinta Sugimoto 	}
85580c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
85680c9abaaSShinta Sugimoto 
85780c9abaaSShinta Sugimoto 	if (orig->ealg) {
85880c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
85980c9abaaSShinta Sugimoto 		if (!x->ealg)
86080c9abaaSShinta Sugimoto 			goto error;
86180c9abaaSShinta Sugimoto 	}
86280c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
86380c9abaaSShinta Sugimoto 
86480c9abaaSShinta Sugimoto 	if (orig->calg) {
86580c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
86680c9abaaSShinta Sugimoto 		if (!x->calg)
86780c9abaaSShinta Sugimoto 			goto error;
86880c9abaaSShinta Sugimoto 	}
86980c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
87080c9abaaSShinta Sugimoto 
87180c9abaaSShinta Sugimoto 	if (orig->encap) {
87280c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
87380c9abaaSShinta Sugimoto 		if (!x->encap)
87480c9abaaSShinta Sugimoto 			goto error;
87580c9abaaSShinta Sugimoto 	}
87680c9abaaSShinta Sugimoto 
87780c9abaaSShinta Sugimoto 	if (orig->coaddr) {
87880c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
87980c9abaaSShinta Sugimoto 				    GFP_KERNEL);
88080c9abaaSShinta Sugimoto 		if (!x->coaddr)
88180c9abaaSShinta Sugimoto 			goto error;
88280c9abaaSShinta Sugimoto 	}
88380c9abaaSShinta Sugimoto 
88480c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
88580c9abaaSShinta Sugimoto 	if (err)
88680c9abaaSShinta Sugimoto 		goto error;
88780c9abaaSShinta Sugimoto 
88880c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
88980c9abaaSShinta Sugimoto 
89080c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
89180c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
89280c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
89380c9abaaSShinta Sugimoto 
89480c9abaaSShinta Sugimoto 	return x;
89580c9abaaSShinta Sugimoto 
89680c9abaaSShinta Sugimoto  error:
89780c9abaaSShinta Sugimoto 	if (errp)
89880c9abaaSShinta Sugimoto 		*errp = err;
89980c9abaaSShinta Sugimoto 	if (x) {
90080c9abaaSShinta Sugimoto 		kfree(x->aalg);
90180c9abaaSShinta Sugimoto 		kfree(x->ealg);
90280c9abaaSShinta Sugimoto 		kfree(x->calg);
90380c9abaaSShinta Sugimoto 		kfree(x->encap);
90480c9abaaSShinta Sugimoto 		kfree(x->coaddr);
90580c9abaaSShinta Sugimoto 	}
90680c9abaaSShinta Sugimoto 	kfree(x);
90780c9abaaSShinta Sugimoto 	return NULL;
90880c9abaaSShinta Sugimoto }
90980c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_clone);
91080c9abaaSShinta Sugimoto 
91180c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
91280c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
91380c9abaaSShinta Sugimoto {
91480c9abaaSShinta Sugimoto 	unsigned int h;
91580c9abaaSShinta Sugimoto 	struct xfrm_state *x;
91680c9abaaSShinta Sugimoto 	struct hlist_node *entry;
91780c9abaaSShinta Sugimoto 
91880c9abaaSShinta Sugimoto 	if (m->reqid) {
91980c9abaaSShinta Sugimoto 		h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
92080c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
92180c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
92280c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
92380c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
92480c9abaaSShinta Sugimoto 				continue;
92580c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
92680c9abaaSShinta Sugimoto 				continue;
92780c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
92880c9abaaSShinta Sugimoto 					  m->old_family) ||
92980c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
93080c9abaaSShinta Sugimoto 					  m->old_family))
93180c9abaaSShinta Sugimoto 				continue;
93280c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
93380c9abaaSShinta Sugimoto 			return x;
93480c9abaaSShinta Sugimoto 		}
93580c9abaaSShinta Sugimoto 	} else {
93680c9abaaSShinta Sugimoto 		h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
93780c9abaaSShinta Sugimoto 				  m->old_family);
93880c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
93980c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
94080c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
94180c9abaaSShinta Sugimoto 				continue;
94280c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
94380c9abaaSShinta Sugimoto 					  m->old_family) ||
94480c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
94580c9abaaSShinta Sugimoto 					  m->old_family))
94680c9abaaSShinta Sugimoto 				continue;
94780c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
94880c9abaaSShinta Sugimoto 			return x;
94980c9abaaSShinta Sugimoto 		}
95080c9abaaSShinta Sugimoto 	}
95180c9abaaSShinta Sugimoto 
95280c9abaaSShinta Sugimoto 	return NULL;
95380c9abaaSShinta Sugimoto }
95480c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
95580c9abaaSShinta Sugimoto 
95680c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
95780c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
95880c9abaaSShinta Sugimoto {
95980c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
96080c9abaaSShinta Sugimoto 	int err;
96180c9abaaSShinta Sugimoto 
96280c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
96380c9abaaSShinta Sugimoto 	if (!xc)
96480c9abaaSShinta Sugimoto 		return NULL;
96580c9abaaSShinta Sugimoto 
96680c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
96780c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
96880c9abaaSShinta Sugimoto 
96980c9abaaSShinta Sugimoto 	/* add state */
97080c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
97180c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
97280c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
97380c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
97480c9abaaSShinta Sugimoto 	} else {
97580c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
97680c9abaaSShinta Sugimoto 			goto error;
97780c9abaaSShinta Sugimoto 	}
97880c9abaaSShinta Sugimoto 
97980c9abaaSShinta Sugimoto 	return xc;
98080c9abaaSShinta Sugimoto error:
98180c9abaaSShinta Sugimoto 	kfree(xc);
98280c9abaaSShinta Sugimoto 	return NULL;
98380c9abaaSShinta Sugimoto }
98480c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
98580c9abaaSShinta Sugimoto #endif
98680c9abaaSShinta Sugimoto 
9871da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
9881da177e4SLinus Torvalds {
9891da177e4SLinus Torvalds 	struct xfrm_state *x1;
9901da177e4SLinus Torvalds 	int err;
991eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
994edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds 	err = -ESRCH;
9971da177e4SLinus Torvalds 	if (!x1)
9981da177e4SLinus Torvalds 		goto out;
9991da177e4SLinus Torvalds 
10001da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
10011da177e4SLinus Torvalds 		xfrm_state_put(x1);
10021da177e4SLinus Torvalds 		err = -EEXIST;
10031da177e4SLinus Torvalds 		goto out;
10041da177e4SLinus Torvalds 	}
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
10071da177e4SLinus Torvalds 		__xfrm_state_insert(x);
10081da177e4SLinus Torvalds 		x = NULL;
10091da177e4SLinus Torvalds 	}
10101da177e4SLinus Torvalds 	err = 0;
10111da177e4SLinus Torvalds 
10121da177e4SLinus Torvalds out:
10131da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds 	if (err)
10161da177e4SLinus Torvalds 		return err;
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds 	if (!x) {
10191da177e4SLinus Torvalds 		xfrm_state_delete(x1);
10201da177e4SLinus Torvalds 		xfrm_state_put(x1);
10211da177e4SLinus Torvalds 		return 0;
10221da177e4SLinus Torvalds 	}
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds 	err = -EINVAL;
10251da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
10261da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
10271da177e4SLinus Torvalds 		if (x->encap && x1->encap)
10281da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1029060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1030060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1031060f02a3SNoriaki TAKAMIYA 		}
1032060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1033060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
10341da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
10351da177e4SLinus Torvalds 		x1->km.dying = 0;
10361da177e4SLinus Torvalds 
1037a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
10381da177e4SLinus Torvalds 		if (x1->curlft.use_time)
10391da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 		err = 0;
10421da177e4SLinus Torvalds 	}
10431da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
10441da177e4SLinus Torvalds 
10451da177e4SLinus Torvalds 	xfrm_state_put(x1);
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	return err;
10481da177e4SLinus Torvalds }
10491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
10521da177e4SLinus Torvalds {
10531da177e4SLinus Torvalds 	if (!x->curlft.use_time)
10541da177e4SLinus Torvalds 		x->curlft.use_time = (unsigned long)xtime.tv_sec;
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
10571da177e4SLinus Torvalds 		return -EINVAL;
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
10601da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
10614666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
1062a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
10631da177e4SLinus Torvalds 		return -EINVAL;
10641da177e4SLinus Torvalds 	}
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	if (!x->km.dying &&
10671da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
10684666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
10694666faabSHerbert Xu 		x->km.dying = 1;
107053bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
10714666faabSHerbert Xu 	}
10721da177e4SLinus Torvalds 	return 0;
10731da177e4SLinus Torvalds }
10741da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
10771da177e4SLinus Torvalds {
10781da177e4SLinus Torvalds 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
10791da177e4SLinus Torvalds 		- skb_headroom(skb);
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds 	if (nhead > 0)
10821da177e4SLinus Torvalds 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 	/* Check tail too... */
10851da177e4SLinus Torvalds 	return 0;
10861da177e4SLinus Torvalds }
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
10891da177e4SLinus Torvalds {
10901da177e4SLinus Torvalds 	int err = xfrm_state_check_expire(x);
10911da177e4SLinus Torvalds 	if (err < 0)
10921da177e4SLinus Torvalds 		goto err;
10931da177e4SLinus Torvalds 	err = xfrm_state_check_space(x, skb);
10941da177e4SLinus Torvalds err:
10951da177e4SLinus Torvalds 	return err;
10961da177e4SLinus Torvalds }
10971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check);
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds struct xfrm_state *
1100a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
11011da177e4SLinus Torvalds 		  unsigned short family)
11021da177e4SLinus Torvalds {
11031da177e4SLinus Torvalds 	struct xfrm_state *x;
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1106edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
11071da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11081da177e4SLinus Torvalds 	return x;
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds struct xfrm_state *
1113eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1114eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1115eb2971b6SMasahide NAKAMURA {
1116eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1117eb2971b6SMasahide NAKAMURA 
1118eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1119edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
1120eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1121eb2971b6SMasahide NAKAMURA 	return x;
1122eb2971b6SMasahide NAKAMURA }
1123eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1124eb2971b6SMasahide NAKAMURA 
1125eb2971b6SMasahide NAKAMURA struct xfrm_state *
11261da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
11271da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
11281da177e4SLinus Torvalds 	      int create, unsigned short family)
11291da177e4SLinus Torvalds {
11301da177e4SLinus Torvalds 	struct xfrm_state *x;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
11332770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
11341da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11352770834cSDavid S. Miller 
11361da177e4SLinus Torvalds 	return x;
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
11391da177e4SLinus Torvalds 
114041a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
114141a49cc3SMasahide NAKAMURA int
114241a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
114341a49cc3SMasahide NAKAMURA 	       unsigned short family)
114441a49cc3SMasahide NAKAMURA {
114541a49cc3SMasahide NAKAMURA 	int err = 0;
114641a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
114741a49cc3SMasahide NAKAMURA 	if (!afinfo)
114841a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
114941a49cc3SMasahide NAKAMURA 
115041a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
115141a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
115241a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
115341a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
115441a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
115541a49cc3SMasahide NAKAMURA 	return err;
115641a49cc3SMasahide NAKAMURA }
115741a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
115841a49cc3SMasahide NAKAMURA 
115941a49cc3SMasahide NAKAMURA int
116041a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
116141a49cc3SMasahide NAKAMURA 		unsigned short family)
116241a49cc3SMasahide NAKAMURA {
116341a49cc3SMasahide NAKAMURA 	int err = 0;
116441a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
116541a49cc3SMasahide NAKAMURA 	if (!afinfo)
116641a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
116741a49cc3SMasahide NAKAMURA 
116841a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
116941a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
117041a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
117141a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
117241a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
117341a49cc3SMasahide NAKAMURA 	return err;
117441a49cc3SMasahide NAKAMURA }
117541a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
117641a49cc3SMasahide NAKAMURA #endif
117741a49cc3SMasahide NAKAMURA 
11781da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
11811da177e4SLinus Torvalds {
11821da177e4SLinus Torvalds 	int i;
11831da177e4SLinus Torvalds 
1184f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
11858f126e37SDavid S. Miller 		struct hlist_node *entry;
11868f126e37SDavid S. Miller 		struct xfrm_state *x;
11878f126e37SDavid S. Miller 
11888f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
11898f126e37SDavid S. Miller 			if (x->km.seq == seq &&
11908f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
11911da177e4SLinus Torvalds 				xfrm_state_hold(x);
11921da177e4SLinus Torvalds 				return x;
11931da177e4SLinus Torvalds 			}
11941da177e4SLinus Torvalds 		}
11951da177e4SLinus Torvalds 	}
11961da177e4SLinus Torvalds 	return NULL;
11971da177e4SLinus Torvalds }
11981da177e4SLinus Torvalds 
11991da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
12001da177e4SLinus Torvalds {
12011da177e4SLinus Torvalds 	struct xfrm_state *x;
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
12041da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
12051da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12061da177e4SLinus Torvalds 	return x;
12071da177e4SLinus Torvalds }
12081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
12111da177e4SLinus Torvalds {
12121da177e4SLinus Torvalds 	u32 res;
12131da177e4SLinus Torvalds 	static u32 acqseq;
12141da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
12151da177e4SLinus Torvalds 
12161da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
12171da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
12181da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
12191da177e4SLinus Torvalds 	return res;
12201da177e4SLinus Torvalds }
12211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds void
122426977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi)
12251da177e4SLinus Torvalds {
1226f034b5d4SDavid S. Miller 	unsigned int h;
12271da177e4SLinus Torvalds 	struct xfrm_state *x0;
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds 	if (x->id.spi)
12301da177e4SLinus Torvalds 		return;
12311da177e4SLinus Torvalds 
12321da177e4SLinus Torvalds 	if (minspi == maxspi) {
12331da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
12341da177e4SLinus Torvalds 		if (x0) {
12351da177e4SLinus Torvalds 			xfrm_state_put(x0);
12361da177e4SLinus Torvalds 			return;
12371da177e4SLinus Torvalds 		}
12381da177e4SLinus Torvalds 		x->id.spi = minspi;
12391da177e4SLinus Torvalds 	} else {
12401da177e4SLinus Torvalds 		u32 spi = 0;
124126977b4eSAl Viro 		u32 low = ntohl(minspi);
124226977b4eSAl Viro 		u32 high = ntohl(maxspi);
124326977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
124426977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
12451da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
12461da177e4SLinus Torvalds 			if (x0 == NULL) {
12471da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
12481da177e4SLinus Torvalds 				break;
12491da177e4SLinus Torvalds 			}
12501da177e4SLinus Torvalds 			xfrm_state_put(x0);
12511da177e4SLinus Torvalds 		}
12521da177e4SLinus Torvalds 	}
12531da177e4SLinus Torvalds 	if (x->id.spi) {
12541da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
12551da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
12568f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
12571da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
12581da177e4SLinus Torvalds 		wake_up(&km_waitq);
12591da177e4SLinus Torvalds 	}
12601da177e4SLinus Torvalds }
12611da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
12621da177e4SLinus Torvalds 
12631da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
12641da177e4SLinus Torvalds 		    void *data)
12651da177e4SLinus Torvalds {
12661da177e4SLinus Torvalds 	int i;
126794b9bb54SJamal Hadi Salim 	struct xfrm_state *x, *last = NULL;
12688f126e37SDavid S. Miller 	struct hlist_node *entry;
12691da177e4SLinus Torvalds 	int count = 0;
12701da177e4SLinus Torvalds 	int err = 0;
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1273f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
12748f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
127594b9bb54SJamal Hadi Salim 			if (!xfrm_id_proto_match(x->id.proto, proto))
127694b9bb54SJamal Hadi Salim 				continue;
127794b9bb54SJamal Hadi Salim 			if (last) {
127894b9bb54SJamal Hadi Salim 				err = func(last, count, data);
127994b9bb54SJamal Hadi Salim 				if (err)
128094b9bb54SJamal Hadi Salim 					goto out;
128194b9bb54SJamal Hadi Salim 			}
128294b9bb54SJamal Hadi Salim 			last = x;
12831da177e4SLinus Torvalds 			count++;
12841da177e4SLinus Torvalds 		}
12851da177e4SLinus Torvalds 	}
12861da177e4SLinus Torvalds 	if (count == 0) {
12871da177e4SLinus Torvalds 		err = -ENOENT;
12881da177e4SLinus Torvalds 		goto out;
12891da177e4SLinus Torvalds 	}
129094b9bb54SJamal Hadi Salim 	err = func(last, 0, data);
12911da177e4SLinus Torvalds out:
12921da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12931da177e4SLinus Torvalds 	return err;
12941da177e4SLinus Torvalds }
12951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
12961da177e4SLinus Torvalds 
1297f8cd5488SJamal Hadi Salim 
1298f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1299f8cd5488SJamal Hadi Salim {
1300f8cd5488SJamal Hadi Salim 	struct km_event c;
1301f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1302f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1303f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1304f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1305f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1306f8cd5488SJamal Hadi Salim 	 *     and there were changes
1307f8cd5488SJamal Hadi Salim 	 *
1308f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1309f8cd5488SJamal Hadi Salim 	 */
1310f8cd5488SJamal Hadi Salim 
1311f8cd5488SJamal Hadi Salim 	switch (event) {
1312f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1313f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1314f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
13152717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
13162717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
13172717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
13182717096aSJamal Hadi Salim 			else
1319f8cd5488SJamal Hadi Salim 				return;
13202717096aSJamal Hadi Salim 		}
1321f8cd5488SJamal Hadi Salim 
1322f8cd5488SJamal Hadi Salim 		break;
1323f8cd5488SJamal Hadi Salim 
1324f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1325f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1326f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
13272717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
13282717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1329f8cd5488SJamal Hadi Salim 			return;
13302717096aSJamal Hadi Salim 		}
1331f8cd5488SJamal Hadi Salim 
1332f8cd5488SJamal Hadi Salim 		break;
1333f8cd5488SJamal Hadi Salim 	}
1334f8cd5488SJamal Hadi Salim 
1335f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1336f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1337f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1338f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1339f8cd5488SJamal Hadi Salim 
1340f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1341a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
13422717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
13432717096aSJamal Hadi Salim }
1344a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify);
1345f8cd5488SJamal Hadi Salim 
1346f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1347f8cd5488SJamal Hadi Salim {
1348f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1349f8cd5488SJamal Hadi Salim 
1350f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1351f8cd5488SJamal Hadi Salim 
13522717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
13532717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1354f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
13552717096aSJamal Hadi Salim 		else
13562717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
13572717096aSJamal Hadi Salim 	}
1358f8cd5488SJamal Hadi Salim 
1359f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1360f8cd5488SJamal Hadi Salim }
1361f8cd5488SJamal Hadi Salim 
1362a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq)
13631da177e4SLinus Torvalds {
13641da177e4SLinus Torvalds 	u32 diff;
1365a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	if (unlikely(seq == 0))
13681da177e4SLinus Torvalds 		return -EINVAL;
13691da177e4SLinus Torvalds 
13701da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
13711da177e4SLinus Torvalds 		return 0;
13721da177e4SLinus Torvalds 
13731da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
13744c4d51a7SHerbert Xu 	if (diff >= min_t(unsigned int, x->props.replay_window,
13754c4d51a7SHerbert Xu 			  sizeof(x->replay.bitmap) * 8)) {
13761da177e4SLinus Torvalds 		x->stats.replay_window++;
13771da177e4SLinus Torvalds 		return -EINVAL;
13781da177e4SLinus Torvalds 	}
13791da177e4SLinus Torvalds 
13801da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
13811da177e4SLinus Torvalds 		x->stats.replay++;
13821da177e4SLinus Torvalds 		return -EINVAL;
13831da177e4SLinus Torvalds 	}
13841da177e4SLinus Torvalds 	return 0;
13851da177e4SLinus Torvalds }
13861da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
13871da177e4SLinus Torvalds 
138861f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
13891da177e4SLinus Torvalds {
13901da177e4SLinus Torvalds 	u32 diff;
139161f4627bSAl Viro 	u32 seq = ntohl(net_seq);
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
13941da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
13951da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
13961da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
13971da177e4SLinus Torvalds 		else
13981da177e4SLinus Torvalds 			x->replay.bitmap = 1;
13991da177e4SLinus Torvalds 		x->replay.seq = seq;
14001da177e4SLinus Torvalds 	} else {
14011da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
14021da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
14031da177e4SLinus Torvalds 	}
1404f8cd5488SJamal Hadi Salim 
1405f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1406f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
14071da177e4SLinus Torvalds }
14081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
14091da177e4SLinus Torvalds 
14101da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
14111da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
14121da177e4SLinus Torvalds 
141326b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
14141da177e4SLinus Torvalds {
14151da177e4SLinus Torvalds 	struct xfrm_mgr *km;
14161da177e4SLinus Torvalds 
141726b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
141826b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
141926b15dadSJamal Hadi Salim 		if (km->notify_policy)
142026b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
142126b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
142226b15dadSJamal Hadi Salim }
142326b15dadSJamal Hadi Salim 
142426b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
142526b15dadSJamal Hadi Salim {
142626b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
142726b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
142826b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
142926b15dadSJamal Hadi Salim 		if (km->notify)
143026b15dadSJamal Hadi Salim 			km->notify(x, c);
143126b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
143226b15dadSJamal Hadi Salim }
143326b15dadSJamal Hadi Salim 
143426b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
143526b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
143626b15dadSJamal Hadi Salim 
143753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
143826b15dadSJamal Hadi Salim {
143926b15dadSJamal Hadi Salim 	struct km_event c;
144026b15dadSJamal Hadi Salim 
1441bf08867fSHerbert Xu 	c.data.hard = hard;
144253bc6b4dSJamal Hadi Salim 	c.pid = pid;
1443f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
144426b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds 	if (hard)
14471da177e4SLinus Torvalds 		wake_up(&km_waitq);
14481da177e4SLinus Torvalds }
14491da177e4SLinus Torvalds 
145053bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
145126b15dadSJamal Hadi Salim /*
145226b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
145326b15dadSJamal Hadi Salim  * We are happy with one success
145426b15dadSJamal Hadi Salim */
1455980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
14561da177e4SLinus Torvalds {
145726b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
14581da177e4SLinus Torvalds 	struct xfrm_mgr *km;
14591da177e4SLinus Torvalds 
14601da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
14611da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
146226b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
146326b15dadSJamal Hadi Salim 		if (!acqret)
146426b15dadSJamal Hadi Salim 			err = acqret;
14651da177e4SLinus Torvalds 	}
14661da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
14671da177e4SLinus Torvalds 	return err;
14681da177e4SLinus Torvalds }
1469980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
14701da177e4SLinus Torvalds 
14715d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
14721da177e4SLinus Torvalds {
14731da177e4SLinus Torvalds 	int err = -EINVAL;
14741da177e4SLinus Torvalds 	struct xfrm_mgr *km;
14751da177e4SLinus Torvalds 
14761da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
14771da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
14781da177e4SLinus Torvalds 		if (km->new_mapping)
14791da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
14801da177e4SLinus Torvalds 		if (!err)
14811da177e4SLinus Torvalds 			break;
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
14841da177e4SLinus Torvalds 	return err;
14851da177e4SLinus Torvalds }
14861da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
14871da177e4SLinus Torvalds 
14886c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
14891da177e4SLinus Torvalds {
149026b15dadSJamal Hadi Salim 	struct km_event c;
14911da177e4SLinus Torvalds 
1492bf08867fSHerbert Xu 	c.data.hard = hard;
14936c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1494f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
149526b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
14961da177e4SLinus Torvalds 
14971da177e4SLinus Torvalds 	if (hard)
14981da177e4SLinus Torvalds 		wake_up(&km_waitq);
14991da177e4SLinus Torvalds }
1500a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
15011da177e4SLinus Torvalds 
150280c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
150380c9abaaSShinta Sugimoto 	       struct xfrm_migrate *m, int num_migrate)
150480c9abaaSShinta Sugimoto {
150580c9abaaSShinta Sugimoto 	int err = -EINVAL;
150680c9abaaSShinta Sugimoto 	int ret;
150780c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
150880c9abaaSShinta Sugimoto 
150980c9abaaSShinta Sugimoto 	read_lock(&xfrm_km_lock);
151080c9abaaSShinta Sugimoto 	list_for_each_entry(km, &xfrm_km_list, list) {
151180c9abaaSShinta Sugimoto 		if (km->migrate) {
151280c9abaaSShinta Sugimoto 			ret = km->migrate(sel, dir, type, m, num_migrate);
151380c9abaaSShinta Sugimoto 			if (!ret)
151480c9abaaSShinta Sugimoto 				err = ret;
151580c9abaaSShinta Sugimoto 		}
151680c9abaaSShinta Sugimoto 	}
151780c9abaaSShinta Sugimoto 	read_unlock(&xfrm_km_lock);
151880c9abaaSShinta Sugimoto 	return err;
151980c9abaaSShinta Sugimoto }
152080c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
152180c9abaaSShinta Sugimoto 
152297a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
152397a64b45SMasahide NAKAMURA {
152497a64b45SMasahide NAKAMURA 	int err = -EINVAL;
152597a64b45SMasahide NAKAMURA 	int ret;
152697a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
152797a64b45SMasahide NAKAMURA 
152897a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
152997a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
153097a64b45SMasahide NAKAMURA 		if (km->report) {
153197a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
153297a64b45SMasahide NAKAMURA 			if (!ret)
153397a64b45SMasahide NAKAMURA 				err = ret;
153497a64b45SMasahide NAKAMURA 		}
153597a64b45SMasahide NAKAMURA 	}
153697a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
153797a64b45SMasahide NAKAMURA 	return err;
153897a64b45SMasahide NAKAMURA }
153997a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
154097a64b45SMasahide NAKAMURA 
15411da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
15421da177e4SLinus Torvalds {
15431da177e4SLinus Torvalds 	int err;
15441da177e4SLinus Torvalds 	u8 *data;
15451da177e4SLinus Torvalds 	struct xfrm_mgr *km;
15461da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
15471da177e4SLinus Torvalds 
15481da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
15491da177e4SLinus Torvalds 		return -EMSGSIZE;
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
15521da177e4SLinus Torvalds 	if (!data)
15531da177e4SLinus Torvalds 		return -ENOMEM;
15541da177e4SLinus Torvalds 
15551da177e4SLinus Torvalds 	err = -EFAULT;
15561da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
15571da177e4SLinus Torvalds 		goto out;
15581da177e4SLinus Torvalds 
15591da177e4SLinus Torvalds 	err = -EINVAL;
15601da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
15611da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1562cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
15631da177e4SLinus Torvalds 					 optlen, &err);
15641da177e4SLinus Torvalds 		if (err >= 0)
15651da177e4SLinus Torvalds 			break;
15661da177e4SLinus Torvalds 	}
15671da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
15681da177e4SLinus Torvalds 
15691da177e4SLinus Torvalds 	if (err >= 0) {
15701da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
15711da177e4SLinus Torvalds 		xfrm_pol_put(pol);
15721da177e4SLinus Torvalds 		err = 0;
15731da177e4SLinus Torvalds 	}
15741da177e4SLinus Torvalds 
15751da177e4SLinus Torvalds out:
15761da177e4SLinus Torvalds 	kfree(data);
15771da177e4SLinus Torvalds 	return err;
15781da177e4SLinus Torvalds }
15791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
15801da177e4SLinus Torvalds 
15811da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
15821da177e4SLinus Torvalds {
15831da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
15841da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
15851da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
15861da177e4SLinus Torvalds 	return 0;
15871da177e4SLinus Torvalds }
15881da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
15911da177e4SLinus Torvalds {
15921da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
15931da177e4SLinus Torvalds 	list_del(&km->list);
15941da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
15951da177e4SLinus Torvalds 	return 0;
15961da177e4SLinus Torvalds }
15971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
15981da177e4SLinus Torvalds 
15991da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
16001da177e4SLinus Torvalds {
16011da177e4SLinus Torvalds 	int err = 0;
16021da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
16031da177e4SLinus Torvalds 		return -EINVAL;
16041da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
16051da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1606f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
16071da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
16081da177e4SLinus Torvalds 		err = -ENOBUFS;
1609edcd5821SDavid S. Miller 	else
16101da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1611f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
16121da177e4SLinus Torvalds 	return err;
16131da177e4SLinus Torvalds }
16141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
16151da177e4SLinus Torvalds 
16161da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
16171da177e4SLinus Torvalds {
16181da177e4SLinus Torvalds 	int err = 0;
16191da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
16201da177e4SLinus Torvalds 		return -EINVAL;
16211da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
16221da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1623f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
16241da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
16251da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
16261da177e4SLinus Torvalds 			err = -EINVAL;
1627edcd5821SDavid S. Miller 		else
16281da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
16291da177e4SLinus Torvalds 	}
1630f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
16311da177e4SLinus Torvalds 	return err;
16321da177e4SLinus Torvalds }
16331da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
16341da177e4SLinus Torvalds 
1635cdca7265SMiika Komu struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
16361da177e4SLinus Torvalds {
16371da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
16381da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
16391da177e4SLinus Torvalds 		return NULL;
16401da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
16411da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1642546be240SHerbert Xu 	if (unlikely(!afinfo))
16431da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
16441da177e4SLinus Torvalds 	return afinfo;
16451da177e4SLinus Torvalds }
16461da177e4SLinus Torvalds 
1647cdca7265SMiika Komu void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
16481da177e4SLinus Torvalds {
1649546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
16501da177e4SLinus Torvalds }
16511da177e4SLinus Torvalds 
1652cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_get_afinfo);
1653cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_put_afinfo);
1654cdca7265SMiika Komu 
16551da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
16561da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
16571da177e4SLinus Torvalds {
16581da177e4SLinus Torvalds 	if (x->tunnel) {
16591da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
16601da177e4SLinus Torvalds 
16611da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
16621da177e4SLinus Torvalds 			xfrm_state_delete(t);
16631da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
16641da177e4SLinus Torvalds 		xfrm_state_put(t);
16651da177e4SLinus Torvalds 		x->tunnel = NULL;
16661da177e4SLinus Torvalds 	}
16671da177e4SLinus Torvalds }
16681da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
16691da177e4SLinus Torvalds 
167080b30c10SHerbert Xu /*
167180b30c10SHerbert Xu  * This function is NOT optimal.  For example, with ESP it will give an
167280b30c10SHerbert Xu  * MTU that's usually two bytes short of being optimal.  However, it will
167380b30c10SHerbert Xu  * usually give an answer that's a multiple of 4 provided the input is
167480b30c10SHerbert Xu  * also a multiple of 4.
167580b30c10SHerbert Xu  */
16761da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
16771da177e4SLinus Torvalds {
16781da177e4SLinus Torvalds 	int res = mtu;
16791da177e4SLinus Torvalds 
16801da177e4SLinus Torvalds 	res -= x->props.header_len;
16811da177e4SLinus Torvalds 
16821da177e4SLinus Torvalds 	for (;;) {
16831da177e4SLinus Torvalds 		int m = res;
16841da177e4SLinus Torvalds 
16851da177e4SLinus Torvalds 		if (m < 68)
16861da177e4SLinus Torvalds 			return 68;
16871da177e4SLinus Torvalds 
16881da177e4SLinus Torvalds 		spin_lock_bh(&x->lock);
16891da177e4SLinus Torvalds 		if (x->km.state == XFRM_STATE_VALID &&
16901da177e4SLinus Torvalds 		    x->type && x->type->get_max_size)
16911da177e4SLinus Torvalds 			m = x->type->get_max_size(x, m);
16921da177e4SLinus Torvalds 		else
16931da177e4SLinus Torvalds 			m += x->props.header_len;
16941da177e4SLinus Torvalds 		spin_unlock_bh(&x->lock);
16951da177e4SLinus Torvalds 
16961da177e4SLinus Torvalds 		if (m <= mtu)
16971da177e4SLinus Torvalds 			break;
16981da177e4SLinus Torvalds 		res -= (m - mtu);
16991da177e4SLinus Torvalds 	}
17001da177e4SLinus Torvalds 
17011da177e4SLinus Torvalds 	return res;
17021da177e4SLinus Torvalds }
17031da177e4SLinus Torvalds 
170472cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
170572cb6962SHerbert Xu {
1706d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1707d094cd83SHerbert Xu 	int family = x->props.family;
170872cb6962SHerbert Xu 	int err;
170972cb6962SHerbert Xu 
1710d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1711d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1712d094cd83SHerbert Xu 	if (!afinfo)
1713d094cd83SHerbert Xu 		goto error;
1714d094cd83SHerbert Xu 
1715d094cd83SHerbert Xu 	err = 0;
1716d094cd83SHerbert Xu 	if (afinfo->init_flags)
1717d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1718d094cd83SHerbert Xu 
1719d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1720d094cd83SHerbert Xu 
1721d094cd83SHerbert Xu 	if (err)
1722d094cd83SHerbert Xu 		goto error;
1723d094cd83SHerbert Xu 
1724d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1725d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
172672cb6962SHerbert Xu 	if (x->type == NULL)
172772cb6962SHerbert Xu 		goto error;
172872cb6962SHerbert Xu 
172972cb6962SHerbert Xu 	err = x->type->init_state(x);
173072cb6962SHerbert Xu 	if (err)
173172cb6962SHerbert Xu 		goto error;
173272cb6962SHerbert Xu 
1733b59f45d0SHerbert Xu 	x->mode = xfrm_get_mode(x->props.mode, family);
1734b59f45d0SHerbert Xu 	if (x->mode == NULL)
1735b59f45d0SHerbert Xu 		goto error;
1736b59f45d0SHerbert Xu 
173772cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
173872cb6962SHerbert Xu 
173972cb6962SHerbert Xu error:
174072cb6962SHerbert Xu 	return err;
174172cb6962SHerbert Xu }
174272cb6962SHerbert Xu 
174372cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
17441da177e4SLinus Torvalds 
17451da177e4SLinus Torvalds void __init xfrm_state_init(void)
17461da177e4SLinus Torvalds {
1747f034b5d4SDavid S. Miller 	unsigned int sz;
17481da177e4SLinus Torvalds 
1749f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
1750f034b5d4SDavid S. Miller 
175144e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
175244e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
175344e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
1754f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
1755f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
1756f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
1757f034b5d4SDavid S. Miller 
1758c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
17591da177e4SLinus Torvalds }
17601da177e4SLinus Torvalds 
1761