xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision 85168c00)
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>
249e0d57fdSYury Polyanskiy #include <linux/ktime.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
269e0d57fdSYury Polyanskiy #include <linux/interrupt.h>
279e0d57fdSYury Polyanskiy #include <linux/kernel.h>
281da177e4SLinus Torvalds 
2944e36b42SDavid S. Miller #include "xfrm_hash.h"
3044e36b42SDavid S. Miller 
311da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
34a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
351da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
361da177e4SLinus Torvalds  */
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
391da177e4SLinus Torvalds 
40f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
411da177e4SLinus Torvalds 
4217c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
4317c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
4417c2a42aSHerbert Xu 
4564d0cd00SAlexey Dobriyan static inline unsigned int xfrm_dst_hash(struct net *net,
462ab38503SDavid S. Miller 					 const xfrm_address_t *daddr,
472ab38503SDavid S. Miller 					 const xfrm_address_t *saddr,
48c1969f29SDavid S. Miller 					 u32 reqid,
49a624c108SDavid S. Miller 					 unsigned short family)
50a624c108SDavid S. Miller {
5164d0cd00SAlexey Dobriyan 	return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
52a624c108SDavid S. Miller }
53a624c108SDavid S. Miller 
5464d0cd00SAlexey Dobriyan static inline unsigned int xfrm_src_hash(struct net *net,
552ab38503SDavid S. Miller 					 const xfrm_address_t *daddr,
562ab38503SDavid S. Miller 					 const xfrm_address_t *saddr,
5744e36b42SDavid S. Miller 					 unsigned short family)
58f034b5d4SDavid S. Miller {
5964d0cd00SAlexey Dobriyan 	return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
60f034b5d4SDavid S. Miller }
61f034b5d4SDavid S. Miller 
622575b654SDavid S. Miller static inline unsigned int
632ab38503SDavid S. Miller xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr,
642ab38503SDavid S. Miller 	      __be32 spi, u8 proto, unsigned short family)
65f034b5d4SDavid S. Miller {
6664d0cd00SAlexey Dobriyan 	return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
67f034b5d4SDavid S. Miller }
68f034b5d4SDavid S. Miller 
69f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
70f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
71f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
72f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
73f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
74f034b5d4SDavid S. Miller {
75f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
76f034b5d4SDavid S. Miller 	struct xfrm_state *x;
77f034b5d4SDavid S. Miller 
78f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
79f034b5d4SDavid S. Miller 		unsigned int h;
80f034b5d4SDavid S. Miller 
81c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
82c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
83c1969f29SDavid S. Miller 				    nhashmask);
84f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
85f034b5d4SDavid S. Miller 
86667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
87667bbcb6SMasahide NAKAMURA 				    x->props.family,
88f034b5d4SDavid S. Miller 				    nhashmask);
89f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
90f034b5d4SDavid S. Miller 
917b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
927b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
937b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
947b4dc360SMasahide NAKAMURA 					    nhashmask);
95f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
96f034b5d4SDavid S. Miller 		}
97f034b5d4SDavid S. Miller 	}
987b4dc360SMasahide NAKAMURA }
99f034b5d4SDavid S. Miller 
10063082733SAlexey Dobriyan static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
101f034b5d4SDavid S. Miller {
10263082733SAlexey Dobriyan 	return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
103f034b5d4SDavid S. Miller }
104f034b5d4SDavid S. Miller 
105f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
106f034b5d4SDavid S. Miller 
10763082733SAlexey Dobriyan static void xfrm_hash_resize(struct work_struct *work)
108f034b5d4SDavid S. Miller {
10963082733SAlexey Dobriyan 	struct net *net = container_of(work, struct net, xfrm.state_hash_work);
110f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
111f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
112f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
113f034b5d4SDavid S. Miller 	int i;
114f034b5d4SDavid S. Miller 
115f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
116f034b5d4SDavid S. Miller 
11763082733SAlexey Dobriyan 	nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
11844e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
119f034b5d4SDavid S. Miller 	if (!ndst)
120f034b5d4SDavid S. Miller 		goto out_unlock;
12144e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
122f034b5d4SDavid S. Miller 	if (!nsrc) {
12344e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
124f034b5d4SDavid S. Miller 		goto out_unlock;
125f034b5d4SDavid S. Miller 	}
12644e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
127f034b5d4SDavid S. Miller 	if (!nspi) {
12844e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
12944e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
130f034b5d4SDavid S. Miller 		goto out_unlock;
131f034b5d4SDavid S. Miller 	}
132f034b5d4SDavid S. Miller 
133f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
134f034b5d4SDavid S. Miller 
135f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
13663082733SAlexey Dobriyan 	for (i = net->xfrm.state_hmask; i >= 0; i--)
13763082733SAlexey Dobriyan 		xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
138f034b5d4SDavid S. Miller 				   nhashmask);
139f034b5d4SDavid S. Miller 
14063082733SAlexey Dobriyan 	odst = net->xfrm.state_bydst;
14163082733SAlexey Dobriyan 	osrc = net->xfrm.state_bysrc;
14263082733SAlexey Dobriyan 	ospi = net->xfrm.state_byspi;
14363082733SAlexey Dobriyan 	ohashmask = net->xfrm.state_hmask;
144f034b5d4SDavid S. Miller 
14563082733SAlexey Dobriyan 	net->xfrm.state_bydst = ndst;
14663082733SAlexey Dobriyan 	net->xfrm.state_bysrc = nsrc;
14763082733SAlexey Dobriyan 	net->xfrm.state_byspi = nspi;
14863082733SAlexey Dobriyan 	net->xfrm.state_hmask = nhashmask;
149f034b5d4SDavid S. Miller 
150f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
151f034b5d4SDavid S. Miller 
152f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
15344e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
15444e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
15544e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
156f034b5d4SDavid S. Miller 
157f034b5d4SDavid S. Miller out_unlock:
158f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
159f034b5d4SDavid S. Miller }
160f034b5d4SDavid S. Miller 
16144abdc30SCong Wang static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
16244abdc30SCong Wang static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1651da177e4SLinus Torvalds 
16653bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1671da177e4SLinus Torvalds 
168980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
16915e47304SEric W. Biederman void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
1701da177e4SLinus Torvalds 
171aa5d62ccSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
172aa5d62ccSHerbert Xu {
173aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
174aa5d62ccSHerbert Xu 	if (unlikely(family >= NPROTO))
175aa5d62ccSHerbert Xu 		return NULL;
17644abdc30SCong Wang 	spin_lock_bh(&xfrm_state_afinfo_lock);
177aa5d62ccSHerbert Xu 	afinfo = xfrm_state_afinfo[family];
178aa5d62ccSHerbert Xu 	if (unlikely(!afinfo))
17944abdc30SCong Wang 		spin_unlock_bh(&xfrm_state_afinfo_lock);
180aa5d62ccSHerbert Xu 	return afinfo;
181aa5d62ccSHerbert Xu }
182aa5d62ccSHerbert Xu 
183aa5d62ccSHerbert Xu static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
184aa5d62ccSHerbert Xu {
18544abdc30SCong Wang 	spin_unlock_bh(&xfrm_state_afinfo_lock);
186aa5d62ccSHerbert Xu }
187aa5d62ccSHerbert Xu 
188533cb5b0SEric Dumazet int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
189aa5d62ccSHerbert Xu {
190aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
191533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
192aa5d62ccSHerbert Xu 	int err = 0;
193aa5d62ccSHerbert Xu 
194aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
195aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
196aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
197aa5d62ccSHerbert Xu 
198aa5d62ccSHerbert Xu 	if (likely(typemap[type->proto] == NULL))
199aa5d62ccSHerbert Xu 		typemap[type->proto] = type;
200aa5d62ccSHerbert Xu 	else
201aa5d62ccSHerbert Xu 		err = -EEXIST;
202aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
203aa5d62ccSHerbert Xu 	return err;
204aa5d62ccSHerbert Xu }
205aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_type);
206aa5d62ccSHerbert Xu 
207533cb5b0SEric Dumazet int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
208aa5d62ccSHerbert Xu {
209aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
210533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
211aa5d62ccSHerbert Xu 	int err = 0;
212aa5d62ccSHerbert Xu 
213aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
214aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
215aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
216aa5d62ccSHerbert Xu 
217aa5d62ccSHerbert Xu 	if (unlikely(typemap[type->proto] != type))
218aa5d62ccSHerbert Xu 		err = -ENOENT;
219aa5d62ccSHerbert Xu 	else
220aa5d62ccSHerbert Xu 		typemap[type->proto] = NULL;
221aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
222aa5d62ccSHerbert Xu 	return err;
223aa5d62ccSHerbert Xu }
224aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_type);
225aa5d62ccSHerbert Xu 
226533cb5b0SEric Dumazet static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
227aa5d62ccSHerbert Xu {
228aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
229533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
230533cb5b0SEric Dumazet 	const struct xfrm_type *type;
231aa5d62ccSHerbert Xu 	int modload_attempted = 0;
232aa5d62ccSHerbert Xu 
233aa5d62ccSHerbert Xu retry:
234aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
235aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
236aa5d62ccSHerbert Xu 		return NULL;
237aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
238aa5d62ccSHerbert Xu 
239aa5d62ccSHerbert Xu 	type = typemap[proto];
240aa5d62ccSHerbert Xu 	if (unlikely(type && !try_module_get(type->owner)))
241aa5d62ccSHerbert Xu 		type = NULL;
242aa5d62ccSHerbert Xu 	if (!type && !modload_attempted) {
243aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
244aa5d62ccSHerbert Xu 		request_module("xfrm-type-%d-%d", family, proto);
245aa5d62ccSHerbert Xu 		modload_attempted = 1;
246aa5d62ccSHerbert Xu 		goto retry;
247aa5d62ccSHerbert Xu 	}
248aa5d62ccSHerbert Xu 
249aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
250aa5d62ccSHerbert Xu 	return type;
251aa5d62ccSHerbert Xu }
252aa5d62ccSHerbert Xu 
253533cb5b0SEric Dumazet static void xfrm_put_type(const struct xfrm_type *type)
254aa5d62ccSHerbert Xu {
255aa5d62ccSHerbert Xu 	module_put(type->owner);
256aa5d62ccSHerbert Xu }
257aa5d62ccSHerbert Xu 
258aa5d62ccSHerbert Xu int xfrm_register_mode(struct xfrm_mode *mode, int family)
259aa5d62ccSHerbert Xu {
260aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
261aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
262aa5d62ccSHerbert Xu 	int err;
263aa5d62ccSHerbert Xu 
264aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
265aa5d62ccSHerbert Xu 		return -EINVAL;
266aa5d62ccSHerbert Xu 
267aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
268aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
269aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
270aa5d62ccSHerbert Xu 
271aa5d62ccSHerbert Xu 	err = -EEXIST;
272aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
27317c2a42aSHerbert Xu 	if (modemap[mode->encap])
27417c2a42aSHerbert Xu 		goto out;
27517c2a42aSHerbert Xu 
27617c2a42aSHerbert Xu 	err = -ENOENT;
27717c2a42aSHerbert Xu 	if (!try_module_get(afinfo->owner))
27817c2a42aSHerbert Xu 		goto out;
27917c2a42aSHerbert Xu 
28017c2a42aSHerbert Xu 	mode->afinfo = afinfo;
281aa5d62ccSHerbert Xu 	modemap[mode->encap] = mode;
282aa5d62ccSHerbert Xu 	err = 0;
283aa5d62ccSHerbert Xu 
28417c2a42aSHerbert Xu out:
285aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
286aa5d62ccSHerbert Xu 	return err;
287aa5d62ccSHerbert Xu }
288aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_mode);
289aa5d62ccSHerbert Xu 
290aa5d62ccSHerbert Xu int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
291aa5d62ccSHerbert Xu {
292aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
293aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
294aa5d62ccSHerbert Xu 	int err;
295aa5d62ccSHerbert Xu 
296aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
297aa5d62ccSHerbert Xu 		return -EINVAL;
298aa5d62ccSHerbert Xu 
299aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
300aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
301aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
302aa5d62ccSHerbert Xu 
303aa5d62ccSHerbert Xu 	err = -ENOENT;
304aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
305aa5d62ccSHerbert Xu 	if (likely(modemap[mode->encap] == mode)) {
306aa5d62ccSHerbert Xu 		modemap[mode->encap] = NULL;
30717c2a42aSHerbert Xu 		module_put(mode->afinfo->owner);
308aa5d62ccSHerbert Xu 		err = 0;
309aa5d62ccSHerbert Xu 	}
310aa5d62ccSHerbert Xu 
311aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
312aa5d62ccSHerbert Xu 	return err;
313aa5d62ccSHerbert Xu }
314aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_mode);
315aa5d62ccSHerbert Xu 
316aa5d62ccSHerbert Xu static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
317aa5d62ccSHerbert Xu {
318aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
319aa5d62ccSHerbert Xu 	struct xfrm_mode *mode;
320aa5d62ccSHerbert Xu 	int modload_attempted = 0;
321aa5d62ccSHerbert Xu 
322aa5d62ccSHerbert Xu 	if (unlikely(encap >= XFRM_MODE_MAX))
323aa5d62ccSHerbert Xu 		return NULL;
324aa5d62ccSHerbert Xu 
325aa5d62ccSHerbert Xu retry:
326aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
327aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
328aa5d62ccSHerbert Xu 		return NULL;
329aa5d62ccSHerbert Xu 
330aa5d62ccSHerbert Xu 	mode = afinfo->mode_map[encap];
331aa5d62ccSHerbert Xu 	if (unlikely(mode && !try_module_get(mode->owner)))
332aa5d62ccSHerbert Xu 		mode = NULL;
333aa5d62ccSHerbert Xu 	if (!mode && !modload_attempted) {
334aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
335aa5d62ccSHerbert Xu 		request_module("xfrm-mode-%d-%d", family, encap);
336aa5d62ccSHerbert Xu 		modload_attempted = 1;
337aa5d62ccSHerbert Xu 		goto retry;
338aa5d62ccSHerbert Xu 	}
339aa5d62ccSHerbert Xu 
340aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
341aa5d62ccSHerbert Xu 	return mode;
342aa5d62ccSHerbert Xu }
343aa5d62ccSHerbert Xu 
344aa5d62ccSHerbert Xu static void xfrm_put_mode(struct xfrm_mode *mode)
345aa5d62ccSHerbert Xu {
346aa5d62ccSHerbert Xu 	module_put(mode->owner);
347aa5d62ccSHerbert Xu }
348aa5d62ccSHerbert Xu 
3491da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
3501da177e4SLinus Torvalds {
3519e0d57fdSYury Polyanskiy 	tasklet_hrtimer_cancel(&x->mtimer);
352a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
3531da177e4SLinus Torvalds 	kfree(x->aalg);
3541da177e4SLinus Torvalds 	kfree(x->ealg);
3551da177e4SLinus Torvalds 	kfree(x->calg);
3561da177e4SLinus Torvalds 	kfree(x->encap);
357060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
358d8647b79SSteffen Klassert 	kfree(x->replay_esn);
359d8647b79SSteffen Klassert 	kfree(x->preplay_esn);
36013996378SHerbert Xu 	if (x->inner_mode)
36113996378SHerbert Xu 		xfrm_put_mode(x->inner_mode);
362df9dcb45SKazunori MIYAZAWA 	if (x->inner_mode_iaf)
363df9dcb45SKazunori MIYAZAWA 		xfrm_put_mode(x->inner_mode_iaf);
36413996378SHerbert Xu 	if (x->outer_mode)
36513996378SHerbert Xu 		xfrm_put_mode(x->outer_mode);
3661da177e4SLinus Torvalds 	if (x->type) {
3671da177e4SLinus Torvalds 		x->type->destructor(x);
3681da177e4SLinus Torvalds 		xfrm_put_type(x->type);
3691da177e4SLinus Torvalds 	}
370df71837dSTrent Jaeger 	security_xfrm_state_free(x);
3711da177e4SLinus Torvalds 	kfree(x);
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
374c7837144SAlexey Dobriyan static void xfrm_state_gc_task(struct work_struct *work)
3751da177e4SLinus Torvalds {
376c7837144SAlexey Dobriyan 	struct net *net = container_of(work, struct net, xfrm.state_gc_work);
37712a169e7SHerbert Xu 	struct xfrm_state *x;
37812a169e7SHerbert Xu 	struct hlist_node *entry, *tmp;
37912a169e7SHerbert Xu 	struct hlist_head gc_list;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
382c7837144SAlexey Dobriyan 	hlist_move_list(&net->xfrm.state_gc_list, &gc_list);
3831da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
3841da177e4SLinus Torvalds 
38512a169e7SHerbert Xu 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
3861da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
3878f126e37SDavid S. Miller 
38850a30657SAlexey Dobriyan 	wake_up(&net->xfrm.km_waitq);
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
3921da177e4SLinus Torvalds {
3931da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
3941da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
3951da177e4SLinus Torvalds 	else
3961da177e4SLinus Torvalds 		return secs*HZ;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
3999e0d57fdSYury Polyanskiy static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
4001da177e4SLinus Torvalds {
4019e0d57fdSYury Polyanskiy 	struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);
4029e0d57fdSYury Polyanskiy 	struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer);
40398806f75SAlexey Dobriyan 	struct net *net = xs_net(x);
4049d729f72SJames Morris 	unsigned long now = get_seconds();
4051da177e4SLinus Torvalds 	long next = LONG_MAX;
4061da177e4SLinus Torvalds 	int warn = 0;
407161a09e7SJoy Latten 	int err = 0;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	spin_lock(&x->lock);
4101da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
4111da177e4SLinus Torvalds 		goto out;
4121da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
4131da177e4SLinus Torvalds 		goto expired;
4141da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
4151da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
4161da177e4SLinus Torvalds 			x->curlft.add_time - now;
417e3c0d047SFan Du 		if (tmo <= 0) {
418e3c0d047SFan Du 			if (x->xflags & XFRM_SOFT_EXPIRE) {
419e3c0d047SFan Du 				/* enter hard expire without soft expire first?!
420e3c0d047SFan Du 				 * setting a new date could trigger this.
421e3c0d047SFan Du 				 * workarbound: fix x->curflt.add_time by below:
422e3c0d047SFan Du 				 */
423e3c0d047SFan Du 				x->curlft.add_time = now - x->saved_tmo - 1;
424e3c0d047SFan Du 				tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
425e3c0d047SFan Du 			} else
4261da177e4SLinus Torvalds 				goto expired;
427e3c0d047SFan Du 		}
4281da177e4SLinus Torvalds 		if (tmo < next)
4291da177e4SLinus Torvalds 			next = tmo;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
4321da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
4331da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - 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->km.dying)
4401da177e4SLinus Torvalds 		goto resched;
4411da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
4421da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
4431da177e4SLinus Torvalds 			x->curlft.add_time - now;
444e3c0d047SFan Du 		if (tmo <= 0) {
4451da177e4SLinus Torvalds 			warn = 1;
446e3c0d047SFan Du 			x->xflags &= ~XFRM_SOFT_EXPIRE;
447e3c0d047SFan Du 		} else if (tmo < next) {
4481da177e4SLinus Torvalds 			next = tmo;
449e3c0d047SFan Du 			x->xflags |= XFRM_SOFT_EXPIRE;
450e3c0d047SFan Du 			x->saved_tmo = tmo;
451e3c0d047SFan Du 		}
4521da177e4SLinus Torvalds 	}
4531da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
4541da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
4551da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4561da177e4SLinus Torvalds 		if (tmo <= 0)
4571da177e4SLinus Torvalds 			warn = 1;
4581da177e4SLinus Torvalds 		else if (tmo < next)
4591da177e4SLinus Torvalds 			next = tmo;
4601da177e4SLinus Torvalds 	}
4611da177e4SLinus Torvalds 
4624666faabSHerbert Xu 	x->km.dying = warn;
4631da177e4SLinus Torvalds 	if (warn)
46453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
4651da177e4SLinus Torvalds resched:
4669e0d57fdSYury Polyanskiy 	if (next != LONG_MAX){
4679e0d57fdSYury Polyanskiy 		tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);
4689e0d57fdSYury Polyanskiy 	}
469a47f0ce0SDavid S. Miller 
4701da177e4SLinus Torvalds 	goto out;
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds expired:
4731da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
4741da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
47598806f75SAlexey Dobriyan 		wake_up(&net->xfrm.km_waitq);
4761da177e4SLinus Torvalds 		next = 2;
4771da177e4SLinus Torvalds 		goto resched;
4781da177e4SLinus Torvalds 	}
479161a09e7SJoy Latten 
480161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
481161a09e7SJoy Latten 	if (!err && x->id.spi)
48253bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
4831da177e4SLinus Torvalds 
484ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
4852532386fSEric Paris 				audit_get_loginuid(current),
4862532386fSEric Paris 				audit_get_sessionid(current), 0);
487161a09e7SJoy Latten 
4881da177e4SLinus Torvalds out:
4891da177e4SLinus Torvalds 	spin_unlock(&x->lock);
4909e0d57fdSYury Polyanskiy 	return HRTIMER_NORESTART;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
4930ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
4940ac84752SDavid S. Miller 
495673c09beSAlexey Dobriyan struct xfrm_state *xfrm_state_alloc(struct net *net)
4961da177e4SLinus Torvalds {
4971da177e4SLinus Torvalds 	struct xfrm_state *x;
4981da177e4SLinus Torvalds 
4990da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	if (x) {
502673c09beSAlexey Dobriyan 		write_pnet(&x->xs_net, net);
5031da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
5041da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
50512a169e7SHerbert Xu 		INIT_LIST_HEAD(&x->km.all);
5068f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
5078f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
5088f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
5099e0d57fdSYury Polyanskiy 		tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_REALTIME, HRTIMER_MODE_ABS);
510b24b8a24SPavel Emelyanov 		setup_timer(&x->rtimer, xfrm_replay_timer_handler,
511b24b8a24SPavel Emelyanov 				(unsigned long)x);
5129d729f72SJames Morris 		x->curlft.add_time = get_seconds();
5131da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
5141da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
5151da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
5161da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
517f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
518f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
519df9dcb45SKazunori MIYAZAWA 		x->inner_mode = NULL;
520df9dcb45SKazunori MIYAZAWA 		x->inner_mode_iaf = NULL;
5211da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
5221da177e4SLinus Torvalds 	}
5231da177e4SLinus Torvalds 	return x;
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
5281da177e4SLinus Torvalds {
52998806f75SAlexey Dobriyan 	struct net *net = xs_net(x);
53098806f75SAlexey Dobriyan 
531547b792cSIlpo Järvinen 	WARN_ON(x->km.state != XFRM_STATE_DEAD);
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
53498806f75SAlexey Dobriyan 	hlist_add_head(&x->gclist, &net->xfrm.state_gc_list);
5351da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
53698806f75SAlexey Dobriyan 	schedule_work(&net->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 {
54298806f75SAlexey Dobriyan 	struct net *net = xs_net(x);
54326b15dadSJamal Hadi Salim 	int err = -ESRCH;
54426b15dadSJamal Hadi Salim 
5451da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
5461da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
5471da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
54812a169e7SHerbert Xu 		list_del(&x->km.all);
5498f126e37SDavid S. Miller 		hlist_del(&x->bydst);
5508f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
551a47f0ce0SDavid S. Miller 		if (x->id.spi)
5528f126e37SDavid S. Miller 			hlist_del(&x->byspi);
55398806f75SAlexey Dobriyan 		net->xfrm.state_num--;
5541da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
5571da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
5581da177e4SLinus Torvalds 		 * is what we are dropping here.
5591da177e4SLinus Torvalds 		 */
5605dba4797SPatrick McHardy 		xfrm_state_put(x);
56126b15dadSJamal Hadi Salim 		err = 0;
5621da177e4SLinus Torvalds 	}
5631da177e4SLinus Torvalds 
56426b15dadSJamal Hadi Salim 	return err;
56526b15dadSJamal Hadi Salim }
56653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
56726b15dadSJamal Hadi Salim 
56826b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
5691da177e4SLinus Torvalds {
57026b15dadSJamal Hadi Salim 	int err;
57126b15dadSJamal Hadi Salim 
5721da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
57326b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
5741da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
57526b15dadSJamal Hadi Salim 
57626b15dadSJamal Hadi Salim 	return err;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
5791da177e4SLinus Torvalds 
5804aa2e62cSJoy Latten #ifdef CONFIG_SECURITY_NETWORK_XFRM
5814aa2e62cSJoy Latten static inline int
5820e602451SAlexey Dobriyan xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
5831da177e4SLinus Torvalds {
5844aa2e62cSJoy Latten 	int i, err = 0;
5854aa2e62cSJoy Latten 
5860e602451SAlexey Dobriyan 	for (i = 0; i <= net->xfrm.state_hmask; i++) {
5874aa2e62cSJoy Latten 		struct hlist_node *entry;
5884aa2e62cSJoy Latten 		struct xfrm_state *x;
5894aa2e62cSJoy Latten 
5900e602451SAlexey Dobriyan 		hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
5914aa2e62cSJoy Latten 			if (xfrm_id_proto_match(x->id.proto, proto) &&
5924aa2e62cSJoy Latten 			   (err = security_xfrm_state_delete(x)) != 0) {
593ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, 0,
594ab5f5e8bSJoy Latten 							audit_info->loginuid,
5952532386fSEric Paris 							audit_info->sessionid,
596ab5f5e8bSJoy Latten 							audit_info->secid);
5974aa2e62cSJoy Latten 				return err;
5984aa2e62cSJoy Latten 			}
5994aa2e62cSJoy Latten 		}
6004aa2e62cSJoy Latten 	}
6014aa2e62cSJoy Latten 
6024aa2e62cSJoy Latten 	return err;
6034aa2e62cSJoy Latten }
6044aa2e62cSJoy Latten #else
6054aa2e62cSJoy Latten static inline int
6060e602451SAlexey Dobriyan xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
6074aa2e62cSJoy Latten {
6084aa2e62cSJoy Latten 	return 0;
6094aa2e62cSJoy Latten }
6104aa2e62cSJoy Latten #endif
6114aa2e62cSJoy Latten 
6120e602451SAlexey Dobriyan int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
6134aa2e62cSJoy Latten {
6149e64cc95SJamal Hadi Salim 	int i, err = 0, cnt = 0;
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6170e602451SAlexey Dobriyan 	err = xfrm_state_flush_secctx_check(net, proto, audit_info);
6184aa2e62cSJoy Latten 	if (err)
6194aa2e62cSJoy Latten 		goto out;
6204aa2e62cSJoy Latten 
6219e64cc95SJamal Hadi Salim 	err = -ESRCH;
6220e602451SAlexey Dobriyan 	for (i = 0; i <= net->xfrm.state_hmask; i++) {
6238f126e37SDavid S. Miller 		struct hlist_node *entry;
6248f126e37SDavid S. Miller 		struct xfrm_state *x;
6251da177e4SLinus Torvalds restart:
6260e602451SAlexey Dobriyan 		hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
6271da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
6285794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
6291da177e4SLinus Torvalds 				xfrm_state_hold(x);
6301da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
6311da177e4SLinus Torvalds 
632161a09e7SJoy Latten 				err = xfrm_state_delete(x);
633ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, err ? 0 : 1,
634ab5f5e8bSJoy Latten 							audit_info->loginuid,
6352532386fSEric Paris 							audit_info->sessionid,
636ab5f5e8bSJoy Latten 							audit_info->secid);
6371da177e4SLinus Torvalds 				xfrm_state_put(x);
6389e64cc95SJamal Hadi Salim 				if (!err)
6399e64cc95SJamal Hadi Salim 					cnt++;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
6421da177e4SLinus Torvalds 				goto restart;
6431da177e4SLinus Torvalds 			}
6441da177e4SLinus Torvalds 		}
6451da177e4SLinus Torvalds 	}
6469e64cc95SJamal Hadi Salim 	if (cnt)
6474aa2e62cSJoy Latten 		err = 0;
6484aa2e62cSJoy Latten 
6494aa2e62cSJoy Latten out:
6501da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6510e602451SAlexey Dobriyan 	wake_up(&net->xfrm.km_waitq);
6524aa2e62cSJoy Latten 	return err;
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
6551da177e4SLinus Torvalds 
656e071041bSAlexey Dobriyan void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
65728d8909bSJamal Hadi Salim {
65828d8909bSJamal Hadi Salim 	spin_lock_bh(&xfrm_state_lock);
659e071041bSAlexey Dobriyan 	si->sadcnt = net->xfrm.state_num;
660e071041bSAlexey Dobriyan 	si->sadhcnt = net->xfrm.state_hmask;
66128d8909bSJamal Hadi Salim 	si->sadhmcnt = xfrm_state_hashmax;
66228d8909bSJamal Hadi Salim 	spin_unlock_bh(&xfrm_state_lock);
66328d8909bSJamal Hadi Salim }
66428d8909bSJamal Hadi Salim EXPORT_SYMBOL(xfrm_sad_getinfo);
66528d8909bSJamal Hadi Salim 
6661da177e4SLinus Torvalds static int
6671a898592SDavid S. Miller xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl,
66804686013SDavid S. Miller 		    const struct xfrm_tmpl *tmpl,
66933765d06SDavid S. Miller 		    const xfrm_address_t *daddr, const xfrm_address_t *saddr,
6701da177e4SLinus Torvalds 		    unsigned short family)
6711da177e4SLinus Torvalds {
6721da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6731da177e4SLinus Torvalds 	if (!afinfo)
6741da177e4SLinus Torvalds 		return -1;
6758444cf71SThomas Egerer 	afinfo->init_tempsel(&x->sel, fl);
6768444cf71SThomas Egerer 
6778444cf71SThomas Egerer 	if (family != tmpl->encap_family) {
6788444cf71SThomas Egerer 		xfrm_state_put_afinfo(afinfo);
6798444cf71SThomas Egerer 		afinfo = xfrm_state_get_afinfo(tmpl->encap_family);
6808444cf71SThomas Egerer 		if (!afinfo)
6818444cf71SThomas Egerer 			return -1;
6828444cf71SThomas Egerer 	}
6838444cf71SThomas Egerer 	afinfo->init_temprop(x, tmpl, daddr, saddr);
6841da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6851da177e4SLinus Torvalds 	return 0;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6889aa60088SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
6899aa60088SDavid S. Miller 					      const xfrm_address_t *daddr,
6909aa60088SDavid S. Miller 					      __be32 spi, u8 proto,
6919aa60088SDavid S. Miller 					      unsigned short family)
692edcd5821SDavid S. Miller {
693221df1edSAlexey Dobriyan 	unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
694edcd5821SDavid S. Miller 	struct xfrm_state *x;
6958f126e37SDavid S. Miller 	struct hlist_node *entry;
696edcd5821SDavid S. Miller 
697221df1edSAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_byspi+h, byspi) {
698edcd5821SDavid S. Miller 		if (x->props.family != family ||
699edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
7001802571bSWei Yongjun 		    x->id.proto     != proto ||
7011802571bSWei Yongjun 		    xfrm_addr_cmp(&x->id.daddr, daddr, family))
702edcd5821SDavid S. Miller 			continue;
703edcd5821SDavid S. Miller 
7043d6acfa7SJamal Hadi Salim 		if ((mark & x->mark.m) != x->mark.v)
7053d6acfa7SJamal Hadi Salim 			continue;
706edcd5821SDavid S. Miller 		xfrm_state_hold(x);
707edcd5821SDavid S. Miller 		return x;
708edcd5821SDavid S. Miller 	}
709edcd5821SDavid S. Miller 
710edcd5821SDavid S. Miller 	return NULL;
711edcd5821SDavid S. Miller }
712edcd5821SDavid S. Miller 
7139aa60088SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
7149aa60088SDavid S. Miller 						     const xfrm_address_t *daddr,
7159aa60088SDavid S. Miller 						     const xfrm_address_t *saddr,
7169aa60088SDavid S. Miller 						     u8 proto, unsigned short family)
717edcd5821SDavid S. Miller {
718221df1edSAlexey Dobriyan 	unsigned int h = xfrm_src_hash(net, daddr, saddr, family);
719edcd5821SDavid S. Miller 	struct xfrm_state *x;
7208f126e37SDavid S. Miller 	struct hlist_node *entry;
721edcd5821SDavid S. Miller 
722221df1edSAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_bysrc+h, bysrc) {
723edcd5821SDavid S. Miller 		if (x->props.family != family ||
7241802571bSWei Yongjun 		    x->id.proto     != proto ||
7251802571bSWei Yongjun 		    xfrm_addr_cmp(&x->id.daddr, daddr, family) ||
7261802571bSWei Yongjun 		    xfrm_addr_cmp(&x->props.saddr, saddr, family))
727edcd5821SDavid S. Miller 			continue;
728edcd5821SDavid S. Miller 
7293d6acfa7SJamal Hadi Salim 		if ((mark & x->mark.m) != x->mark.v)
7303d6acfa7SJamal Hadi Salim 			continue;
731edcd5821SDavid S. Miller 		xfrm_state_hold(x);
732edcd5821SDavid S. Miller 		return x;
733edcd5821SDavid S. Miller 	}
734edcd5821SDavid S. Miller 
735edcd5821SDavid S. Miller 	return NULL;
736edcd5821SDavid S. Miller }
737edcd5821SDavid S. Miller 
738edcd5821SDavid S. Miller static inline struct xfrm_state *
739edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
740edcd5821SDavid S. Miller {
741221df1edSAlexey Dobriyan 	struct net *net = xs_net(x);
742bd55775cSJamal Hadi Salim 	u32 mark = x->mark.v & x->mark.m;
743221df1edSAlexey Dobriyan 
744edcd5821SDavid S. Miller 	if (use_spi)
745bd55775cSJamal Hadi Salim 		return __xfrm_state_lookup(net, mark, &x->id.daddr,
746bd55775cSJamal Hadi Salim 					   x->id.spi, x->id.proto, family);
747edcd5821SDavid S. Miller 	else
748bd55775cSJamal Hadi Salim 		return __xfrm_state_lookup_byaddr(net, mark,
749bd55775cSJamal Hadi Salim 						  &x->id.daddr,
750edcd5821SDavid S. Miller 						  &x->props.saddr,
751edcd5821SDavid S. Miller 						  x->id.proto, family);
752edcd5821SDavid S. Miller }
753edcd5821SDavid S. Miller 
75498806f75SAlexey Dobriyan static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
7552fab22f2SPatrick McHardy {
7562fab22f2SPatrick McHardy 	if (have_hash_collision &&
75798806f75SAlexey Dobriyan 	    (net->xfrm.state_hmask + 1) < xfrm_state_hashmax &&
75898806f75SAlexey Dobriyan 	    net->xfrm.state_num > net->xfrm.state_hmask)
75998806f75SAlexey Dobriyan 		schedule_work(&net->xfrm.state_hash_work);
7602fab22f2SPatrick McHardy }
7612fab22f2SPatrick McHardy 
76208ec9af1SDavid S. Miller static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
7634a08ab0fSDavid S. Miller 			       const struct flowi *fl, unsigned short family,
76408ec9af1SDavid S. Miller 			       struct xfrm_state **best, int *acq_in_progress,
76508ec9af1SDavid S. Miller 			       int *error)
76608ec9af1SDavid S. Miller {
76708ec9af1SDavid S. Miller 	/* Resolution logic:
76808ec9af1SDavid S. Miller 	 * 1. There is a valid state with matching selector. Done.
76908ec9af1SDavid S. Miller 	 * 2. Valid state with inappropriate selector. Skip.
77008ec9af1SDavid S. Miller 	 *
77108ec9af1SDavid S. Miller 	 * Entering area of "sysdeps".
77208ec9af1SDavid S. Miller 	 *
77308ec9af1SDavid S. Miller 	 * 3. If state is not valid, selector is temporary, it selects
77408ec9af1SDavid S. Miller 	 *    only session which triggered previous resolution. Key
77508ec9af1SDavid S. Miller 	 *    manager will do something to install a state with proper
77608ec9af1SDavid S. Miller 	 *    selector.
77708ec9af1SDavid S. Miller 	 */
77808ec9af1SDavid S. Miller 	if (x->km.state == XFRM_STATE_VALID) {
77908ec9af1SDavid S. Miller 		if ((x->sel.family &&
78008ec9af1SDavid S. Miller 		     !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
78108ec9af1SDavid S. Miller 		    !security_xfrm_state_pol_flow_match(x, pol, fl))
78208ec9af1SDavid S. Miller 			return;
78308ec9af1SDavid S. Miller 
78408ec9af1SDavid S. Miller 		if (!*best ||
78508ec9af1SDavid S. Miller 		    (*best)->km.dying > x->km.dying ||
78608ec9af1SDavid S. Miller 		    ((*best)->km.dying == x->km.dying &&
78708ec9af1SDavid S. Miller 		     (*best)->curlft.add_time < x->curlft.add_time))
78808ec9af1SDavid S. Miller 			*best = x;
78908ec9af1SDavid S. Miller 	} else if (x->km.state == XFRM_STATE_ACQ) {
79008ec9af1SDavid S. Miller 		*acq_in_progress = 1;
79108ec9af1SDavid S. Miller 	} else if (x->km.state == XFRM_STATE_ERROR ||
79208ec9af1SDavid S. Miller 		   x->km.state == XFRM_STATE_EXPIRED) {
79308ec9af1SDavid S. Miller 		if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
79408ec9af1SDavid S. Miller 		    security_xfrm_state_pol_flow_match(x, pol, fl))
79508ec9af1SDavid S. Miller 			*error = -ESRCH;
79608ec9af1SDavid S. Miller 	}
79708ec9af1SDavid S. Miller }
79808ec9af1SDavid S. Miller 
7991da177e4SLinus Torvalds struct xfrm_state *
80033765d06SDavid S. Miller xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
801b520e9f6SDavid S. Miller 		const struct flowi *fl, struct xfrm_tmpl *tmpl,
8021da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
8031da177e4SLinus Torvalds 		unsigned short family)
8041da177e4SLinus Torvalds {
80508ec9af1SDavid S. Miller 	static xfrm_address_t saddr_wildcard = { };
8065447c5e4SAlexey Dobriyan 	struct net *net = xp_net(pol);
8076a783c90SNicolas Dichtel 	unsigned int h, h_wildcard;
8088f126e37SDavid S. Miller 	struct hlist_node *entry;
80937b08e34SDavid S. Miller 	struct xfrm_state *x, *x0, *to_put;
8101da177e4SLinus Torvalds 	int acquire_in_progress = 0;
8111da177e4SLinus Torvalds 	int error = 0;
8121da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
813bd55775cSJamal Hadi Salim 	u32 mark = pol->mark.v & pol->mark.m;
8148444cf71SThomas Egerer 	unsigned short encap_family = tmpl->encap_family;
8151da177e4SLinus Torvalds 
81637b08e34SDavid S. Miller 	to_put = NULL;
81737b08e34SDavid S. Miller 
8181da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
8198444cf71SThomas Egerer 	h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
8205447c5e4SAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
8218444cf71SThomas Egerer 		if (x->props.family == encap_family &&
8221da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
8233d6acfa7SJamal Hadi Salim 		    (mark & x->mark.m) == x->mark.v &&
824fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
8258444cf71SThomas Egerer 		    xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
8261da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
8271da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
82808ec9af1SDavid S. Miller 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
8291f673c5fSDavid S. Miller 			xfrm_state_look_at(pol, x, fl, encap_family,
83008ec9af1SDavid S. Miller 					   &best, &acquire_in_progress, &error);
8311da177e4SLinus Torvalds 	}
83208ec9af1SDavid S. Miller 	if (best)
83308ec9af1SDavid S. Miller 		goto found;
83408ec9af1SDavid S. Miller 
8358444cf71SThomas Egerer 	h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
8366a783c90SNicolas Dichtel 	hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, bydst) {
8378444cf71SThomas Egerer 		if (x->props.family == encap_family &&
83808ec9af1SDavid S. Miller 		    x->props.reqid == tmpl->reqid &&
8393d6acfa7SJamal Hadi Salim 		    (mark & x->mark.m) == x->mark.v &&
84008ec9af1SDavid S. Miller 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
8418444cf71SThomas Egerer 		    xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
84208ec9af1SDavid S. Miller 		    tmpl->mode == x->props.mode &&
84308ec9af1SDavid S. Miller 		    tmpl->id.proto == x->id.proto &&
84408ec9af1SDavid S. Miller 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
8451f673c5fSDavid S. Miller 			xfrm_state_look_at(pol, x, fl, encap_family,
84608ec9af1SDavid S. Miller 					   &best, &acquire_in_progress, &error);
8471da177e4SLinus Torvalds 	}
8481da177e4SLinus Torvalds 
84908ec9af1SDavid S. Miller found:
8501da177e4SLinus Torvalds 	x = best;
8511da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
8525c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
853bd55775cSJamal Hadi Salim 		    (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi,
8548444cf71SThomas Egerer 					      tmpl->id.proto, encap_family)) != NULL) {
85537b08e34SDavid S. Miller 			to_put = x0;
8561da177e4SLinus Torvalds 			error = -EEXIST;
8571da177e4SLinus Torvalds 			goto out;
8581da177e4SLinus Torvalds 		}
8595447c5e4SAlexey Dobriyan 		x = xfrm_state_alloc(net);
8601da177e4SLinus Torvalds 		if (x == NULL) {
8611da177e4SLinus Torvalds 			error = -ENOMEM;
8621da177e4SLinus Torvalds 			goto out;
8631da177e4SLinus Torvalds 		}
8648444cf71SThomas Egerer 		/* Initialize temporary state matching only
8651da177e4SLinus Torvalds 		 * to current session. */
8668444cf71SThomas Egerer 		xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
867bd55775cSJamal Hadi Salim 		memcpy(&x->mark, &pol->mark, sizeof(x->mark));
8681da177e4SLinus Torvalds 
8691d28f42cSDavid S. Miller 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
870e0d1caa7SVenkat Yekkirala 		if (error) {
871e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
87237b08e34SDavid S. Miller 			to_put = x;
873e0d1caa7SVenkat Yekkirala 			x = NULL;
874e0d1caa7SVenkat Yekkirala 			goto out;
875e0d1caa7SVenkat Yekkirala 		}
876e0d1caa7SVenkat Yekkirala 
8771da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
8781da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
8795447c5e4SAlexey Dobriyan 			list_add(&x->km.all, &net->xfrm.state_all);
8805447c5e4SAlexey Dobriyan 			hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
8818444cf71SThomas Egerer 			h = xfrm_src_hash(net, daddr, saddr, encap_family);
8825447c5e4SAlexey Dobriyan 			hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
8831da177e4SLinus Torvalds 			if (x->id.spi) {
8848444cf71SThomas Egerer 				h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
8855447c5e4SAlexey Dobriyan 				hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
8861da177e4SLinus Torvalds 			}
887b27aeadbSAlexey Dobriyan 			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
8889e0d57fdSYury Polyanskiy 			tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
8895447c5e4SAlexey Dobriyan 			net->xfrm.state_num++;
8905447c5e4SAlexey Dobriyan 			xfrm_hash_grow_check(net, x->bydst.next != NULL);
8911da177e4SLinus Torvalds 		} else {
8921da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
89337b08e34SDavid S. Miller 			to_put = x;
8941da177e4SLinus Torvalds 			x = NULL;
8951da177e4SLinus Torvalds 			error = -ESRCH;
8961da177e4SLinus Torvalds 		}
8971da177e4SLinus Torvalds 	}
8981da177e4SLinus Torvalds out:
8991da177e4SLinus Torvalds 	if (x)
9001da177e4SLinus Torvalds 		xfrm_state_hold(x);
9011da177e4SLinus Torvalds 	else
9021da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
9031da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
90437b08e34SDavid S. Miller 	if (to_put)
90537b08e34SDavid S. Miller 		xfrm_state_put(to_put);
9061da177e4SLinus Torvalds 	return x;
9071da177e4SLinus Torvalds }
9081da177e4SLinus Torvalds 
909628529b6SJamal Hadi Salim struct xfrm_state *
910bd55775cSJamal Hadi Salim xfrm_stateonly_find(struct net *net, u32 mark,
9115447c5e4SAlexey Dobriyan 		    xfrm_address_t *daddr, xfrm_address_t *saddr,
912628529b6SJamal Hadi Salim 		    unsigned short family, u8 mode, u8 proto, u32 reqid)
913628529b6SJamal Hadi Salim {
9144bda4f25SPavel Emelyanov 	unsigned int h;
915628529b6SJamal Hadi Salim 	struct xfrm_state *rx = NULL, *x = NULL;
916628529b6SJamal Hadi Salim 	struct hlist_node *entry;
917628529b6SJamal Hadi Salim 
918628529b6SJamal Hadi Salim 	spin_lock(&xfrm_state_lock);
9195447c5e4SAlexey Dobriyan 	h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
9205447c5e4SAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
921628529b6SJamal Hadi Salim 		if (x->props.family == family &&
922628529b6SJamal Hadi Salim 		    x->props.reqid == reqid &&
9233d6acfa7SJamal Hadi Salim 		    (mark & x->mark.m) == x->mark.v &&
924628529b6SJamal Hadi Salim 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
925628529b6SJamal Hadi Salim 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
926628529b6SJamal Hadi Salim 		    mode == x->props.mode &&
927628529b6SJamal Hadi Salim 		    proto == x->id.proto &&
928628529b6SJamal Hadi Salim 		    x->km.state == XFRM_STATE_VALID) {
929628529b6SJamal Hadi Salim 			rx = x;
930628529b6SJamal Hadi Salim 			break;
931628529b6SJamal Hadi Salim 		}
932628529b6SJamal Hadi Salim 	}
933628529b6SJamal Hadi Salim 
934628529b6SJamal Hadi Salim 	if (rx)
935628529b6SJamal Hadi Salim 		xfrm_state_hold(rx);
936628529b6SJamal Hadi Salim 	spin_unlock(&xfrm_state_lock);
937628529b6SJamal Hadi Salim 
938628529b6SJamal Hadi Salim 
939628529b6SJamal Hadi Salim 	return rx;
940628529b6SJamal Hadi Salim }
941628529b6SJamal Hadi Salim EXPORT_SYMBOL(xfrm_stateonly_find);
942628529b6SJamal Hadi Salim 
9431da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
9441da177e4SLinus Torvalds {
94598806f75SAlexey Dobriyan 	struct net *net = xs_net(x);
946a624c108SDavid S. Miller 	unsigned int h;
9471da177e4SLinus Torvalds 
94898806f75SAlexey Dobriyan 	list_add(&x->km.all, &net->xfrm.state_all);
9494c563f76STimo Teras 
95098806f75SAlexey Dobriyan 	h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
951c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
95298806f75SAlexey Dobriyan 	hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
9531da177e4SLinus Torvalds 
95498806f75SAlexey Dobriyan 	h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
95598806f75SAlexey Dobriyan 	hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
9566c44e6b7SMasahide NAKAMURA 
9577b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
95898806f75SAlexey Dobriyan 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
9596c44e6b7SMasahide NAKAMURA 				  x->props.family);
9601da177e4SLinus Torvalds 
96198806f75SAlexey Dobriyan 		hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
9626c44e6b7SMasahide NAKAMURA 	}
9631da177e4SLinus Torvalds 
9649e0d57fdSYury Polyanskiy 	tasklet_hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
965a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
966a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
967f8cd5488SJamal Hadi Salim 
96898806f75SAlexey Dobriyan 	wake_up(&net->xfrm.km_waitq);
969f034b5d4SDavid S. Miller 
97098806f75SAlexey Dobriyan 	net->xfrm.state_num++;
971f034b5d4SDavid S. Miller 
97298806f75SAlexey Dobriyan 	xfrm_hash_grow_check(net, x->bydst.next != NULL);
9731da177e4SLinus Torvalds }
9741da177e4SLinus Torvalds 
975c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
976c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
977c7f5ea3aSDavid S. Miller {
97898806f75SAlexey Dobriyan 	struct net *net = xs_net(xnew);
979c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
980c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
981c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
982c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
983c7f5ea3aSDavid S. Miller 	unsigned int h;
9843d6acfa7SJamal Hadi Salim 	u32 mark = xnew->mark.v & xnew->mark.m;
985c7f5ea3aSDavid S. Miller 
98698806f75SAlexey Dobriyan 	h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
98798806f75SAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
988c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
989c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
9903d6acfa7SJamal Hadi Salim 		    (mark & x->mark.m) == x->mark.v &&
991c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
992c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
99334996cb9SHerbert Xu 			x->genid++;
994c7f5ea3aSDavid S. Miller 	}
995c7f5ea3aSDavid S. Miller }
996c7f5ea3aSDavid S. Miller 
9971da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
9981da177e4SLinus Torvalds {
9991da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1000c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
10011da177e4SLinus Torvalds 	__xfrm_state_insert(x);
10021da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10031da177e4SLinus Torvalds }
10041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
10051da177e4SLinus Torvalds 
10062770834cSDavid S. Miller /* xfrm_state_lock is held */
1007a70486f0SDavid S. Miller static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m,
1008a70486f0SDavid S. Miller 					  unsigned short family, u8 mode,
1009a70486f0SDavid S. Miller 					  u32 reqid, u8 proto,
1010a70486f0SDavid S. Miller 					  const xfrm_address_t *daddr,
1011a70486f0SDavid S. Miller 					  const xfrm_address_t *saddr, int create)
10122770834cSDavid S. Miller {
10135447c5e4SAlexey Dobriyan 	unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
10148f126e37SDavid S. Miller 	struct hlist_node *entry;
10152770834cSDavid S. Miller 	struct xfrm_state *x;
10163d6acfa7SJamal Hadi Salim 	u32 mark = m->v & m->m;
10172770834cSDavid S. Miller 
10185447c5e4SAlexey Dobriyan 	hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
10192770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
10202770834cSDavid S. Miller 		    x->props.mode   != mode ||
10212770834cSDavid S. Miller 		    x->props.family != family ||
10222770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
102375e252d9SJoy Latten 		    x->id.spi       != 0 ||
10241802571bSWei Yongjun 		    x->id.proto	    != proto ||
10253d6acfa7SJamal Hadi Salim 		    (mark & x->mark.m) != x->mark.v ||
10261802571bSWei Yongjun 		    xfrm_addr_cmp(&x->id.daddr, daddr, family) ||
10271802571bSWei Yongjun 		    xfrm_addr_cmp(&x->props.saddr, saddr, family))
10282770834cSDavid S. Miller 			continue;
10292770834cSDavid S. Miller 
10302770834cSDavid S. Miller 		xfrm_state_hold(x);
10312770834cSDavid S. Miller 		return x;
10322770834cSDavid S. Miller 	}
10332770834cSDavid S. Miller 
10342770834cSDavid S. Miller 	if (!create)
10352770834cSDavid S. Miller 		return NULL;
10362770834cSDavid S. Miller 
10375447c5e4SAlexey Dobriyan 	x = xfrm_state_alloc(net);
10382770834cSDavid S. Miller 	if (likely(x)) {
10392770834cSDavid S. Miller 		switch (family) {
10402770834cSDavid S. Miller 		case AF_INET:
10412770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
10422770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
10432770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
10442770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
10452770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
10462770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
10472770834cSDavid S. Miller 			break;
10482770834cSDavid S. Miller 
10492770834cSDavid S. Miller 		case AF_INET6:
10504e3fd7a0SAlexey Dobriyan 			*(struct in6_addr *)x->sel.daddr.a6 = *(struct in6_addr *)daddr;
10514e3fd7a0SAlexey Dobriyan 			*(struct in6_addr *)x->sel.saddr.a6 = *(struct in6_addr *)saddr;
10522770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
10532770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
10544e3fd7a0SAlexey Dobriyan 			*(struct in6_addr *)x->props.saddr.a6 = *(struct in6_addr *)saddr;
10554e3fd7a0SAlexey Dobriyan 			*(struct in6_addr *)x->id.daddr.a6 = *(struct in6_addr *)daddr;
10562770834cSDavid S. Miller 			break;
10573ff50b79SStephen Hemminger 		}
10582770834cSDavid S. Miller 
10592770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
10602770834cSDavid S. Miller 		x->id.proto = proto;
10612770834cSDavid S. Miller 		x->props.family = family;
10622770834cSDavid S. Miller 		x->props.mode = mode;
10632770834cSDavid S. Miller 		x->props.reqid = reqid;
1064bd55775cSJamal Hadi Salim 		x->mark.v = m->v;
1065bd55775cSJamal Hadi Salim 		x->mark.m = m->m;
1066b27aeadbSAlexey Dobriyan 		x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
10672770834cSDavid S. Miller 		xfrm_state_hold(x);
10689e0d57fdSYury Polyanskiy 		tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
10695447c5e4SAlexey Dobriyan 		list_add(&x->km.all, &net->xfrm.state_all);
10705447c5e4SAlexey Dobriyan 		hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
10715447c5e4SAlexey Dobriyan 		h = xfrm_src_hash(net, daddr, saddr, family);
10725447c5e4SAlexey Dobriyan 		hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
1073918049f0SDavid S. Miller 
10745447c5e4SAlexey Dobriyan 		net->xfrm.state_num++;
1075918049f0SDavid S. Miller 
10765447c5e4SAlexey Dobriyan 		xfrm_hash_grow_check(net, x->bydst.next != NULL);
10772770834cSDavid S. Miller 	}
10782770834cSDavid S. Miller 
10792770834cSDavid S. Miller 	return x;
10802770834cSDavid S. Miller }
10812770834cSDavid S. Miller 
1082bd55775cSJamal Hadi Salim static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
10851da177e4SLinus Torvalds {
10865447c5e4SAlexey Dobriyan 	struct net *net = xs_net(x);
108737b08e34SDavid S. Miller 	struct xfrm_state *x1, *to_put;
10881da177e4SLinus Torvalds 	int family;
10891da177e4SLinus Torvalds 	int err;
1090bd55775cSJamal Hadi Salim 	u32 mark = x->mark.v & x->mark.m;
1091eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	family = x->props.family;
10941da177e4SLinus Torvalds 
109537b08e34SDavid S. Miller 	to_put = NULL;
109637b08e34SDavid S. Miller 
10971da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10981da177e4SLinus Torvalds 
1099edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
11001da177e4SLinus Torvalds 	if (x1) {
110137b08e34SDavid S. Miller 		to_put = x1;
11021da177e4SLinus Torvalds 		x1 = NULL;
11031da177e4SLinus Torvalds 		err = -EEXIST;
11041da177e4SLinus Torvalds 		goto out;
11051da177e4SLinus Torvalds 	}
11061da177e4SLinus Torvalds 
1107eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
1108bd55775cSJamal Hadi Salim 		x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq);
110975e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
111075e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
111137b08e34SDavid S. Miller 			to_put = x1;
11121da177e4SLinus Torvalds 			x1 = NULL;
11131da177e4SLinus Torvalds 		}
11141da177e4SLinus Torvalds 	}
11151da177e4SLinus Torvalds 
1116eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
1117bd55775cSJamal Hadi Salim 		x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
1118bd55775cSJamal Hadi Salim 				     x->props.reqid, x->id.proto,
11191da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
11201da177e4SLinus Torvalds 
1121c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
11221da177e4SLinus Torvalds 	__xfrm_state_insert(x);
11231da177e4SLinus Torvalds 	err = 0;
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds out:
11261da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11271da177e4SLinus Torvalds 
11281da177e4SLinus Torvalds 	if (x1) {
11291da177e4SLinus Torvalds 		xfrm_state_delete(x1);
11301da177e4SLinus Torvalds 		xfrm_state_put(x1);
11311da177e4SLinus Torvalds 	}
11321da177e4SLinus Torvalds 
113337b08e34SDavid S. Miller 	if (to_put)
113437b08e34SDavid S. Miller 		xfrm_state_put(to_put);
113537b08e34SDavid S. Miller 
11361da177e4SLinus Torvalds 	return err;
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
11391da177e4SLinus Torvalds 
114080c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
11416666351dSEric Dumazet static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
114280c9abaaSShinta Sugimoto {
114398806f75SAlexey Dobriyan 	struct net *net = xs_net(orig);
114480c9abaaSShinta Sugimoto 	int err = -ENOMEM;
114598806f75SAlexey Dobriyan 	struct xfrm_state *x = xfrm_state_alloc(net);
114680c9abaaSShinta Sugimoto 	if (!x)
1147553f9118SHerbert Xu 		goto out;
114880c9abaaSShinta Sugimoto 
114980c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
115080c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
115180c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
115280c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
115380c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
115480c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
115580c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
115680c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
115780c9abaaSShinta Sugimoto 
115880c9abaaSShinta Sugimoto 	if (orig->aalg) {
11594447bb33SMartin Willi 		x->aalg = xfrm_algo_auth_clone(orig->aalg);
116080c9abaaSShinta Sugimoto 		if (!x->aalg)
116180c9abaaSShinta Sugimoto 			goto error;
116280c9abaaSShinta Sugimoto 	}
116380c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
116480c9abaaSShinta Sugimoto 
116580c9abaaSShinta Sugimoto 	if (orig->ealg) {
116680c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
116780c9abaaSShinta Sugimoto 		if (!x->ealg)
116880c9abaaSShinta Sugimoto 			goto error;
116980c9abaaSShinta Sugimoto 	}
117080c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
117180c9abaaSShinta Sugimoto 
117280c9abaaSShinta Sugimoto 	if (orig->calg) {
117380c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
117480c9abaaSShinta Sugimoto 		if (!x->calg)
117580c9abaaSShinta Sugimoto 			goto error;
117680c9abaaSShinta Sugimoto 	}
117780c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
117880c9abaaSShinta Sugimoto 
117980c9abaaSShinta Sugimoto 	if (orig->encap) {
118080c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
118180c9abaaSShinta Sugimoto 		if (!x->encap)
118280c9abaaSShinta Sugimoto 			goto error;
118380c9abaaSShinta Sugimoto 	}
118480c9abaaSShinta Sugimoto 
118580c9abaaSShinta Sugimoto 	if (orig->coaddr) {
118680c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
118780c9abaaSShinta Sugimoto 				    GFP_KERNEL);
118880c9abaaSShinta Sugimoto 		if (!x->coaddr)
118980c9abaaSShinta Sugimoto 			goto error;
119080c9abaaSShinta Sugimoto 	}
119180c9abaaSShinta Sugimoto 
1192af2f464eSSteffen Klassert 	if (orig->replay_esn) {
1193af2f464eSSteffen Klassert 		err = xfrm_replay_clone(x, orig);
1194af2f464eSSteffen Klassert 		if (err)
1195af2f464eSSteffen Klassert 			goto error;
1196af2f464eSSteffen Klassert 	}
1197af2f464eSSteffen Klassert 
1198bd55775cSJamal Hadi Salim 	memcpy(&x->mark, &orig->mark, sizeof(x->mark));
1199bd55775cSJamal Hadi Salim 
120080c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
120180c9abaaSShinta Sugimoto 	if (err)
120280c9abaaSShinta Sugimoto 		goto error;
120380c9abaaSShinta Sugimoto 
120480c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
120580c9abaaSShinta Sugimoto 
120680c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
120780c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
120880c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
120980c9abaaSShinta Sugimoto 
121080c9abaaSShinta Sugimoto 	return x;
121180c9abaaSShinta Sugimoto 
121280c9abaaSShinta Sugimoto  error:
1213553f9118SHerbert Xu 	xfrm_state_put(x);
1214553f9118SHerbert Xu out:
121580c9abaaSShinta Sugimoto 	if (errp)
121680c9abaaSShinta Sugimoto 		*errp = err;
121780c9abaaSShinta Sugimoto 	return NULL;
121880c9abaaSShinta Sugimoto }
121980c9abaaSShinta Sugimoto 
122080c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
122180c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
122280c9abaaSShinta Sugimoto {
122380c9abaaSShinta Sugimoto 	unsigned int h;
122480c9abaaSShinta Sugimoto 	struct xfrm_state *x;
122580c9abaaSShinta Sugimoto 	struct hlist_node *entry;
122680c9abaaSShinta Sugimoto 
122780c9abaaSShinta Sugimoto 	if (m->reqid) {
122864d0cd00SAlexey Dobriyan 		h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr,
122980c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
123073d189dcSAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
123180c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
123280c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
123380c9abaaSShinta Sugimoto 				continue;
123480c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
123580c9abaaSShinta Sugimoto 				continue;
123680c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
123780c9abaaSShinta Sugimoto 					  m->old_family) ||
123880c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
123980c9abaaSShinta Sugimoto 					  m->old_family))
124080c9abaaSShinta Sugimoto 				continue;
124180c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
124280c9abaaSShinta Sugimoto 			return x;
124380c9abaaSShinta Sugimoto 		}
124480c9abaaSShinta Sugimoto 	} else {
124564d0cd00SAlexey Dobriyan 		h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr,
124680c9abaaSShinta Sugimoto 				  m->old_family);
1247d320bbb3SAlexey Dobriyan 		hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
124880c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
124980c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
125080c9abaaSShinta Sugimoto 				continue;
125180c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
125280c9abaaSShinta Sugimoto 					  m->old_family) ||
125380c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
125480c9abaaSShinta Sugimoto 					  m->old_family))
125580c9abaaSShinta Sugimoto 				continue;
125680c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
125780c9abaaSShinta Sugimoto 			return x;
125880c9abaaSShinta Sugimoto 		}
125980c9abaaSShinta Sugimoto 	}
126080c9abaaSShinta Sugimoto 
126180c9abaaSShinta Sugimoto 	return NULL;
126280c9abaaSShinta Sugimoto }
126380c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
126480c9abaaSShinta Sugimoto 
126580c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
126680c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
126780c9abaaSShinta Sugimoto {
126880c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
126980c9abaaSShinta Sugimoto 	int err;
127080c9abaaSShinta Sugimoto 
127180c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
127280c9abaaSShinta Sugimoto 	if (!xc)
127380c9abaaSShinta Sugimoto 		return NULL;
127480c9abaaSShinta Sugimoto 
127580c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
127680c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
127780c9abaaSShinta Sugimoto 
127880c9abaaSShinta Sugimoto 	/* add state */
127980c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
128080c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
128180c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
128280c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
128380c9abaaSShinta Sugimoto 	} else {
128480c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
128580c9abaaSShinta Sugimoto 			goto error;
128680c9abaaSShinta Sugimoto 	}
128780c9abaaSShinta Sugimoto 
128880c9abaaSShinta Sugimoto 	return xc;
128980c9abaaSShinta Sugimoto error:
129078347c8cSThomas Egerer 	xfrm_state_put(xc);
129180c9abaaSShinta Sugimoto 	return NULL;
129280c9abaaSShinta Sugimoto }
129380c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
129480c9abaaSShinta Sugimoto #endif
129580c9abaaSShinta Sugimoto 
12961da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
12971da177e4SLinus Torvalds {
129837b08e34SDavid S. Miller 	struct xfrm_state *x1, *to_put;
12991da177e4SLinus Torvalds 	int err;
1300eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
13011da177e4SLinus Torvalds 
130237b08e34SDavid S. Miller 	to_put = NULL;
130337b08e34SDavid S. Miller 
13041da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1305edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 	err = -ESRCH;
13081da177e4SLinus Torvalds 	if (!x1)
13091da177e4SLinus Torvalds 		goto out;
13101da177e4SLinus Torvalds 
13111da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
131237b08e34SDavid S. Miller 		to_put = x1;
13131da177e4SLinus Torvalds 		err = -EEXIST;
13141da177e4SLinus Torvalds 		goto out;
13151da177e4SLinus Torvalds 	}
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
13181da177e4SLinus Torvalds 		__xfrm_state_insert(x);
13191da177e4SLinus Torvalds 		x = NULL;
13201da177e4SLinus Torvalds 	}
13211da177e4SLinus Torvalds 	err = 0;
13221da177e4SLinus Torvalds 
13231da177e4SLinus Torvalds out:
13241da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13251da177e4SLinus Torvalds 
132637b08e34SDavid S. Miller 	if (to_put)
132737b08e34SDavid S. Miller 		xfrm_state_put(to_put);
132837b08e34SDavid S. Miller 
13291da177e4SLinus Torvalds 	if (err)
13301da177e4SLinus Torvalds 		return err;
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	if (!x) {
13331da177e4SLinus Torvalds 		xfrm_state_delete(x1);
13341da177e4SLinus Torvalds 		xfrm_state_put(x1);
13351da177e4SLinus Torvalds 		return 0;
13361da177e4SLinus Torvalds 	}
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds 	err = -EINVAL;
13391da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
13401da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
13411da177e4SLinus Torvalds 		if (x->encap && x1->encap)
13421da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1343060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1344060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1345060f02a3SNoriaki TAKAMIYA 		}
1346060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1347060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
13481da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
13491da177e4SLinus Torvalds 		x1->km.dying = 0;
13501da177e4SLinus Torvalds 
13519e0d57fdSYury Polyanskiy 		tasklet_hrtimer_start(&x1->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
13521da177e4SLinus Torvalds 		if (x1->curlft.use_time)
13531da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 		err = 0;
13568fcbc637STushar Gohad 		x->km.state = XFRM_STATE_DEAD;
13578fcbc637STushar Gohad 		__xfrm_state_put(x);
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 	xfrm_state_put(x1);
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 	return err;
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
13681da177e4SLinus Torvalds {
13691da177e4SLinus Torvalds 	if (!x->curlft.use_time)
13709d729f72SJames Morris 		x->curlft.use_time = get_seconds();
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
13731da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
13744666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
13759e0d57fdSYury Polyanskiy 		tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL);
13761da177e4SLinus Torvalds 		return -EINVAL;
13771da177e4SLinus Torvalds 	}
13781da177e4SLinus Torvalds 
13791da177e4SLinus Torvalds 	if (!x->km.dying &&
13801da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
13814666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
13824666faabSHerbert Xu 		x->km.dying = 1;
138353bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
13844666faabSHerbert Xu 	}
13851da177e4SLinus Torvalds 	return 0;
13861da177e4SLinus Torvalds }
13871da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
13881da177e4SLinus Torvalds 
13891da177e4SLinus Torvalds struct xfrm_state *
1390a70486f0SDavid S. Miller xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi,
1391bd55775cSJamal Hadi Salim 		  u8 proto, unsigned short family)
13921da177e4SLinus Torvalds {
13931da177e4SLinus Torvalds 	struct xfrm_state *x;
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1396bd55775cSJamal Hadi Salim 	x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
13971da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13981da177e4SLinus Torvalds 	return x;
13991da177e4SLinus Torvalds }
14001da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds struct xfrm_state *
1403bd55775cSJamal Hadi Salim xfrm_state_lookup_byaddr(struct net *net, u32 mark,
1404a70486f0SDavid S. Miller 			 const xfrm_address_t *daddr, const xfrm_address_t *saddr,
1405eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1406eb2971b6SMasahide NAKAMURA {
1407eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1408eb2971b6SMasahide NAKAMURA 
1409eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1410bd55775cSJamal Hadi Salim 	x = __xfrm_state_lookup_byaddr(net, mark, daddr, saddr, proto, family);
1411eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1412eb2971b6SMasahide NAKAMURA 	return x;
1413eb2971b6SMasahide NAKAMURA }
1414eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1415eb2971b6SMasahide NAKAMURA 
1416eb2971b6SMasahide NAKAMURA struct xfrm_state *
1417bd55775cSJamal Hadi Salim xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto,
1418a70486f0SDavid S. Miller 	      const xfrm_address_t *daddr, const xfrm_address_t *saddr,
14191da177e4SLinus Torvalds 	      int create, unsigned short family)
14201da177e4SLinus Torvalds {
14211da177e4SLinus Torvalds 	struct xfrm_state *x;
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1424bd55775cSJamal Hadi Salim 	x = __find_acq_core(net, mark, family, mode, reqid, proto, daddr, saddr, create);
14251da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14262770834cSDavid S. Miller 
14271da177e4SLinus Torvalds 	return x;
14281da177e4SLinus Torvalds }
14291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
14301da177e4SLinus Torvalds 
143141a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
143241a49cc3SMasahide NAKAMURA int
143341a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
143441a49cc3SMasahide NAKAMURA 	       unsigned short family)
143541a49cc3SMasahide NAKAMURA {
143641a49cc3SMasahide NAKAMURA 	int err = 0;
143741a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
143841a49cc3SMasahide NAKAMURA 	if (!afinfo)
143941a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
144041a49cc3SMasahide NAKAMURA 
144141a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
144241a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
144341a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
144441a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
144541a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
144641a49cc3SMasahide NAKAMURA 	return err;
144741a49cc3SMasahide NAKAMURA }
144841a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
144941a49cc3SMasahide NAKAMURA 
145041a49cc3SMasahide NAKAMURA int
145141a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
145241a49cc3SMasahide NAKAMURA 		unsigned short family)
145341a49cc3SMasahide NAKAMURA {
145441a49cc3SMasahide NAKAMURA 	int err = 0;
145541a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
145641a49cc3SMasahide NAKAMURA 	if (!afinfo)
145741a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
145841a49cc3SMasahide NAKAMURA 
145941a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
146041a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
146141a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
146241a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
146341a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
146441a49cc3SMasahide NAKAMURA 	return err;
146541a49cc3SMasahide NAKAMURA }
146641a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
146741a49cc3SMasahide NAKAMURA #endif
146841a49cc3SMasahide NAKAMURA 
14691da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
14701da177e4SLinus Torvalds 
1471bd55775cSJamal Hadi Salim static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)
14721da177e4SLinus Torvalds {
14731da177e4SLinus Torvalds 	int i;
14741da177e4SLinus Torvalds 
14755447c5e4SAlexey Dobriyan 	for (i = 0; i <= net->xfrm.state_hmask; i++) {
14768f126e37SDavid S. Miller 		struct hlist_node *entry;
14778f126e37SDavid S. Miller 		struct xfrm_state *x;
14788f126e37SDavid S. Miller 
14795447c5e4SAlexey Dobriyan 		hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
14808f126e37SDavid S. Miller 			if (x->km.seq == seq &&
14813d6acfa7SJamal Hadi Salim 			    (mark & x->mark.m) == x->mark.v &&
14828f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
14831da177e4SLinus Torvalds 				xfrm_state_hold(x);
14841da177e4SLinus Torvalds 				return x;
14851da177e4SLinus Torvalds 			}
14861da177e4SLinus Torvalds 		}
14871da177e4SLinus Torvalds 	}
14881da177e4SLinus Torvalds 	return NULL;
14891da177e4SLinus Torvalds }
14901da177e4SLinus Torvalds 
1491bd55775cSJamal Hadi Salim struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)
14921da177e4SLinus Torvalds {
14931da177e4SLinus Torvalds 	struct xfrm_state *x;
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1496bd55775cSJamal Hadi Salim 	x = __xfrm_find_acq_byseq(net, mark, seq);
14971da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14981da177e4SLinus Torvalds 	return x;
14991da177e4SLinus Torvalds }
15001da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
15011da177e4SLinus Torvalds 
15021da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
15031da177e4SLinus Torvalds {
15041da177e4SLinus Torvalds 	u32 res;
15056836b9bdSjamal 	static atomic_t acqseq;
15061da177e4SLinus Torvalds 
15076836b9bdSjamal 	do {
15086836b9bdSjamal 		res = atomic_inc_return(&acqseq);
15096836b9bdSjamal 	} while (!res);
15106836b9bdSjamal 
15111da177e4SLinus Torvalds 	return res;
15121da177e4SLinus Torvalds }
15131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
15141da177e4SLinus Torvalds 
1515658b219eSHerbert Xu int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
15161da177e4SLinus Torvalds {
1517221df1edSAlexey Dobriyan 	struct net *net = xs_net(x);
1518f034b5d4SDavid S. Miller 	unsigned int h;
15191da177e4SLinus Torvalds 	struct xfrm_state *x0;
1520658b219eSHerbert Xu 	int err = -ENOENT;
1521658b219eSHerbert Xu 	__be32 minspi = htonl(low);
1522658b219eSHerbert Xu 	__be32 maxspi = htonl(high);
1523bd55775cSJamal Hadi Salim 	u32 mark = x->mark.v & x->mark.m;
15241da177e4SLinus Torvalds 
1525658b219eSHerbert Xu 	spin_lock_bh(&x->lock);
1526658b219eSHerbert Xu 	if (x->km.state == XFRM_STATE_DEAD)
1527658b219eSHerbert Xu 		goto unlock;
1528658b219eSHerbert Xu 
1529658b219eSHerbert Xu 	err = 0;
15301da177e4SLinus Torvalds 	if (x->id.spi)
1531658b219eSHerbert Xu 		goto unlock;
1532658b219eSHerbert Xu 
1533658b219eSHerbert Xu 	err = -ENOENT;
15341da177e4SLinus Torvalds 
15351da177e4SLinus Torvalds 	if (minspi == maxspi) {
1536bd55775cSJamal Hadi Salim 		x0 = xfrm_state_lookup(net, mark, &x->id.daddr, minspi, x->id.proto, x->props.family);
15371da177e4SLinus Torvalds 		if (x0) {
15381da177e4SLinus Torvalds 			xfrm_state_put(x0);
1539658b219eSHerbert Xu 			goto unlock;
15401da177e4SLinus Torvalds 		}
15411da177e4SLinus Torvalds 		x->id.spi = minspi;
15421da177e4SLinus Torvalds 	} else {
15431da177e4SLinus Torvalds 		u32 spi = 0;
154426977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
154526977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
1546bd55775cSJamal Hadi Salim 			x0 = xfrm_state_lookup(net, mark, &x->id.daddr, htonl(spi), x->id.proto, x->props.family);
15471da177e4SLinus Torvalds 			if (x0 == NULL) {
15481da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
15491da177e4SLinus Torvalds 				break;
15501da177e4SLinus Torvalds 			}
15511da177e4SLinus Torvalds 			xfrm_state_put(x0);
15521da177e4SLinus Torvalds 		}
15531da177e4SLinus Torvalds 	}
15541da177e4SLinus Torvalds 	if (x->id.spi) {
15551da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
155612604d8aSAlexey Dobriyan 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
155712604d8aSAlexey Dobriyan 		hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
15581da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
1559658b219eSHerbert Xu 
1560658b219eSHerbert Xu 		err = 0;
15611da177e4SLinus Torvalds 	}
1562658b219eSHerbert Xu 
1563658b219eSHerbert Xu unlock:
1564658b219eSHerbert Xu 	spin_unlock_bh(&x->lock);
1565658b219eSHerbert Xu 
1566658b219eSHerbert Xu 	return err;
15671da177e4SLinus Torvalds }
15681da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
15691da177e4SLinus Torvalds 
1570284fa7daSAlexey Dobriyan int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
15714c563f76STimo Teras 		    int (*func)(struct xfrm_state *, int, void*),
15721da177e4SLinus Torvalds 		    void *data)
15731da177e4SLinus Torvalds {
157412a169e7SHerbert Xu 	struct xfrm_state *state;
157512a169e7SHerbert Xu 	struct xfrm_state_walk *x;
15761da177e4SLinus Torvalds 	int err = 0;
15771da177e4SLinus Torvalds 
157812a169e7SHerbert Xu 	if (walk->seq != 0 && list_empty(&walk->all))
15794c563f76STimo Teras 		return 0;
15804c563f76STimo Teras 
15811da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
158212a169e7SHerbert Xu 	if (list_empty(&walk->all))
1583284fa7daSAlexey Dobriyan 		x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all);
158412a169e7SHerbert Xu 	else
158512a169e7SHerbert Xu 		x = list_entry(&walk->all, struct xfrm_state_walk, all);
1586284fa7daSAlexey Dobriyan 	list_for_each_entry_from(x, &net->xfrm.state_all, all) {
158712a169e7SHerbert Xu 		if (x->state == XFRM_STATE_DEAD)
15884c563f76STimo Teras 			continue;
158912a169e7SHerbert Xu 		state = container_of(x, struct xfrm_state, km);
159012a169e7SHerbert Xu 		if (!xfrm_id_proto_match(state->id.proto, walk->proto))
159194b9bb54SJamal Hadi Salim 			continue;
159212a169e7SHerbert Xu 		err = func(state, walk->seq, data);
15934c563f76STimo Teras 		if (err) {
159412a169e7SHerbert Xu 			list_move_tail(&walk->all, &x->all);
159594b9bb54SJamal Hadi Salim 			goto out;
159694b9bb54SJamal Hadi Salim 		}
159712a169e7SHerbert Xu 		walk->seq++;
15984c563f76STimo Teras 	}
159912a169e7SHerbert Xu 	if (walk->seq == 0) {
16001da177e4SLinus Torvalds 		err = -ENOENT;
16011da177e4SLinus Torvalds 		goto out;
16021da177e4SLinus Torvalds 	}
160312a169e7SHerbert Xu 	list_del_init(&walk->all);
16041da177e4SLinus Torvalds out:
16051da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
16061da177e4SLinus Torvalds 	return err;
16071da177e4SLinus Torvalds }
16081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
16091da177e4SLinus Torvalds 
16105c182458SHerbert Xu void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
16115c182458SHerbert Xu {
161212a169e7SHerbert Xu 	INIT_LIST_HEAD(&walk->all);
16135c182458SHerbert Xu 	walk->proto = proto;
161412a169e7SHerbert Xu 	walk->state = XFRM_STATE_DEAD;
161512a169e7SHerbert Xu 	walk->seq = 0;
16165c182458SHerbert Xu }
16175c182458SHerbert Xu EXPORT_SYMBOL(xfrm_state_walk_init);
16185c182458SHerbert Xu 
1619abb81c4fSHerbert Xu void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1620abb81c4fSHerbert Xu {
162112a169e7SHerbert Xu 	if (list_empty(&walk->all))
16225c182458SHerbert Xu 		return;
16235c182458SHerbert Xu 
162412a169e7SHerbert Xu 	spin_lock_bh(&xfrm_state_lock);
162512a169e7SHerbert Xu 	list_del(&walk->all);
16267d0b591cSChuck Ebbert 	spin_unlock_bh(&xfrm_state_lock);
1627abb81c4fSHerbert Xu }
1628abb81c4fSHerbert Xu EXPORT_SYMBOL(xfrm_state_walk_done);
1629abb81c4fSHerbert Xu 
1630f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1631f8cd5488SJamal Hadi Salim {
1632f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1633f8cd5488SJamal Hadi Salim 
1634f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1635f8cd5488SJamal Hadi Salim 
16362717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
1637a6483b79SAlexey Dobriyan 		if (xfrm_aevent_is_on(xs_net(x)))
16389fdc4883SSteffen Klassert 			x->repl->notify(x, XFRM_REPLAY_TIMEOUT);
16392717096aSJamal Hadi Salim 		else
16402717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
16412717096aSJamal Hadi Salim 	}
1642f8cd5488SJamal Hadi Salim 
1643f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1644f8cd5488SJamal Hadi Salim }
1645f8cd5488SJamal Hadi Salim 
1646df01812eSDenis Cheng static LIST_HEAD(xfrm_km_list);
16471da177e4SLinus Torvalds 
1648214e005bSDavid S. Miller void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
16491da177e4SLinus Torvalds {
16501da177e4SLinus Torvalds 	struct xfrm_mgr *km;
16511da177e4SLinus Torvalds 
165285168c00SCong Wang 	rcu_read_lock();
165385168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list)
165426b15dadSJamal Hadi Salim 		if (km->notify_policy)
165526b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
165685168c00SCong Wang 	rcu_read_unlock();
165726b15dadSJamal Hadi Salim }
165826b15dadSJamal Hadi Salim 
1659214e005bSDavid S. Miller void km_state_notify(struct xfrm_state *x, const struct km_event *c)
166026b15dadSJamal Hadi Salim {
166126b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
166285168c00SCong Wang 	rcu_read_lock();
166385168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list)
166426b15dadSJamal Hadi Salim 		if (km->notify)
166526b15dadSJamal Hadi Salim 			km->notify(x, c);
166685168c00SCong Wang 	rcu_read_unlock();
166726b15dadSJamal Hadi Salim }
166826b15dadSJamal Hadi Salim 
166926b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
167026b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
167126b15dadSJamal Hadi Salim 
167215e47304SEric W. Biederman void km_state_expired(struct xfrm_state *x, int hard, u32 portid)
167326b15dadSJamal Hadi Salim {
167498806f75SAlexey Dobriyan 	struct net *net = xs_net(x);
167526b15dadSJamal Hadi Salim 	struct km_event c;
167626b15dadSJamal Hadi Salim 
1677bf08867fSHerbert Xu 	c.data.hard = hard;
167815e47304SEric W. Biederman 	c.portid = portid;
1679f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
168026b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
16811da177e4SLinus Torvalds 
16821da177e4SLinus Torvalds 	if (hard)
168398806f75SAlexey Dobriyan 		wake_up(&net->xfrm.km_waitq);
16841da177e4SLinus Torvalds }
16851da177e4SLinus Torvalds 
168653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
168726b15dadSJamal Hadi Salim /*
168826b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
168926b15dadSJamal Hadi Salim  * We are happy with one success
169026b15dadSJamal Hadi Salim */
1691980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
16921da177e4SLinus Torvalds {
169326b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
16941da177e4SLinus Torvalds 	struct xfrm_mgr *km;
16951da177e4SLinus Torvalds 
169685168c00SCong Wang 	rcu_read_lock();
169785168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list) {
169865e0736bSFan Du 		acqret = km->acquire(x, t, pol);
169926b15dadSJamal Hadi Salim 		if (!acqret)
170026b15dadSJamal Hadi Salim 			err = acqret;
17011da177e4SLinus Torvalds 	}
170285168c00SCong Wang 	rcu_read_unlock();
17031da177e4SLinus Torvalds 	return err;
17041da177e4SLinus Torvalds }
1705980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
17061da177e4SLinus Torvalds 
17075d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
17081da177e4SLinus Torvalds {
17091da177e4SLinus Torvalds 	int err = -EINVAL;
17101da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17111da177e4SLinus Torvalds 
171285168c00SCong Wang 	rcu_read_lock();
171385168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list) {
17141da177e4SLinus Torvalds 		if (km->new_mapping)
17151da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
17161da177e4SLinus Torvalds 		if (!err)
17171da177e4SLinus Torvalds 			break;
17181da177e4SLinus Torvalds 	}
171985168c00SCong Wang 	rcu_read_unlock();
17201da177e4SLinus Torvalds 	return err;
17211da177e4SLinus Torvalds }
17221da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
17231da177e4SLinus Torvalds 
172415e47304SEric W. Biederman void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid)
17251da177e4SLinus Torvalds {
172698806f75SAlexey Dobriyan 	struct net *net = xp_net(pol);
172726b15dadSJamal Hadi Salim 	struct km_event c;
17281da177e4SLinus Torvalds 
1729bf08867fSHerbert Xu 	c.data.hard = hard;
173015e47304SEric W. Biederman 	c.portid = portid;
1731f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
173226b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
17331da177e4SLinus Torvalds 
17341da177e4SLinus Torvalds 	if (hard)
173598806f75SAlexey Dobriyan 		wake_up(&net->xfrm.km_waitq);
17361da177e4SLinus Torvalds }
1737a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
17381da177e4SLinus Torvalds 
17392d60abc2SEric Dumazet #ifdef CONFIG_XFRM_MIGRATE
1740183cad12SDavid S. Miller int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
1741183cad12SDavid S. Miller 	       const struct xfrm_migrate *m, int num_migrate,
1742183cad12SDavid S. Miller 	       const struct xfrm_kmaddress *k)
174380c9abaaSShinta Sugimoto {
174480c9abaaSShinta Sugimoto 	int err = -EINVAL;
174580c9abaaSShinta Sugimoto 	int ret;
174680c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
174780c9abaaSShinta Sugimoto 
174885168c00SCong Wang 	rcu_read_lock();
174985168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list) {
175080c9abaaSShinta Sugimoto 		if (km->migrate) {
175113c1d189SArnaud Ebalard 			ret = km->migrate(sel, dir, type, m, num_migrate, k);
175280c9abaaSShinta Sugimoto 			if (!ret)
175380c9abaaSShinta Sugimoto 				err = ret;
175480c9abaaSShinta Sugimoto 		}
175580c9abaaSShinta Sugimoto 	}
175685168c00SCong Wang 	rcu_read_unlock();
175780c9abaaSShinta Sugimoto 	return err;
175880c9abaaSShinta Sugimoto }
175980c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
17602d60abc2SEric Dumazet #endif
176180c9abaaSShinta Sugimoto 
1762db983c11SAlexey Dobriyan int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
176397a64b45SMasahide NAKAMURA {
176497a64b45SMasahide NAKAMURA 	int err = -EINVAL;
176597a64b45SMasahide NAKAMURA 	int ret;
176697a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
176797a64b45SMasahide NAKAMURA 
176885168c00SCong Wang 	rcu_read_lock();
176985168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list) {
177097a64b45SMasahide NAKAMURA 		if (km->report) {
1771db983c11SAlexey Dobriyan 			ret = km->report(net, proto, sel, addr);
177297a64b45SMasahide NAKAMURA 			if (!ret)
177397a64b45SMasahide NAKAMURA 				err = ret;
177497a64b45SMasahide NAKAMURA 		}
177597a64b45SMasahide NAKAMURA 	}
177685168c00SCong Wang 	rcu_read_unlock();
177797a64b45SMasahide NAKAMURA 	return err;
177897a64b45SMasahide NAKAMURA }
177997a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
178097a64b45SMasahide NAKAMURA 
17811da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
17821da177e4SLinus Torvalds {
17831da177e4SLinus Torvalds 	int err;
17841da177e4SLinus Torvalds 	u8 *data;
17851da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17861da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
17871da177e4SLinus Torvalds 
17881da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
17891da177e4SLinus Torvalds 		return -EMSGSIZE;
17901da177e4SLinus Torvalds 
17911da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
17921da177e4SLinus Torvalds 	if (!data)
17931da177e4SLinus Torvalds 		return -ENOMEM;
17941da177e4SLinus Torvalds 
17951da177e4SLinus Torvalds 	err = -EFAULT;
17961da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
17971da177e4SLinus Torvalds 		goto out;
17981da177e4SLinus Torvalds 
17991da177e4SLinus Torvalds 	err = -EINVAL;
180085168c00SCong Wang 	rcu_read_lock();
180185168c00SCong Wang 	list_for_each_entry_rcu(km, &xfrm_km_list, list) {
1802cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
18031da177e4SLinus Torvalds 					 optlen, &err);
18041da177e4SLinus Torvalds 		if (err >= 0)
18051da177e4SLinus Torvalds 			break;
18061da177e4SLinus Torvalds 	}
180785168c00SCong Wang 	rcu_read_unlock();
18081da177e4SLinus Torvalds 
18091da177e4SLinus Torvalds 	if (err >= 0) {
18101da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
18111da177e4SLinus Torvalds 		xfrm_pol_put(pol);
18121da177e4SLinus Torvalds 		err = 0;
18131da177e4SLinus Torvalds 	}
18141da177e4SLinus Torvalds 
18151da177e4SLinus Torvalds out:
18161da177e4SLinus Torvalds 	kfree(data);
18171da177e4SLinus Torvalds 	return err;
18181da177e4SLinus Torvalds }
18191da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
18201da177e4SLinus Torvalds 
182185168c00SCong Wang static DEFINE_SPINLOCK(xfrm_km_lock);
182285168c00SCong Wang 
18231da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
18241da177e4SLinus Torvalds {
182585168c00SCong Wang 	spin_lock_bh(&xfrm_km_lock);
182685168c00SCong Wang 	list_add_tail_rcu(&km->list, &xfrm_km_list);
182785168c00SCong Wang 	spin_unlock_bh(&xfrm_km_lock);
18281da177e4SLinus Torvalds 	return 0;
18291da177e4SLinus Torvalds }
18301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
18311da177e4SLinus Torvalds 
18321da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
18331da177e4SLinus Torvalds {
183485168c00SCong Wang 	spin_lock_bh(&xfrm_km_lock);
183585168c00SCong Wang 	list_del_rcu(&km->list);
183685168c00SCong Wang 	spin_unlock_bh(&xfrm_km_lock);
183785168c00SCong Wang 	synchronize_rcu();
18381da177e4SLinus Torvalds 	return 0;
18391da177e4SLinus Torvalds }
18401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
18411da177e4SLinus Torvalds 
18421da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
18431da177e4SLinus Torvalds {
18441da177e4SLinus Torvalds 	int err = 0;
18451da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18461da177e4SLinus Torvalds 		return -EINVAL;
18471da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18481da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
184944abdc30SCong Wang 	spin_lock_bh(&xfrm_state_afinfo_lock);
18501da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
18511da177e4SLinus Torvalds 		err = -ENOBUFS;
1852edcd5821SDavid S. Miller 	else
185344abdc30SCong Wang 		rcu_assign_pointer(xfrm_state_afinfo[afinfo->family], afinfo);
185444abdc30SCong Wang 	spin_unlock_bh(&xfrm_state_afinfo_lock);
18551da177e4SLinus Torvalds 	return err;
18561da177e4SLinus Torvalds }
18571da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
18581da177e4SLinus Torvalds 
18591da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
18601da177e4SLinus Torvalds {
18611da177e4SLinus Torvalds 	int err = 0;
18621da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18631da177e4SLinus Torvalds 		return -EINVAL;
18641da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18651da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
186644abdc30SCong Wang 	spin_lock_bh(&xfrm_state_afinfo_lock);
18671da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
18681da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
18691da177e4SLinus Torvalds 			err = -EINVAL;
1870edcd5821SDavid S. Miller 		else
187144abdc30SCong Wang 			RCU_INIT_POINTER(xfrm_state_afinfo[afinfo->family], NULL);
18721da177e4SLinus Torvalds 	}
187344abdc30SCong Wang 	spin_unlock_bh(&xfrm_state_afinfo_lock);
187444abdc30SCong Wang 	synchronize_rcu();
18751da177e4SLinus Torvalds 	return err;
18761da177e4SLinus Torvalds }
18771da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
18781da177e4SLinus Torvalds 
187917c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
18801da177e4SLinus Torvalds {
18811da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
18821da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
18831da177e4SLinus Torvalds 		return NULL;
188444abdc30SCong Wang 	rcu_read_lock();
188544abdc30SCong Wang 	afinfo = rcu_dereference(xfrm_state_afinfo[family]);
1886546be240SHerbert Xu 	if (unlikely(!afinfo))
188744abdc30SCong Wang 		rcu_read_unlock();
18881da177e4SLinus Torvalds 	return afinfo;
18891da177e4SLinus Torvalds }
18901da177e4SLinus Torvalds 
189117c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
18921da177e4SLinus Torvalds {
189344abdc30SCong Wang 	rcu_read_unlock();
18941da177e4SLinus Torvalds }
18951da177e4SLinus Torvalds 
18961da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
18971da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
18981da177e4SLinus Torvalds {
18991da177e4SLinus Torvalds 	if (x->tunnel) {
19001da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
19031da177e4SLinus Torvalds 			xfrm_state_delete(t);
19041da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
19051da177e4SLinus Torvalds 		xfrm_state_put(t);
19061da177e4SLinus Torvalds 		x->tunnel = NULL;
19071da177e4SLinus Torvalds 	}
19081da177e4SLinus Torvalds }
19091da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
19121da177e4SLinus Torvalds {
1913c5c25238SPatrick McHardy 	int res;
19141da177e4SLinus Torvalds 
19151da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
19161da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_VALID &&
1917c5c25238SPatrick McHardy 	    x->type && x->type->get_mtu)
1918c5c25238SPatrick McHardy 		res = x->type->get_mtu(x, mtu);
19191da177e4SLinus Torvalds 	else
192028121617SPatrick McHardy 		res = mtu - x->props.header_len;
19211da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
19221da177e4SLinus Torvalds 	return res;
19231da177e4SLinus Torvalds }
19241da177e4SLinus Torvalds 
1925a454f0ccSWei Yongjun int __xfrm_init_state(struct xfrm_state *x, bool init_replay)
192672cb6962SHerbert Xu {
1927d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1928df9dcb45SKazunori MIYAZAWA 	struct xfrm_mode *inner_mode;
1929d094cd83SHerbert Xu 	int family = x->props.family;
193072cb6962SHerbert Xu 	int err;
193172cb6962SHerbert Xu 
1932d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1933d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1934d094cd83SHerbert Xu 	if (!afinfo)
1935d094cd83SHerbert Xu 		goto error;
1936d094cd83SHerbert Xu 
1937d094cd83SHerbert Xu 	err = 0;
1938d094cd83SHerbert Xu 	if (afinfo->init_flags)
1939d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1940d094cd83SHerbert Xu 
1941d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1942d094cd83SHerbert Xu 
1943d094cd83SHerbert Xu 	if (err)
1944d094cd83SHerbert Xu 		goto error;
1945d094cd83SHerbert Xu 
1946d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1947df9dcb45SKazunori MIYAZAWA 
1948df9dcb45SKazunori MIYAZAWA 	if (x->sel.family != AF_UNSPEC) {
1949df9dcb45SKazunori MIYAZAWA 		inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1950df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
195113996378SHerbert Xu 			goto error;
195213996378SHerbert Xu 
1953df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1954df9dcb45SKazunori MIYAZAWA 		    family != x->sel.family) {
1955df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
195613996378SHerbert Xu 			goto error;
1957df9dcb45SKazunori MIYAZAWA 		}
1958df9dcb45SKazunori MIYAZAWA 
1959df9dcb45SKazunori MIYAZAWA 		x->inner_mode = inner_mode;
1960df9dcb45SKazunori MIYAZAWA 	} else {
1961df9dcb45SKazunori MIYAZAWA 		struct xfrm_mode *inner_mode_iaf;
1962d81d2285SMartin Willi 		int iafamily = AF_INET;
1963df9dcb45SKazunori MIYAZAWA 
1964d81d2285SMartin Willi 		inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
1965df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
1966df9dcb45SKazunori MIYAZAWA 			goto error;
1967df9dcb45SKazunori MIYAZAWA 
1968df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
1969df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
1970df9dcb45SKazunori MIYAZAWA 			goto error;
1971df9dcb45SKazunori MIYAZAWA 		}
1972df9dcb45SKazunori MIYAZAWA 		x->inner_mode = inner_mode;
1973d81d2285SMartin Willi 
1974d81d2285SMartin Willi 		if (x->props.family == AF_INET)
1975d81d2285SMartin Willi 			iafamily = AF_INET6;
1976d81d2285SMartin Willi 
1977d81d2285SMartin Willi 		inner_mode_iaf = xfrm_get_mode(x->props.mode, iafamily);
1978d81d2285SMartin Willi 		if (inner_mode_iaf) {
1979d81d2285SMartin Willi 			if (inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)
1980df9dcb45SKazunori MIYAZAWA 				x->inner_mode_iaf = inner_mode_iaf;
1981d81d2285SMartin Willi 			else
1982d81d2285SMartin Willi 				xfrm_put_mode(inner_mode_iaf);
1983df9dcb45SKazunori MIYAZAWA 		}
1984df9dcb45SKazunori MIYAZAWA 	}
198513996378SHerbert Xu 
1986d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
198772cb6962SHerbert Xu 	if (x->type == NULL)
198872cb6962SHerbert Xu 		goto error;
198972cb6962SHerbert Xu 
199072cb6962SHerbert Xu 	err = x->type->init_state(x);
199172cb6962SHerbert Xu 	if (err)
199272cb6962SHerbert Xu 		goto error;
199372cb6962SHerbert Xu 
199413996378SHerbert Xu 	x->outer_mode = xfrm_get_mode(x->props.mode, family);
1995599901c3SJulia Lawall 	if (x->outer_mode == NULL) {
1996599901c3SJulia Lawall 		err = -EPROTONOSUPPORT;
1997b59f45d0SHerbert Xu 		goto error;
1998599901c3SJulia Lawall 	}
1999b59f45d0SHerbert Xu 
2000a454f0ccSWei Yongjun 	if (init_replay) {
2001a454f0ccSWei Yongjun 		err = xfrm_init_replay(x);
2002a454f0ccSWei Yongjun 		if (err)
2003a454f0ccSWei Yongjun 			goto error;
2004a454f0ccSWei Yongjun 	}
2005a454f0ccSWei Yongjun 
200672cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
200772cb6962SHerbert Xu 
200872cb6962SHerbert Xu error:
200972cb6962SHerbert Xu 	return err;
201072cb6962SHerbert Xu }
201172cb6962SHerbert Xu 
2012a454f0ccSWei Yongjun EXPORT_SYMBOL(__xfrm_init_state);
2013a454f0ccSWei Yongjun 
2014a454f0ccSWei Yongjun int xfrm_init_state(struct xfrm_state *x)
2015a454f0ccSWei Yongjun {
2016a454f0ccSWei Yongjun 	return __xfrm_init_state(x, true);
2017a454f0ccSWei Yongjun }
2018a454f0ccSWei Yongjun 
201972cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
20201da177e4SLinus Torvalds 
2021d62ddc21SAlexey Dobriyan int __net_init xfrm_state_init(struct net *net)
20221da177e4SLinus Torvalds {
2023f034b5d4SDavid S. Miller 	unsigned int sz;
20241da177e4SLinus Torvalds 
20259d4139c7SAlexey Dobriyan 	INIT_LIST_HEAD(&net->xfrm.state_all);
20269d4139c7SAlexey Dobriyan 
2027f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
2028f034b5d4SDavid S. Miller 
202973d189dcSAlexey Dobriyan 	net->xfrm.state_bydst = xfrm_hash_alloc(sz);
203073d189dcSAlexey Dobriyan 	if (!net->xfrm.state_bydst)
203173d189dcSAlexey Dobriyan 		goto out_bydst;
2032d320bbb3SAlexey Dobriyan 	net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
2033d320bbb3SAlexey Dobriyan 	if (!net->xfrm.state_bysrc)
2034d320bbb3SAlexey Dobriyan 		goto out_bysrc;
2035b754a4fdSAlexey Dobriyan 	net->xfrm.state_byspi = xfrm_hash_alloc(sz);
2036b754a4fdSAlexey Dobriyan 	if (!net->xfrm.state_byspi)
2037b754a4fdSAlexey Dobriyan 		goto out_byspi;
2038529983ecSAlexey Dobriyan 	net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2039f034b5d4SDavid S. Miller 
20400bf7c5b0SAlexey Dobriyan 	net->xfrm.state_num = 0;
204163082733SAlexey Dobriyan 	INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
2042b8a0ae20SAlexey Dobriyan 	INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
2043c7837144SAlexey Dobriyan 	INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
204450a30657SAlexey Dobriyan 	init_waitqueue_head(&net->xfrm.km_waitq);
2045d62ddc21SAlexey Dobriyan 	return 0;
204673d189dcSAlexey Dobriyan 
2047b754a4fdSAlexey Dobriyan out_byspi:
2048b754a4fdSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bysrc, sz);
2049d320bbb3SAlexey Dobriyan out_bysrc:
2050d320bbb3SAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bydst, sz);
205173d189dcSAlexey Dobriyan out_bydst:
205273d189dcSAlexey Dobriyan 	return -ENOMEM;
2053d62ddc21SAlexey Dobriyan }
2054d62ddc21SAlexey Dobriyan 
2055d62ddc21SAlexey Dobriyan void xfrm_state_fini(struct net *net)
2056d62ddc21SAlexey Dobriyan {
20577c2776eeSAlexey Dobriyan 	struct xfrm_audit audit_info;
205873d189dcSAlexey Dobriyan 	unsigned int sz;
205973d189dcSAlexey Dobriyan 
20607c2776eeSAlexey Dobriyan 	flush_work(&net->xfrm.state_hash_work);
2061e1760bd5SEric W. Biederman 	audit_info.loginuid = INVALID_UID;
20627c2776eeSAlexey Dobriyan 	audit_info.sessionid = -1;
20637c2776eeSAlexey Dobriyan 	audit_info.secid = 0;
20647c2776eeSAlexey Dobriyan 	xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
20657c2776eeSAlexey Dobriyan 	flush_work(&net->xfrm.state_gc_work);
20667c2776eeSAlexey Dobriyan 
20679d4139c7SAlexey Dobriyan 	WARN_ON(!list_empty(&net->xfrm.state_all));
206873d189dcSAlexey Dobriyan 
2069529983ecSAlexey Dobriyan 	sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
2070b754a4fdSAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_byspi));
2071b754a4fdSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_byspi, sz);
2072d320bbb3SAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
2073d320bbb3SAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bysrc, sz);
207473d189dcSAlexey Dobriyan 	WARN_ON(!hlist_empty(net->xfrm.state_bydst));
207573d189dcSAlexey Dobriyan 	xfrm_hash_free(net->xfrm.state_bydst, sz);
20761da177e4SLinus Torvalds }
20771da177e4SLinus Torvalds 
2078ab5f5e8bSJoy Latten #ifdef CONFIG_AUDITSYSCALL
2079cf35f43eSIlpo Järvinen static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2080ab5f5e8bSJoy Latten 				     struct audit_buffer *audit_buf)
2081ab5f5e8bSJoy Latten {
208268277accSPaul Moore 	struct xfrm_sec_ctx *ctx = x->security;
208368277accSPaul Moore 	u32 spi = ntohl(x->id.spi);
208468277accSPaul Moore 
208568277accSPaul Moore 	if (ctx)
2086ab5f5e8bSJoy Latten 		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
208768277accSPaul Moore 				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
2088ab5f5e8bSJoy Latten 
2089ab5f5e8bSJoy Latten 	switch(x->props.family) {
2090ab5f5e8bSJoy Latten 	case AF_INET:
209121454aaaSHarvey Harrison 		audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
209221454aaaSHarvey Harrison 				 &x->props.saddr.a4, &x->id.daddr.a4);
2093ab5f5e8bSJoy Latten 		break;
2094ab5f5e8bSJoy Latten 	case AF_INET6:
20955b095d98SHarvey Harrison 		audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
2096fdb46ee7SHarvey Harrison 				 x->props.saddr.a6, x->id.daddr.a6);
2097ab5f5e8bSJoy Latten 		break;
2098ab5f5e8bSJoy Latten 	}
209968277accSPaul Moore 
210068277accSPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2101ab5f5e8bSJoy Latten }
2102ab5f5e8bSJoy Latten 
2103cf35f43eSIlpo Järvinen static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2104afeb14b4SPaul Moore 				      struct audit_buffer *audit_buf)
2105afeb14b4SPaul Moore {
2106b71d1d42SEric Dumazet 	const struct iphdr *iph4;
2107b71d1d42SEric Dumazet 	const struct ipv6hdr *iph6;
2108afeb14b4SPaul Moore 
2109afeb14b4SPaul Moore 	switch (family) {
2110afeb14b4SPaul Moore 	case AF_INET:
2111afeb14b4SPaul Moore 		iph4 = ip_hdr(skb);
211221454aaaSHarvey Harrison 		audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
211321454aaaSHarvey Harrison 				 &iph4->saddr, &iph4->daddr);
2114afeb14b4SPaul Moore 		break;
2115afeb14b4SPaul Moore 	case AF_INET6:
2116afeb14b4SPaul Moore 		iph6 = ipv6_hdr(skb);
2117afeb14b4SPaul Moore 		audit_log_format(audit_buf,
21185b095d98SHarvey Harrison 				 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
2119fdb46ee7SHarvey Harrison 				 &iph6->saddr,&iph6->daddr,
2120afeb14b4SPaul Moore 				 iph6->flow_lbl[0] & 0x0f,
2121afeb14b4SPaul Moore 				 iph6->flow_lbl[1],
2122afeb14b4SPaul Moore 				 iph6->flow_lbl[2]);
2123afeb14b4SPaul Moore 		break;
2124afeb14b4SPaul Moore 	}
2125afeb14b4SPaul Moore }
2126afeb14b4SPaul Moore 
212768277accSPaul Moore void xfrm_audit_state_add(struct xfrm_state *x, int result,
2128e1760bd5SEric W. Biederman 			  kuid_t auid, u32 sessionid, u32 secid)
2129ab5f5e8bSJoy Latten {
2130ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2131ab5f5e8bSJoy Latten 
2132afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-add");
2133ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2134ab5f5e8bSJoy Latten 		return;
21352532386fSEric Paris 	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
2136afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2137afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2138ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2139ab5f5e8bSJoy Latten }
2140ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2141ab5f5e8bSJoy Latten 
214268277accSPaul Moore void xfrm_audit_state_delete(struct xfrm_state *x, int result,
2143e1760bd5SEric W. Biederman 			     kuid_t auid, u32 sessionid, u32 secid)
2144ab5f5e8bSJoy Latten {
2145ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2146ab5f5e8bSJoy Latten 
2147afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-delete");
2148ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2149ab5f5e8bSJoy Latten 		return;
21502532386fSEric Paris 	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
2151afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2152afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2153ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2154ab5f5e8bSJoy Latten }
2155ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2156afeb14b4SPaul Moore 
2157afeb14b4SPaul Moore void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2158afeb14b4SPaul Moore 				      struct sk_buff *skb)
2159afeb14b4SPaul Moore {
2160afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2161afeb14b4SPaul Moore 	u32 spi;
2162afeb14b4SPaul Moore 
2163afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replay-overflow");
2164afeb14b4SPaul Moore 	if (audit_buf == NULL)
2165afeb14b4SPaul Moore 		return;
2166afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2167afeb14b4SPaul Moore 	/* don't record the sequence number because it's inherent in this kind
2168afeb14b4SPaul Moore 	 * of audit message */
2169afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2170afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2171afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2172afeb14b4SPaul Moore }
2173afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2174afeb14b4SPaul Moore 
21759fdc4883SSteffen Klassert void xfrm_audit_state_replay(struct xfrm_state *x,
2176afeb14b4SPaul Moore 			     struct sk_buff *skb, __be32 net_seq)
2177afeb14b4SPaul Moore {
2178afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2179afeb14b4SPaul Moore 	u32 spi;
2180afeb14b4SPaul Moore 
2181afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replayed-pkt");
2182afeb14b4SPaul Moore 	if (audit_buf == NULL)
2183afeb14b4SPaul Moore 		return;
2184afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2185afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2186afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2187afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2188afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2189afeb14b4SPaul Moore }
21909fdc4883SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_audit_state_replay);
2191afeb14b4SPaul Moore 
2192afeb14b4SPaul Moore void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2193afeb14b4SPaul Moore {
2194afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2195afeb14b4SPaul Moore 
2196afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2197afeb14b4SPaul Moore 	if (audit_buf == NULL)
2198afeb14b4SPaul Moore 		return;
2199afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2200afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2201afeb14b4SPaul Moore }
2202afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2203afeb14b4SPaul Moore 
2204afeb14b4SPaul Moore void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2205afeb14b4SPaul Moore 			       __be32 net_spi, __be32 net_seq)
2206afeb14b4SPaul Moore {
2207afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2208afeb14b4SPaul Moore 	u32 spi;
2209afeb14b4SPaul Moore 
2210afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2211afeb14b4SPaul Moore 	if (audit_buf == NULL)
2212afeb14b4SPaul Moore 		return;
2213afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2214afeb14b4SPaul Moore 	spi = ntohl(net_spi);
2215afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2216afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2217afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2218afeb14b4SPaul Moore }
2219afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2220afeb14b4SPaul Moore 
2221afeb14b4SPaul Moore void xfrm_audit_state_icvfail(struct xfrm_state *x,
2222afeb14b4SPaul Moore 			      struct sk_buff *skb, u8 proto)
2223afeb14b4SPaul Moore {
2224afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2225afeb14b4SPaul Moore 	__be32 net_spi;
2226afeb14b4SPaul Moore 	__be32 net_seq;
2227afeb14b4SPaul Moore 
2228afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-icv-failure");
2229afeb14b4SPaul Moore 	if (audit_buf == NULL)
2230afeb14b4SPaul Moore 		return;
2231afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2232afeb14b4SPaul Moore 	if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2233afeb14b4SPaul Moore 		u32 spi = ntohl(net_spi);
2234afeb14b4SPaul Moore 		audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2235afeb14b4SPaul Moore 				 spi, spi, ntohl(net_seq));
2236afeb14b4SPaul Moore 	}
2237afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2238afeb14b4SPaul Moore }
2239afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
2240ab5f5e8bSJoy Latten #endif /* CONFIG_AUDITSYSCALL */
2241