xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision b754a4fd)
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>
2268277accSPaul Moore #include <linux/audit.h>
23b5890d8bSJesper Juhl #include <asm/uaccess.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 
3001e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
31a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32a70fcb0bSDavid S. Miller 
3301e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
34a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35a70fcb0bSDavid S. Miller 
3601e67d08SDavid S. Miller u32 sysctl_xfrm_acq_expires __read_mostly = 30;
3701e67d08SDavid S. Miller 
381da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
41a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
421da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
461da177e4SLinus Torvalds 
47f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
48f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
49f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
509d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
511da177e4SLinus Torvalds 
5217c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
5317c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
5417c2a42aSHerbert Xu 
55afeb14b4SPaul Moore #ifdef CONFIG_AUDITSYSCALL
56afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
57afeb14b4SPaul Moore 				    struct sk_buff *skb, __be32 net_seq);
58afeb14b4SPaul Moore #else
59afeb14b4SPaul Moore #define xfrm_audit_state_replay(x, s, sq)	do { ; } while (0)
60afeb14b4SPaul Moore #endif /* CONFIG_AUDITSYSCALL */
61afeb14b4SPaul Moore 
62c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
63c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
64c1969f29SDavid S. Miller 					 u32 reqid,
65a624c108SDavid S. Miller 					 unsigned short family)
66a624c108SDavid S. Miller {
67c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
68a624c108SDavid S. Miller }
69a624c108SDavid S. Miller 
70667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
71667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
7244e36b42SDavid S. Miller 					 unsigned short family)
73f034b5d4SDavid S. Miller {
74667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
75f034b5d4SDavid S. Miller }
76f034b5d4SDavid S. Miller 
772575b654SDavid S. Miller static inline unsigned int
788122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
79f034b5d4SDavid S. Miller {
80c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
81f034b5d4SDavid S. Miller }
82f034b5d4SDavid S. Miller 
83f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
84f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
85f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
86f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
87f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
88f034b5d4SDavid S. Miller {
89f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
90f034b5d4SDavid S. Miller 	struct xfrm_state *x;
91f034b5d4SDavid S. Miller 
92f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
93f034b5d4SDavid S. Miller 		unsigned int h;
94f034b5d4SDavid S. Miller 
95c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
96c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
97c1969f29SDavid S. Miller 				    nhashmask);
98f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
99f034b5d4SDavid S. Miller 
100667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
101667bbcb6SMasahide NAKAMURA 				    x->props.family,
102f034b5d4SDavid S. Miller 				    nhashmask);
103f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
104f034b5d4SDavid S. Miller 
1057b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1067b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1077b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1087b4dc360SMasahide NAKAMURA 					    nhashmask);
109f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
110f034b5d4SDavid S. Miller 		}
111f034b5d4SDavid S. Miller 	}
1127b4dc360SMasahide NAKAMURA }
113f034b5d4SDavid S. Miller 
114f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
115f034b5d4SDavid S. Miller {
116f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
117f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
118f034b5d4SDavid S. Miller }
119f034b5d4SDavid S. Miller 
120f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
121f034b5d4SDavid S. Miller 
122c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
123f034b5d4SDavid S. Miller {
124f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
125f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
126f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
127f034b5d4SDavid S. Miller 	int i;
128f034b5d4SDavid S. Miller 
129f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
130f034b5d4SDavid S. Miller 
131f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
13244e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
133f034b5d4SDavid S. Miller 	if (!ndst)
134f034b5d4SDavid S. Miller 		goto out_unlock;
13544e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
136f034b5d4SDavid S. Miller 	if (!nsrc) {
13744e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
138f034b5d4SDavid S. Miller 		goto out_unlock;
139f034b5d4SDavid S. Miller 	}
14044e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
141f034b5d4SDavid S. Miller 	if (!nspi) {
14244e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
14344e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
144f034b5d4SDavid S. Miller 		goto out_unlock;
145f034b5d4SDavid S. Miller 	}
146f034b5d4SDavid S. Miller 
147f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
148f034b5d4SDavid S. Miller 
149f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
150f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
15173d189dcSAlexey Dobriyan 		xfrm_hash_transfer(init_net.xfrm.state_bydst+i, ndst, nsrc, nspi,
152f034b5d4SDavid S. Miller 				   nhashmask);
153f034b5d4SDavid S. Miller 
15473d189dcSAlexey Dobriyan 	odst = init_net.xfrm.state_bydst;
155d320bbb3SAlexey Dobriyan 	osrc = init_net.xfrm.state_bysrc;
156b754a4fdSAlexey Dobriyan 	ospi = init_net.xfrm.state_byspi;
157f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
158f034b5d4SDavid S. Miller 
15973d189dcSAlexey Dobriyan 	init_net.xfrm.state_bydst = ndst;
160d320bbb3SAlexey Dobriyan 	init_net.xfrm.state_bysrc = nsrc;
161b754a4fdSAlexey Dobriyan 	init_net.xfrm.state_byspi = nspi;
162f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
163f034b5d4SDavid S. Miller 
164f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
165f034b5d4SDavid S. Miller 
166f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
16744e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
16844e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
16944e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
170f034b5d4SDavid S. Miller 
171f034b5d4SDavid S. Miller out_unlock:
172f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
173f034b5d4SDavid S. Miller }
174f034b5d4SDavid S. Miller 
175c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
176f034b5d4SDavid S. Miller 
1771da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1781da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1811da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
18412a169e7SHerbert Xu static HLIST_HEAD(xfrm_state_gc_list);
1851da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1861da177e4SLinus Torvalds 
18753bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1881da177e4SLinus Torvalds 
189980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
19053bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
1911da177e4SLinus Torvalds 
192aa5d62ccSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
193aa5d62ccSHerbert Xu {
194aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
195aa5d62ccSHerbert Xu 	if (unlikely(family >= NPROTO))
196aa5d62ccSHerbert Xu 		return NULL;
197aa5d62ccSHerbert Xu 	write_lock_bh(&xfrm_state_afinfo_lock);
198aa5d62ccSHerbert Xu 	afinfo = xfrm_state_afinfo[family];
199aa5d62ccSHerbert Xu 	if (unlikely(!afinfo))
200aa5d62ccSHerbert Xu 		write_unlock_bh(&xfrm_state_afinfo_lock);
201aa5d62ccSHerbert Xu 	return afinfo;
202aa5d62ccSHerbert Xu }
203aa5d62ccSHerbert Xu 
204aa5d62ccSHerbert Xu static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
2059a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
206aa5d62ccSHerbert Xu {
207aa5d62ccSHerbert Xu 	write_unlock_bh(&xfrm_state_afinfo_lock);
208aa5d62ccSHerbert Xu }
209aa5d62ccSHerbert Xu 
210533cb5b0SEric Dumazet int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
211aa5d62ccSHerbert Xu {
212aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
213533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
214aa5d62ccSHerbert Xu 	int err = 0;
215aa5d62ccSHerbert Xu 
216aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
217aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
218aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
219aa5d62ccSHerbert Xu 
220aa5d62ccSHerbert Xu 	if (likely(typemap[type->proto] == NULL))
221aa5d62ccSHerbert Xu 		typemap[type->proto] = type;
222aa5d62ccSHerbert Xu 	else
223aa5d62ccSHerbert Xu 		err = -EEXIST;
224aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
225aa5d62ccSHerbert Xu 	return err;
226aa5d62ccSHerbert Xu }
227aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_type);
228aa5d62ccSHerbert Xu 
229533cb5b0SEric Dumazet int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
230aa5d62ccSHerbert Xu {
231aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
232533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
233aa5d62ccSHerbert Xu 	int err = 0;
234aa5d62ccSHerbert Xu 
235aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
236aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
237aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
238aa5d62ccSHerbert Xu 
239aa5d62ccSHerbert Xu 	if (unlikely(typemap[type->proto] != type))
240aa5d62ccSHerbert Xu 		err = -ENOENT;
241aa5d62ccSHerbert Xu 	else
242aa5d62ccSHerbert Xu 		typemap[type->proto] = NULL;
243aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
244aa5d62ccSHerbert Xu 	return err;
245aa5d62ccSHerbert Xu }
246aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_type);
247aa5d62ccSHerbert Xu 
248533cb5b0SEric Dumazet static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
249aa5d62ccSHerbert Xu {
250aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
251533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
252533cb5b0SEric Dumazet 	const struct xfrm_type *type;
253aa5d62ccSHerbert Xu 	int modload_attempted = 0;
254aa5d62ccSHerbert Xu 
255aa5d62ccSHerbert Xu retry:
256aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
257aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
258aa5d62ccSHerbert Xu 		return NULL;
259aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
260aa5d62ccSHerbert Xu 
261aa5d62ccSHerbert Xu 	type = typemap[proto];
262aa5d62ccSHerbert Xu 	if (unlikely(type && !try_module_get(type->owner)))
263aa5d62ccSHerbert Xu 		type = NULL;
264aa5d62ccSHerbert Xu 	if (!type && !modload_attempted) {
265aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
266aa5d62ccSHerbert Xu 		request_module("xfrm-type-%d-%d", family, proto);
267aa5d62ccSHerbert Xu 		modload_attempted = 1;
268aa5d62ccSHerbert Xu 		goto retry;
269aa5d62ccSHerbert Xu 	}
270aa5d62ccSHerbert Xu 
271aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
272aa5d62ccSHerbert Xu 	return type;
273aa5d62ccSHerbert Xu }
274aa5d62ccSHerbert Xu 
275533cb5b0SEric Dumazet static void xfrm_put_type(const struct xfrm_type *type)
276aa5d62ccSHerbert Xu {
277aa5d62ccSHerbert Xu 	module_put(type->owner);
278aa5d62ccSHerbert Xu }
279aa5d62ccSHerbert Xu 
280aa5d62ccSHerbert Xu int xfrm_register_mode(struct xfrm_mode *mode, int family)
281aa5d62ccSHerbert Xu {
282aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
283aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
284aa5d62ccSHerbert Xu 	int err;
285aa5d62ccSHerbert Xu 
286aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
287aa5d62ccSHerbert Xu 		return -EINVAL;
288aa5d62ccSHerbert Xu 
289aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
290aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
291aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
292aa5d62ccSHerbert Xu 
293aa5d62ccSHerbert Xu 	err = -EEXIST;
294aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
29517c2a42aSHerbert Xu 	if (modemap[mode->encap])
29617c2a42aSHerbert Xu 		goto out;
29717c2a42aSHerbert Xu 
29817c2a42aSHerbert Xu 	err = -ENOENT;
29917c2a42aSHerbert Xu 	if (!try_module_get(afinfo->owner))
30017c2a42aSHerbert Xu 		goto out;
30117c2a42aSHerbert Xu 
30217c2a42aSHerbert Xu 	mode->afinfo = afinfo;
303aa5d62ccSHerbert Xu 	modemap[mode->encap] = mode;
304aa5d62ccSHerbert Xu 	err = 0;
305aa5d62ccSHerbert Xu 
30617c2a42aSHerbert Xu out:
307aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
308aa5d62ccSHerbert Xu 	return err;
309aa5d62ccSHerbert Xu }
310aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_mode);
311aa5d62ccSHerbert Xu 
312aa5d62ccSHerbert Xu int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
313aa5d62ccSHerbert Xu {
314aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
315aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
316aa5d62ccSHerbert Xu 	int err;
317aa5d62ccSHerbert Xu 
318aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
319aa5d62ccSHerbert Xu 		return -EINVAL;
320aa5d62ccSHerbert Xu 
321aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
322aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
323aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
324aa5d62ccSHerbert Xu 
325aa5d62ccSHerbert Xu 	err = -ENOENT;
326aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
327aa5d62ccSHerbert Xu 	if (likely(modemap[mode->encap] == mode)) {
328aa5d62ccSHerbert Xu 		modemap[mode->encap] = NULL;
32917c2a42aSHerbert Xu 		module_put(mode->afinfo->owner);
330aa5d62ccSHerbert Xu 		err = 0;
331aa5d62ccSHerbert Xu 	}
332aa5d62ccSHerbert Xu 
333aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
334aa5d62ccSHerbert Xu 	return err;
335aa5d62ccSHerbert Xu }
336aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_mode);
337aa5d62ccSHerbert Xu 
338aa5d62ccSHerbert Xu static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
339aa5d62ccSHerbert Xu {
340aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
341aa5d62ccSHerbert Xu 	struct xfrm_mode *mode;
342aa5d62ccSHerbert Xu 	int modload_attempted = 0;
343aa5d62ccSHerbert Xu 
344aa5d62ccSHerbert Xu 	if (unlikely(encap >= XFRM_MODE_MAX))
345aa5d62ccSHerbert Xu 		return NULL;
346aa5d62ccSHerbert Xu 
347aa5d62ccSHerbert Xu retry:
348aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
349aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
350aa5d62ccSHerbert Xu 		return NULL;
351aa5d62ccSHerbert Xu 
352aa5d62ccSHerbert Xu 	mode = afinfo->mode_map[encap];
353aa5d62ccSHerbert Xu 	if (unlikely(mode && !try_module_get(mode->owner)))
354aa5d62ccSHerbert Xu 		mode = NULL;
355aa5d62ccSHerbert Xu 	if (!mode && !modload_attempted) {
356aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
357aa5d62ccSHerbert Xu 		request_module("xfrm-mode-%d-%d", family, encap);
358aa5d62ccSHerbert Xu 		modload_attempted = 1;
359aa5d62ccSHerbert Xu 		goto retry;
360aa5d62ccSHerbert Xu 	}
361aa5d62ccSHerbert Xu 
362aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
363aa5d62ccSHerbert Xu 	return mode;
364aa5d62ccSHerbert Xu }
365aa5d62ccSHerbert Xu 
366aa5d62ccSHerbert Xu static void xfrm_put_mode(struct xfrm_mode *mode)
367aa5d62ccSHerbert Xu {
368aa5d62ccSHerbert Xu 	module_put(mode->owner);
369aa5d62ccSHerbert Xu }
370aa5d62ccSHerbert Xu 
3711da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
3721da177e4SLinus Torvalds {
373a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
374a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
3751da177e4SLinus Torvalds 	kfree(x->aalg);
3761da177e4SLinus Torvalds 	kfree(x->ealg);
3771da177e4SLinus Torvalds 	kfree(x->calg);
3781da177e4SLinus Torvalds 	kfree(x->encap);
379060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
38013996378SHerbert Xu 	if (x->inner_mode)
38113996378SHerbert Xu 		xfrm_put_mode(x->inner_mode);
382df9dcb45SKazunori MIYAZAWA 	if (x->inner_mode_iaf)
383df9dcb45SKazunori MIYAZAWA 		xfrm_put_mode(x->inner_mode_iaf);
38413996378SHerbert Xu 	if (x->outer_mode)
38513996378SHerbert Xu 		xfrm_put_mode(x->outer_mode);
3861da177e4SLinus Torvalds 	if (x->type) {
3871da177e4SLinus Torvalds 		x->type->destructor(x);
3881da177e4SLinus Torvalds 		xfrm_put_type(x->type);
3891da177e4SLinus Torvalds 	}
390df71837dSTrent Jaeger 	security_xfrm_state_free(x);
3911da177e4SLinus Torvalds 	kfree(x);
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
394c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
3951da177e4SLinus Torvalds {
39612a169e7SHerbert Xu 	struct xfrm_state *x;
39712a169e7SHerbert Xu 	struct hlist_node *entry, *tmp;
39812a169e7SHerbert Xu 	struct hlist_head gc_list;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
40112a169e7SHerbert Xu 	hlist_move_list(&xfrm_state_gc_list, &gc_list);
4021da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
4031da177e4SLinus Torvalds 
40412a169e7SHerbert Xu 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
4051da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
4068f126e37SDavid S. Miller 
4071da177e4SLinus Torvalds 	wake_up(&km_waitq);
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
4111da177e4SLinus Torvalds {
4121da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
4131da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
4141da177e4SLinus Torvalds 	else
4151da177e4SLinus Torvalds 		return secs*HZ;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
4219d729f72SJames Morris 	unsigned long now = get_seconds();
4221da177e4SLinus Torvalds 	long next = LONG_MAX;
4231da177e4SLinus Torvalds 	int warn = 0;
424161a09e7SJoy Latten 	int err = 0;
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	spin_lock(&x->lock);
4271da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
4281da177e4SLinus Torvalds 		goto out;
4291da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
4301da177e4SLinus Torvalds 		goto expired;
4311da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
4321da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
4331da177e4SLinus Torvalds 			x->curlft.add_time - now;
4341da177e4SLinus Torvalds 		if (tmo <= 0)
4351da177e4SLinus Torvalds 			goto expired;
4361da177e4SLinus Torvalds 		if (tmo < next)
4371da177e4SLinus Torvalds 			next = tmo;
4381da177e4SLinus Torvalds 	}
4391da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
4401da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
4411da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4421da177e4SLinus Torvalds 		if (tmo <= 0)
4431da177e4SLinus Torvalds 			goto expired;
4441da177e4SLinus Torvalds 		if (tmo < next)
4451da177e4SLinus Torvalds 			next = tmo;
4461da177e4SLinus Torvalds 	}
4471da177e4SLinus Torvalds 	if (x->km.dying)
4481da177e4SLinus Torvalds 		goto resched;
4491da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
4501da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
4511da177e4SLinus Torvalds 			x->curlft.add_time - now;
4521da177e4SLinus Torvalds 		if (tmo <= 0)
4531da177e4SLinus Torvalds 			warn = 1;
4541da177e4SLinus Torvalds 		else if (tmo < next)
4551da177e4SLinus Torvalds 			next = tmo;
4561da177e4SLinus Torvalds 	}
4571da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
4581da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
4591da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4601da177e4SLinus Torvalds 		if (tmo <= 0)
4611da177e4SLinus Torvalds 			warn = 1;
4621da177e4SLinus Torvalds 		else if (tmo < next)
4631da177e4SLinus Torvalds 			next = tmo;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
4664666faabSHerbert Xu 	x->km.dying = warn;
4671da177e4SLinus Torvalds 	if (warn)
46853bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
4691da177e4SLinus Torvalds resched:
470a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
471a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
472a47f0ce0SDavid S. Miller 
4731da177e4SLinus Torvalds 	goto out;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds expired:
4761da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
4771da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
4781da177e4SLinus Torvalds 		wake_up(&km_waitq);
4791da177e4SLinus Torvalds 		next = 2;
4801da177e4SLinus Torvalds 		goto resched;
4811da177e4SLinus Torvalds 	}
482161a09e7SJoy Latten 
483161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
484161a09e7SJoy Latten 	if (!err && x->id.spi)
48553bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
4861da177e4SLinus Torvalds 
487ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
4882532386fSEric Paris 				audit_get_loginuid(current),
4892532386fSEric Paris 				audit_get_sessionid(current), 0);
490161a09e7SJoy Latten 
4911da177e4SLinus Torvalds out:
4921da177e4SLinus Torvalds 	spin_unlock(&x->lock);
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
4950ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
4960ac84752SDavid S. Miller 
497673c09beSAlexey Dobriyan struct xfrm_state *xfrm_state_alloc(struct net *net)
4981da177e4SLinus Torvalds {
4991da177e4SLinus Torvalds 	struct xfrm_state *x;
5001da177e4SLinus Torvalds 
5010da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	if (x) {
504673c09beSAlexey Dobriyan 		write_pnet(&x->xs_net, net);
5051da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
5061da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
50712a169e7SHerbert Xu 		INIT_LIST_HEAD(&x->km.all);
5088f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
5098f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
5108f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
511b24b8a24SPavel Emelyanov 		setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
512b24b8a24SPavel Emelyanov 		setup_timer(&x->rtimer, xfrm_replay_timer_handler,
513b24b8a24SPavel Emelyanov 				(unsigned long)x);
5149d729f72SJames Morris 		x->curlft.add_time = get_seconds();
5151da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
5161da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
5171da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
5181da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
519f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
520f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
521df9dcb45SKazunori MIYAZAWA 		x->inner_mode = NULL;
522df9dcb45SKazunori MIYAZAWA 		x->inner_mode_iaf = NULL;
5231da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 	return x;
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
5301da177e4SLinus Torvalds {
531547b792cSIlpo Järvinen 	WARN_ON(x->km.state != XFRM_STATE_DEAD);
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
53412a169e7SHerbert Xu 	hlist_add_head(&x->gclist, &xfrm_state_gc_list);
5351da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
5361da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
5391da177e4SLinus Torvalds 
54053bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
5411da177e4SLinus Torvalds {
54226b15dadSJamal Hadi Salim 	int err = -ESRCH;
54326b15dadSJamal Hadi Salim 
5441da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
5451da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
5461da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
54712a169e7SHerbert Xu 		list_del(&x->km.all);
5488f126e37SDavid S. Miller 		hlist_del(&x->bydst);
5498f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
550a47f0ce0SDavid S. Miller 		if (x->id.spi)
5518f126e37SDavid S. Miller 			hlist_del(&x->byspi);
552f034b5d4SDavid S. Miller 		xfrm_state_num--;
5531da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
5561da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
5571da177e4SLinus Torvalds 		 * is what we are dropping here.
5581da177e4SLinus Torvalds 		 */
5595dba4797SPatrick McHardy 		xfrm_state_put(x);
56026b15dadSJamal Hadi Salim 		err = 0;
5611da177e4SLinus Torvalds 	}
5621da177e4SLinus Torvalds 
56326b15dadSJamal Hadi Salim 	return err;
56426b15dadSJamal Hadi Salim }
56553bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
56626b15dadSJamal Hadi Salim 
56726b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
5681da177e4SLinus Torvalds {
56926b15dadSJamal Hadi Salim 	int err;
57026b15dadSJamal Hadi Salim 
5711da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
57226b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
5731da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
57426b15dadSJamal Hadi Salim 
57526b15dadSJamal Hadi Salim 	return err;
5761da177e4SLinus Torvalds }
5771da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
5781da177e4SLinus Torvalds 
5794aa2e62cSJoy Latten #ifdef CONFIG_SECURITY_NETWORK_XFRM
5804aa2e62cSJoy Latten static inline int
5814aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
5821da177e4SLinus Torvalds {
5834aa2e62cSJoy Latten 	int i, err = 0;
5844aa2e62cSJoy Latten 
5854aa2e62cSJoy Latten 	for (i = 0; i <= xfrm_state_hmask; i++) {
5864aa2e62cSJoy Latten 		struct hlist_node *entry;
5874aa2e62cSJoy Latten 		struct xfrm_state *x;
5884aa2e62cSJoy Latten 
58973d189dcSAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
5904aa2e62cSJoy Latten 			if (xfrm_id_proto_match(x->id.proto, proto) &&
5914aa2e62cSJoy Latten 			   (err = security_xfrm_state_delete(x)) != 0) {
592ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, 0,
593ab5f5e8bSJoy Latten 							audit_info->loginuid,
5942532386fSEric Paris 							audit_info->sessionid,
595ab5f5e8bSJoy Latten 							audit_info->secid);
5964aa2e62cSJoy Latten 				return err;
5974aa2e62cSJoy Latten 			}
5984aa2e62cSJoy Latten 		}
5994aa2e62cSJoy Latten 	}
6004aa2e62cSJoy Latten 
6014aa2e62cSJoy Latten 	return err;
6024aa2e62cSJoy Latten }
6034aa2e62cSJoy Latten #else
6044aa2e62cSJoy Latten static inline int
6054aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
6064aa2e62cSJoy Latten {
6074aa2e62cSJoy Latten 	return 0;
6084aa2e62cSJoy Latten }
6094aa2e62cSJoy Latten #endif
6104aa2e62cSJoy Latten 
6114aa2e62cSJoy Latten int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
6124aa2e62cSJoy Latten {
6134aa2e62cSJoy Latten 	int i, err = 0;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6164aa2e62cSJoy Latten 	err = xfrm_state_flush_secctx_check(proto, audit_info);
6174aa2e62cSJoy Latten 	if (err)
6184aa2e62cSJoy Latten 		goto out;
6194aa2e62cSJoy Latten 
620a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
6218f126e37SDavid S. Miller 		struct hlist_node *entry;
6228f126e37SDavid S. Miller 		struct xfrm_state *x;
6231da177e4SLinus Torvalds restart:
62473d189dcSAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
6251da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
6265794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
6271da177e4SLinus Torvalds 				xfrm_state_hold(x);
6281da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
6291da177e4SLinus Torvalds 
630161a09e7SJoy Latten 				err = xfrm_state_delete(x);
631ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, err ? 0 : 1,
632ab5f5e8bSJoy Latten 							audit_info->loginuid,
6332532386fSEric Paris 							audit_info->sessionid,
634ab5f5e8bSJoy Latten 							audit_info->secid);
6351da177e4SLinus Torvalds 				xfrm_state_put(x);
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
6381da177e4SLinus Torvalds 				goto restart;
6391da177e4SLinus Torvalds 			}
6401da177e4SLinus Torvalds 		}
6411da177e4SLinus Torvalds 	}
6424aa2e62cSJoy Latten 	err = 0;
6434aa2e62cSJoy Latten 
6444aa2e62cSJoy Latten out:
6451da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6461da177e4SLinus Torvalds 	wake_up(&km_waitq);
6474aa2e62cSJoy Latten 	return err;
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
6501da177e4SLinus Torvalds 
651af11e316SJamal Hadi Salim void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
65228d8909bSJamal Hadi Salim {
65328d8909bSJamal Hadi Salim 	spin_lock_bh(&xfrm_state_lock);
65428d8909bSJamal Hadi Salim 	si->sadcnt = xfrm_state_num;
65528d8909bSJamal Hadi Salim 	si->sadhcnt = xfrm_state_hmask;
65628d8909bSJamal Hadi Salim 	si->sadhmcnt = xfrm_state_hashmax;
65728d8909bSJamal Hadi Salim 	spin_unlock_bh(&xfrm_state_lock);
65828d8909bSJamal Hadi Salim }
65928d8909bSJamal Hadi Salim EXPORT_SYMBOL(xfrm_sad_getinfo);
66028d8909bSJamal Hadi Salim 
6611da177e4SLinus Torvalds static int
6621da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
6631da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
6641da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
6651da177e4SLinus Torvalds 		  unsigned short family)
6661da177e4SLinus Torvalds {
6671da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6681da177e4SLinus Torvalds 	if (!afinfo)
6691da177e4SLinus Torvalds 		return -1;
6701da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
6711da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6721da177e4SLinus Torvalds 	return 0;
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
675a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
676edcd5821SDavid S. Miller {
677edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
678edcd5821SDavid S. Miller 	struct xfrm_state *x;
6798f126e37SDavid S. Miller 	struct hlist_node *entry;
680edcd5821SDavid S. Miller 
681b754a4fdSAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_byspi+h, byspi) {
682edcd5821SDavid S. Miller 		if (x->props.family != family ||
683edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
684edcd5821SDavid S. Miller 		    x->id.proto     != proto)
685edcd5821SDavid S. Miller 			continue;
686edcd5821SDavid S. Miller 
687edcd5821SDavid S. Miller 		switch (family) {
688edcd5821SDavid S. Miller 		case AF_INET:
689edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
690edcd5821SDavid S. Miller 				continue;
691edcd5821SDavid S. Miller 			break;
692edcd5821SDavid S. Miller 		case AF_INET6:
693edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
694edcd5821SDavid S. Miller 					     (struct in6_addr *)
695edcd5821SDavid S. Miller 					     x->id.daddr.a6))
696edcd5821SDavid S. Miller 				continue;
697edcd5821SDavid S. Miller 			break;
6983ff50b79SStephen Hemminger 		}
699edcd5821SDavid S. Miller 
700edcd5821SDavid S. Miller 		xfrm_state_hold(x);
701edcd5821SDavid S. Miller 		return x;
702edcd5821SDavid S. Miller 	}
703edcd5821SDavid S. Miller 
704edcd5821SDavid S. Miller 	return NULL;
705edcd5821SDavid S. Miller }
706edcd5821SDavid S. Miller 
707edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
708edcd5821SDavid S. Miller {
709667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
710edcd5821SDavid S. Miller 	struct xfrm_state *x;
7118f126e37SDavid S. Miller 	struct hlist_node *entry;
712edcd5821SDavid S. Miller 
713d320bbb3SAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
714edcd5821SDavid S. Miller 		if (x->props.family != family ||
715edcd5821SDavid S. Miller 		    x->id.proto     != proto)
716edcd5821SDavid S. Miller 			continue;
717edcd5821SDavid S. Miller 
718edcd5821SDavid S. Miller 		switch (family) {
719edcd5821SDavid S. Miller 		case AF_INET:
720edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
721edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
722edcd5821SDavid S. Miller 				continue;
723edcd5821SDavid S. Miller 			break;
724edcd5821SDavid S. Miller 		case AF_INET6:
725edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
726edcd5821SDavid S. Miller 					     (struct in6_addr *)
727edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
728edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
729edcd5821SDavid S. Miller 					     (struct in6_addr *)
730edcd5821SDavid S. Miller 					     x->props.saddr.a6))
731edcd5821SDavid S. Miller 				continue;
732edcd5821SDavid S. Miller 			break;
7333ff50b79SStephen Hemminger 		}
734edcd5821SDavid S. Miller 
735edcd5821SDavid S. Miller 		xfrm_state_hold(x);
736edcd5821SDavid S. Miller 		return x;
737edcd5821SDavid S. Miller 	}
738edcd5821SDavid S. Miller 
739edcd5821SDavid S. Miller 	return NULL;
740edcd5821SDavid S. Miller }
741edcd5821SDavid S. Miller 
742edcd5821SDavid S. Miller static inline struct xfrm_state *
743edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
744edcd5821SDavid S. Miller {
745edcd5821SDavid S. Miller 	if (use_spi)
746edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
747edcd5821SDavid S. Miller 					   x->id.proto, family);
748edcd5821SDavid S. Miller 	else
749edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
750edcd5821SDavid S. Miller 						  &x->props.saddr,
751edcd5821SDavid S. Miller 						  x->id.proto, family);
752edcd5821SDavid S. Miller }
753edcd5821SDavid S. Miller 
7542fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
7552fab22f2SPatrick McHardy {
7562fab22f2SPatrick McHardy 	if (have_hash_collision &&
7572fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
7582fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
7592fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
7602fab22f2SPatrick McHardy }
7612fab22f2SPatrick McHardy 
7621da177e4SLinus Torvalds struct xfrm_state *
7631da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
7641da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
7651da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
7661da177e4SLinus Torvalds 		unsigned short family)
7671da177e4SLinus Torvalds {
7684bda4f25SPavel Emelyanov 	unsigned int h;
7698f126e37SDavid S. Miller 	struct hlist_node *entry;
77037b08e34SDavid S. Miller 	struct xfrm_state *x, *x0, *to_put;
7711da177e4SLinus Torvalds 	int acquire_in_progress = 0;
7721da177e4SLinus Torvalds 	int error = 0;
7731da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
7741da177e4SLinus Torvalds 
77537b08e34SDavid S. Miller 	to_put = NULL;
77637b08e34SDavid S. Miller 
7771da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7784bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
77973d189dcSAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
7801da177e4SLinus Torvalds 		if (x->props.family == family &&
7811da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
782fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
7831da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
7841da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
7851da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
7861da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
7871da177e4SLinus Torvalds 			/* Resolution logic:
7881da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
7891da177e4SLinus Torvalds 			      Done.
7901da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 			   Entering area of "sysdeps".
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
7951da177e4SLinus Torvalds 			      it selects only session which triggered
7961da177e4SLinus Torvalds 			      previous resolution. Key manager will do
7971da177e4SLinus Torvalds 			      something to install a state with proper
7981da177e4SLinus Torvalds 			      selector.
7991da177e4SLinus Torvalds 			 */
8001da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
801df9dcb45SKazunori MIYAZAWA 				if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
802e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
8031da177e4SLinus Torvalds 					continue;
8041da177e4SLinus Torvalds 				if (!best ||
8051da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
8061da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
8071da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
8081da177e4SLinus Torvalds 					best = x;
8091da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
8101da177e4SLinus Torvalds 				acquire_in_progress = 1;
8111da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
8121da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
81348b8d783SJoakim Koskela 				if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
814e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
8151da177e4SLinus Torvalds 					error = -ESRCH;
8161da177e4SLinus Torvalds 			}
8171da177e4SLinus Torvalds 		}
8181da177e4SLinus Torvalds 	}
8191da177e4SLinus Torvalds 
8201da177e4SLinus Torvalds 	x = best;
8211da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
8225c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
823edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
824edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
82537b08e34SDavid S. Miller 			to_put = x0;
8261da177e4SLinus Torvalds 			error = -EEXIST;
8271da177e4SLinus Torvalds 			goto out;
8281da177e4SLinus Torvalds 		}
829673c09beSAlexey Dobriyan 		x = xfrm_state_alloc(&init_net);
8301da177e4SLinus Torvalds 		if (x == NULL) {
8311da177e4SLinus Torvalds 			error = -ENOMEM;
8321da177e4SLinus Torvalds 			goto out;
8331da177e4SLinus Torvalds 		}
8341da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
8351da177e4SLinus Torvalds 		 * to current session. */
8361da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
8371da177e4SLinus Torvalds 
838e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
839e0d1caa7SVenkat Yekkirala 		if (error) {
840e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
84137b08e34SDavid S. Miller 			to_put = x;
842e0d1caa7SVenkat Yekkirala 			x = NULL;
843e0d1caa7SVenkat Yekkirala 			goto out;
844e0d1caa7SVenkat Yekkirala 		}
845e0d1caa7SVenkat Yekkirala 
8461da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
8471da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
8489d4139c7SAlexey Dobriyan 			list_add(&x->km.all, &init_net.xfrm.state_all);
84973d189dcSAlexey Dobriyan 			hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
850667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
851d320bbb3SAlexey Dobriyan 			hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
8521da177e4SLinus Torvalds 			if (x->id.spi) {
8531da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
854b754a4fdSAlexey Dobriyan 				hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
8551da177e4SLinus Torvalds 			}
85601e67d08SDavid S. Miller 			x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
85701e67d08SDavid S. Miller 			x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
8581da177e4SLinus Torvalds 			add_timer(&x->timer);
8592fab22f2SPatrick McHardy 			xfrm_state_num++;
8602fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
8611da177e4SLinus Torvalds 		} else {
8621da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
86337b08e34SDavid S. Miller 			to_put = x;
8641da177e4SLinus Torvalds 			x = NULL;
8651da177e4SLinus Torvalds 			error = -ESRCH;
8661da177e4SLinus Torvalds 		}
8671da177e4SLinus Torvalds 	}
8681da177e4SLinus Torvalds out:
8691da177e4SLinus Torvalds 	if (x)
8701da177e4SLinus Torvalds 		xfrm_state_hold(x);
8711da177e4SLinus Torvalds 	else
8721da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
8731da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
87437b08e34SDavid S. Miller 	if (to_put)
87537b08e34SDavid S. Miller 		xfrm_state_put(to_put);
8761da177e4SLinus Torvalds 	return x;
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds 
879628529b6SJamal Hadi Salim struct xfrm_state *
880628529b6SJamal Hadi Salim xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
881628529b6SJamal Hadi Salim 		    unsigned short family, u8 mode, u8 proto, u32 reqid)
882628529b6SJamal Hadi Salim {
8834bda4f25SPavel Emelyanov 	unsigned int h;
884628529b6SJamal Hadi Salim 	struct xfrm_state *rx = NULL, *x = NULL;
885628529b6SJamal Hadi Salim 	struct hlist_node *entry;
886628529b6SJamal Hadi Salim 
887628529b6SJamal Hadi Salim 	spin_lock(&xfrm_state_lock);
8884bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, reqid, family);
88973d189dcSAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
890628529b6SJamal Hadi Salim 		if (x->props.family == family &&
891628529b6SJamal Hadi Salim 		    x->props.reqid == reqid &&
892628529b6SJamal Hadi Salim 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
893628529b6SJamal Hadi Salim 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
894628529b6SJamal Hadi Salim 		    mode == x->props.mode &&
895628529b6SJamal Hadi Salim 		    proto == x->id.proto &&
896628529b6SJamal Hadi Salim 		    x->km.state == XFRM_STATE_VALID) {
897628529b6SJamal Hadi Salim 			rx = x;
898628529b6SJamal Hadi Salim 			break;
899628529b6SJamal Hadi Salim 		}
900628529b6SJamal Hadi Salim 	}
901628529b6SJamal Hadi Salim 
902628529b6SJamal Hadi Salim 	if (rx)
903628529b6SJamal Hadi Salim 		xfrm_state_hold(rx);
904628529b6SJamal Hadi Salim 	spin_unlock(&xfrm_state_lock);
905628529b6SJamal Hadi Salim 
906628529b6SJamal Hadi Salim 
907628529b6SJamal Hadi Salim 	return rx;
908628529b6SJamal Hadi Salim }
909628529b6SJamal Hadi Salim EXPORT_SYMBOL(xfrm_stateonly_find);
910628529b6SJamal Hadi Salim 
9111da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
9121da177e4SLinus Torvalds {
913a624c108SDavid S. Miller 	unsigned int h;
9141da177e4SLinus Torvalds 
9159d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
9169d4a706dSDavid S. Miller 
9179d4139c7SAlexey Dobriyan 	list_add(&x->km.all, &init_net.xfrm.state_all);
9184c563f76STimo Teras 
919c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
920c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
92173d189dcSAlexey Dobriyan 	hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
9221da177e4SLinus Torvalds 
923667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
924d320bbb3SAlexey Dobriyan 	hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
9256c44e6b7SMasahide NAKAMURA 
9267b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
9276c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
9286c44e6b7SMasahide NAKAMURA 				  x->props.family);
9291da177e4SLinus Torvalds 
930b754a4fdSAlexey Dobriyan 		hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
9316c44e6b7SMasahide NAKAMURA 	}
9321da177e4SLinus Torvalds 
933a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
934a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
935a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
936f8cd5488SJamal Hadi Salim 
9371da177e4SLinus Torvalds 	wake_up(&km_waitq);
938f034b5d4SDavid S. Miller 
939f034b5d4SDavid S. Miller 	xfrm_state_num++;
940f034b5d4SDavid S. Miller 
941918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
9421da177e4SLinus Torvalds }
9431da177e4SLinus Torvalds 
944c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
945c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
946c7f5ea3aSDavid S. Miller {
947c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
948c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
949c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
950c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
951c7f5ea3aSDavid S. Miller 	unsigned int h;
952c7f5ea3aSDavid S. Miller 
953c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
95473d189dcSAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
955c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
956c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
957c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
958c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
959c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
960c7f5ea3aSDavid S. Miller 	}
961c7f5ea3aSDavid S. Miller }
962c7f5ea3aSDavid S. Miller 
9631da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
9641da177e4SLinus Torvalds {
9651da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
966c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
9671da177e4SLinus Torvalds 	__xfrm_state_insert(x);
9681da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9691da177e4SLinus Torvalds }
9701da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
9711da177e4SLinus Torvalds 
9722770834cSDavid S. Miller /* xfrm_state_lock is held */
9732770834cSDavid 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)
9742770834cSDavid S. Miller {
975c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
9768f126e37SDavid S. Miller 	struct hlist_node *entry;
9772770834cSDavid S. Miller 	struct xfrm_state *x;
9782770834cSDavid S. Miller 
97973d189dcSAlexey Dobriyan 	hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
9802770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
9812770834cSDavid S. Miller 		    x->props.mode   != mode ||
9822770834cSDavid S. Miller 		    x->props.family != family ||
9832770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
98475e252d9SJoy Latten 		    x->id.spi       != 0 ||
98575e252d9SJoy Latten 		    x->id.proto	    != proto)
9862770834cSDavid S. Miller 			continue;
9872770834cSDavid S. Miller 
9882770834cSDavid S. Miller 		switch (family) {
9892770834cSDavid S. Miller 		case AF_INET:
9902770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
9912770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
9922770834cSDavid S. Miller 				continue;
9932770834cSDavid S. Miller 			break;
9942770834cSDavid S. Miller 		case AF_INET6:
9952770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
9962770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
9972770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
9982770834cSDavid S. Miller 					     x->props.saddr.a6,
9992770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
10002770834cSDavid S. Miller 				continue;
10012770834cSDavid S. Miller 			break;
10023ff50b79SStephen Hemminger 		}
10032770834cSDavid S. Miller 
10042770834cSDavid S. Miller 		xfrm_state_hold(x);
10052770834cSDavid S. Miller 		return x;
10062770834cSDavid S. Miller 	}
10072770834cSDavid S. Miller 
10082770834cSDavid S. Miller 	if (!create)
10092770834cSDavid S. Miller 		return NULL;
10102770834cSDavid S. Miller 
1011673c09beSAlexey Dobriyan 	x = xfrm_state_alloc(&init_net);
10122770834cSDavid S. Miller 	if (likely(x)) {
10132770834cSDavid S. Miller 		switch (family) {
10142770834cSDavid S. Miller 		case AF_INET:
10152770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
10162770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
10172770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
10182770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
10192770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
10202770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
10212770834cSDavid S. Miller 			break;
10222770834cSDavid S. Miller 
10232770834cSDavid S. Miller 		case AF_INET6:
10242770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
10252770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10262770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
10272770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10282770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
10292770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
10302770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
10312770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10322770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
10332770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10342770834cSDavid S. Miller 			break;
10353ff50b79SStephen Hemminger 		}
10362770834cSDavid S. Miller 
10372770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
10382770834cSDavid S. Miller 		x->id.proto = proto;
10392770834cSDavid S. Miller 		x->props.family = family;
10402770834cSDavid S. Miller 		x->props.mode = mode;
10412770834cSDavid S. Miller 		x->props.reqid = reqid;
104201e67d08SDavid S. Miller 		x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
10432770834cSDavid S. Miller 		xfrm_state_hold(x);
104401e67d08SDavid S. Miller 		x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
10452770834cSDavid S. Miller 		add_timer(&x->timer);
10469d4139c7SAlexey Dobriyan 		list_add(&x->km.all, &init_net.xfrm.state_all);
104773d189dcSAlexey Dobriyan 		hlist_add_head(&x->bydst, init_net.xfrm.state_bydst+h);
1048667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
1049d320bbb3SAlexey Dobriyan 		hlist_add_head(&x->bysrc, init_net.xfrm.state_bysrc+h);
1050918049f0SDavid S. Miller 
1051918049f0SDavid S. Miller 		xfrm_state_num++;
1052918049f0SDavid S. Miller 
1053918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
10542770834cSDavid S. Miller 	}
10552770834cSDavid S. Miller 
10562770834cSDavid S. Miller 	return x;
10572770834cSDavid S. Miller }
10582770834cSDavid S. Miller 
10591da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
10621da177e4SLinus Torvalds {
106337b08e34SDavid S. Miller 	struct xfrm_state *x1, *to_put;
10641da177e4SLinus Torvalds 	int family;
10651da177e4SLinus Torvalds 	int err;
1066eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	family = x->props.family;
10691da177e4SLinus Torvalds 
107037b08e34SDavid S. Miller 	to_put = NULL;
107137b08e34SDavid S. Miller 
10721da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10731da177e4SLinus Torvalds 
1074edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
10751da177e4SLinus Torvalds 	if (x1) {
107637b08e34SDavid S. Miller 		to_put = x1;
10771da177e4SLinus Torvalds 		x1 = NULL;
10781da177e4SLinus Torvalds 		err = -EEXIST;
10791da177e4SLinus Torvalds 		goto out;
10801da177e4SLinus Torvalds 	}
10811da177e4SLinus Torvalds 
1082eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
10831da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
108475e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
108575e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
108637b08e34SDavid S. Miller 			to_put = x1;
10871da177e4SLinus Torvalds 			x1 = NULL;
10881da177e4SLinus Torvalds 		}
10891da177e4SLinus Torvalds 	}
10901da177e4SLinus Torvalds 
1091eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
10922770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
10932770834cSDavid S. Miller 				     x->id.proto,
10941da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
10951da177e4SLinus Torvalds 
1096c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
10971da177e4SLinus Torvalds 	__xfrm_state_insert(x);
10981da177e4SLinus Torvalds 	err = 0;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds out:
11011da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 	if (x1) {
11041da177e4SLinus Torvalds 		xfrm_state_delete(x1);
11051da177e4SLinus Torvalds 		xfrm_state_put(x1);
11061da177e4SLinus Torvalds 	}
11071da177e4SLinus Torvalds 
110837b08e34SDavid S. Miller 	if (to_put)
110937b08e34SDavid S. Miller 		xfrm_state_put(to_put);
111037b08e34SDavid S. Miller 
11111da177e4SLinus Torvalds 	return err;
11121da177e4SLinus Torvalds }
11131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
11141da177e4SLinus Torvalds 
111580c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
11166666351dSEric Dumazet static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
111780c9abaaSShinta Sugimoto {
111880c9abaaSShinta Sugimoto 	int err = -ENOMEM;
1119673c09beSAlexey Dobriyan 	struct xfrm_state *x = xfrm_state_alloc(&init_net);
112080c9abaaSShinta Sugimoto 	if (!x)
112180c9abaaSShinta Sugimoto 		goto error;
112280c9abaaSShinta Sugimoto 
112380c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
112480c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
112580c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
112680c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
112780c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
112880c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
112980c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
113080c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
113180c9abaaSShinta Sugimoto 
113280c9abaaSShinta Sugimoto 	if (orig->aalg) {
113380c9abaaSShinta Sugimoto 		x->aalg = xfrm_algo_clone(orig->aalg);
113480c9abaaSShinta Sugimoto 		if (!x->aalg)
113580c9abaaSShinta Sugimoto 			goto error;
113680c9abaaSShinta Sugimoto 	}
113780c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
113880c9abaaSShinta Sugimoto 
113980c9abaaSShinta Sugimoto 	if (orig->ealg) {
114080c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
114180c9abaaSShinta Sugimoto 		if (!x->ealg)
114280c9abaaSShinta Sugimoto 			goto error;
114380c9abaaSShinta Sugimoto 	}
114480c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
114580c9abaaSShinta Sugimoto 
114680c9abaaSShinta Sugimoto 	if (orig->calg) {
114780c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
114880c9abaaSShinta Sugimoto 		if (!x->calg)
114980c9abaaSShinta Sugimoto 			goto error;
115080c9abaaSShinta Sugimoto 	}
115180c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
115280c9abaaSShinta Sugimoto 
115380c9abaaSShinta Sugimoto 	if (orig->encap) {
115480c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
115580c9abaaSShinta Sugimoto 		if (!x->encap)
115680c9abaaSShinta Sugimoto 			goto error;
115780c9abaaSShinta Sugimoto 	}
115880c9abaaSShinta Sugimoto 
115980c9abaaSShinta Sugimoto 	if (orig->coaddr) {
116080c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
116180c9abaaSShinta Sugimoto 				    GFP_KERNEL);
116280c9abaaSShinta Sugimoto 		if (!x->coaddr)
116380c9abaaSShinta Sugimoto 			goto error;
116480c9abaaSShinta Sugimoto 	}
116580c9abaaSShinta Sugimoto 
116680c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
116780c9abaaSShinta Sugimoto 	if (err)
116880c9abaaSShinta Sugimoto 		goto error;
116980c9abaaSShinta Sugimoto 
117080c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
117180c9abaaSShinta Sugimoto 
117280c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
117380c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
117480c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
117580c9abaaSShinta Sugimoto 
117680c9abaaSShinta Sugimoto 	return x;
117780c9abaaSShinta Sugimoto 
117880c9abaaSShinta Sugimoto  error:
117980c9abaaSShinta Sugimoto 	if (errp)
118080c9abaaSShinta Sugimoto 		*errp = err;
118180c9abaaSShinta Sugimoto 	if (x) {
118280c9abaaSShinta Sugimoto 		kfree(x->aalg);
118380c9abaaSShinta Sugimoto 		kfree(x->ealg);
118480c9abaaSShinta Sugimoto 		kfree(x->calg);
118580c9abaaSShinta Sugimoto 		kfree(x->encap);
118680c9abaaSShinta Sugimoto 		kfree(x->coaddr);
118780c9abaaSShinta Sugimoto 	}
118880c9abaaSShinta Sugimoto 	kfree(x);
118980c9abaaSShinta Sugimoto 	return NULL;
119080c9abaaSShinta Sugimoto }
119180c9abaaSShinta Sugimoto 
119280c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
119380c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
119480c9abaaSShinta Sugimoto {
119580c9abaaSShinta Sugimoto 	unsigned int h;
119680c9abaaSShinta Sugimoto 	struct xfrm_state *x;
119780c9abaaSShinta Sugimoto 	struct hlist_node *entry;
119880c9abaaSShinta Sugimoto 
119980c9abaaSShinta Sugimoto 	if (m->reqid) {
120080c9abaaSShinta Sugimoto 		h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
120180c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
120273d189dcSAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
120380c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
120480c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
120580c9abaaSShinta Sugimoto 				continue;
120680c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
120780c9abaaSShinta Sugimoto 				continue;
120880c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
120980c9abaaSShinta Sugimoto 					  m->old_family) ||
121080c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
121180c9abaaSShinta Sugimoto 					  m->old_family))
121280c9abaaSShinta Sugimoto 				continue;
121380c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
121480c9abaaSShinta Sugimoto 			return x;
121580c9abaaSShinta Sugimoto 		}
121680c9abaaSShinta Sugimoto 	} else {
121780c9abaaSShinta Sugimoto 		h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
121880c9abaaSShinta Sugimoto 				  m->old_family);
1219d320bbb3SAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
122080c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
122180c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
122280c9abaaSShinta Sugimoto 				continue;
122380c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
122480c9abaaSShinta Sugimoto 					  m->old_family) ||
122580c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
122680c9abaaSShinta Sugimoto 					  m->old_family))
122780c9abaaSShinta Sugimoto 				continue;
122880c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
122980c9abaaSShinta Sugimoto 			return x;
123080c9abaaSShinta Sugimoto 		}
123180c9abaaSShinta Sugimoto 	}
123280c9abaaSShinta Sugimoto 
123380c9abaaSShinta Sugimoto 	return NULL;
123480c9abaaSShinta Sugimoto }
123580c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
123680c9abaaSShinta Sugimoto 
123780c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
123880c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
123980c9abaaSShinta Sugimoto {
124080c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
124180c9abaaSShinta Sugimoto 	int err;
124280c9abaaSShinta Sugimoto 
124380c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
124480c9abaaSShinta Sugimoto 	if (!xc)
124580c9abaaSShinta Sugimoto 		return NULL;
124680c9abaaSShinta Sugimoto 
124780c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
124880c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
124980c9abaaSShinta Sugimoto 
125080c9abaaSShinta Sugimoto 	/* add state */
125180c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
125280c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
125380c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
125480c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
125580c9abaaSShinta Sugimoto 	} else {
125680c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
125780c9abaaSShinta Sugimoto 			goto error;
125880c9abaaSShinta Sugimoto 	}
125980c9abaaSShinta Sugimoto 
126080c9abaaSShinta Sugimoto 	return xc;
126180c9abaaSShinta Sugimoto error:
126280c9abaaSShinta Sugimoto 	kfree(xc);
126380c9abaaSShinta Sugimoto 	return NULL;
126480c9abaaSShinta Sugimoto }
126580c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
126680c9abaaSShinta Sugimoto #endif
126780c9abaaSShinta Sugimoto 
12681da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
12691da177e4SLinus Torvalds {
127037b08e34SDavid S. Miller 	struct xfrm_state *x1, *to_put;
12711da177e4SLinus Torvalds 	int err;
1272eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
12731da177e4SLinus Torvalds 
127437b08e34SDavid S. Miller 	to_put = NULL;
127537b08e34SDavid S. Miller 
12761da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1277edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
12781da177e4SLinus Torvalds 
12791da177e4SLinus Torvalds 	err = -ESRCH;
12801da177e4SLinus Torvalds 	if (!x1)
12811da177e4SLinus Torvalds 		goto out;
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
128437b08e34SDavid S. Miller 		to_put = x1;
12851da177e4SLinus Torvalds 		err = -EEXIST;
12861da177e4SLinus Torvalds 		goto out;
12871da177e4SLinus Torvalds 	}
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
12901da177e4SLinus Torvalds 		__xfrm_state_insert(x);
12911da177e4SLinus Torvalds 		x = NULL;
12921da177e4SLinus Torvalds 	}
12931da177e4SLinus Torvalds 	err = 0;
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds out:
12961da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12971da177e4SLinus Torvalds 
129837b08e34SDavid S. Miller 	if (to_put)
129937b08e34SDavid S. Miller 		xfrm_state_put(to_put);
130037b08e34SDavid S. Miller 
13011da177e4SLinus Torvalds 	if (err)
13021da177e4SLinus Torvalds 		return err;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	if (!x) {
13051da177e4SLinus Torvalds 		xfrm_state_delete(x1);
13061da177e4SLinus Torvalds 		xfrm_state_put(x1);
13071da177e4SLinus Torvalds 		return 0;
13081da177e4SLinus Torvalds 	}
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds 	err = -EINVAL;
13111da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
13121da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
13131da177e4SLinus Torvalds 		if (x->encap && x1->encap)
13141da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1315060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1316060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1317060f02a3SNoriaki TAKAMIYA 		}
1318060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1319060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
13201da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
13211da177e4SLinus Torvalds 		x1->km.dying = 0;
13221da177e4SLinus Torvalds 
1323a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
13241da177e4SLinus Torvalds 		if (x1->curlft.use_time)
13251da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
13261da177e4SLinus Torvalds 
13271da177e4SLinus Torvalds 		err = 0;
13281da177e4SLinus Torvalds 	}
13291da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
13301da177e4SLinus Torvalds 
13311da177e4SLinus Torvalds 	xfrm_state_put(x1);
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	return err;
13341da177e4SLinus Torvalds }
13351da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
13361da177e4SLinus Torvalds 
13371da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
13381da177e4SLinus Torvalds {
13391da177e4SLinus Torvalds 	if (!x->curlft.use_time)
13409d729f72SJames Morris 		x->curlft.use_time = get_seconds();
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
13431da177e4SLinus Torvalds 		return -EINVAL;
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
13461da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
13474666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
1348a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
13491da177e4SLinus Torvalds 		return -EINVAL;
13501da177e4SLinus Torvalds 	}
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 	if (!x->km.dying &&
13531da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
13544666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
13554666faabSHerbert Xu 		x->km.dying = 1;
135653bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
13574666faabSHerbert Xu 	}
13581da177e4SLinus Torvalds 	return 0;
13591da177e4SLinus Torvalds }
13601da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
13611da177e4SLinus Torvalds 
13621da177e4SLinus Torvalds struct xfrm_state *
1363a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
13641da177e4SLinus Torvalds 		  unsigned short family)
13651da177e4SLinus Torvalds {
13661da177e4SLinus Torvalds 	struct xfrm_state *x;
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1369edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
13701da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13711da177e4SLinus Torvalds 	return x;
13721da177e4SLinus Torvalds }
13731da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds struct xfrm_state *
1376eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1377eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1378eb2971b6SMasahide NAKAMURA {
1379eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1380eb2971b6SMasahide NAKAMURA 
1381eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1382edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
1383eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1384eb2971b6SMasahide NAKAMURA 	return x;
1385eb2971b6SMasahide NAKAMURA }
1386eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1387eb2971b6SMasahide NAKAMURA 
1388eb2971b6SMasahide NAKAMURA struct xfrm_state *
13891da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
13901da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
13911da177e4SLinus Torvalds 	      int create, unsigned short family)
13921da177e4SLinus Torvalds {
13931da177e4SLinus Torvalds 	struct xfrm_state *x;
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
13962770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
13971da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13982770834cSDavid S. Miller 
13991da177e4SLinus Torvalds 	return x;
14001da177e4SLinus Torvalds }
14011da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
14021da177e4SLinus Torvalds 
140341a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
140441a49cc3SMasahide NAKAMURA int
140541a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
140641a49cc3SMasahide NAKAMURA 	       unsigned short family)
140741a49cc3SMasahide NAKAMURA {
140841a49cc3SMasahide NAKAMURA 	int err = 0;
140941a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
141041a49cc3SMasahide NAKAMURA 	if (!afinfo)
141141a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
141241a49cc3SMasahide NAKAMURA 
141341a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
141441a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
141541a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
141641a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
141741a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
141841a49cc3SMasahide NAKAMURA 	return err;
141941a49cc3SMasahide NAKAMURA }
142041a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
142141a49cc3SMasahide NAKAMURA 
142241a49cc3SMasahide NAKAMURA int
142341a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
142441a49cc3SMasahide NAKAMURA 		unsigned short family)
142541a49cc3SMasahide NAKAMURA {
142641a49cc3SMasahide NAKAMURA 	int err = 0;
142741a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
142841a49cc3SMasahide NAKAMURA 	if (!afinfo)
142941a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
143041a49cc3SMasahide NAKAMURA 
143141a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
143241a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
143341a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
143441a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
143541a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
143641a49cc3SMasahide NAKAMURA 	return err;
143741a49cc3SMasahide NAKAMURA }
143841a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
143941a49cc3SMasahide NAKAMURA #endif
144041a49cc3SMasahide NAKAMURA 
14411da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
14441da177e4SLinus Torvalds {
14451da177e4SLinus Torvalds 	int i;
14461da177e4SLinus Torvalds 
1447f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
14488f126e37SDavid S. Miller 		struct hlist_node *entry;
14498f126e37SDavid S. Miller 		struct xfrm_state *x;
14508f126e37SDavid S. Miller 
145173d189dcSAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+i, bydst) {
14528f126e37SDavid S. Miller 			if (x->km.seq == seq &&
14538f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
14541da177e4SLinus Torvalds 				xfrm_state_hold(x);
14551da177e4SLinus Torvalds 				return x;
14561da177e4SLinus Torvalds 			}
14571da177e4SLinus Torvalds 		}
14581da177e4SLinus Torvalds 	}
14591da177e4SLinus Torvalds 	return NULL;
14601da177e4SLinus Torvalds }
14611da177e4SLinus Torvalds 
14621da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
14631da177e4SLinus Torvalds {
14641da177e4SLinus Torvalds 	struct xfrm_state *x;
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
14671da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
14681da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14691da177e4SLinus Torvalds 	return x;
14701da177e4SLinus Torvalds }
14711da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
14721da177e4SLinus Torvalds 
14731da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
14741da177e4SLinus Torvalds {
14751da177e4SLinus Torvalds 	u32 res;
14761da177e4SLinus Torvalds 	static u32 acqseq;
14771da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
14781da177e4SLinus Torvalds 
14791da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
14801da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
14811da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
14821da177e4SLinus Torvalds 	return res;
14831da177e4SLinus Torvalds }
14841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
14851da177e4SLinus Torvalds 
1486658b219eSHerbert Xu int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
14871da177e4SLinus Torvalds {
1488f034b5d4SDavid S. Miller 	unsigned int h;
14891da177e4SLinus Torvalds 	struct xfrm_state *x0;
1490658b219eSHerbert Xu 	int err = -ENOENT;
1491658b219eSHerbert Xu 	__be32 minspi = htonl(low);
1492658b219eSHerbert Xu 	__be32 maxspi = htonl(high);
14931da177e4SLinus Torvalds 
1494658b219eSHerbert Xu 	spin_lock_bh(&x->lock);
1495658b219eSHerbert Xu 	if (x->km.state == XFRM_STATE_DEAD)
1496658b219eSHerbert Xu 		goto unlock;
1497658b219eSHerbert Xu 
1498658b219eSHerbert Xu 	err = 0;
14991da177e4SLinus Torvalds 	if (x->id.spi)
1500658b219eSHerbert Xu 		goto unlock;
1501658b219eSHerbert Xu 
1502658b219eSHerbert Xu 	err = -ENOENT;
15031da177e4SLinus Torvalds 
15041da177e4SLinus Torvalds 	if (minspi == maxspi) {
15051da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
15061da177e4SLinus Torvalds 		if (x0) {
15071da177e4SLinus Torvalds 			xfrm_state_put(x0);
1508658b219eSHerbert Xu 			goto unlock;
15091da177e4SLinus Torvalds 		}
15101da177e4SLinus Torvalds 		x->id.spi = minspi;
15111da177e4SLinus Torvalds 	} else {
15121da177e4SLinus Torvalds 		u32 spi = 0;
151326977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
151426977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
15151da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
15161da177e4SLinus Torvalds 			if (x0 == NULL) {
15171da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
15181da177e4SLinus Torvalds 				break;
15191da177e4SLinus Torvalds 			}
15201da177e4SLinus Torvalds 			xfrm_state_put(x0);
15211da177e4SLinus Torvalds 		}
15221da177e4SLinus Torvalds 	}
15231da177e4SLinus Torvalds 	if (x->id.spi) {
15241da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
15251da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
1526b754a4fdSAlexey Dobriyan 		hlist_add_head(&x->byspi, init_net.xfrm.state_byspi+h);
15271da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
1528658b219eSHerbert Xu 
1529658b219eSHerbert Xu 		err = 0;
15301da177e4SLinus Torvalds 	}
1531658b219eSHerbert Xu 
1532658b219eSHerbert Xu unlock:
1533658b219eSHerbert Xu 	spin_unlock_bh(&x->lock);
1534658b219eSHerbert Xu 
1535658b219eSHerbert Xu 	return err;
15361da177e4SLinus Torvalds }
15371da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
15381da177e4SLinus Torvalds 
15394c563f76STimo Teras int xfrm_state_walk(struct xfrm_state_walk *walk,
15404c563f76STimo Teras 		    int (*func)(struct xfrm_state *, int, void*),
15411da177e4SLinus Torvalds 		    void *data)
15421da177e4SLinus Torvalds {
154312a169e7SHerbert Xu 	struct xfrm_state *state;
154412a169e7SHerbert Xu 	struct xfrm_state_walk *x;
15451da177e4SLinus Torvalds 	int err = 0;
15461da177e4SLinus Torvalds 
154712a169e7SHerbert Xu 	if (walk->seq != 0 && list_empty(&walk->all))
15484c563f76STimo Teras 		return 0;
15494c563f76STimo Teras 
15501da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
155112a169e7SHerbert Xu 	if (list_empty(&walk->all))
15529d4139c7SAlexey Dobriyan 		x = list_first_entry(&init_net.xfrm.state_all, struct xfrm_state_walk, all);
155312a169e7SHerbert Xu 	else
155412a169e7SHerbert Xu 		x = list_entry(&walk->all, struct xfrm_state_walk, all);
15559d4139c7SAlexey Dobriyan 	list_for_each_entry_from(x, &init_net.xfrm.state_all, all) {
155612a169e7SHerbert Xu 		if (x->state == XFRM_STATE_DEAD)
15574c563f76STimo Teras 			continue;
155812a169e7SHerbert Xu 		state = container_of(x, struct xfrm_state, km);
155912a169e7SHerbert Xu 		if (!xfrm_id_proto_match(state->id.proto, walk->proto))
156094b9bb54SJamal Hadi Salim 			continue;
156112a169e7SHerbert Xu 		err = func(state, walk->seq, data);
15624c563f76STimo Teras 		if (err) {
156312a169e7SHerbert Xu 			list_move_tail(&walk->all, &x->all);
156494b9bb54SJamal Hadi Salim 			goto out;
156594b9bb54SJamal Hadi Salim 		}
156612a169e7SHerbert Xu 		walk->seq++;
15674c563f76STimo Teras 	}
156812a169e7SHerbert Xu 	if (walk->seq == 0) {
15691da177e4SLinus Torvalds 		err = -ENOENT;
15701da177e4SLinus Torvalds 		goto out;
15711da177e4SLinus Torvalds 	}
157212a169e7SHerbert Xu 	list_del_init(&walk->all);
15731da177e4SLinus Torvalds out:
15741da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
15751da177e4SLinus Torvalds 	return err;
15761da177e4SLinus Torvalds }
15771da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
15781da177e4SLinus Torvalds 
15795c182458SHerbert Xu void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
15805c182458SHerbert Xu {
158112a169e7SHerbert Xu 	INIT_LIST_HEAD(&walk->all);
15825c182458SHerbert Xu 	walk->proto = proto;
158312a169e7SHerbert Xu 	walk->state = XFRM_STATE_DEAD;
158412a169e7SHerbert Xu 	walk->seq = 0;
15855c182458SHerbert Xu }
15865c182458SHerbert Xu EXPORT_SYMBOL(xfrm_state_walk_init);
15875c182458SHerbert Xu 
1588abb81c4fSHerbert Xu void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1589abb81c4fSHerbert Xu {
159012a169e7SHerbert Xu 	if (list_empty(&walk->all))
15915c182458SHerbert Xu 		return;
15925c182458SHerbert Xu 
159312a169e7SHerbert Xu 	spin_lock_bh(&xfrm_state_lock);
159412a169e7SHerbert Xu 	list_del(&walk->all);
159512a169e7SHerbert Xu 	spin_lock_bh(&xfrm_state_lock);
1596abb81c4fSHerbert Xu }
1597abb81c4fSHerbert Xu EXPORT_SYMBOL(xfrm_state_walk_done);
1598abb81c4fSHerbert Xu 
1599f8cd5488SJamal Hadi Salim 
1600f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1601f8cd5488SJamal Hadi Salim {
1602f8cd5488SJamal Hadi Salim 	struct km_event c;
1603f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1604f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1605f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1606f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1607f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1608f8cd5488SJamal Hadi Salim 	 *     and there were changes
1609f8cd5488SJamal Hadi Salim 	 *
1610f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1611f8cd5488SJamal Hadi Salim 	 */
1612f8cd5488SJamal Hadi Salim 
1613f8cd5488SJamal Hadi Salim 	switch (event) {
1614f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1615f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1616f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
16172717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
16182717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
16192717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
16202717096aSJamal Hadi Salim 			else
1621f8cd5488SJamal Hadi Salim 				return;
16222717096aSJamal Hadi Salim 		}
1623f8cd5488SJamal Hadi Salim 
1624f8cd5488SJamal Hadi Salim 		break;
1625f8cd5488SJamal Hadi Salim 
1626f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1627f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1628f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
16292717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
16302717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1631f8cd5488SJamal Hadi Salim 			return;
16322717096aSJamal Hadi Salim 		}
1633f8cd5488SJamal Hadi Salim 
1634f8cd5488SJamal Hadi Salim 		break;
1635f8cd5488SJamal Hadi Salim 	}
1636f8cd5488SJamal Hadi Salim 
1637f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1638f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1639f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1640f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1641f8cd5488SJamal Hadi Salim 
1642f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1643a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
16442717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
16452717096aSJamal Hadi Salim }
1646f8cd5488SJamal Hadi Salim 
1647f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1648f8cd5488SJamal Hadi Salim {
1649f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1650f8cd5488SJamal Hadi Salim 
1651f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1652f8cd5488SJamal Hadi Salim 
16532717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
16542717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1655f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
16562717096aSJamal Hadi Salim 		else
16572717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
16582717096aSJamal Hadi Salim 	}
1659f8cd5488SJamal Hadi Salim 
1660f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1661f8cd5488SJamal Hadi Salim }
1662f8cd5488SJamal Hadi Salim 
1663afeb14b4SPaul Moore int xfrm_replay_check(struct xfrm_state *x,
1664afeb14b4SPaul Moore 		      struct sk_buff *skb, __be32 net_seq)
16651da177e4SLinus Torvalds {
16661da177e4SLinus Torvalds 	u32 diff;
1667a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
16681da177e4SLinus Torvalds 
16691da177e4SLinus Torvalds 	if (unlikely(seq == 0))
1670afeb14b4SPaul Moore 		goto err;
16711da177e4SLinus Torvalds 
16721da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
16731da177e4SLinus Torvalds 		return 0;
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
16764c4d51a7SHerbert Xu 	if (diff >= min_t(unsigned int, x->props.replay_window,
16774c4d51a7SHerbert Xu 			  sizeof(x->replay.bitmap) * 8)) {
16781da177e4SLinus Torvalds 		x->stats.replay_window++;
1679afeb14b4SPaul Moore 		goto err;
16801da177e4SLinus Torvalds 	}
16811da177e4SLinus Torvalds 
16821da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
16831da177e4SLinus Torvalds 		x->stats.replay++;
1684afeb14b4SPaul Moore 		goto err;
16851da177e4SLinus Torvalds 	}
16861da177e4SLinus Torvalds 	return 0;
1687afeb14b4SPaul Moore 
1688afeb14b4SPaul Moore err:
1689afeb14b4SPaul Moore 	xfrm_audit_state_replay(x, skb, net_seq);
1690afeb14b4SPaul Moore 	return -EINVAL;
16911da177e4SLinus Torvalds }
16921da177e4SLinus Torvalds 
169361f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
16941da177e4SLinus Torvalds {
16951da177e4SLinus Torvalds 	u32 diff;
169661f4627bSAl Viro 	u32 seq = ntohl(net_seq);
16971da177e4SLinus Torvalds 
16981da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
16991da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
17001da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
17011da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
17021da177e4SLinus Torvalds 		else
17031da177e4SLinus Torvalds 			x->replay.bitmap = 1;
17041da177e4SLinus Torvalds 		x->replay.seq = seq;
17051da177e4SLinus Torvalds 	} else {
17061da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
17071da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
17081da177e4SLinus Torvalds 	}
1709f8cd5488SJamal Hadi Salim 
1710f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1711f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
17121da177e4SLinus Torvalds }
17131da177e4SLinus Torvalds 
1714df01812eSDenis Cheng static LIST_HEAD(xfrm_km_list);
17151da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
17161da177e4SLinus Torvalds 
171726b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
17181da177e4SLinus Torvalds {
17191da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17201da177e4SLinus Torvalds 
172126b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
172226b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
172326b15dadSJamal Hadi Salim 		if (km->notify_policy)
172426b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
172526b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
172626b15dadSJamal Hadi Salim }
172726b15dadSJamal Hadi Salim 
172826b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
172926b15dadSJamal Hadi Salim {
173026b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
173126b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
173226b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
173326b15dadSJamal Hadi Salim 		if (km->notify)
173426b15dadSJamal Hadi Salim 			km->notify(x, c);
173526b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
173626b15dadSJamal Hadi Salim }
173726b15dadSJamal Hadi Salim 
173826b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
173926b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
174026b15dadSJamal Hadi Salim 
174153bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
174226b15dadSJamal Hadi Salim {
174326b15dadSJamal Hadi Salim 	struct km_event c;
174426b15dadSJamal Hadi Salim 
1745bf08867fSHerbert Xu 	c.data.hard = hard;
174653bc6b4dSJamal Hadi Salim 	c.pid = pid;
1747f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
174826b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
17491da177e4SLinus Torvalds 
17501da177e4SLinus Torvalds 	if (hard)
17511da177e4SLinus Torvalds 		wake_up(&km_waitq);
17521da177e4SLinus Torvalds }
17531da177e4SLinus Torvalds 
175453bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
175526b15dadSJamal Hadi Salim /*
175626b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
175726b15dadSJamal Hadi Salim  * We are happy with one success
175826b15dadSJamal Hadi Salim */
1759980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
17601da177e4SLinus Torvalds {
176126b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
17621da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17631da177e4SLinus Torvalds 
17641da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17651da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
176626b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
176726b15dadSJamal Hadi Salim 		if (!acqret)
176826b15dadSJamal Hadi Salim 			err = acqret;
17691da177e4SLinus Torvalds 	}
17701da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17711da177e4SLinus Torvalds 	return err;
17721da177e4SLinus Torvalds }
1773980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
17741da177e4SLinus Torvalds 
17755d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
17761da177e4SLinus Torvalds {
17771da177e4SLinus Torvalds 	int err = -EINVAL;
17781da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17791da177e4SLinus Torvalds 
17801da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17811da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
17821da177e4SLinus Torvalds 		if (km->new_mapping)
17831da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
17841da177e4SLinus Torvalds 		if (!err)
17851da177e4SLinus Torvalds 			break;
17861da177e4SLinus Torvalds 	}
17871da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17881da177e4SLinus Torvalds 	return err;
17891da177e4SLinus Torvalds }
17901da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
17911da177e4SLinus Torvalds 
17926c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
17931da177e4SLinus Torvalds {
179426b15dadSJamal Hadi Salim 	struct km_event c;
17951da177e4SLinus Torvalds 
1796bf08867fSHerbert Xu 	c.data.hard = hard;
17976c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1798f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
179926b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
18001da177e4SLinus Torvalds 
18011da177e4SLinus Torvalds 	if (hard)
18021da177e4SLinus Torvalds 		wake_up(&km_waitq);
18031da177e4SLinus Torvalds }
1804a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
18051da177e4SLinus Torvalds 
18062d60abc2SEric Dumazet #ifdef CONFIG_XFRM_MIGRATE
180780c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
180813c1d189SArnaud Ebalard 	       struct xfrm_migrate *m, int num_migrate,
180913c1d189SArnaud Ebalard 	       struct xfrm_kmaddress *k)
181080c9abaaSShinta Sugimoto {
181180c9abaaSShinta Sugimoto 	int err = -EINVAL;
181280c9abaaSShinta Sugimoto 	int ret;
181380c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
181480c9abaaSShinta Sugimoto 
181580c9abaaSShinta Sugimoto 	read_lock(&xfrm_km_lock);
181680c9abaaSShinta Sugimoto 	list_for_each_entry(km, &xfrm_km_list, list) {
181780c9abaaSShinta Sugimoto 		if (km->migrate) {
181813c1d189SArnaud Ebalard 			ret = km->migrate(sel, dir, type, m, num_migrate, k);
181980c9abaaSShinta Sugimoto 			if (!ret)
182080c9abaaSShinta Sugimoto 				err = ret;
182180c9abaaSShinta Sugimoto 		}
182280c9abaaSShinta Sugimoto 	}
182380c9abaaSShinta Sugimoto 	read_unlock(&xfrm_km_lock);
182480c9abaaSShinta Sugimoto 	return err;
182580c9abaaSShinta Sugimoto }
182680c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
18272d60abc2SEric Dumazet #endif
182880c9abaaSShinta Sugimoto 
182997a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
183097a64b45SMasahide NAKAMURA {
183197a64b45SMasahide NAKAMURA 	int err = -EINVAL;
183297a64b45SMasahide NAKAMURA 	int ret;
183397a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
183497a64b45SMasahide NAKAMURA 
183597a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
183697a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
183797a64b45SMasahide NAKAMURA 		if (km->report) {
183897a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
183997a64b45SMasahide NAKAMURA 			if (!ret)
184097a64b45SMasahide NAKAMURA 				err = ret;
184197a64b45SMasahide NAKAMURA 		}
184297a64b45SMasahide NAKAMURA 	}
184397a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
184497a64b45SMasahide NAKAMURA 	return err;
184597a64b45SMasahide NAKAMURA }
184697a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
184797a64b45SMasahide NAKAMURA 
18481da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
18491da177e4SLinus Torvalds {
18501da177e4SLinus Torvalds 	int err;
18511da177e4SLinus Torvalds 	u8 *data;
18521da177e4SLinus Torvalds 	struct xfrm_mgr *km;
18531da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
18541da177e4SLinus Torvalds 
18551da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
18561da177e4SLinus Torvalds 		return -EMSGSIZE;
18571da177e4SLinus Torvalds 
18581da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
18591da177e4SLinus Torvalds 	if (!data)
18601da177e4SLinus Torvalds 		return -ENOMEM;
18611da177e4SLinus Torvalds 
18621da177e4SLinus Torvalds 	err = -EFAULT;
18631da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
18641da177e4SLinus Torvalds 		goto out;
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	err = -EINVAL;
18671da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
18681da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1869cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
18701da177e4SLinus Torvalds 					 optlen, &err);
18711da177e4SLinus Torvalds 		if (err >= 0)
18721da177e4SLinus Torvalds 			break;
18731da177e4SLinus Torvalds 	}
18741da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
18751da177e4SLinus Torvalds 
18761da177e4SLinus Torvalds 	if (err >= 0) {
18771da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
18781da177e4SLinus Torvalds 		xfrm_pol_put(pol);
18791da177e4SLinus Torvalds 		err = 0;
18801da177e4SLinus Torvalds 	}
18811da177e4SLinus Torvalds 
18821da177e4SLinus Torvalds out:
18831da177e4SLinus Torvalds 	kfree(data);
18841da177e4SLinus Torvalds 	return err;
18851da177e4SLinus Torvalds }
18861da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
18891da177e4SLinus Torvalds {
18901da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18911da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
18921da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18931da177e4SLinus Torvalds 	return 0;
18941da177e4SLinus Torvalds }
18951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
18981da177e4SLinus Torvalds {
18991da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
19001da177e4SLinus Torvalds 	list_del(&km->list);
19011da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
19021da177e4SLinus Torvalds 	return 0;
19031da177e4SLinus Torvalds }
19041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
19051da177e4SLinus Torvalds 
19061da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
19071da177e4SLinus Torvalds {
19081da177e4SLinus Torvalds 	int err = 0;
19091da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
19101da177e4SLinus Torvalds 		return -EINVAL;
19111da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
19121da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1913f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
19141da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
19151da177e4SLinus Torvalds 		err = -ENOBUFS;
1916edcd5821SDavid S. Miller 	else
19171da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1918f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
19191da177e4SLinus Torvalds 	return err;
19201da177e4SLinus Torvalds }
19211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
19221da177e4SLinus Torvalds 
19231da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
19241da177e4SLinus Torvalds {
19251da177e4SLinus Torvalds 	int err = 0;
19261da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
19271da177e4SLinus Torvalds 		return -EINVAL;
19281da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
19291da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1930f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
19311da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
19321da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
19331da177e4SLinus Torvalds 			err = -EINVAL;
1934edcd5821SDavid S. Miller 		else
19351da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
19361da177e4SLinus Torvalds 	}
1937f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
19381da177e4SLinus Torvalds 	return err;
19391da177e4SLinus Torvalds }
19401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
19411da177e4SLinus Torvalds 
194217c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
19431da177e4SLinus Torvalds {
19441da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
19451da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
19461da177e4SLinus Torvalds 		return NULL;
19471da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
19481da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1949546be240SHerbert Xu 	if (unlikely(!afinfo))
19501da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
19511da177e4SLinus Torvalds 	return afinfo;
19521da177e4SLinus Torvalds }
19531da177e4SLinus Torvalds 
195417c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
19559a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
19561da177e4SLinus Torvalds {
1957546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
19581da177e4SLinus Torvalds }
19591da177e4SLinus Torvalds 
19601da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
19611da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
19621da177e4SLinus Torvalds {
19631da177e4SLinus Torvalds 	if (x->tunnel) {
19641da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
19651da177e4SLinus Torvalds 
19661da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
19671da177e4SLinus Torvalds 			xfrm_state_delete(t);
19681da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
19691da177e4SLinus Torvalds 		xfrm_state_put(t);
19701da177e4SLinus Torvalds 		x->tunnel = NULL;
19711da177e4SLinus Torvalds 	}
19721da177e4SLinus Torvalds }
19731da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
19741da177e4SLinus Torvalds 
19751da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
19761da177e4SLinus Torvalds {
1977c5c25238SPatrick McHardy 	int res;
19781da177e4SLinus Torvalds 
19791da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
19801da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_VALID &&
1981c5c25238SPatrick McHardy 	    x->type && x->type->get_mtu)
1982c5c25238SPatrick McHardy 		res = x->type->get_mtu(x, mtu);
19831da177e4SLinus Torvalds 	else
198428121617SPatrick McHardy 		res = mtu - x->props.header_len;
19851da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
19861da177e4SLinus Torvalds 	return res;
19871da177e4SLinus Torvalds }
19881da177e4SLinus Torvalds 
198972cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
199072cb6962SHerbert Xu {
1991d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1992df9dcb45SKazunori MIYAZAWA 	struct xfrm_mode *inner_mode;
1993d094cd83SHerbert Xu 	int family = x->props.family;
199472cb6962SHerbert Xu 	int err;
199572cb6962SHerbert Xu 
1996d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1997d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1998d094cd83SHerbert Xu 	if (!afinfo)
1999d094cd83SHerbert Xu 		goto error;
2000d094cd83SHerbert Xu 
2001d094cd83SHerbert Xu 	err = 0;
2002d094cd83SHerbert Xu 	if (afinfo->init_flags)
2003d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
2004d094cd83SHerbert Xu 
2005d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
2006d094cd83SHerbert Xu 
2007d094cd83SHerbert Xu 	if (err)
2008d094cd83SHerbert Xu 		goto error;
2009d094cd83SHerbert Xu 
2010d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
2011df9dcb45SKazunori MIYAZAWA 
2012df9dcb45SKazunori MIYAZAWA 	if (x->sel.family != AF_UNSPEC) {
2013df9dcb45SKazunori MIYAZAWA 		inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
2014df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
201513996378SHerbert Xu 			goto error;
201613996378SHerbert Xu 
2017df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
2018df9dcb45SKazunori MIYAZAWA 		    family != x->sel.family) {
2019df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
202013996378SHerbert Xu 			goto error;
2021df9dcb45SKazunori MIYAZAWA 		}
2022df9dcb45SKazunori MIYAZAWA 
2023df9dcb45SKazunori MIYAZAWA 		x->inner_mode = inner_mode;
2024df9dcb45SKazunori MIYAZAWA 	} else {
2025df9dcb45SKazunori MIYAZAWA 		struct xfrm_mode *inner_mode_iaf;
2026df9dcb45SKazunori MIYAZAWA 
2027df9dcb45SKazunori MIYAZAWA 		inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
2028df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
2029df9dcb45SKazunori MIYAZAWA 			goto error;
2030df9dcb45SKazunori MIYAZAWA 
2031df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
2032df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
2033df9dcb45SKazunori MIYAZAWA 			goto error;
2034df9dcb45SKazunori MIYAZAWA 		}
2035df9dcb45SKazunori MIYAZAWA 
2036df9dcb45SKazunori MIYAZAWA 		inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
2037df9dcb45SKazunori MIYAZAWA 		if (inner_mode_iaf == NULL)
2038df9dcb45SKazunori MIYAZAWA 			goto error;
2039df9dcb45SKazunori MIYAZAWA 
2040df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2041df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode_iaf);
2042df9dcb45SKazunori MIYAZAWA 			goto error;
2043df9dcb45SKazunori MIYAZAWA 		}
2044df9dcb45SKazunori MIYAZAWA 
2045df9dcb45SKazunori MIYAZAWA 		if (x->props.family == AF_INET) {
2046df9dcb45SKazunori MIYAZAWA 			x->inner_mode = inner_mode;
2047df9dcb45SKazunori MIYAZAWA 			x->inner_mode_iaf = inner_mode_iaf;
2048df9dcb45SKazunori MIYAZAWA 		} else {
2049df9dcb45SKazunori MIYAZAWA 			x->inner_mode = inner_mode_iaf;
2050df9dcb45SKazunori MIYAZAWA 			x->inner_mode_iaf = inner_mode;
2051df9dcb45SKazunori MIYAZAWA 		}
2052df9dcb45SKazunori MIYAZAWA 	}
205313996378SHerbert Xu 
2054d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
205572cb6962SHerbert Xu 	if (x->type == NULL)
205672cb6962SHerbert Xu 		goto error;
205772cb6962SHerbert Xu 
205872cb6962SHerbert Xu 	err = x->type->init_state(x);
205972cb6962SHerbert Xu 	if (err)
206072cb6962SHerbert Xu 		goto error;
206172cb6962SHerbert Xu 
206213996378SHerbert Xu 	x->outer_mode = xfrm_get_mode(x->props.mode, family);
206313996378SHerbert Xu 	if (x->outer_mode == NULL)
2064b59f45d0SHerbert Xu 		goto error;
2065b59f45d0SHerbert Xu 
206672cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
206772cb6962SHerbert Xu 
206872cb6962SHerbert Xu error:
206972cb6962SHerbert Xu 	return err;
207072cb6962SHerbert Xu }
207172cb6962SHerbert Xu 
207272cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
20731da177e4SLinus Torvalds 
2074d62ddc21SAlexey Dobriyan int __net_init xfrm_state_init(struct net *net)
20751da177e4SLinus Torvalds {
2076f034b5d4SDavid S. Miller 	unsigned int sz;
20771da177e4SLinus Torvalds 
20789d4139c7SAlexey Dobriyan 	INIT_LIST_HEAD(&net->xfrm.state_all);
20799d4139c7SAlexey Dobriyan 
2080f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
2081f034b5d4SDavid S. Miller 
208273d189dcSAlexey Dobriyan 	net->xfrm.state_bydst = xfrm_hash_alloc(sz);
208373d189dcSAlexey Dobriyan 	if (!net->xfrm.state_bydst)
208473d189dcSAlexey Dobriyan 		goto out_bydst;
2085d320bbb3SAlexey Dobriyan 	net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2086d320bbb3SAlexey Dobriyan 	if (!net->xfrm.state_bysrc)
2087d320bbb3SAlexey Dobriyan 		goto out_bysrc;
2088b754a4fdSAlexey Dobriyan 	net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2089b754a4fdSAlexey Dobriyan 	if (!net->xfrm.state_byspi)
2090b754a4fdSAlexey Dobriyan 		goto out_byspi;
2091f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2092f034b5d4SDavid S. Miller 
2093c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
2094d62ddc21SAlexey Dobriyan 	return 0;
209573d189dcSAlexey Dobriyan 
2096b754a4fdSAlexey Dobriyan out_byspi:
2097b754a4fdSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bysrc, sz);
2098d320bbb3SAlexey Dobriyan out_bysrc:
2099d320bbb3SAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bydst, sz);
210073d189dcSAlexey Dobriyan out_bydst:
210173d189dcSAlexey Dobriyan 	return -ENOMEM;
2102d62ddc21SAlexey Dobriyan }
2103d62ddc21SAlexey Dobriyan 
2104d62ddc21SAlexey Dobriyan void xfrm_state_fini(struct net *net)
2105d62ddc21SAlexey Dobriyan {
210673d189dcSAlexey Dobriyan 	unsigned int sz;
210773d189dcSAlexey Dobriyan 
21089d4139c7SAlexey Dobriyan 	WARN_ON(!list_empty(&net->xfrm.state_all));
210973d189dcSAlexey Dobriyan 
211073d189dcSAlexey Dobriyan 	sz = (xfrm_state_hmask + 1) * sizeof(struct hlist_head);
2111b754a4fdSAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2112b754a4fdSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_byspi, sz);
2113d320bbb3SAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2114d320bbb3SAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bysrc, sz);
211573d189dcSAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_bydst));
211673d189dcSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bydst, sz);
21171da177e4SLinus Torvalds }
21181da177e4SLinus Torvalds 
2119ab5f5e8bSJoy Latten #ifdef CONFIG_AUDITSYSCALL
2120cf35f43eSIlpo Järvinen static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2121ab5f5e8bSJoy Latten 				     struct audit_buffer *audit_buf)
2122ab5f5e8bSJoy Latten {
212368277accSPaul Moore 	struct xfrm_sec_ctx *ctx = x->security;
212468277accSPaul Moore 	u32 spi = ntohl(x->id.spi);
212568277accSPaul Moore 
212668277accSPaul Moore 	if (ctx)
2127ab5f5e8bSJoy Latten 		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
212868277accSPaul Moore 				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
2129ab5f5e8bSJoy Latten 
2130ab5f5e8bSJoy Latten 	switch(x->props.family) {
2131ab5f5e8bSJoy Latten 	case AF_INET:
213221454aaaSHarvey Harrison 		audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
213321454aaaSHarvey Harrison 				 &x->props.saddr.a4, &x->id.daddr.a4);
2134ab5f5e8bSJoy Latten 		break;
2135ab5f5e8bSJoy Latten 	case AF_INET6:
21365b095d98SHarvey Harrison 		audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
2137fdb46ee7SHarvey Harrison 				 x->props.saddr.a6, x->id.daddr.a6);
2138ab5f5e8bSJoy Latten 		break;
2139ab5f5e8bSJoy Latten 	}
214068277accSPaul Moore 
214168277accSPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2142ab5f5e8bSJoy Latten }
2143ab5f5e8bSJoy Latten 
2144cf35f43eSIlpo Järvinen static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2145afeb14b4SPaul Moore 				      struct audit_buffer *audit_buf)
2146afeb14b4SPaul Moore {
2147afeb14b4SPaul Moore 	struct iphdr *iph4;
2148afeb14b4SPaul Moore 	struct ipv6hdr *iph6;
2149afeb14b4SPaul Moore 
2150afeb14b4SPaul Moore 	switch (family) {
2151afeb14b4SPaul Moore 	case AF_INET:
2152afeb14b4SPaul Moore 		iph4 = ip_hdr(skb);
215321454aaaSHarvey Harrison 		audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
215421454aaaSHarvey Harrison 				 &iph4->saddr, &iph4->daddr);
2155afeb14b4SPaul Moore 		break;
2156afeb14b4SPaul Moore 	case AF_INET6:
2157afeb14b4SPaul Moore 		iph6 = ipv6_hdr(skb);
2158afeb14b4SPaul Moore 		audit_log_format(audit_buf,
21595b095d98SHarvey Harrison 				 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
2160fdb46ee7SHarvey Harrison 				 &iph6->saddr,&iph6->daddr,
2161afeb14b4SPaul Moore 				 iph6->flow_lbl[0] & 0x0f,
2162afeb14b4SPaul Moore 				 iph6->flow_lbl[1],
2163afeb14b4SPaul Moore 				 iph6->flow_lbl[2]);
2164afeb14b4SPaul Moore 		break;
2165afeb14b4SPaul Moore 	}
2166afeb14b4SPaul Moore }
2167afeb14b4SPaul Moore 
216868277accSPaul Moore void xfrm_audit_state_add(struct xfrm_state *x, int result,
21692532386fSEric Paris 			  uid_t auid, u32 sessionid, u32 secid)
2170ab5f5e8bSJoy Latten {
2171ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2172ab5f5e8bSJoy Latten 
2173afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-add");
2174ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2175ab5f5e8bSJoy Latten 		return;
21762532386fSEric Paris 	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
2177afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2178afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2179ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2180ab5f5e8bSJoy Latten }
2181ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2182ab5f5e8bSJoy Latten 
218368277accSPaul Moore void xfrm_audit_state_delete(struct xfrm_state *x, int result,
21842532386fSEric Paris 			     uid_t auid, u32 sessionid, u32 secid)
2185ab5f5e8bSJoy Latten {
2186ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2187ab5f5e8bSJoy Latten 
2188afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-delete");
2189ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2190ab5f5e8bSJoy Latten 		return;
21912532386fSEric Paris 	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
2192afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2193afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2194ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2195ab5f5e8bSJoy Latten }
2196ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2197afeb14b4SPaul Moore 
2198afeb14b4SPaul Moore void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2199afeb14b4SPaul Moore 				      struct sk_buff *skb)
2200afeb14b4SPaul Moore {
2201afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2202afeb14b4SPaul Moore 	u32 spi;
2203afeb14b4SPaul Moore 
2204afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replay-overflow");
2205afeb14b4SPaul Moore 	if (audit_buf == NULL)
2206afeb14b4SPaul Moore 		return;
2207afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2208afeb14b4SPaul Moore 	/* don't record the sequence number because it's inherent in this kind
2209afeb14b4SPaul Moore 	 * of audit message */
2210afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2211afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2212afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2213afeb14b4SPaul Moore }
2214afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2215afeb14b4SPaul Moore 
2216afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
2217afeb14b4SPaul Moore 			     struct sk_buff *skb, __be32 net_seq)
2218afeb14b4SPaul Moore {
2219afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2220afeb14b4SPaul Moore 	u32 spi;
2221afeb14b4SPaul Moore 
2222afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replayed-pkt");
2223afeb14b4SPaul Moore 	if (audit_buf == NULL)
2224afeb14b4SPaul Moore 		return;
2225afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2226afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2227afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2228afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2229afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2230afeb14b4SPaul Moore }
2231afeb14b4SPaul Moore 
2232afeb14b4SPaul Moore void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2233afeb14b4SPaul Moore {
2234afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2235afeb14b4SPaul Moore 
2236afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2237afeb14b4SPaul Moore 	if (audit_buf == NULL)
2238afeb14b4SPaul Moore 		return;
2239afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2240afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2241afeb14b4SPaul Moore }
2242afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2243afeb14b4SPaul Moore 
2244afeb14b4SPaul Moore void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2245afeb14b4SPaul Moore 			       __be32 net_spi, __be32 net_seq)
2246afeb14b4SPaul Moore {
2247afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2248afeb14b4SPaul Moore 	u32 spi;
2249afeb14b4SPaul Moore 
2250afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2251afeb14b4SPaul Moore 	if (audit_buf == NULL)
2252afeb14b4SPaul Moore 		return;
2253afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2254afeb14b4SPaul Moore 	spi = ntohl(net_spi);
2255afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2256afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2257afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2258afeb14b4SPaul Moore }
2259afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2260afeb14b4SPaul Moore 
2261afeb14b4SPaul Moore void xfrm_audit_state_icvfail(struct xfrm_state *x,
2262afeb14b4SPaul Moore 			      struct sk_buff *skb, u8 proto)
2263afeb14b4SPaul Moore {
2264afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2265afeb14b4SPaul Moore 	__be32 net_spi;
2266afeb14b4SPaul Moore 	__be32 net_seq;
2267afeb14b4SPaul Moore 
2268afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-icv-failure");
2269afeb14b4SPaul Moore 	if (audit_buf == NULL)
2270afeb14b4SPaul Moore 		return;
2271afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2272afeb14b4SPaul Moore 	if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2273afeb14b4SPaul Moore 		u32 spi = ntohl(net_spi);
2274afeb14b4SPaul Moore 		audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2275afeb14b4SPaul Moore 				 spi, spi, ntohl(net_seq));
2276afeb14b4SPaul Moore 	}
2277afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2278afeb14b4SPaul Moore }
2279afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
2280ab5f5e8bSJoy Latten #endif /* CONFIG_AUDITSYSCALL */
2281