xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision cb969f07)
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>
211da177e4SLinus Torvalds #include <asm/uaccess.h>
221da177e4SLinus Torvalds 
23ee857a7dSDavid S. Miller struct sock *xfrm_nl;
24ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl);
25ee857a7dSDavid S. Miller 
26f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
27a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
28a70fcb0bSDavid S. Miller 
29f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
30a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
31a70fcb0bSDavid S. Miller 
321da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
351da177e4SLinus Torvalds    2. Hash table by daddr to find what SAs exist for given
361da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint
421da177e4SLinus Torvalds  * of tunnel or destination of transport mode) allowed by selector.
431da177e4SLinus Torvalds  *
441da177e4SLinus Torvalds  * Main use is finding SA after policy selected tunnel or transport mode.
451da177e4SLinus Torvalds  * Also, it can be used by ah/esp icmp error handler to find offending SA.
461da177e4SLinus Torvalds  */
471da177e4SLinus Torvalds static struct list_head xfrm_state_bydst[XFRM_DST_HSIZE];
481da177e4SLinus Torvalds static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
511da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
541da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
571da177e4SLinus Torvalds static struct list_head xfrm_state_gc_list = LIST_HEAD_INIT(xfrm_state_gc_list);
581da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds static int xfrm_state_gc_flush_bundles;
611da177e4SLinus Torvalds 
6253bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
651da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
661da177e4SLinus Torvalds 
67980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
6853bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	if (del_timer(&x->timer))
731da177e4SLinus Torvalds 		BUG();
74f8cd5488SJamal Hadi Salim 	if (del_timer(&x->rtimer))
75f8cd5488SJamal Hadi Salim 		BUG();
761da177e4SLinus Torvalds 	kfree(x->aalg);
771da177e4SLinus Torvalds 	kfree(x->ealg);
781da177e4SLinus Torvalds 	kfree(x->calg);
791da177e4SLinus Torvalds 	kfree(x->encap);
80b59f45d0SHerbert Xu 	if (x->mode)
81b59f45d0SHerbert Xu 		xfrm_put_mode(x->mode);
821da177e4SLinus Torvalds 	if (x->type) {
831da177e4SLinus Torvalds 		x->type->destructor(x);
841da177e4SLinus Torvalds 		xfrm_put_type(x->type);
851da177e4SLinus Torvalds 	}
86df71837dSTrent Jaeger 	security_xfrm_state_free(x);
871da177e4SLinus Torvalds 	kfree(x);
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds static void xfrm_state_gc_task(void *data)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds 	struct xfrm_state *x;
931da177e4SLinus Torvalds 	struct list_head *entry, *tmp;
941da177e4SLinus Torvalds 	struct list_head gc_list = LIST_HEAD_INIT(gc_list);
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	if (xfrm_state_gc_flush_bundles) {
971da177e4SLinus Torvalds 		xfrm_state_gc_flush_bundles = 0;
981da177e4SLinus Torvalds 		xfrm_flush_bundles();
991da177e4SLinus Torvalds 	}
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
1021da177e4SLinus Torvalds 	list_splice_init(&xfrm_state_gc_list, &gc_list);
1031da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	list_for_each_safe(entry, tmp, &gc_list) {
1061da177e4SLinus Torvalds 		x = list_entry(entry, struct xfrm_state, bydst);
1071da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds 	wake_up(&km_waitq);
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
1151da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
1161da177e4SLinus Torvalds 	else
1171da177e4SLinus Torvalds 	        return secs*HZ;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
1231da177e4SLinus Torvalds 	unsigned long now = (unsigned long)xtime.tv_sec;
1241da177e4SLinus Torvalds 	long next = LONG_MAX;
1251da177e4SLinus Torvalds 	int warn = 0;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	spin_lock(&x->lock);
1281da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
1291da177e4SLinus Torvalds 		goto out;
1301da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
1311da177e4SLinus Torvalds 		goto expired;
1321da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
1331da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
1341da177e4SLinus Torvalds 			x->curlft.add_time - now;
1351da177e4SLinus Torvalds 		if (tmo <= 0)
1361da177e4SLinus Torvalds 			goto expired;
1371da177e4SLinus Torvalds 		if (tmo < next)
1381da177e4SLinus Torvalds 			next = tmo;
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
1411da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
1421da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
1431da177e4SLinus Torvalds 		if (tmo <= 0)
1441da177e4SLinus Torvalds 			goto expired;
1451da177e4SLinus Torvalds 		if (tmo < next)
1461da177e4SLinus Torvalds 			next = tmo;
1471da177e4SLinus Torvalds 	}
1481da177e4SLinus Torvalds 	if (x->km.dying)
1491da177e4SLinus Torvalds 		goto resched;
1501da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
1511da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
1521da177e4SLinus Torvalds 			x->curlft.add_time - now;
1531da177e4SLinus Torvalds 		if (tmo <= 0)
1541da177e4SLinus Torvalds 			warn = 1;
1551da177e4SLinus Torvalds 		else if (tmo < next)
1561da177e4SLinus Torvalds 			next = tmo;
1571da177e4SLinus Torvalds 	}
1581da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
1591da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
1601da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
1611da177e4SLinus Torvalds 		if (tmo <= 0)
1621da177e4SLinus Torvalds 			warn = 1;
1631da177e4SLinus Torvalds 		else if (tmo < next)
1641da177e4SLinus Torvalds 			next = tmo;
1651da177e4SLinus Torvalds 	}
1661da177e4SLinus Torvalds 
1674666faabSHerbert Xu 	x->km.dying = warn;
1681da177e4SLinus Torvalds 	if (warn)
16953bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
1701da177e4SLinus Torvalds resched:
1711da177e4SLinus Torvalds 	if (next != LONG_MAX &&
1721da177e4SLinus Torvalds 	    !mod_timer(&x->timer, jiffies + make_jiffies(next)))
1731da177e4SLinus Torvalds 		xfrm_state_hold(x);
1741da177e4SLinus Torvalds 	goto out;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds expired:
1771da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
1781da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
1791da177e4SLinus Torvalds 		wake_up(&km_waitq);
1801da177e4SLinus Torvalds 		next = 2;
1811da177e4SLinus Torvalds 		goto resched;
1821da177e4SLinus Torvalds 	}
1834666faabSHerbert Xu 	if (!__xfrm_state_delete(x) && x->id.spi)
18453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds out:
1871da177e4SLinus Torvalds 	spin_unlock(&x->lock);
1881da177e4SLinus Torvalds 	xfrm_state_put(x);
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
1910ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
1920ac84752SDavid S. Miller 
1931da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds 	struct xfrm_state *x;
1961da177e4SLinus Torvalds 
1970da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	if (x) {
2001da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
2011da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
2021da177e4SLinus Torvalds 		INIT_LIST_HEAD(&x->bydst);
2031da177e4SLinus Torvalds 		INIT_LIST_HEAD(&x->byspi);
2041da177e4SLinus Torvalds 		init_timer(&x->timer);
2051da177e4SLinus Torvalds 		x->timer.function = xfrm_timer_handler;
2061da177e4SLinus Torvalds 		x->timer.data	  = (unsigned long)x;
207f8cd5488SJamal Hadi Salim 		init_timer(&x->rtimer);
208f8cd5488SJamal Hadi Salim 		x->rtimer.function = xfrm_replay_timer_handler;
209f8cd5488SJamal Hadi Salim 		x->rtimer.data     = (unsigned long)x;
2101da177e4SLinus Torvalds 		x->curlft.add_time = (unsigned long)xtime.tv_sec;
2111da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
2121da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
2131da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
2141da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
215f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
216f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
2171da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
2181da177e4SLinus Torvalds 	}
2191da177e4SLinus Torvalds 	return x;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
2241da177e4SLinus Torvalds {
2251da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
2281da177e4SLinus Torvalds 	list_add(&x->bydst, &xfrm_state_gc_list);
2291da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
2301da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
2331da177e4SLinus Torvalds 
23453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
2351da177e4SLinus Torvalds {
23626b15dadSJamal Hadi Salim 	int err = -ESRCH;
23726b15dadSJamal Hadi Salim 
2381da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
2391da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
2401da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
2411da177e4SLinus Torvalds 		list_del(&x->bydst);
24221380b81SHerbert Xu 		__xfrm_state_put(x);
2431da177e4SLinus Torvalds 		if (x->id.spi) {
2441da177e4SLinus Torvalds 			list_del(&x->byspi);
24521380b81SHerbert Xu 			__xfrm_state_put(x);
2461da177e4SLinus Torvalds 		}
2471da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
2481da177e4SLinus Torvalds 		if (del_timer(&x->timer))
24921380b81SHerbert Xu 			__xfrm_state_put(x);
250f8cd5488SJamal Hadi Salim 		if (del_timer(&x->rtimer))
251f8cd5488SJamal Hadi Salim 			__xfrm_state_put(x);
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 		/* The number two in this test is the reference
2541da177e4SLinus Torvalds 		 * mentioned in the comment below plus the reference
2551da177e4SLinus Torvalds 		 * our caller holds.  A larger value means that
2561da177e4SLinus Torvalds 		 * there are DSTs attached to this xfrm_state.
2571da177e4SLinus Torvalds 		 */
2581da177e4SLinus Torvalds 		if (atomic_read(&x->refcnt) > 2) {
2591da177e4SLinus Torvalds 			xfrm_state_gc_flush_bundles = 1;
2601da177e4SLinus Torvalds 			schedule_work(&xfrm_state_gc_work);
2611da177e4SLinus Torvalds 		}
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
2641da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
2651da177e4SLinus Torvalds 		 * is what we are dropping here.
2661da177e4SLinus Torvalds 		 */
26721380b81SHerbert Xu 		__xfrm_state_put(x);
26826b15dadSJamal Hadi Salim 		err = 0;
2691da177e4SLinus Torvalds 	}
2701da177e4SLinus Torvalds 
27126b15dadSJamal Hadi Salim 	return err;
27226b15dadSJamal Hadi Salim }
27353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
27426b15dadSJamal Hadi Salim 
27526b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
2761da177e4SLinus Torvalds {
27726b15dadSJamal Hadi Salim 	int err;
27826b15dadSJamal Hadi Salim 
2791da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
28026b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
2811da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
28226b15dadSJamal Hadi Salim 
28326b15dadSJamal Hadi Salim 	return err;
2841da177e4SLinus Torvalds }
2851da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds void xfrm_state_flush(u8 proto)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	int i;
2901da177e4SLinus Torvalds 	struct xfrm_state *x;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
2931da177e4SLinus Torvalds 	for (i = 0; i < XFRM_DST_HSIZE; i++) {
2941da177e4SLinus Torvalds restart:
2951da177e4SLinus Torvalds 		list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
2961da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
2971da177e4SLinus Torvalds 			    (proto == IPSEC_PROTO_ANY || x->id.proto == proto)) {
2981da177e4SLinus Torvalds 				xfrm_state_hold(x);
2991da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 				xfrm_state_delete(x);
3021da177e4SLinus Torvalds 				xfrm_state_put(x);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
3051da177e4SLinus Torvalds 				goto restart;
3061da177e4SLinus Torvalds 			}
3071da177e4SLinus Torvalds 		}
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
3101da177e4SLinus Torvalds 	wake_up(&km_waitq);
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds static int
3151da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
3161da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
3171da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
3181da177e4SLinus Torvalds 		  unsigned short family)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
3211da177e4SLinus Torvalds 	if (!afinfo)
3221da177e4SLinus Torvalds 		return -1;
3231da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
3241da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
3251da177e4SLinus Torvalds 	return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds struct xfrm_state *
3291da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
3301da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
3311da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
3321da177e4SLinus Torvalds 		unsigned short family)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds 	unsigned h = xfrm_dst_hash(daddr, family);
3351da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
3361da177e4SLinus Torvalds 	int acquire_in_progress = 0;
3371da177e4SLinus Torvalds 	int error = 0;
3381da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
3391da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	afinfo = xfrm_state_get_afinfo(family);
3421da177e4SLinus Torvalds 	if (afinfo == NULL) {
3431da177e4SLinus Torvalds 		*err = -EAFNOSUPPORT;
3441da177e4SLinus Torvalds 		return NULL;
3451da177e4SLinus Torvalds 	}
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
3481da177e4SLinus Torvalds 	list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
3491da177e4SLinus Torvalds 		if (x->props.family == family &&
3501da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
3511da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
3521da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
3531da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
3541da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
3551da177e4SLinus Torvalds 			/* Resolution logic:
3561da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
3571da177e4SLinus Torvalds 			      Done.
3581da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 			   Entering area of "sysdeps".
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
3631da177e4SLinus Torvalds 			      it selects only session which triggered
3641da177e4SLinus Torvalds 			      previous resolution. Key manager will do
3651da177e4SLinus Torvalds 			      something to install a state with proper
3661da177e4SLinus Torvalds 			      selector.
3671da177e4SLinus Torvalds 			 */
3681da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
369df71837dSTrent Jaeger 				if (!xfrm_selector_match(&x->sel, fl, family) ||
370e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
3711da177e4SLinus Torvalds 					continue;
3721da177e4SLinus Torvalds 				if (!best ||
3731da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
3741da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
3751da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
3761da177e4SLinus Torvalds 					best = x;
3771da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
3781da177e4SLinus Torvalds 				acquire_in_progress = 1;
3791da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
3801da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
381df71837dSTrent Jaeger  				if (xfrm_selector_match(&x->sel, fl, family) &&
382e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
3831da177e4SLinus Torvalds 					error = -ESRCH;
3841da177e4SLinus Torvalds 			}
3851da177e4SLinus Torvalds 		}
3861da177e4SLinus Torvalds 	}
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	x = best;
3891da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
3905c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
3915c5d281aSPatrick McHardy 		    (x0 = afinfo->state_lookup(daddr, tmpl->id.spi,
3925c5d281aSPatrick McHardy 		                               tmpl->id.proto)) != NULL) {
3931da177e4SLinus Torvalds 			xfrm_state_put(x0);
3941da177e4SLinus Torvalds 			error = -EEXIST;
3951da177e4SLinus Torvalds 			goto out;
3961da177e4SLinus Torvalds 		}
3971da177e4SLinus Torvalds 		x = xfrm_state_alloc();
3981da177e4SLinus Torvalds 		if (x == NULL) {
3991da177e4SLinus Torvalds 			error = -ENOMEM;
4001da177e4SLinus Torvalds 			goto out;
4011da177e4SLinus Torvalds 		}
4021da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
4031da177e4SLinus Torvalds 		 * to current session. */
4041da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
4051da177e4SLinus Torvalds 
406e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
407e0d1caa7SVenkat Yekkirala 		if (error) {
408e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
409e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
410e0d1caa7SVenkat Yekkirala 			x = NULL;
411e0d1caa7SVenkat Yekkirala 			goto out;
412e0d1caa7SVenkat Yekkirala 		}
413e0d1caa7SVenkat Yekkirala 
4141da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
4151da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
4161da177e4SLinus Torvalds 			list_add_tail(&x->bydst, xfrm_state_bydst+h);
4171da177e4SLinus Torvalds 			xfrm_state_hold(x);
4181da177e4SLinus Torvalds 			if (x->id.spi) {
4191da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
4201da177e4SLinus Torvalds 				list_add(&x->byspi, xfrm_state_byspi+h);
4211da177e4SLinus Torvalds 				xfrm_state_hold(x);
4221da177e4SLinus Torvalds 			}
4231da177e4SLinus Torvalds 			x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
4241da177e4SLinus Torvalds 			xfrm_state_hold(x);
4251da177e4SLinus Torvalds 			x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
4261da177e4SLinus Torvalds 			add_timer(&x->timer);
4271da177e4SLinus Torvalds 		} else {
4281da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
4291da177e4SLinus Torvalds 			xfrm_state_put(x);
4301da177e4SLinus Torvalds 			x = NULL;
4311da177e4SLinus Torvalds 			error = -ESRCH;
4321da177e4SLinus Torvalds 		}
4331da177e4SLinus Torvalds 	}
4341da177e4SLinus Torvalds out:
4351da177e4SLinus Torvalds 	if (x)
4361da177e4SLinus Torvalds 		xfrm_state_hold(x);
4371da177e4SLinus Torvalds 	else
4381da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
4391da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
4401da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
4411da177e4SLinus Torvalds 	return x;
4421da177e4SLinus Torvalds }
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
4451da177e4SLinus Torvalds {
4461da177e4SLinus Torvalds 	unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	list_add(&x->bydst, xfrm_state_bydst+h);
4491da177e4SLinus Torvalds 	xfrm_state_hold(x);
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 	h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	list_add(&x->byspi, xfrm_state_byspi+h);
4541da177e4SLinus Torvalds 	xfrm_state_hold(x);
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	if (!mod_timer(&x->timer, jiffies + HZ))
4571da177e4SLinus Torvalds 		xfrm_state_hold(x);
4581da177e4SLinus Torvalds 
459f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
460f8cd5488SJamal Hadi Salim 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
461f8cd5488SJamal Hadi Salim 		xfrm_state_hold(x);
462f8cd5488SJamal Hadi Salim 
4631da177e4SLinus Torvalds 	wake_up(&km_waitq);
4641da177e4SLinus Torvalds }
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
4671da177e4SLinus Torvalds {
4681da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
4691da177e4SLinus Torvalds 	__xfrm_state_insert(x);
4701da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
471399c180aSDavid S. Miller 
472399c180aSDavid S. Miller 	xfrm_flush_all_bundles();
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
4791da177e4SLinus Torvalds {
4801da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
4811da177e4SLinus Torvalds 	struct xfrm_state *x1;
4821da177e4SLinus Torvalds 	int family;
4831da177e4SLinus Torvalds 	int err;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	family = x->props.family;
4861da177e4SLinus Torvalds 	afinfo = xfrm_state_get_afinfo(family);
4871da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
4881da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
4931da177e4SLinus Torvalds 	if (x1) {
4941da177e4SLinus Torvalds 		xfrm_state_put(x1);
4951da177e4SLinus Torvalds 		x1 = NULL;
4961da177e4SLinus Torvalds 		err = -EEXIST;
4971da177e4SLinus Torvalds 		goto out;
4981da177e4SLinus Torvalds 	}
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	if (x->km.seq) {
5011da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
5021da177e4SLinus Torvalds 		if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
5031da177e4SLinus Torvalds 			xfrm_state_put(x1);
5041da177e4SLinus Torvalds 			x1 = NULL;
5051da177e4SLinus Torvalds 		}
5061da177e4SLinus Torvalds 	}
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	if (!x1)
5091da177e4SLinus Torvalds 		x1 = afinfo->find_acq(
5101da177e4SLinus Torvalds 			x->props.mode, x->props.reqid, x->id.proto,
5111da177e4SLinus Torvalds 			&x->id.daddr, &x->props.saddr, 0);
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	__xfrm_state_insert(x);
5141da177e4SLinus Torvalds 	err = 0;
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds out:
5171da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
5181da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
5191da177e4SLinus Torvalds 
520399c180aSDavid S. Miller 	if (!err)
521399c180aSDavid S. Miller 		xfrm_flush_all_bundles();
522399c180aSDavid S. Miller 
5231da177e4SLinus Torvalds 	if (x1) {
5241da177e4SLinus Torvalds 		xfrm_state_delete(x1);
5251da177e4SLinus Torvalds 		xfrm_state_put(x1);
5261da177e4SLinus Torvalds 	}
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 	return err;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
5331da177e4SLinus Torvalds {
5341da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
5351da177e4SLinus Torvalds 	struct xfrm_state *x1;
5361da177e4SLinus Torvalds 	int err;
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	afinfo = xfrm_state_get_afinfo(x->props.family);
5391da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
5401da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
5431da177e4SLinus Torvalds 	x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	err = -ESRCH;
5461da177e4SLinus Torvalds 	if (!x1)
5471da177e4SLinus Torvalds 		goto out;
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
5501da177e4SLinus Torvalds 		xfrm_state_put(x1);
5511da177e4SLinus Torvalds 		err = -EEXIST;
5521da177e4SLinus Torvalds 		goto out;
5531da177e4SLinus Torvalds 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
5561da177e4SLinus Torvalds 		__xfrm_state_insert(x);
5571da177e4SLinus Torvalds 		x = NULL;
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 	err = 0;
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds out:
5621da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
5631da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	if (err)
5661da177e4SLinus Torvalds 		return err;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	if (!x) {
5691da177e4SLinus Torvalds 		xfrm_state_delete(x1);
5701da177e4SLinus Torvalds 		xfrm_state_put(x1);
5711da177e4SLinus Torvalds 		return 0;
5721da177e4SLinus Torvalds 	}
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds 	err = -EINVAL;
5751da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
5761da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
5771da177e4SLinus Torvalds 		if (x->encap && x1->encap)
5781da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
5791da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
5801da177e4SLinus Torvalds 		x1->km.dying = 0;
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 		if (!mod_timer(&x1->timer, jiffies + HZ))
5831da177e4SLinus Torvalds 			xfrm_state_hold(x1);
5841da177e4SLinus Torvalds 		if (x1->curlft.use_time)
5851da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 		err = 0;
5881da177e4SLinus Torvalds 	}
5891da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds 	xfrm_state_put(x1);
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 	return err;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds 	if (!x->curlft.use_time)
6001da177e4SLinus Torvalds 		x->curlft.use_time = (unsigned long)xtime.tv_sec;
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
6031da177e4SLinus Torvalds 		return -EINVAL;
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
6061da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
6074666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
6084666faabSHerbert Xu 		if (!mod_timer(&x->timer, jiffies))
6091da177e4SLinus Torvalds 			xfrm_state_hold(x);
6101da177e4SLinus Torvalds 		return -EINVAL;
6111da177e4SLinus Torvalds 	}
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	if (!x->km.dying &&
6141da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
6154666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
6164666faabSHerbert Xu 		x->km.dying = 1;
61753bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
6184666faabSHerbert Xu 	}
6191da177e4SLinus Torvalds 	return 0;
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
6261da177e4SLinus Torvalds 		- skb_headroom(skb);
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	if (nhead > 0)
6291da177e4SLinus Torvalds 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	/* Check tail too... */
6321da177e4SLinus Torvalds 	return 0;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	int err = xfrm_state_check_expire(x);
6381da177e4SLinus Torvalds 	if (err < 0)
6391da177e4SLinus Torvalds 		goto err;
6401da177e4SLinus Torvalds 	err = xfrm_state_check_space(x, skb);
6411da177e4SLinus Torvalds err:
6421da177e4SLinus Torvalds 	return err;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check);
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds struct xfrm_state *
6471da177e4SLinus Torvalds xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
6481da177e4SLinus Torvalds 		  unsigned short family)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	struct xfrm_state *x;
6511da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6521da177e4SLinus Torvalds 	if (!afinfo)
6531da177e4SLinus Torvalds 		return NULL;
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6561da177e4SLinus Torvalds 	x = afinfo->state_lookup(daddr, spi, proto);
6571da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6581da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6591da177e4SLinus Torvalds 	return x;
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds struct xfrm_state *
6641da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
6651da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
6661da177e4SLinus Torvalds 	      int create, unsigned short family)
6671da177e4SLinus Torvalds {
6681da177e4SLinus Torvalds 	struct xfrm_state *x;
6691da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6701da177e4SLinus Torvalds 	if (!afinfo)
6711da177e4SLinus Torvalds 		return NULL;
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6741da177e4SLinus Torvalds 	x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
6751da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6761da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6771da177e4SLinus Torvalds 	return x;
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
6841da177e4SLinus Torvalds {
6851da177e4SLinus Torvalds 	int i;
6861da177e4SLinus Torvalds 	struct xfrm_state *x;
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 	for (i = 0; i < XFRM_DST_HSIZE; i++) {
6891da177e4SLinus Torvalds 		list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
6901da177e4SLinus Torvalds 			if (x->km.seq == seq && x->km.state == XFRM_STATE_ACQ) {
6911da177e4SLinus Torvalds 				xfrm_state_hold(x);
6921da177e4SLinus Torvalds 				return x;
6931da177e4SLinus Torvalds 			}
6941da177e4SLinus Torvalds 		}
6951da177e4SLinus Torvalds 	}
6961da177e4SLinus Torvalds 	return NULL;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
7001da177e4SLinus Torvalds {
7011da177e4SLinus Torvalds 	struct xfrm_state *x;
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7041da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
7051da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
7061da177e4SLinus Torvalds 	return x;
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
7111da177e4SLinus Torvalds {
7121da177e4SLinus Torvalds 	u32 res;
7131da177e4SLinus Torvalds 	static u32 acqseq;
7141da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
7171da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
7181da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
7191da177e4SLinus Torvalds 	return res;
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds void
7241da177e4SLinus Torvalds xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
7251da177e4SLinus Torvalds {
7261da177e4SLinus Torvalds 	u32 h;
7271da177e4SLinus Torvalds 	struct xfrm_state *x0;
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	if (x->id.spi)
7301da177e4SLinus Torvalds 		return;
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	if (minspi == maxspi) {
7331da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
7341da177e4SLinus Torvalds 		if (x0) {
7351da177e4SLinus Torvalds 			xfrm_state_put(x0);
7361da177e4SLinus Torvalds 			return;
7371da177e4SLinus Torvalds 		}
7381da177e4SLinus Torvalds 		x->id.spi = minspi;
7391da177e4SLinus Torvalds 	} else {
7401da177e4SLinus Torvalds 		u32 spi = 0;
7411da177e4SLinus Torvalds 		minspi = ntohl(minspi);
7421da177e4SLinus Torvalds 		maxspi = ntohl(maxspi);
7431da177e4SLinus Torvalds 		for (h=0; h<maxspi-minspi+1; h++) {
7441da177e4SLinus Torvalds 			spi = minspi + net_random()%(maxspi-minspi+1);
7451da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
7461da177e4SLinus Torvalds 			if (x0 == NULL) {
7471da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
7481da177e4SLinus Torvalds 				break;
7491da177e4SLinus Torvalds 			}
7501da177e4SLinus Torvalds 			xfrm_state_put(x0);
7511da177e4SLinus Torvalds 		}
7521da177e4SLinus Torvalds 	}
7531da177e4SLinus Torvalds 	if (x->id.spi) {
7541da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
7551da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
7561da177e4SLinus Torvalds 		list_add(&x->byspi, xfrm_state_byspi+h);
7571da177e4SLinus Torvalds 		xfrm_state_hold(x);
7581da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
7591da177e4SLinus Torvalds 		wake_up(&km_waitq);
7601da177e4SLinus Torvalds 	}
7611da177e4SLinus Torvalds }
7621da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
7651da177e4SLinus Torvalds 		    void *data)
7661da177e4SLinus Torvalds {
7671da177e4SLinus Torvalds 	int i;
7681da177e4SLinus Torvalds 	struct xfrm_state *x;
7691da177e4SLinus Torvalds 	int count = 0;
7701da177e4SLinus Torvalds 	int err = 0;
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7731da177e4SLinus Torvalds 	for (i = 0; i < XFRM_DST_HSIZE; i++) {
7741da177e4SLinus Torvalds 		list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
7751da177e4SLinus Torvalds 			if (proto == IPSEC_PROTO_ANY || x->id.proto == proto)
7761da177e4SLinus Torvalds 				count++;
7771da177e4SLinus Torvalds 		}
7781da177e4SLinus Torvalds 	}
7791da177e4SLinus Torvalds 	if (count == 0) {
7801da177e4SLinus Torvalds 		err = -ENOENT;
7811da177e4SLinus Torvalds 		goto out;
7821da177e4SLinus Torvalds 	}
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	for (i = 0; i < XFRM_DST_HSIZE; i++) {
7851da177e4SLinus Torvalds 		list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
7861da177e4SLinus Torvalds 			if (proto != IPSEC_PROTO_ANY && x->id.proto != proto)
7871da177e4SLinus Torvalds 				continue;
7881da177e4SLinus Torvalds 			err = func(x, --count, data);
7891da177e4SLinus Torvalds 			if (err)
7901da177e4SLinus Torvalds 				goto out;
7911da177e4SLinus Torvalds 		}
7921da177e4SLinus Torvalds 	}
7931da177e4SLinus Torvalds out:
7941da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
7951da177e4SLinus Torvalds 	return err;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
7981da177e4SLinus Torvalds 
799f8cd5488SJamal Hadi Salim 
800f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
801f8cd5488SJamal Hadi Salim {
802f8cd5488SJamal Hadi Salim 	struct km_event c;
803f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
804f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
805f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
806f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
807f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
808f8cd5488SJamal Hadi Salim 	 *     and there were changes
809f8cd5488SJamal Hadi Salim 	 *
810f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
811f8cd5488SJamal Hadi Salim 	 */
812f8cd5488SJamal Hadi Salim 
813f8cd5488SJamal Hadi Salim 	switch (event) {
814f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
815f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
816f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
8172717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
8182717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
8192717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
8202717096aSJamal Hadi Salim 			else
821f8cd5488SJamal Hadi Salim 				return;
8222717096aSJamal Hadi Salim 		}
823f8cd5488SJamal Hadi Salim 
824f8cd5488SJamal Hadi Salim 		break;
825f8cd5488SJamal Hadi Salim 
826f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
827f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
828f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
8292717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
8302717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
831f8cd5488SJamal Hadi Salim 			return;
8322717096aSJamal Hadi Salim 		}
833f8cd5488SJamal Hadi Salim 
834f8cd5488SJamal Hadi Salim 		break;
835f8cd5488SJamal Hadi Salim 	}
836f8cd5488SJamal Hadi Salim 
837f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
838f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
839f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
840f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
841f8cd5488SJamal Hadi Salim 
842f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
8432717096aSJamal Hadi Salim 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) {
844f8cd5488SJamal Hadi Salim 		xfrm_state_hold(x);
8452717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
8462717096aSJamal Hadi Salim 	}
847f8cd5488SJamal Hadi Salim }
848a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify);
849f8cd5488SJamal Hadi Salim 
850f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
851f8cd5488SJamal Hadi Salim {
852f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
853f8cd5488SJamal Hadi Salim 
854f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
855f8cd5488SJamal Hadi Salim 
8562717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
8572717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
858f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
8592717096aSJamal Hadi Salim 		else
8602717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
8612717096aSJamal Hadi Salim 	}
862f8cd5488SJamal Hadi Salim 
863f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
8642717096aSJamal Hadi Salim 	xfrm_state_put(x);
865f8cd5488SJamal Hadi Salim }
866f8cd5488SJamal Hadi Salim 
8671da177e4SLinus Torvalds int xfrm_replay_check(struct xfrm_state *x, u32 seq)
8681da177e4SLinus Torvalds {
8691da177e4SLinus Torvalds 	u32 diff;
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	seq = ntohl(seq);
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	if (unlikely(seq == 0))
8741da177e4SLinus Torvalds 		return -EINVAL;
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
8771da177e4SLinus Torvalds 		return 0;
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
8801da177e4SLinus Torvalds 	if (diff >= x->props.replay_window) {
8811da177e4SLinus Torvalds 		x->stats.replay_window++;
8821da177e4SLinus Torvalds 		return -EINVAL;
8831da177e4SLinus Torvalds 	}
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
8861da177e4SLinus Torvalds 		x->stats.replay++;
8871da177e4SLinus Torvalds 		return -EINVAL;
8881da177e4SLinus Torvalds 	}
8891da177e4SLinus Torvalds 	return 0;
8901da177e4SLinus Torvalds }
8911da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
8941da177e4SLinus Torvalds {
8951da177e4SLinus Torvalds 	u32 diff;
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	seq = ntohl(seq);
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
9001da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
9011da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
9021da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
9031da177e4SLinus Torvalds 		else
9041da177e4SLinus Torvalds 			x->replay.bitmap = 1;
9051da177e4SLinus Torvalds 		x->replay.seq = seq;
9061da177e4SLinus Torvalds 	} else {
9071da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
9081da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
9091da177e4SLinus Torvalds 	}
910f8cd5488SJamal Hadi Salim 
911f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
912f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
9171da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
9181da177e4SLinus Torvalds 
91926b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
9201da177e4SLinus Torvalds {
9211da177e4SLinus Torvalds 	struct xfrm_mgr *km;
9221da177e4SLinus Torvalds 
92326b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
92426b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
92526b15dadSJamal Hadi Salim 		if (km->notify_policy)
92626b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
92726b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
92826b15dadSJamal Hadi Salim }
92926b15dadSJamal Hadi Salim 
93026b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
93126b15dadSJamal Hadi Salim {
93226b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
93326b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
93426b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
93526b15dadSJamal Hadi Salim 		if (km->notify)
93626b15dadSJamal Hadi Salim 			km->notify(x, c);
93726b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
93826b15dadSJamal Hadi Salim }
93926b15dadSJamal Hadi Salim 
94026b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
94126b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
94226b15dadSJamal Hadi Salim 
94353bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
94426b15dadSJamal Hadi Salim {
94526b15dadSJamal Hadi Salim 	struct km_event c;
94626b15dadSJamal Hadi Salim 
947bf08867fSHerbert Xu 	c.data.hard = hard;
94853bc6b4dSJamal Hadi Salim 	c.pid = pid;
949f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
95026b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	if (hard)
9531da177e4SLinus Torvalds 		wake_up(&km_waitq);
9541da177e4SLinus Torvalds }
9551da177e4SLinus Torvalds 
95653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
95726b15dadSJamal Hadi Salim /*
95826b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
95926b15dadSJamal Hadi Salim  * We are happy with one success
96026b15dadSJamal Hadi Salim */
961980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
9621da177e4SLinus Torvalds {
96326b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
9641da177e4SLinus Torvalds 	struct xfrm_mgr *km;
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
9671da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
96826b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
96926b15dadSJamal Hadi Salim 		if (!acqret)
97026b15dadSJamal Hadi Salim 			err = acqret;
9711da177e4SLinus Torvalds 	}
9721da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
9731da177e4SLinus Torvalds 	return err;
9741da177e4SLinus Torvalds }
975980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
9761da177e4SLinus Torvalds 
9771da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds 	int err = -EINVAL;
9801da177e4SLinus Torvalds 	struct xfrm_mgr *km;
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
9831da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
9841da177e4SLinus Torvalds 		if (km->new_mapping)
9851da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
9861da177e4SLinus Torvalds 		if (!err)
9871da177e4SLinus Torvalds 			break;
9881da177e4SLinus Torvalds 	}
9891da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
9901da177e4SLinus Torvalds 	return err;
9911da177e4SLinus Torvalds }
9921da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
9931da177e4SLinus Torvalds 
9946c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
9951da177e4SLinus Torvalds {
99626b15dadSJamal Hadi Salim 	struct km_event c;
9971da177e4SLinus Torvalds 
998bf08867fSHerbert Xu 	c.data.hard = hard;
9996c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1000f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
100126b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	if (hard)
10041da177e4SLinus Torvalds 		wake_up(&km_waitq);
10051da177e4SLinus Torvalds }
1006a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
10091da177e4SLinus Torvalds {
10101da177e4SLinus Torvalds 	int err;
10111da177e4SLinus Torvalds 	u8 *data;
10121da177e4SLinus Torvalds 	struct xfrm_mgr *km;
10131da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
10161da177e4SLinus Torvalds 		return -EMSGSIZE;
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
10191da177e4SLinus Torvalds 	if (!data)
10201da177e4SLinus Torvalds 		return -ENOMEM;
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	err = -EFAULT;
10231da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
10241da177e4SLinus Torvalds 		goto out;
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 	err = -EINVAL;
10271da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
10281da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1029cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
10301da177e4SLinus Torvalds 					 optlen, &err);
10311da177e4SLinus Torvalds 		if (err >= 0)
10321da177e4SLinus Torvalds 			break;
10331da177e4SLinus Torvalds 	}
10341da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	if (err >= 0) {
10371da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
10381da177e4SLinus Torvalds 		xfrm_pol_put(pol);
10391da177e4SLinus Torvalds 		err = 0;
10401da177e4SLinus Torvalds 	}
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds out:
10431da177e4SLinus Torvalds 	kfree(data);
10441da177e4SLinus Torvalds 	return err;
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
10491da177e4SLinus Torvalds {
10501da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
10511da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
10521da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
10531da177e4SLinus Torvalds 	return 0;
10541da177e4SLinus Torvalds }
10551da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
10581da177e4SLinus Torvalds {
10591da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
10601da177e4SLinus Torvalds 	list_del(&km->list);
10611da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
10621da177e4SLinus Torvalds 	return 0;
10631da177e4SLinus Torvalds }
10641da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
10671da177e4SLinus Torvalds {
10681da177e4SLinus Torvalds 	int err = 0;
10691da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
10701da177e4SLinus Torvalds 		return -EINVAL;
10711da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
10721da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1073f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
10741da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
10751da177e4SLinus Torvalds 		err = -ENOBUFS;
10761da177e4SLinus Torvalds 	else {
10771da177e4SLinus Torvalds 		afinfo->state_bydst = xfrm_state_bydst;
10781da177e4SLinus Torvalds 		afinfo->state_byspi = xfrm_state_byspi;
10791da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
10801da177e4SLinus Torvalds 	}
1081f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
10821da177e4SLinus Torvalds 	return err;
10831da177e4SLinus Torvalds }
10841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
10871da177e4SLinus Torvalds {
10881da177e4SLinus Torvalds 	int err = 0;
10891da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
10901da177e4SLinus Torvalds 		return -EINVAL;
10911da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
10921da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1093f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
10941da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
10951da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
10961da177e4SLinus Torvalds 			err = -EINVAL;
10971da177e4SLinus Torvalds 		else {
10981da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
10991da177e4SLinus Torvalds 			afinfo->state_byspi = NULL;
11001da177e4SLinus Torvalds 			afinfo->state_bydst = NULL;
11011da177e4SLinus Torvalds 		}
11021da177e4SLinus Torvalds 	}
1103f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
11041da177e4SLinus Torvalds 	return err;
11051da177e4SLinus Torvalds }
11061da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
11091da177e4SLinus Torvalds {
11101da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
11111da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
11121da177e4SLinus Torvalds 		return NULL;
11131da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
11141da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1115546be240SHerbert Xu 	if (unlikely(!afinfo))
11161da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
11171da177e4SLinus Torvalds 	return afinfo;
11181da177e4SLinus Torvalds }
11191da177e4SLinus Torvalds 
11201da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
11211da177e4SLinus Torvalds {
1122546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
11231da177e4SLinus Torvalds }
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
11261da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
11271da177e4SLinus Torvalds {
11281da177e4SLinus Torvalds 	if (x->tunnel) {
11291da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
11321da177e4SLinus Torvalds 			xfrm_state_delete(t);
11331da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
11341da177e4SLinus Torvalds 		xfrm_state_put(t);
11351da177e4SLinus Torvalds 		x->tunnel = NULL;
11361da177e4SLinus Torvalds 	}
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
11391da177e4SLinus Torvalds 
114080b30c10SHerbert Xu /*
114180b30c10SHerbert Xu  * This function is NOT optimal.  For example, with ESP it will give an
114280b30c10SHerbert Xu  * MTU that's usually two bytes short of being optimal.  However, it will
114380b30c10SHerbert Xu  * usually give an answer that's a multiple of 4 provided the input is
114480b30c10SHerbert Xu  * also a multiple of 4.
114580b30c10SHerbert Xu  */
11461da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
11471da177e4SLinus Torvalds {
11481da177e4SLinus Torvalds 	int res = mtu;
11491da177e4SLinus Torvalds 
11501da177e4SLinus Torvalds 	res -= x->props.header_len;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	for (;;) {
11531da177e4SLinus Torvalds 		int m = res;
11541da177e4SLinus Torvalds 
11551da177e4SLinus Torvalds 		if (m < 68)
11561da177e4SLinus Torvalds 			return 68;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 		spin_lock_bh(&x->lock);
11591da177e4SLinus Torvalds 		if (x->km.state == XFRM_STATE_VALID &&
11601da177e4SLinus Torvalds 		    x->type && x->type->get_max_size)
11611da177e4SLinus Torvalds 			m = x->type->get_max_size(x, m);
11621da177e4SLinus Torvalds 		else
11631da177e4SLinus Torvalds 			m += x->props.header_len;
11641da177e4SLinus Torvalds 		spin_unlock_bh(&x->lock);
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 		if (m <= mtu)
11671da177e4SLinus Torvalds 			break;
11681da177e4SLinus Torvalds 		res -= (m - mtu);
11691da177e4SLinus Torvalds 	}
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 	return res;
11721da177e4SLinus Torvalds }
11731da177e4SLinus Torvalds 
117472cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
117572cb6962SHerbert Xu {
1176d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1177d094cd83SHerbert Xu 	int family = x->props.family;
117872cb6962SHerbert Xu 	int err;
117972cb6962SHerbert Xu 
1180d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1181d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1182d094cd83SHerbert Xu 	if (!afinfo)
1183d094cd83SHerbert Xu 		goto error;
1184d094cd83SHerbert Xu 
1185d094cd83SHerbert Xu 	err = 0;
1186d094cd83SHerbert Xu 	if (afinfo->init_flags)
1187d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1188d094cd83SHerbert Xu 
1189d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1190d094cd83SHerbert Xu 
1191d094cd83SHerbert Xu 	if (err)
1192d094cd83SHerbert Xu 		goto error;
1193d094cd83SHerbert Xu 
1194d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1195d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
119672cb6962SHerbert Xu 	if (x->type == NULL)
119772cb6962SHerbert Xu 		goto error;
119872cb6962SHerbert Xu 
119972cb6962SHerbert Xu 	err = x->type->init_state(x);
120072cb6962SHerbert Xu 	if (err)
120172cb6962SHerbert Xu 		goto error;
120272cb6962SHerbert Xu 
1203b59f45d0SHerbert Xu 	x->mode = xfrm_get_mode(x->props.mode, family);
1204b59f45d0SHerbert Xu 	if (x->mode == NULL)
1205b59f45d0SHerbert Xu 		goto error;
1206b59f45d0SHerbert Xu 
120772cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
120872cb6962SHerbert Xu 
120972cb6962SHerbert Xu error:
121072cb6962SHerbert Xu 	return err;
121172cb6962SHerbert Xu }
121272cb6962SHerbert Xu 
121372cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds void __init xfrm_state_init(void)
12161da177e4SLinus Torvalds {
12171da177e4SLinus Torvalds 	int i;
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 	for (i=0; i<XFRM_DST_HSIZE; i++) {
12201da177e4SLinus Torvalds 		INIT_LIST_HEAD(&xfrm_state_bydst[i]);
12211da177e4SLinus Torvalds 		INIT_LIST_HEAD(&xfrm_state_byspi[i]);
12221da177e4SLinus Torvalds 	}
12231da177e4SLinus Torvalds 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
12241da177e4SLinus Torvalds }
12251da177e4SLinus Torvalds 
1226