xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision df9dcb45)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * xfrm_state.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Changes:
51da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI
61da177e4SLinus Torvalds  * 	Kazunori MIYAZAWA @USAGI
71da177e4SLinus Torvalds  * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
81da177e4SLinus Torvalds  * 		IPv6 support
91da177e4SLinus Torvalds  * 	YOSHIFUJI Hideaki @USAGI
101da177e4SLinus Torvalds  * 		Split up af-specific functions
111da177e4SLinus Torvalds  *	Derek Atkins <derek@ihtfp.com>
121da177e4SLinus Torvalds  *		Add UDP Encapsulation
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/workqueue.h>
171da177e4SLinus Torvalds #include <net/xfrm.h>
181da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
191da177e4SLinus Torvalds #include <linux/ipsec.h>
201da177e4SLinus Torvalds #include <linux/module.h>
21f034b5d4SDavid S. Miller #include <linux/cache.h>
2268277accSPaul Moore #include <linux/audit.h>
23b5890d8bSJesper Juhl #include <asm/uaccess.h>
241da177e4SLinus Torvalds 
2544e36b42SDavid S. Miller #include "xfrm_hash.h"
2644e36b42SDavid S. Miller 
27ee857a7dSDavid S. Miller struct sock *xfrm_nl;
28ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl);
29ee857a7dSDavid S. Miller 
3001e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
31a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32a70fcb0bSDavid S. Miller 
3301e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
34a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35a70fcb0bSDavid S. Miller 
3601e67d08SDavid S. Miller u32 sysctl_xfrm_acq_expires __read_mostly = 30;
3701e67d08SDavid S. Miller 
381da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
41a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
421da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint
481da177e4SLinus Torvalds  * of tunnel or destination of transport mode) allowed by selector.
491da177e4SLinus Torvalds  *
501da177e4SLinus Torvalds  * Main use is finding SA after policy selected tunnel or transport mode.
511da177e4SLinus Torvalds  * Also, it can be used by ah/esp icmp error handler to find offending SA.
521da177e4SLinus Torvalds  */
53f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly;
54f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly;
55f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly;
56f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
57f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
58f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
599d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
601da177e4SLinus Torvalds 
6117c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
6217c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
6317c2a42aSHerbert Xu 
64afeb14b4SPaul Moore #ifdef CONFIG_AUDITSYSCALL
65afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
66afeb14b4SPaul Moore 				    struct sk_buff *skb, __be32 net_seq);
67afeb14b4SPaul Moore #else
68afeb14b4SPaul Moore #define xfrm_audit_state_replay(x, s, sq)	do { ; } while (0)
69afeb14b4SPaul Moore #endif /* CONFIG_AUDITSYSCALL */
70afeb14b4SPaul Moore 
71c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
72c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
73c1969f29SDavid S. Miller 					 u32 reqid,
74a624c108SDavid S. Miller 					 unsigned short family)
75a624c108SDavid S. Miller {
76c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
77a624c108SDavid S. Miller }
78a624c108SDavid S. Miller 
79667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
80667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
8144e36b42SDavid S. Miller 					 unsigned short family)
82f034b5d4SDavid S. Miller {
83667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
84f034b5d4SDavid S. Miller }
85f034b5d4SDavid S. Miller 
862575b654SDavid S. Miller static inline unsigned int
878122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
88f034b5d4SDavid S. Miller {
89c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
90f034b5d4SDavid S. Miller }
91f034b5d4SDavid S. Miller 
92f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
93f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
94f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
95f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
96f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
97f034b5d4SDavid S. Miller {
98f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
99f034b5d4SDavid S. Miller 	struct xfrm_state *x;
100f034b5d4SDavid S. Miller 
101f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
102f034b5d4SDavid S. Miller 		unsigned int h;
103f034b5d4SDavid S. Miller 
104c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
105c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
106c1969f29SDavid S. Miller 				    nhashmask);
107f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
108f034b5d4SDavid S. Miller 
109667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
110667bbcb6SMasahide NAKAMURA 				    x->props.family,
111f034b5d4SDavid S. Miller 				    nhashmask);
112f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
113f034b5d4SDavid S. Miller 
1147b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1157b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1167b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1177b4dc360SMasahide NAKAMURA 					    nhashmask);
118f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
119f034b5d4SDavid S. Miller 		}
120f034b5d4SDavid S. Miller 	}
1217b4dc360SMasahide NAKAMURA }
122f034b5d4SDavid S. Miller 
123f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
124f034b5d4SDavid S. Miller {
125f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
126f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
127f034b5d4SDavid S. Miller }
128f034b5d4SDavid S. Miller 
129f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
130f034b5d4SDavid S. Miller 
131c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
132f034b5d4SDavid S. Miller {
133f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
134f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
135f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
136f034b5d4SDavid S. Miller 	int i;
137f034b5d4SDavid S. Miller 
138f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
139f034b5d4SDavid S. Miller 
140f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
14144e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
142f034b5d4SDavid S. Miller 	if (!ndst)
143f034b5d4SDavid S. Miller 		goto out_unlock;
14444e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
145f034b5d4SDavid S. Miller 	if (!nsrc) {
14644e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
147f034b5d4SDavid S. Miller 		goto out_unlock;
148f034b5d4SDavid S. Miller 	}
14944e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
150f034b5d4SDavid S. Miller 	if (!nspi) {
15144e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
15244e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
153f034b5d4SDavid S. Miller 		goto out_unlock;
154f034b5d4SDavid S. Miller 	}
155f034b5d4SDavid S. Miller 
156f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
157f034b5d4SDavid S. Miller 
158f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
159f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
160f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
161f034b5d4SDavid S. Miller 				   nhashmask);
162f034b5d4SDavid S. Miller 
163f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
164f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
165f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
166f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
167f034b5d4SDavid S. Miller 
168f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
169f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
170f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
171f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
172f034b5d4SDavid S. Miller 
173f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
174f034b5d4SDavid S. Miller 
175f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
17644e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
17744e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
17844e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
179f034b5d4SDavid S. Miller 
180f034b5d4SDavid S. Miller out_unlock:
181f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
182f034b5d4SDavid S. Miller }
183f034b5d4SDavid S. Miller 
184c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
185f034b5d4SDavid S. Miller 
1861da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1871da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1901da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1938f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1941da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1951da177e4SLinus Torvalds 
19653bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1971da177e4SLinus Torvalds 
198980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
19953bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
2001da177e4SLinus Torvalds 
201aa5d62ccSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
202aa5d62ccSHerbert Xu {
203aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
204aa5d62ccSHerbert Xu 	if (unlikely(family >= NPROTO))
205aa5d62ccSHerbert Xu 		return NULL;
206aa5d62ccSHerbert Xu 	write_lock_bh(&xfrm_state_afinfo_lock);
207aa5d62ccSHerbert Xu 	afinfo = xfrm_state_afinfo[family];
208aa5d62ccSHerbert Xu 	if (unlikely(!afinfo))
209aa5d62ccSHerbert Xu 		write_unlock_bh(&xfrm_state_afinfo_lock);
210aa5d62ccSHerbert Xu 	return afinfo;
211aa5d62ccSHerbert Xu }
212aa5d62ccSHerbert Xu 
213aa5d62ccSHerbert Xu static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
2149a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
215aa5d62ccSHerbert Xu {
216aa5d62ccSHerbert Xu 	write_unlock_bh(&xfrm_state_afinfo_lock);
217aa5d62ccSHerbert Xu }
218aa5d62ccSHerbert Xu 
219533cb5b0SEric Dumazet int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
220aa5d62ccSHerbert Xu {
221aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
222533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
223aa5d62ccSHerbert Xu 	int err = 0;
224aa5d62ccSHerbert Xu 
225aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
226aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
227aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
228aa5d62ccSHerbert Xu 
229aa5d62ccSHerbert Xu 	if (likely(typemap[type->proto] == NULL))
230aa5d62ccSHerbert Xu 		typemap[type->proto] = type;
231aa5d62ccSHerbert Xu 	else
232aa5d62ccSHerbert Xu 		err = -EEXIST;
233aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
234aa5d62ccSHerbert Xu 	return err;
235aa5d62ccSHerbert Xu }
236aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_type);
237aa5d62ccSHerbert Xu 
238533cb5b0SEric Dumazet int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
239aa5d62ccSHerbert Xu {
240aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
241533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
242aa5d62ccSHerbert Xu 	int err = 0;
243aa5d62ccSHerbert Xu 
244aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
245aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
246aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
247aa5d62ccSHerbert Xu 
248aa5d62ccSHerbert Xu 	if (unlikely(typemap[type->proto] != type))
249aa5d62ccSHerbert Xu 		err = -ENOENT;
250aa5d62ccSHerbert Xu 	else
251aa5d62ccSHerbert Xu 		typemap[type->proto] = NULL;
252aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
253aa5d62ccSHerbert Xu 	return err;
254aa5d62ccSHerbert Xu }
255aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_type);
256aa5d62ccSHerbert Xu 
257533cb5b0SEric Dumazet static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
258aa5d62ccSHerbert Xu {
259aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
260533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
261533cb5b0SEric Dumazet 	const struct xfrm_type *type;
262aa5d62ccSHerbert Xu 	int modload_attempted = 0;
263aa5d62ccSHerbert Xu 
264aa5d62ccSHerbert Xu retry:
265aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
266aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
267aa5d62ccSHerbert Xu 		return NULL;
268aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
269aa5d62ccSHerbert Xu 
270aa5d62ccSHerbert Xu 	type = typemap[proto];
271aa5d62ccSHerbert Xu 	if (unlikely(type && !try_module_get(type->owner)))
272aa5d62ccSHerbert Xu 		type = NULL;
273aa5d62ccSHerbert Xu 	if (!type && !modload_attempted) {
274aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
275aa5d62ccSHerbert Xu 		request_module("xfrm-type-%d-%d", family, proto);
276aa5d62ccSHerbert Xu 		modload_attempted = 1;
277aa5d62ccSHerbert Xu 		goto retry;
278aa5d62ccSHerbert Xu 	}
279aa5d62ccSHerbert Xu 
280aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
281aa5d62ccSHerbert Xu 	return type;
282aa5d62ccSHerbert Xu }
283aa5d62ccSHerbert Xu 
284533cb5b0SEric Dumazet static void xfrm_put_type(const struct xfrm_type *type)
285aa5d62ccSHerbert Xu {
286aa5d62ccSHerbert Xu 	module_put(type->owner);
287aa5d62ccSHerbert Xu }
288aa5d62ccSHerbert Xu 
289aa5d62ccSHerbert Xu int xfrm_register_mode(struct xfrm_mode *mode, int family)
290aa5d62ccSHerbert Xu {
291aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
292aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
293aa5d62ccSHerbert Xu 	int err;
294aa5d62ccSHerbert Xu 
295aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
296aa5d62ccSHerbert Xu 		return -EINVAL;
297aa5d62ccSHerbert Xu 
298aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
299aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
300aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
301aa5d62ccSHerbert Xu 
302aa5d62ccSHerbert Xu 	err = -EEXIST;
303aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
30417c2a42aSHerbert Xu 	if (modemap[mode->encap])
30517c2a42aSHerbert Xu 		goto out;
30617c2a42aSHerbert Xu 
30717c2a42aSHerbert Xu 	err = -ENOENT;
30817c2a42aSHerbert Xu 	if (!try_module_get(afinfo->owner))
30917c2a42aSHerbert Xu 		goto out;
31017c2a42aSHerbert Xu 
31117c2a42aSHerbert Xu 	mode->afinfo = afinfo;
312aa5d62ccSHerbert Xu 	modemap[mode->encap] = mode;
313aa5d62ccSHerbert Xu 	err = 0;
314aa5d62ccSHerbert Xu 
31517c2a42aSHerbert Xu out:
316aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
317aa5d62ccSHerbert Xu 	return err;
318aa5d62ccSHerbert Xu }
319aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_mode);
320aa5d62ccSHerbert Xu 
321aa5d62ccSHerbert Xu int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
322aa5d62ccSHerbert Xu {
323aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
324aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
325aa5d62ccSHerbert Xu 	int err;
326aa5d62ccSHerbert Xu 
327aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
328aa5d62ccSHerbert Xu 		return -EINVAL;
329aa5d62ccSHerbert Xu 
330aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
331aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
332aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
333aa5d62ccSHerbert Xu 
334aa5d62ccSHerbert Xu 	err = -ENOENT;
335aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
336aa5d62ccSHerbert Xu 	if (likely(modemap[mode->encap] == mode)) {
337aa5d62ccSHerbert Xu 		modemap[mode->encap] = NULL;
33817c2a42aSHerbert Xu 		module_put(mode->afinfo->owner);
339aa5d62ccSHerbert Xu 		err = 0;
340aa5d62ccSHerbert Xu 	}
341aa5d62ccSHerbert Xu 
342aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
343aa5d62ccSHerbert Xu 	return err;
344aa5d62ccSHerbert Xu }
345aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_mode);
346aa5d62ccSHerbert Xu 
347aa5d62ccSHerbert Xu static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
348aa5d62ccSHerbert Xu {
349aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
350aa5d62ccSHerbert Xu 	struct xfrm_mode *mode;
351aa5d62ccSHerbert Xu 	int modload_attempted = 0;
352aa5d62ccSHerbert Xu 
353aa5d62ccSHerbert Xu 	if (unlikely(encap >= XFRM_MODE_MAX))
354aa5d62ccSHerbert Xu 		return NULL;
355aa5d62ccSHerbert Xu 
356aa5d62ccSHerbert Xu retry:
357aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
358aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
359aa5d62ccSHerbert Xu 		return NULL;
360aa5d62ccSHerbert Xu 
361aa5d62ccSHerbert Xu 	mode = afinfo->mode_map[encap];
362aa5d62ccSHerbert Xu 	if (unlikely(mode && !try_module_get(mode->owner)))
363aa5d62ccSHerbert Xu 		mode = NULL;
364aa5d62ccSHerbert Xu 	if (!mode && !modload_attempted) {
365aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
366aa5d62ccSHerbert Xu 		request_module("xfrm-mode-%d-%d", family, encap);
367aa5d62ccSHerbert Xu 		modload_attempted = 1;
368aa5d62ccSHerbert Xu 		goto retry;
369aa5d62ccSHerbert Xu 	}
370aa5d62ccSHerbert Xu 
371aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
372aa5d62ccSHerbert Xu 	return mode;
373aa5d62ccSHerbert Xu }
374aa5d62ccSHerbert Xu 
375aa5d62ccSHerbert Xu static void xfrm_put_mode(struct xfrm_mode *mode)
376aa5d62ccSHerbert Xu {
377aa5d62ccSHerbert Xu 	module_put(mode->owner);
378aa5d62ccSHerbert Xu }
379aa5d62ccSHerbert Xu 
3801da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
3811da177e4SLinus Torvalds {
382a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
383a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
3841da177e4SLinus Torvalds 	kfree(x->aalg);
3851da177e4SLinus Torvalds 	kfree(x->ealg);
3861da177e4SLinus Torvalds 	kfree(x->calg);
3871da177e4SLinus Torvalds 	kfree(x->encap);
388060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
38913996378SHerbert Xu 	if (x->inner_mode)
39013996378SHerbert Xu 		xfrm_put_mode(x->inner_mode);
391df9dcb45SKazunori MIYAZAWA 	if (x->inner_mode_iaf)
392df9dcb45SKazunori MIYAZAWA 		xfrm_put_mode(x->inner_mode_iaf);
39313996378SHerbert Xu 	if (x->outer_mode)
39413996378SHerbert Xu 		xfrm_put_mode(x->outer_mode);
3951da177e4SLinus Torvalds 	if (x->type) {
3961da177e4SLinus Torvalds 		x->type->destructor(x);
3971da177e4SLinus Torvalds 		xfrm_put_type(x->type);
3981da177e4SLinus Torvalds 	}
399df71837dSTrent Jaeger 	security_xfrm_state_free(x);
4001da177e4SLinus Torvalds 	kfree(x);
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds 
403c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
4041da177e4SLinus Torvalds {
4051da177e4SLinus Torvalds 	struct xfrm_state *x;
4068f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
4078f126e37SDavid S. Miller 	struct hlist_head gc_list;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
4108f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
4118f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
4121da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
4131da177e4SLinus Torvalds 
4148f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
4151da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
4168f126e37SDavid S. Miller 
4171da177e4SLinus Torvalds 	wake_up(&km_waitq);
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
4211da177e4SLinus Torvalds {
4221da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
4231da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
4241da177e4SLinus Torvalds 	else
4251da177e4SLinus Torvalds 		return secs*HZ;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
4319d729f72SJames Morris 	unsigned long now = get_seconds();
4321da177e4SLinus Torvalds 	long next = LONG_MAX;
4331da177e4SLinus Torvalds 	int warn = 0;
434161a09e7SJoy Latten 	int err = 0;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	spin_lock(&x->lock);
4371da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
4381da177e4SLinus Torvalds 		goto out;
4391da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
4401da177e4SLinus Torvalds 		goto expired;
4411da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
4421da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
4431da177e4SLinus Torvalds 			x->curlft.add_time - now;
4441da177e4SLinus Torvalds 		if (tmo <= 0)
4451da177e4SLinus Torvalds 			goto expired;
4461da177e4SLinus Torvalds 		if (tmo < next)
4471da177e4SLinus Torvalds 			next = tmo;
4481da177e4SLinus Torvalds 	}
4491da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
4501da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
4511da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4521da177e4SLinus Torvalds 		if (tmo <= 0)
4531da177e4SLinus Torvalds 			goto expired;
4541da177e4SLinus Torvalds 		if (tmo < next)
4551da177e4SLinus Torvalds 			next = tmo;
4561da177e4SLinus Torvalds 	}
4571da177e4SLinus Torvalds 	if (x->km.dying)
4581da177e4SLinus Torvalds 		goto resched;
4591da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
4601da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
4611da177e4SLinus Torvalds 			x->curlft.add_time - now;
4621da177e4SLinus Torvalds 		if (tmo <= 0)
4631da177e4SLinus Torvalds 			warn = 1;
4641da177e4SLinus Torvalds 		else if (tmo < next)
4651da177e4SLinus Torvalds 			next = tmo;
4661da177e4SLinus Torvalds 	}
4671da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
4681da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
4691da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4701da177e4SLinus Torvalds 		if (tmo <= 0)
4711da177e4SLinus Torvalds 			warn = 1;
4721da177e4SLinus Torvalds 		else if (tmo < next)
4731da177e4SLinus Torvalds 			next = tmo;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
4764666faabSHerbert Xu 	x->km.dying = warn;
4771da177e4SLinus Torvalds 	if (warn)
47853bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
4791da177e4SLinus Torvalds resched:
480a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
481a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
482a47f0ce0SDavid S. Miller 
4831da177e4SLinus Torvalds 	goto out;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds expired:
4861da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
4871da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
4881da177e4SLinus Torvalds 		wake_up(&km_waitq);
4891da177e4SLinus Torvalds 		next = 2;
4901da177e4SLinus Torvalds 		goto resched;
4911da177e4SLinus Torvalds 	}
492161a09e7SJoy Latten 
493161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
494161a09e7SJoy Latten 	if (!err && x->id.spi)
49553bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
4961da177e4SLinus Torvalds 
497ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
4980c11b942SAl Viro 				audit_get_loginuid(current), 0);
499161a09e7SJoy Latten 
5001da177e4SLinus Torvalds out:
5011da177e4SLinus Torvalds 	spin_unlock(&x->lock);
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
5040ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
5050ac84752SDavid S. Miller 
5061da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
5071da177e4SLinus Torvalds {
5081da177e4SLinus Torvalds 	struct xfrm_state *x;
5091da177e4SLinus Torvalds 
5100da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	if (x) {
5131da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
5141da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
5158f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
5168f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
5178f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
518b24b8a24SPavel Emelyanov 		setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
519b24b8a24SPavel Emelyanov 		setup_timer(&x->rtimer, xfrm_replay_timer_handler,
520b24b8a24SPavel Emelyanov 				(unsigned long)x);
5219d729f72SJames Morris 		x->curlft.add_time = get_seconds();
5221da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
5231da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
5241da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
5251da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
526f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
527f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
528df9dcb45SKazunori MIYAZAWA 		x->inner_mode = NULL;
529df9dcb45SKazunori MIYAZAWA 		x->inner_mode_iaf = NULL;
5301da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
5311da177e4SLinus Torvalds 	}
5321da177e4SLinus Torvalds 	return x;
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
5371da177e4SLinus Torvalds {
5381da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
5418f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
5421da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
5431da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
5461da177e4SLinus Torvalds 
54753bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
5481da177e4SLinus Torvalds {
54926b15dadSJamal Hadi Salim 	int err = -ESRCH;
55026b15dadSJamal Hadi Salim 
5511da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
5521da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
5531da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
5548f126e37SDavid S. Miller 		hlist_del(&x->bydst);
5558f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
556a47f0ce0SDavid S. Miller 		if (x->id.spi)
5578f126e37SDavid S. Miller 			hlist_del(&x->byspi);
558f034b5d4SDavid S. Miller 		xfrm_state_num--;
5591da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
5621da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
5631da177e4SLinus Torvalds 		 * is what we are dropping here.
5641da177e4SLinus Torvalds 		 */
5655dba4797SPatrick McHardy 		xfrm_state_put(x);
56626b15dadSJamal Hadi Salim 		err = 0;
5671da177e4SLinus Torvalds 	}
5681da177e4SLinus Torvalds 
56926b15dadSJamal Hadi Salim 	return err;
57026b15dadSJamal Hadi Salim }
57153bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
57226b15dadSJamal Hadi Salim 
57326b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
5741da177e4SLinus Torvalds {
57526b15dadSJamal Hadi Salim 	int err;
57626b15dadSJamal Hadi Salim 
5771da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
57826b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
5791da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
58026b15dadSJamal Hadi Salim 
58126b15dadSJamal Hadi Salim 	return err;
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
5841da177e4SLinus Torvalds 
5854aa2e62cSJoy Latten #ifdef CONFIG_SECURITY_NETWORK_XFRM
5864aa2e62cSJoy Latten static inline int
5874aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
5881da177e4SLinus Torvalds {
5894aa2e62cSJoy Latten 	int i, err = 0;
5904aa2e62cSJoy Latten 
5914aa2e62cSJoy Latten 	for (i = 0; i <= xfrm_state_hmask; i++) {
5924aa2e62cSJoy Latten 		struct hlist_node *entry;
5934aa2e62cSJoy Latten 		struct xfrm_state *x;
5944aa2e62cSJoy Latten 
5954aa2e62cSJoy Latten 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
5964aa2e62cSJoy Latten 			if (xfrm_id_proto_match(x->id.proto, proto) &&
5974aa2e62cSJoy Latten 			   (err = security_xfrm_state_delete(x)) != 0) {
598ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, 0,
599ab5f5e8bSJoy Latten 							audit_info->loginuid,
600ab5f5e8bSJoy Latten 							audit_info->secid);
6014aa2e62cSJoy Latten 				return err;
6024aa2e62cSJoy Latten 			}
6034aa2e62cSJoy Latten 		}
6044aa2e62cSJoy Latten 	}
6054aa2e62cSJoy Latten 
6064aa2e62cSJoy Latten 	return err;
6074aa2e62cSJoy Latten }
6084aa2e62cSJoy Latten #else
6094aa2e62cSJoy Latten static inline int
6104aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
6114aa2e62cSJoy Latten {
6124aa2e62cSJoy Latten 	return 0;
6134aa2e62cSJoy Latten }
6144aa2e62cSJoy Latten #endif
6154aa2e62cSJoy Latten 
6164aa2e62cSJoy Latten int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
6174aa2e62cSJoy Latten {
6184aa2e62cSJoy Latten 	int i, err = 0;
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6214aa2e62cSJoy Latten 	err = xfrm_state_flush_secctx_check(proto, audit_info);
6224aa2e62cSJoy Latten 	if (err)
6234aa2e62cSJoy Latten 		goto out;
6244aa2e62cSJoy Latten 
625a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
6268f126e37SDavid S. Miller 		struct hlist_node *entry;
6278f126e37SDavid S. Miller 		struct xfrm_state *x;
6281da177e4SLinus Torvalds restart:
6298f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
6301da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
6315794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
6321da177e4SLinus Torvalds 				xfrm_state_hold(x);
6331da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
6341da177e4SLinus Torvalds 
635161a09e7SJoy Latten 				err = xfrm_state_delete(x);
636ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, err ? 0 : 1,
637ab5f5e8bSJoy Latten 							audit_info->loginuid,
638ab5f5e8bSJoy Latten 							audit_info->secid);
6391da177e4SLinus Torvalds 				xfrm_state_put(x);
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
6421da177e4SLinus Torvalds 				goto restart;
6431da177e4SLinus Torvalds 			}
6441da177e4SLinus Torvalds 		}
6451da177e4SLinus Torvalds 	}
6464aa2e62cSJoy Latten 	err = 0;
6474aa2e62cSJoy Latten 
6484aa2e62cSJoy Latten out:
6491da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6501da177e4SLinus Torvalds 	wake_up(&km_waitq);
6514aa2e62cSJoy Latten 	return err;
6521da177e4SLinus Torvalds }
6531da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
6541da177e4SLinus Torvalds 
655af11e316SJamal Hadi Salim void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
65628d8909bSJamal Hadi Salim {
65728d8909bSJamal Hadi Salim 	spin_lock_bh(&xfrm_state_lock);
65828d8909bSJamal Hadi Salim 	si->sadcnt = xfrm_state_num;
65928d8909bSJamal Hadi Salim 	si->sadhcnt = xfrm_state_hmask;
66028d8909bSJamal Hadi Salim 	si->sadhmcnt = xfrm_state_hashmax;
66128d8909bSJamal Hadi Salim 	spin_unlock_bh(&xfrm_state_lock);
66228d8909bSJamal Hadi Salim }
66328d8909bSJamal Hadi Salim EXPORT_SYMBOL(xfrm_sad_getinfo);
66428d8909bSJamal Hadi Salim 
6651da177e4SLinus Torvalds static int
6661da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
6671da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
6681da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
6691da177e4SLinus Torvalds 		  unsigned short family)
6701da177e4SLinus Torvalds {
6711da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6721da177e4SLinus Torvalds 	if (!afinfo)
6731da177e4SLinus Torvalds 		return -1;
6741da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
6751da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6761da177e4SLinus Torvalds 	return 0;
6771da177e4SLinus Torvalds }
6781da177e4SLinus Torvalds 
679a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
680edcd5821SDavid S. Miller {
681edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
682edcd5821SDavid S. Miller 	struct xfrm_state *x;
6838f126e37SDavid S. Miller 	struct hlist_node *entry;
684edcd5821SDavid S. Miller 
6858f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
686edcd5821SDavid S. Miller 		if (x->props.family != family ||
687edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
688edcd5821SDavid S. Miller 		    x->id.proto     != proto)
689edcd5821SDavid S. Miller 			continue;
690edcd5821SDavid S. Miller 
691edcd5821SDavid S. Miller 		switch (family) {
692edcd5821SDavid S. Miller 		case AF_INET:
693edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
694edcd5821SDavid S. Miller 				continue;
695edcd5821SDavid S. Miller 			break;
696edcd5821SDavid S. Miller 		case AF_INET6:
697edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
698edcd5821SDavid S. Miller 					     (struct in6_addr *)
699edcd5821SDavid S. Miller 					     x->id.daddr.a6))
700edcd5821SDavid S. Miller 				continue;
701edcd5821SDavid S. Miller 			break;
7023ff50b79SStephen Hemminger 		}
703edcd5821SDavid S. Miller 
704edcd5821SDavid S. Miller 		xfrm_state_hold(x);
705edcd5821SDavid S. Miller 		return x;
706edcd5821SDavid S. Miller 	}
707edcd5821SDavid S. Miller 
708edcd5821SDavid S. Miller 	return NULL;
709edcd5821SDavid S. Miller }
710edcd5821SDavid S. Miller 
711edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
712edcd5821SDavid S. Miller {
713667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
714edcd5821SDavid S. Miller 	struct xfrm_state *x;
7158f126e37SDavid S. Miller 	struct hlist_node *entry;
716edcd5821SDavid S. Miller 
7178f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
718edcd5821SDavid S. Miller 		if (x->props.family != family ||
719edcd5821SDavid S. Miller 		    x->id.proto     != proto)
720edcd5821SDavid S. Miller 			continue;
721edcd5821SDavid S. Miller 
722edcd5821SDavid S. Miller 		switch (family) {
723edcd5821SDavid S. Miller 		case AF_INET:
724edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
725edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
726edcd5821SDavid S. Miller 				continue;
727edcd5821SDavid S. Miller 			break;
728edcd5821SDavid S. Miller 		case AF_INET6:
729edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
730edcd5821SDavid S. Miller 					     (struct in6_addr *)
731edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
732edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
733edcd5821SDavid S. Miller 					     (struct in6_addr *)
734edcd5821SDavid S. Miller 					     x->props.saddr.a6))
735edcd5821SDavid S. Miller 				continue;
736edcd5821SDavid S. Miller 			break;
7373ff50b79SStephen Hemminger 		}
738edcd5821SDavid S. Miller 
739edcd5821SDavid S. Miller 		xfrm_state_hold(x);
740edcd5821SDavid S. Miller 		return x;
741edcd5821SDavid S. Miller 	}
742edcd5821SDavid S. Miller 
743edcd5821SDavid S. Miller 	return NULL;
744edcd5821SDavid S. Miller }
745edcd5821SDavid S. Miller 
746edcd5821SDavid S. Miller static inline struct xfrm_state *
747edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
748edcd5821SDavid S. Miller {
749edcd5821SDavid S. Miller 	if (use_spi)
750edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
751edcd5821SDavid S. Miller 					   x->id.proto, family);
752edcd5821SDavid S. Miller 	else
753edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
754edcd5821SDavid S. Miller 						  &x->props.saddr,
755edcd5821SDavid S. Miller 						  x->id.proto, family);
756edcd5821SDavid S. Miller }
757edcd5821SDavid S. Miller 
7582fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
7592fab22f2SPatrick McHardy {
7602fab22f2SPatrick McHardy 	if (have_hash_collision &&
7612fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
7622fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
7632fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
7642fab22f2SPatrick McHardy }
7652fab22f2SPatrick McHardy 
7661da177e4SLinus Torvalds struct xfrm_state *
7671da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
7681da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
7691da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
7701da177e4SLinus Torvalds 		unsigned short family)
7711da177e4SLinus Torvalds {
7724bda4f25SPavel Emelyanov 	unsigned int h;
7738f126e37SDavid S. Miller 	struct hlist_node *entry;
7741da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
7751da177e4SLinus Torvalds 	int acquire_in_progress = 0;
7761da177e4SLinus Torvalds 	int error = 0;
7771da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
7781da177e4SLinus Torvalds 
7791da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7804bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
7818f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
7821da177e4SLinus Torvalds 		if (x->props.family == family &&
7831da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
784fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
7851da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
7861da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
7871da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
7881da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
7891da177e4SLinus Torvalds 			/* Resolution logic:
7901da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
7911da177e4SLinus Torvalds 			      Done.
7921da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 			   Entering area of "sysdeps".
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
7971da177e4SLinus Torvalds 			      it selects only session which triggered
7981da177e4SLinus Torvalds 			      previous resolution. Key manager will do
7991da177e4SLinus Torvalds 			      something to install a state with proper
8001da177e4SLinus Torvalds 			      selector.
8011da177e4SLinus Torvalds 			 */
8021da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
803df9dcb45SKazunori MIYAZAWA 				if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
804e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
8051da177e4SLinus Torvalds 					continue;
8061da177e4SLinus Torvalds 				if (!best ||
8071da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
8081da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
8091da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
8101da177e4SLinus Torvalds 					best = x;
8111da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
8121da177e4SLinus Torvalds 				acquire_in_progress = 1;
8131da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
8141da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
81548b8d783SJoakim Koskela 				if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
816e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
8171da177e4SLinus Torvalds 					error = -ESRCH;
8181da177e4SLinus Torvalds 			}
8191da177e4SLinus Torvalds 		}
8201da177e4SLinus Torvalds 	}
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	x = best;
8231da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
8245c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
825edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
826edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
8271da177e4SLinus Torvalds 			xfrm_state_put(x0);
8281da177e4SLinus Torvalds 			error = -EEXIST;
8291da177e4SLinus Torvalds 			goto out;
8301da177e4SLinus Torvalds 		}
8311da177e4SLinus Torvalds 		x = xfrm_state_alloc();
8321da177e4SLinus Torvalds 		if (x == NULL) {
8331da177e4SLinus Torvalds 			error = -ENOMEM;
8341da177e4SLinus Torvalds 			goto out;
8351da177e4SLinus Torvalds 		}
8361da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
8371da177e4SLinus Torvalds 		 * to current session. */
8381da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
8391da177e4SLinus Torvalds 
840e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
841e0d1caa7SVenkat Yekkirala 		if (error) {
842e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
843e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
844e0d1caa7SVenkat Yekkirala 			x = NULL;
845e0d1caa7SVenkat Yekkirala 			goto out;
846e0d1caa7SVenkat Yekkirala 		}
847e0d1caa7SVenkat Yekkirala 
8481da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
8491da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
8508f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
851667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
8528f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
8531da177e4SLinus Torvalds 			if (x->id.spi) {
8541da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
8558f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
8561da177e4SLinus Torvalds 			}
85701e67d08SDavid S. Miller 			x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
85801e67d08SDavid S. Miller 			x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
8591da177e4SLinus Torvalds 			add_timer(&x->timer);
8602fab22f2SPatrick McHardy 			xfrm_state_num++;
8612fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
8621da177e4SLinus Torvalds 		} else {
8631da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
8641da177e4SLinus Torvalds 			xfrm_state_put(x);
8651da177e4SLinus Torvalds 			x = NULL;
8661da177e4SLinus Torvalds 			error = -ESRCH;
8671da177e4SLinus Torvalds 		}
8681da177e4SLinus Torvalds 	}
8691da177e4SLinus Torvalds out:
8701da177e4SLinus Torvalds 	if (x)
8711da177e4SLinus Torvalds 		xfrm_state_hold(x);
8721da177e4SLinus Torvalds 	else
8731da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
8741da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8751da177e4SLinus Torvalds 	return x;
8761da177e4SLinus Torvalds }
8771da177e4SLinus Torvalds 
878628529b6SJamal Hadi Salim struct xfrm_state *
879628529b6SJamal Hadi Salim xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
880628529b6SJamal Hadi Salim 		    unsigned short family, u8 mode, u8 proto, u32 reqid)
881628529b6SJamal Hadi Salim {
8824bda4f25SPavel Emelyanov 	unsigned int h;
883628529b6SJamal Hadi Salim 	struct xfrm_state *rx = NULL, *x = NULL;
884628529b6SJamal Hadi Salim 	struct hlist_node *entry;
885628529b6SJamal Hadi Salim 
886628529b6SJamal Hadi Salim 	spin_lock(&xfrm_state_lock);
8874bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, reqid, family);
888628529b6SJamal Hadi Salim 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
889628529b6SJamal Hadi Salim 		if (x->props.family == family &&
890628529b6SJamal Hadi Salim 		    x->props.reqid == reqid &&
891628529b6SJamal Hadi Salim 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
892628529b6SJamal Hadi Salim 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
893628529b6SJamal Hadi Salim 		    mode == x->props.mode &&
894628529b6SJamal Hadi Salim 		    proto == x->id.proto &&
895628529b6SJamal Hadi Salim 		    x->km.state == XFRM_STATE_VALID) {
896628529b6SJamal Hadi Salim 			rx = x;
897628529b6SJamal Hadi Salim 			break;
898628529b6SJamal Hadi Salim 		}
899628529b6SJamal Hadi Salim 	}
900628529b6SJamal Hadi Salim 
901628529b6SJamal Hadi Salim 	if (rx)
902628529b6SJamal Hadi Salim 		xfrm_state_hold(rx);
903628529b6SJamal Hadi Salim 	spin_unlock(&xfrm_state_lock);
904628529b6SJamal Hadi Salim 
905628529b6SJamal Hadi Salim 
906628529b6SJamal Hadi Salim 	return rx;
907628529b6SJamal Hadi Salim }
908628529b6SJamal Hadi Salim EXPORT_SYMBOL(xfrm_stateonly_find);
909628529b6SJamal Hadi Salim 
9101da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
9111da177e4SLinus Torvalds {
912a624c108SDavid S. Miller 	unsigned int h;
9131da177e4SLinus Torvalds 
9149d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
9159d4a706dSDavid S. Miller 
916c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
917c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
9188f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
9191da177e4SLinus Torvalds 
920667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
9218f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
9226c44e6b7SMasahide NAKAMURA 
9237b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
9246c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
9256c44e6b7SMasahide NAKAMURA 				  x->props.family);
9261da177e4SLinus Torvalds 
9278f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
9286c44e6b7SMasahide NAKAMURA 	}
9291da177e4SLinus Torvalds 
930a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
931a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
932a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
933f8cd5488SJamal Hadi Salim 
9341da177e4SLinus Torvalds 	wake_up(&km_waitq);
935f034b5d4SDavid S. Miller 
936f034b5d4SDavid S. Miller 	xfrm_state_num++;
937f034b5d4SDavid S. Miller 
938918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
9391da177e4SLinus Torvalds }
9401da177e4SLinus Torvalds 
941c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
942c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
943c7f5ea3aSDavid S. Miller {
944c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
945c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
946c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
947c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
948c7f5ea3aSDavid S. Miller 	unsigned int h;
949c7f5ea3aSDavid S. Miller 
950c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
951c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
952c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
953c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
954c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
955c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
956c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
957c7f5ea3aSDavid S. Miller 	}
958c7f5ea3aSDavid S. Miller }
959c7f5ea3aSDavid S. Miller 
9601da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
9611da177e4SLinus Torvalds {
9621da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
963c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
9641da177e4SLinus Torvalds 	__xfrm_state_insert(x);
9651da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9661da177e4SLinus Torvalds }
9671da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
9681da177e4SLinus Torvalds 
9692770834cSDavid S. Miller /* xfrm_state_lock is held */
9702770834cSDavid S. Miller static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
9712770834cSDavid S. Miller {
972c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
9738f126e37SDavid S. Miller 	struct hlist_node *entry;
9742770834cSDavid S. Miller 	struct xfrm_state *x;
9752770834cSDavid S. Miller 
9768f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
9772770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
9782770834cSDavid S. Miller 		    x->props.mode   != mode ||
9792770834cSDavid S. Miller 		    x->props.family != family ||
9802770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
98175e252d9SJoy Latten 		    x->id.spi       != 0 ||
98275e252d9SJoy Latten 		    x->id.proto	    != proto)
9832770834cSDavid S. Miller 			continue;
9842770834cSDavid S. Miller 
9852770834cSDavid S. Miller 		switch (family) {
9862770834cSDavid S. Miller 		case AF_INET:
9872770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
9882770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
9892770834cSDavid S. Miller 				continue;
9902770834cSDavid S. Miller 			break;
9912770834cSDavid S. Miller 		case AF_INET6:
9922770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
9932770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
9942770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
9952770834cSDavid S. Miller 					     x->props.saddr.a6,
9962770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
9972770834cSDavid S. Miller 				continue;
9982770834cSDavid S. Miller 			break;
9993ff50b79SStephen Hemminger 		}
10002770834cSDavid S. Miller 
10012770834cSDavid S. Miller 		xfrm_state_hold(x);
10022770834cSDavid S. Miller 		return x;
10032770834cSDavid S. Miller 	}
10042770834cSDavid S. Miller 
10052770834cSDavid S. Miller 	if (!create)
10062770834cSDavid S. Miller 		return NULL;
10072770834cSDavid S. Miller 
10082770834cSDavid S. Miller 	x = xfrm_state_alloc();
10092770834cSDavid S. Miller 	if (likely(x)) {
10102770834cSDavid S. Miller 		switch (family) {
10112770834cSDavid S. Miller 		case AF_INET:
10122770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
10132770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
10142770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
10152770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
10162770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
10172770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
10182770834cSDavid S. Miller 			break;
10192770834cSDavid S. Miller 
10202770834cSDavid S. Miller 		case AF_INET6:
10212770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
10222770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10232770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
10242770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10252770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
10262770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
10272770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
10282770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10292770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
10302770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10312770834cSDavid S. Miller 			break;
10323ff50b79SStephen Hemminger 		}
10332770834cSDavid S. Miller 
10342770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
10352770834cSDavid S. Miller 		x->id.proto = proto;
10362770834cSDavid S. Miller 		x->props.family = family;
10372770834cSDavid S. Miller 		x->props.mode = mode;
10382770834cSDavid S. Miller 		x->props.reqid = reqid;
103901e67d08SDavid S. Miller 		x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
10402770834cSDavid S. Miller 		xfrm_state_hold(x);
104101e67d08SDavid S. Miller 		x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
10422770834cSDavid S. Miller 		add_timer(&x->timer);
10438f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
1044667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
10458f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
1046918049f0SDavid S. Miller 
1047918049f0SDavid S. Miller 		xfrm_state_num++;
1048918049f0SDavid S. Miller 
1049918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
10502770834cSDavid S. Miller 	}
10512770834cSDavid S. Miller 
10522770834cSDavid S. Miller 	return x;
10532770834cSDavid S. Miller }
10542770834cSDavid S. Miller 
10551da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
10581da177e4SLinus Torvalds {
10591da177e4SLinus Torvalds 	struct xfrm_state *x1;
10601da177e4SLinus Torvalds 	int family;
10611da177e4SLinus Torvalds 	int err;
1062eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	family = x->props.family;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10671da177e4SLinus Torvalds 
1068edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
10691da177e4SLinus Torvalds 	if (x1) {
10701da177e4SLinus Torvalds 		xfrm_state_put(x1);
10711da177e4SLinus Torvalds 		x1 = NULL;
10721da177e4SLinus Torvalds 		err = -EEXIST;
10731da177e4SLinus Torvalds 		goto out;
10741da177e4SLinus Torvalds 	}
10751da177e4SLinus Torvalds 
1076eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
10771da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
107875e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
107975e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
10801da177e4SLinus Torvalds 			xfrm_state_put(x1);
10811da177e4SLinus Torvalds 			x1 = NULL;
10821da177e4SLinus Torvalds 		}
10831da177e4SLinus Torvalds 	}
10841da177e4SLinus Torvalds 
1085eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
10862770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
10872770834cSDavid S. Miller 				     x->id.proto,
10881da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
10891da177e4SLinus Torvalds 
1090c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
10911da177e4SLinus Torvalds 	__xfrm_state_insert(x);
10921da177e4SLinus Torvalds 	err = 0;
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds out:
10951da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds 	if (x1) {
10981da177e4SLinus Torvalds 		xfrm_state_delete(x1);
10991da177e4SLinus Torvalds 		xfrm_state_put(x1);
11001da177e4SLinus Torvalds 	}
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	return err;
11031da177e4SLinus Torvalds }
11041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
11051da177e4SLinus Torvalds 
110680c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
11076666351dSEric Dumazet static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
110880c9abaaSShinta Sugimoto {
110980c9abaaSShinta Sugimoto 	int err = -ENOMEM;
111080c9abaaSShinta Sugimoto 	struct xfrm_state *x = xfrm_state_alloc();
111180c9abaaSShinta Sugimoto 	if (!x)
111280c9abaaSShinta Sugimoto 		goto error;
111380c9abaaSShinta Sugimoto 
111480c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
111580c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
111680c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
111780c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
111880c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
111980c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
112080c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
112180c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
112280c9abaaSShinta Sugimoto 
112380c9abaaSShinta Sugimoto 	if (orig->aalg) {
112480c9abaaSShinta Sugimoto 		x->aalg = xfrm_algo_clone(orig->aalg);
112580c9abaaSShinta Sugimoto 		if (!x->aalg)
112680c9abaaSShinta Sugimoto 			goto error;
112780c9abaaSShinta Sugimoto 	}
112880c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
112980c9abaaSShinta Sugimoto 
113080c9abaaSShinta Sugimoto 	if (orig->ealg) {
113180c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
113280c9abaaSShinta Sugimoto 		if (!x->ealg)
113380c9abaaSShinta Sugimoto 			goto error;
113480c9abaaSShinta Sugimoto 	}
113580c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
113680c9abaaSShinta Sugimoto 
113780c9abaaSShinta Sugimoto 	if (orig->calg) {
113880c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
113980c9abaaSShinta Sugimoto 		if (!x->calg)
114080c9abaaSShinta Sugimoto 			goto error;
114180c9abaaSShinta Sugimoto 	}
114280c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
114380c9abaaSShinta Sugimoto 
114480c9abaaSShinta Sugimoto 	if (orig->encap) {
114580c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
114680c9abaaSShinta Sugimoto 		if (!x->encap)
114780c9abaaSShinta Sugimoto 			goto error;
114880c9abaaSShinta Sugimoto 	}
114980c9abaaSShinta Sugimoto 
115080c9abaaSShinta Sugimoto 	if (orig->coaddr) {
115180c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
115280c9abaaSShinta Sugimoto 				    GFP_KERNEL);
115380c9abaaSShinta Sugimoto 		if (!x->coaddr)
115480c9abaaSShinta Sugimoto 			goto error;
115580c9abaaSShinta Sugimoto 	}
115680c9abaaSShinta Sugimoto 
115780c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
115880c9abaaSShinta Sugimoto 	if (err)
115980c9abaaSShinta Sugimoto 		goto error;
116080c9abaaSShinta Sugimoto 
116180c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
116280c9abaaSShinta Sugimoto 
116380c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
116480c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
116580c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
116680c9abaaSShinta Sugimoto 
116780c9abaaSShinta Sugimoto 	return x;
116880c9abaaSShinta Sugimoto 
116980c9abaaSShinta Sugimoto  error:
117080c9abaaSShinta Sugimoto 	if (errp)
117180c9abaaSShinta Sugimoto 		*errp = err;
117280c9abaaSShinta Sugimoto 	if (x) {
117380c9abaaSShinta Sugimoto 		kfree(x->aalg);
117480c9abaaSShinta Sugimoto 		kfree(x->ealg);
117580c9abaaSShinta Sugimoto 		kfree(x->calg);
117680c9abaaSShinta Sugimoto 		kfree(x->encap);
117780c9abaaSShinta Sugimoto 		kfree(x->coaddr);
117880c9abaaSShinta Sugimoto 	}
117980c9abaaSShinta Sugimoto 	kfree(x);
118080c9abaaSShinta Sugimoto 	return NULL;
118180c9abaaSShinta Sugimoto }
118280c9abaaSShinta Sugimoto 
118380c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
118480c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
118580c9abaaSShinta Sugimoto {
118680c9abaaSShinta Sugimoto 	unsigned int h;
118780c9abaaSShinta Sugimoto 	struct xfrm_state *x;
118880c9abaaSShinta Sugimoto 	struct hlist_node *entry;
118980c9abaaSShinta Sugimoto 
119080c9abaaSShinta Sugimoto 	if (m->reqid) {
119180c9abaaSShinta Sugimoto 		h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
119280c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
119380c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
119480c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
119580c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
119680c9abaaSShinta Sugimoto 				continue;
119780c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
119880c9abaaSShinta Sugimoto 				continue;
119980c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
120080c9abaaSShinta Sugimoto 					  m->old_family) ||
120180c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
120280c9abaaSShinta Sugimoto 					  m->old_family))
120380c9abaaSShinta Sugimoto 				continue;
120480c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
120580c9abaaSShinta Sugimoto 			return x;
120680c9abaaSShinta Sugimoto 		}
120780c9abaaSShinta Sugimoto 	} else {
120880c9abaaSShinta Sugimoto 		h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
120980c9abaaSShinta Sugimoto 				  m->old_family);
121080c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
121180c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
121280c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
121380c9abaaSShinta Sugimoto 				continue;
121480c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
121580c9abaaSShinta Sugimoto 					  m->old_family) ||
121680c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
121780c9abaaSShinta Sugimoto 					  m->old_family))
121880c9abaaSShinta Sugimoto 				continue;
121980c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
122080c9abaaSShinta Sugimoto 			return x;
122180c9abaaSShinta Sugimoto 		}
122280c9abaaSShinta Sugimoto 	}
122380c9abaaSShinta Sugimoto 
122480c9abaaSShinta Sugimoto 	return NULL;
122580c9abaaSShinta Sugimoto }
122680c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
122780c9abaaSShinta Sugimoto 
122880c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
122980c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
123080c9abaaSShinta Sugimoto {
123180c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
123280c9abaaSShinta Sugimoto 	int err;
123380c9abaaSShinta Sugimoto 
123480c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
123580c9abaaSShinta Sugimoto 	if (!xc)
123680c9abaaSShinta Sugimoto 		return NULL;
123780c9abaaSShinta Sugimoto 
123880c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
123980c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
124080c9abaaSShinta Sugimoto 
124180c9abaaSShinta Sugimoto 	/* add state */
124280c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
124380c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
124480c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
124580c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
124680c9abaaSShinta Sugimoto 	} else {
124780c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
124880c9abaaSShinta Sugimoto 			goto error;
124980c9abaaSShinta Sugimoto 	}
125080c9abaaSShinta Sugimoto 
125180c9abaaSShinta Sugimoto 	return xc;
125280c9abaaSShinta Sugimoto error:
125380c9abaaSShinta Sugimoto 	kfree(xc);
125480c9abaaSShinta Sugimoto 	return NULL;
125580c9abaaSShinta Sugimoto }
125680c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
125780c9abaaSShinta Sugimoto #endif
125880c9abaaSShinta Sugimoto 
12591da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
12601da177e4SLinus Torvalds {
12611da177e4SLinus Torvalds 	struct xfrm_state *x1;
12621da177e4SLinus Torvalds 	int err;
1263eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1266edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	err = -ESRCH;
12691da177e4SLinus Torvalds 	if (!x1)
12701da177e4SLinus Torvalds 		goto out;
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
12731da177e4SLinus Torvalds 		xfrm_state_put(x1);
12741da177e4SLinus Torvalds 		err = -EEXIST;
12751da177e4SLinus Torvalds 		goto out;
12761da177e4SLinus Torvalds 	}
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
12791da177e4SLinus Torvalds 		__xfrm_state_insert(x);
12801da177e4SLinus Torvalds 		x = NULL;
12811da177e4SLinus Torvalds 	}
12821da177e4SLinus Torvalds 	err = 0;
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds out:
12851da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 	if (err)
12881da177e4SLinus Torvalds 		return err;
12891da177e4SLinus Torvalds 
12901da177e4SLinus Torvalds 	if (!x) {
12911da177e4SLinus Torvalds 		xfrm_state_delete(x1);
12921da177e4SLinus Torvalds 		xfrm_state_put(x1);
12931da177e4SLinus Torvalds 		return 0;
12941da177e4SLinus Torvalds 	}
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	err = -EINVAL;
12971da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
12981da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
12991da177e4SLinus Torvalds 		if (x->encap && x1->encap)
13001da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1301060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1302060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1303060f02a3SNoriaki TAKAMIYA 		}
1304060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1305060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
13061da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
13071da177e4SLinus Torvalds 		x1->km.dying = 0;
13081da177e4SLinus Torvalds 
1309a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
13101da177e4SLinus Torvalds 		if (x1->curlft.use_time)
13111da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 		err = 0;
13141da177e4SLinus Torvalds 	}
13151da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 	xfrm_state_put(x1);
13181da177e4SLinus Torvalds 
13191da177e4SLinus Torvalds 	return err;
13201da177e4SLinus Torvalds }
13211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
13221da177e4SLinus Torvalds 
13231da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
13241da177e4SLinus Torvalds {
13251da177e4SLinus Torvalds 	if (!x->curlft.use_time)
13269d729f72SJames Morris 		x->curlft.use_time = get_seconds();
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
13291da177e4SLinus Torvalds 		return -EINVAL;
13301da177e4SLinus Torvalds 
13311da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
13321da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
13334666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
1334a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
13351da177e4SLinus Torvalds 		return -EINVAL;
13361da177e4SLinus Torvalds 	}
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds 	if (!x->km.dying &&
13391da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
13404666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
13414666faabSHerbert Xu 		x->km.dying = 1;
134253bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
13434666faabSHerbert Xu 	}
13441da177e4SLinus Torvalds 	return 0;
13451da177e4SLinus Torvalds }
13461da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds struct xfrm_state *
1349a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
13501da177e4SLinus Torvalds 		  unsigned short family)
13511da177e4SLinus Torvalds {
13521da177e4SLinus Torvalds 	struct xfrm_state *x;
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1355edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
13561da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13571da177e4SLinus Torvalds 	return x;
13581da177e4SLinus Torvalds }
13591da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds struct xfrm_state *
1362eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1363eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1364eb2971b6SMasahide NAKAMURA {
1365eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1366eb2971b6SMasahide NAKAMURA 
1367eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1368edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
1369eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1370eb2971b6SMasahide NAKAMURA 	return x;
1371eb2971b6SMasahide NAKAMURA }
1372eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1373eb2971b6SMasahide NAKAMURA 
1374eb2971b6SMasahide NAKAMURA struct xfrm_state *
13751da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
13761da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
13771da177e4SLinus Torvalds 	      int create, unsigned short family)
13781da177e4SLinus Torvalds {
13791da177e4SLinus Torvalds 	struct xfrm_state *x;
13801da177e4SLinus Torvalds 
13811da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
13822770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
13831da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13842770834cSDavid S. Miller 
13851da177e4SLinus Torvalds 	return x;
13861da177e4SLinus Torvalds }
13871da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
13881da177e4SLinus Torvalds 
138941a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
139041a49cc3SMasahide NAKAMURA int
139141a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
139241a49cc3SMasahide NAKAMURA 	       unsigned short family)
139341a49cc3SMasahide NAKAMURA {
139441a49cc3SMasahide NAKAMURA 	int err = 0;
139541a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
139641a49cc3SMasahide NAKAMURA 	if (!afinfo)
139741a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
139841a49cc3SMasahide NAKAMURA 
139941a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
140041a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
140141a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
140241a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
140341a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
140441a49cc3SMasahide NAKAMURA 	return err;
140541a49cc3SMasahide NAKAMURA }
140641a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
140741a49cc3SMasahide NAKAMURA 
140841a49cc3SMasahide NAKAMURA int
140941a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
141041a49cc3SMasahide NAKAMURA 		unsigned short family)
141141a49cc3SMasahide NAKAMURA {
141241a49cc3SMasahide NAKAMURA 	int err = 0;
141341a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
141441a49cc3SMasahide NAKAMURA 	if (!afinfo)
141541a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
141641a49cc3SMasahide NAKAMURA 
141741a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
141841a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
141941a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
142041a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
142141a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
142241a49cc3SMasahide NAKAMURA 	return err;
142341a49cc3SMasahide NAKAMURA }
142441a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
142541a49cc3SMasahide NAKAMURA #endif
142641a49cc3SMasahide NAKAMURA 
14271da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
14281da177e4SLinus Torvalds 
14291da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
14301da177e4SLinus Torvalds {
14311da177e4SLinus Torvalds 	int i;
14321da177e4SLinus Torvalds 
1433f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
14348f126e37SDavid S. Miller 		struct hlist_node *entry;
14358f126e37SDavid S. Miller 		struct xfrm_state *x;
14368f126e37SDavid S. Miller 
14378f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
14388f126e37SDavid S. Miller 			if (x->km.seq == seq &&
14398f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
14401da177e4SLinus Torvalds 				xfrm_state_hold(x);
14411da177e4SLinus Torvalds 				return x;
14421da177e4SLinus Torvalds 			}
14431da177e4SLinus Torvalds 		}
14441da177e4SLinus Torvalds 	}
14451da177e4SLinus Torvalds 	return NULL;
14461da177e4SLinus Torvalds }
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
14491da177e4SLinus Torvalds {
14501da177e4SLinus Torvalds 	struct xfrm_state *x;
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
14531da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
14541da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14551da177e4SLinus Torvalds 	return x;
14561da177e4SLinus Torvalds }
14571da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
14581da177e4SLinus Torvalds 
14591da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
14601da177e4SLinus Torvalds {
14611da177e4SLinus Torvalds 	u32 res;
14621da177e4SLinus Torvalds 	static u32 acqseq;
14631da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
14641da177e4SLinus Torvalds 
14651da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
14661da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
14671da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
14681da177e4SLinus Torvalds 	return res;
14691da177e4SLinus Torvalds }
14701da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
14711da177e4SLinus Torvalds 
1472658b219eSHerbert Xu int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
14731da177e4SLinus Torvalds {
1474f034b5d4SDavid S. Miller 	unsigned int h;
14751da177e4SLinus Torvalds 	struct xfrm_state *x0;
1476658b219eSHerbert Xu 	int err = -ENOENT;
1477658b219eSHerbert Xu 	__be32 minspi = htonl(low);
1478658b219eSHerbert Xu 	__be32 maxspi = htonl(high);
14791da177e4SLinus Torvalds 
1480658b219eSHerbert Xu 	spin_lock_bh(&x->lock);
1481658b219eSHerbert Xu 	if (x->km.state == XFRM_STATE_DEAD)
1482658b219eSHerbert Xu 		goto unlock;
1483658b219eSHerbert Xu 
1484658b219eSHerbert Xu 	err = 0;
14851da177e4SLinus Torvalds 	if (x->id.spi)
1486658b219eSHerbert Xu 		goto unlock;
1487658b219eSHerbert Xu 
1488658b219eSHerbert Xu 	err = -ENOENT;
14891da177e4SLinus Torvalds 
14901da177e4SLinus Torvalds 	if (minspi == maxspi) {
14911da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
14921da177e4SLinus Torvalds 		if (x0) {
14931da177e4SLinus Torvalds 			xfrm_state_put(x0);
1494658b219eSHerbert Xu 			goto unlock;
14951da177e4SLinus Torvalds 		}
14961da177e4SLinus Torvalds 		x->id.spi = minspi;
14971da177e4SLinus Torvalds 	} else {
14981da177e4SLinus Torvalds 		u32 spi = 0;
149926977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
150026977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
15011da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
15021da177e4SLinus Torvalds 			if (x0 == NULL) {
15031da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
15041da177e4SLinus Torvalds 				break;
15051da177e4SLinus Torvalds 			}
15061da177e4SLinus Torvalds 			xfrm_state_put(x0);
15071da177e4SLinus Torvalds 		}
15081da177e4SLinus Torvalds 	}
15091da177e4SLinus Torvalds 	if (x->id.spi) {
15101da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
15111da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
15128f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
15131da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
1514658b219eSHerbert Xu 
1515658b219eSHerbert Xu 		err = 0;
15161da177e4SLinus Torvalds 	}
1517658b219eSHerbert Xu 
1518658b219eSHerbert Xu unlock:
1519658b219eSHerbert Xu 	spin_unlock_bh(&x->lock);
1520658b219eSHerbert Xu 
1521658b219eSHerbert Xu 	return err;
15221da177e4SLinus Torvalds }
15231da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
15241da177e4SLinus Torvalds 
15251da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
15261da177e4SLinus Torvalds 		    void *data)
15271da177e4SLinus Torvalds {
15281da177e4SLinus Torvalds 	int i;
152994b9bb54SJamal Hadi Salim 	struct xfrm_state *x, *last = NULL;
15308f126e37SDavid S. Miller 	struct hlist_node *entry;
15311da177e4SLinus Torvalds 	int count = 0;
15321da177e4SLinus Torvalds 	int err = 0;
15331da177e4SLinus Torvalds 
15341da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1535f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
15368f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
153794b9bb54SJamal Hadi Salim 			if (!xfrm_id_proto_match(x->id.proto, proto))
153894b9bb54SJamal Hadi Salim 				continue;
153994b9bb54SJamal Hadi Salim 			if (last) {
154094b9bb54SJamal Hadi Salim 				err = func(last, count, data);
154194b9bb54SJamal Hadi Salim 				if (err)
154294b9bb54SJamal Hadi Salim 					goto out;
154394b9bb54SJamal Hadi Salim 			}
154494b9bb54SJamal Hadi Salim 			last = x;
15451da177e4SLinus Torvalds 			count++;
15461da177e4SLinus Torvalds 		}
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 	if (count == 0) {
15491da177e4SLinus Torvalds 		err = -ENOENT;
15501da177e4SLinus Torvalds 		goto out;
15511da177e4SLinus Torvalds 	}
155294b9bb54SJamal Hadi Salim 	err = func(last, 0, data);
15531da177e4SLinus Torvalds out:
15541da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
15551da177e4SLinus Torvalds 	return err;
15561da177e4SLinus Torvalds }
15571da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
15581da177e4SLinus Torvalds 
1559f8cd5488SJamal Hadi Salim 
1560f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1561f8cd5488SJamal Hadi Salim {
1562f8cd5488SJamal Hadi Salim 	struct km_event c;
1563f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1564f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1565f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1566f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1567f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1568f8cd5488SJamal Hadi Salim 	 *     and there were changes
1569f8cd5488SJamal Hadi Salim 	 *
1570f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1571f8cd5488SJamal Hadi Salim 	 */
1572f8cd5488SJamal Hadi Salim 
1573f8cd5488SJamal Hadi Salim 	switch (event) {
1574f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1575f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1576f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
15772717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
15782717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
15792717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
15802717096aSJamal Hadi Salim 			else
1581f8cd5488SJamal Hadi Salim 				return;
15822717096aSJamal Hadi Salim 		}
1583f8cd5488SJamal Hadi Salim 
1584f8cd5488SJamal Hadi Salim 		break;
1585f8cd5488SJamal Hadi Salim 
1586f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1587f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1588f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
15892717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
15902717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1591f8cd5488SJamal Hadi Salim 			return;
15922717096aSJamal Hadi Salim 		}
1593f8cd5488SJamal Hadi Salim 
1594f8cd5488SJamal Hadi Salim 		break;
1595f8cd5488SJamal Hadi Salim 	}
1596f8cd5488SJamal Hadi Salim 
1597f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1598f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1599f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1600f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1601f8cd5488SJamal Hadi Salim 
1602f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1603a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
16042717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
16052717096aSJamal Hadi Salim }
1606f8cd5488SJamal Hadi Salim 
1607f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1608f8cd5488SJamal Hadi Salim {
1609f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1610f8cd5488SJamal Hadi Salim 
1611f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1612f8cd5488SJamal Hadi Salim 
16132717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
16142717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1615f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
16162717096aSJamal Hadi Salim 		else
16172717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
16182717096aSJamal Hadi Salim 	}
1619f8cd5488SJamal Hadi Salim 
1620f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1621f8cd5488SJamal Hadi Salim }
1622f8cd5488SJamal Hadi Salim 
1623afeb14b4SPaul Moore int xfrm_replay_check(struct xfrm_state *x,
1624afeb14b4SPaul Moore 		      struct sk_buff *skb, __be32 net_seq)
16251da177e4SLinus Torvalds {
16261da177e4SLinus Torvalds 	u32 diff;
1627a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
16281da177e4SLinus Torvalds 
16291da177e4SLinus Torvalds 	if (unlikely(seq == 0))
1630afeb14b4SPaul Moore 		goto err;
16311da177e4SLinus Torvalds 
16321da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
16331da177e4SLinus Torvalds 		return 0;
16341da177e4SLinus Torvalds 
16351da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
16364c4d51a7SHerbert Xu 	if (diff >= min_t(unsigned int, x->props.replay_window,
16374c4d51a7SHerbert Xu 			  sizeof(x->replay.bitmap) * 8)) {
16381da177e4SLinus Torvalds 		x->stats.replay_window++;
1639afeb14b4SPaul Moore 		goto err;
16401da177e4SLinus Torvalds 	}
16411da177e4SLinus Torvalds 
16421da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
16431da177e4SLinus Torvalds 		x->stats.replay++;
1644afeb14b4SPaul Moore 		goto err;
16451da177e4SLinus Torvalds 	}
16461da177e4SLinus Torvalds 	return 0;
1647afeb14b4SPaul Moore 
1648afeb14b4SPaul Moore err:
1649afeb14b4SPaul Moore 	xfrm_audit_state_replay(x, skb, net_seq);
1650afeb14b4SPaul Moore 	return -EINVAL;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds 
165361f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
16541da177e4SLinus Torvalds {
16551da177e4SLinus Torvalds 	u32 diff;
165661f4627bSAl Viro 	u32 seq = ntohl(net_seq);
16571da177e4SLinus Torvalds 
16581da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
16591da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
16601da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
16611da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
16621da177e4SLinus Torvalds 		else
16631da177e4SLinus Torvalds 			x->replay.bitmap = 1;
16641da177e4SLinus Torvalds 		x->replay.seq = seq;
16651da177e4SLinus Torvalds 	} else {
16661da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
16671da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
16681da177e4SLinus Torvalds 	}
1669f8cd5488SJamal Hadi Salim 
1670f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1671f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
16721da177e4SLinus Torvalds }
16731da177e4SLinus Torvalds 
1674df01812eSDenis Cheng static LIST_HEAD(xfrm_km_list);
16751da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
16761da177e4SLinus Torvalds 
167726b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
16781da177e4SLinus Torvalds {
16791da177e4SLinus Torvalds 	struct xfrm_mgr *km;
16801da177e4SLinus Torvalds 
168126b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
168226b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
168326b15dadSJamal Hadi Salim 		if (km->notify_policy)
168426b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
168526b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
168626b15dadSJamal Hadi Salim }
168726b15dadSJamal Hadi Salim 
168826b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
168926b15dadSJamal Hadi Salim {
169026b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
169126b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
169226b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
169326b15dadSJamal Hadi Salim 		if (km->notify)
169426b15dadSJamal Hadi Salim 			km->notify(x, c);
169526b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
169626b15dadSJamal Hadi Salim }
169726b15dadSJamal Hadi Salim 
169826b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
169926b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
170026b15dadSJamal Hadi Salim 
170153bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
170226b15dadSJamal Hadi Salim {
170326b15dadSJamal Hadi Salim 	struct km_event c;
170426b15dadSJamal Hadi Salim 
1705bf08867fSHerbert Xu 	c.data.hard = hard;
170653bc6b4dSJamal Hadi Salim 	c.pid = pid;
1707f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
170826b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
17091da177e4SLinus Torvalds 
17101da177e4SLinus Torvalds 	if (hard)
17111da177e4SLinus Torvalds 		wake_up(&km_waitq);
17121da177e4SLinus Torvalds }
17131da177e4SLinus Torvalds 
171453bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
171526b15dadSJamal Hadi Salim /*
171626b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
171726b15dadSJamal Hadi Salim  * We are happy with one success
171826b15dadSJamal Hadi Salim */
1719980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
17201da177e4SLinus Torvalds {
172126b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
17221da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17231da177e4SLinus Torvalds 
17241da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17251da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
172626b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
172726b15dadSJamal Hadi Salim 		if (!acqret)
172826b15dadSJamal Hadi Salim 			err = acqret;
17291da177e4SLinus Torvalds 	}
17301da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17311da177e4SLinus Torvalds 	return err;
17321da177e4SLinus Torvalds }
1733980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
17341da177e4SLinus Torvalds 
17355d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
17361da177e4SLinus Torvalds {
17371da177e4SLinus Torvalds 	int err = -EINVAL;
17381da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17391da177e4SLinus Torvalds 
17401da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17411da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
17421da177e4SLinus Torvalds 		if (km->new_mapping)
17431da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
17441da177e4SLinus Torvalds 		if (!err)
17451da177e4SLinus Torvalds 			break;
17461da177e4SLinus Torvalds 	}
17471da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17481da177e4SLinus Torvalds 	return err;
17491da177e4SLinus Torvalds }
17501da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
17511da177e4SLinus Torvalds 
17526c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
17531da177e4SLinus Torvalds {
175426b15dadSJamal Hadi Salim 	struct km_event c;
17551da177e4SLinus Torvalds 
1756bf08867fSHerbert Xu 	c.data.hard = hard;
17576c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1758f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
175926b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
17601da177e4SLinus Torvalds 
17611da177e4SLinus Torvalds 	if (hard)
17621da177e4SLinus Torvalds 		wake_up(&km_waitq);
17631da177e4SLinus Torvalds }
1764a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
17651da177e4SLinus Torvalds 
17662d60abc2SEric Dumazet #ifdef CONFIG_XFRM_MIGRATE
176780c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
176880c9abaaSShinta Sugimoto 	       struct xfrm_migrate *m, int num_migrate)
176980c9abaaSShinta Sugimoto {
177080c9abaaSShinta Sugimoto 	int err = -EINVAL;
177180c9abaaSShinta Sugimoto 	int ret;
177280c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
177380c9abaaSShinta Sugimoto 
177480c9abaaSShinta Sugimoto 	read_lock(&xfrm_km_lock);
177580c9abaaSShinta Sugimoto 	list_for_each_entry(km, &xfrm_km_list, list) {
177680c9abaaSShinta Sugimoto 		if (km->migrate) {
177780c9abaaSShinta Sugimoto 			ret = km->migrate(sel, dir, type, m, num_migrate);
177880c9abaaSShinta Sugimoto 			if (!ret)
177980c9abaaSShinta Sugimoto 				err = ret;
178080c9abaaSShinta Sugimoto 		}
178180c9abaaSShinta Sugimoto 	}
178280c9abaaSShinta Sugimoto 	read_unlock(&xfrm_km_lock);
178380c9abaaSShinta Sugimoto 	return err;
178480c9abaaSShinta Sugimoto }
178580c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
17862d60abc2SEric Dumazet #endif
178780c9abaaSShinta Sugimoto 
178897a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
178997a64b45SMasahide NAKAMURA {
179097a64b45SMasahide NAKAMURA 	int err = -EINVAL;
179197a64b45SMasahide NAKAMURA 	int ret;
179297a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
179397a64b45SMasahide NAKAMURA 
179497a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
179597a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
179697a64b45SMasahide NAKAMURA 		if (km->report) {
179797a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
179897a64b45SMasahide NAKAMURA 			if (!ret)
179997a64b45SMasahide NAKAMURA 				err = ret;
180097a64b45SMasahide NAKAMURA 		}
180197a64b45SMasahide NAKAMURA 	}
180297a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
180397a64b45SMasahide NAKAMURA 	return err;
180497a64b45SMasahide NAKAMURA }
180597a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
180697a64b45SMasahide NAKAMURA 
18071da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
18081da177e4SLinus Torvalds {
18091da177e4SLinus Torvalds 	int err;
18101da177e4SLinus Torvalds 	u8 *data;
18111da177e4SLinus Torvalds 	struct xfrm_mgr *km;
18121da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
18131da177e4SLinus Torvalds 
18141da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
18151da177e4SLinus Torvalds 		return -EMSGSIZE;
18161da177e4SLinus Torvalds 
18171da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
18181da177e4SLinus Torvalds 	if (!data)
18191da177e4SLinus Torvalds 		return -ENOMEM;
18201da177e4SLinus Torvalds 
18211da177e4SLinus Torvalds 	err = -EFAULT;
18221da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
18231da177e4SLinus Torvalds 		goto out;
18241da177e4SLinus Torvalds 
18251da177e4SLinus Torvalds 	err = -EINVAL;
18261da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
18271da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1828cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
18291da177e4SLinus Torvalds 					 optlen, &err);
18301da177e4SLinus Torvalds 		if (err >= 0)
18311da177e4SLinus Torvalds 			break;
18321da177e4SLinus Torvalds 	}
18331da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
18341da177e4SLinus Torvalds 
18351da177e4SLinus Torvalds 	if (err >= 0) {
18361da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
18371da177e4SLinus Torvalds 		xfrm_pol_put(pol);
18381da177e4SLinus Torvalds 		err = 0;
18391da177e4SLinus Torvalds 	}
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds out:
18421da177e4SLinus Torvalds 	kfree(data);
18431da177e4SLinus Torvalds 	return err;
18441da177e4SLinus Torvalds }
18451da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
18461da177e4SLinus Torvalds 
18471da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
18481da177e4SLinus Torvalds {
18491da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18501da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
18511da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18521da177e4SLinus Torvalds 	return 0;
18531da177e4SLinus Torvalds }
18541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
18551da177e4SLinus Torvalds 
18561da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
18571da177e4SLinus Torvalds {
18581da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18591da177e4SLinus Torvalds 	list_del(&km->list);
18601da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18611da177e4SLinus Torvalds 	return 0;
18621da177e4SLinus Torvalds }
18631da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
18661da177e4SLinus Torvalds {
18671da177e4SLinus Torvalds 	int err = 0;
18681da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18691da177e4SLinus Torvalds 		return -EINVAL;
18701da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18711da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1872f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
18731da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
18741da177e4SLinus Torvalds 		err = -ENOBUFS;
1875edcd5821SDavid S. Miller 	else
18761da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1877f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
18781da177e4SLinus Torvalds 	return err;
18791da177e4SLinus Torvalds }
18801da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
18811da177e4SLinus Torvalds 
18821da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
18831da177e4SLinus Torvalds {
18841da177e4SLinus Torvalds 	int err = 0;
18851da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18861da177e4SLinus Torvalds 		return -EINVAL;
18871da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18881da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1889f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
18901da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
18911da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
18921da177e4SLinus Torvalds 			err = -EINVAL;
1893edcd5821SDavid S. Miller 		else
18941da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
18951da177e4SLinus Torvalds 	}
1896f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
18971da177e4SLinus Torvalds 	return err;
18981da177e4SLinus Torvalds }
18991da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
19001da177e4SLinus Torvalds 
190117c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
19021da177e4SLinus Torvalds {
19031da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
19041da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
19051da177e4SLinus Torvalds 		return NULL;
19061da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
19071da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1908546be240SHerbert Xu 	if (unlikely(!afinfo))
19091da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
19101da177e4SLinus Torvalds 	return afinfo;
19111da177e4SLinus Torvalds }
19121da177e4SLinus Torvalds 
191317c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
19149a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
19151da177e4SLinus Torvalds {
1916546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
19171da177e4SLinus Torvalds }
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
19201da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
19211da177e4SLinus Torvalds {
19221da177e4SLinus Torvalds 	if (x->tunnel) {
19231da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
19241da177e4SLinus Torvalds 
19251da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
19261da177e4SLinus Torvalds 			xfrm_state_delete(t);
19271da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
19281da177e4SLinus Torvalds 		xfrm_state_put(t);
19291da177e4SLinus Torvalds 		x->tunnel = NULL;
19301da177e4SLinus Torvalds 	}
19311da177e4SLinus Torvalds }
19321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
19331da177e4SLinus Torvalds 
19341da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
19351da177e4SLinus Torvalds {
1936c5c25238SPatrick McHardy 	int res;
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
19391da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_VALID &&
1940c5c25238SPatrick McHardy 	    x->type && x->type->get_mtu)
1941c5c25238SPatrick McHardy 		res = x->type->get_mtu(x, mtu);
19421da177e4SLinus Torvalds 	else
194328121617SPatrick McHardy 		res = mtu - x->props.header_len;
19441da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
19451da177e4SLinus Torvalds 	return res;
19461da177e4SLinus Torvalds }
19471da177e4SLinus Torvalds 
194872cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
194972cb6962SHerbert Xu {
1950d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1951df9dcb45SKazunori MIYAZAWA 	struct xfrm_mode *inner_mode;
1952d094cd83SHerbert Xu 	int family = x->props.family;
195372cb6962SHerbert Xu 	int err;
195472cb6962SHerbert Xu 
1955d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1956d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1957d094cd83SHerbert Xu 	if (!afinfo)
1958d094cd83SHerbert Xu 		goto error;
1959d094cd83SHerbert Xu 
1960d094cd83SHerbert Xu 	err = 0;
1961d094cd83SHerbert Xu 	if (afinfo->init_flags)
1962d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1963d094cd83SHerbert Xu 
1964d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1965d094cd83SHerbert Xu 
1966d094cd83SHerbert Xu 	if (err)
1967d094cd83SHerbert Xu 		goto error;
1968d094cd83SHerbert Xu 
1969d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
1970df9dcb45SKazunori MIYAZAWA 
1971df9dcb45SKazunori MIYAZAWA 	if (x->sel.family != AF_UNSPEC) {
1972df9dcb45SKazunori MIYAZAWA 		inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
1973df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
197413996378SHerbert Xu 			goto error;
197513996378SHerbert Xu 
1976df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
1977df9dcb45SKazunori MIYAZAWA 		    family != x->sel.family) {
1978df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
197913996378SHerbert Xu 			goto error;
1980df9dcb45SKazunori MIYAZAWA 		}
1981df9dcb45SKazunori MIYAZAWA 
1982df9dcb45SKazunori MIYAZAWA 		x->inner_mode = inner_mode;
1983df9dcb45SKazunori MIYAZAWA 	} else {
1984df9dcb45SKazunori MIYAZAWA 		struct xfrm_mode *inner_mode_iaf;
1985df9dcb45SKazunori MIYAZAWA 
1986df9dcb45SKazunori MIYAZAWA 		inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
1987df9dcb45SKazunori MIYAZAWA 		if (inner_mode == NULL)
1988df9dcb45SKazunori MIYAZAWA 			goto error;
1989df9dcb45SKazunori MIYAZAWA 
1990df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
1991df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode);
1992df9dcb45SKazunori MIYAZAWA 			goto error;
1993df9dcb45SKazunori MIYAZAWA 		}
1994df9dcb45SKazunori MIYAZAWA 
1995df9dcb45SKazunori MIYAZAWA 		inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
1996df9dcb45SKazunori MIYAZAWA 		if (inner_mode_iaf == NULL)
1997df9dcb45SKazunori MIYAZAWA 			goto error;
1998df9dcb45SKazunori MIYAZAWA 
1999df9dcb45SKazunori MIYAZAWA 		if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
2000df9dcb45SKazunori MIYAZAWA 			xfrm_put_mode(inner_mode_iaf);
2001df9dcb45SKazunori MIYAZAWA 			goto error;
2002df9dcb45SKazunori MIYAZAWA 		}
2003df9dcb45SKazunori MIYAZAWA 
2004df9dcb45SKazunori MIYAZAWA 		if (x->props.family == AF_INET) {
2005df9dcb45SKazunori MIYAZAWA 			x->inner_mode = inner_mode;
2006df9dcb45SKazunori MIYAZAWA 			x->inner_mode_iaf = inner_mode_iaf;
2007df9dcb45SKazunori MIYAZAWA 		} else {
2008df9dcb45SKazunori MIYAZAWA 			x->inner_mode = inner_mode_iaf;
2009df9dcb45SKazunori MIYAZAWA 			x->inner_mode_iaf = inner_mode;
2010df9dcb45SKazunori MIYAZAWA 		}
2011df9dcb45SKazunori MIYAZAWA 	}
201213996378SHerbert Xu 
2013d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
201472cb6962SHerbert Xu 	if (x->type == NULL)
201572cb6962SHerbert Xu 		goto error;
201672cb6962SHerbert Xu 
201772cb6962SHerbert Xu 	err = x->type->init_state(x);
201872cb6962SHerbert Xu 	if (err)
201972cb6962SHerbert Xu 		goto error;
202072cb6962SHerbert Xu 
202113996378SHerbert Xu 	x->outer_mode = xfrm_get_mode(x->props.mode, family);
202213996378SHerbert Xu 	if (x->outer_mode == NULL)
2023b59f45d0SHerbert Xu 		goto error;
2024b59f45d0SHerbert Xu 
202572cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
202672cb6962SHerbert Xu 
202772cb6962SHerbert Xu error:
202872cb6962SHerbert Xu 	return err;
202972cb6962SHerbert Xu }
203072cb6962SHerbert Xu 
203172cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
20321da177e4SLinus Torvalds 
20331da177e4SLinus Torvalds void __init xfrm_state_init(void)
20341da177e4SLinus Torvalds {
2035f034b5d4SDavid S. Miller 	unsigned int sz;
20361da177e4SLinus Torvalds 
2037f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
2038f034b5d4SDavid S. Miller 
203944e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
204044e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
204144e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
2042f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2043f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2044f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2045f034b5d4SDavid S. Miller 
2046c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
20471da177e4SLinus Torvalds }
20481da177e4SLinus Torvalds 
2049ab5f5e8bSJoy Latten #ifdef CONFIG_AUDITSYSCALL
2050cf35f43eSIlpo Järvinen static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2051ab5f5e8bSJoy Latten 				     struct audit_buffer *audit_buf)
2052ab5f5e8bSJoy Latten {
205368277accSPaul Moore 	struct xfrm_sec_ctx *ctx = x->security;
205468277accSPaul Moore 	u32 spi = ntohl(x->id.spi);
205568277accSPaul Moore 
205668277accSPaul Moore 	if (ctx)
2057ab5f5e8bSJoy Latten 		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
205868277accSPaul Moore 				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
2059ab5f5e8bSJoy Latten 
2060ab5f5e8bSJoy Latten 	switch(x->props.family) {
2061ab5f5e8bSJoy Latten 	case AF_INET:
206268277accSPaul Moore 		audit_log_format(audit_buf,
206368277accSPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2064ab5f5e8bSJoy Latten 				 NIPQUAD(x->props.saddr.a4),
2065ab5f5e8bSJoy Latten 				 NIPQUAD(x->id.daddr.a4));
2066ab5f5e8bSJoy Latten 		break;
2067ab5f5e8bSJoy Latten 	case AF_INET6:
2068ab5f5e8bSJoy Latten 		audit_log_format(audit_buf,
2069ab5f5e8bSJoy Latten 				 " src=" NIP6_FMT " dst=" NIP6_FMT,
207068277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->props.saddr.a6),
207168277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->id.daddr.a6));
2072ab5f5e8bSJoy Latten 		break;
2073ab5f5e8bSJoy Latten 	}
207468277accSPaul Moore 
207568277accSPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2076ab5f5e8bSJoy Latten }
2077ab5f5e8bSJoy Latten 
2078cf35f43eSIlpo Järvinen static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2079afeb14b4SPaul Moore 				      struct audit_buffer *audit_buf)
2080afeb14b4SPaul Moore {
2081afeb14b4SPaul Moore 	struct iphdr *iph4;
2082afeb14b4SPaul Moore 	struct ipv6hdr *iph6;
2083afeb14b4SPaul Moore 
2084afeb14b4SPaul Moore 	switch (family) {
2085afeb14b4SPaul Moore 	case AF_INET:
2086afeb14b4SPaul Moore 		iph4 = ip_hdr(skb);
2087afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2088afeb14b4SPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2089afeb14b4SPaul Moore 				 NIPQUAD(iph4->saddr),
2090afeb14b4SPaul Moore 				 NIPQUAD(iph4->daddr));
2091afeb14b4SPaul Moore 		break;
2092afeb14b4SPaul Moore 	case AF_INET6:
2093afeb14b4SPaul Moore 		iph6 = ipv6_hdr(skb);
2094afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2095afeb14b4SPaul Moore 				 " src=" NIP6_FMT " dst=" NIP6_FMT
2096afeb14b4SPaul Moore 				 " flowlbl=0x%x%x%x",
2097afeb14b4SPaul Moore 				 NIP6(iph6->saddr),
2098afeb14b4SPaul Moore 				 NIP6(iph6->daddr),
2099afeb14b4SPaul Moore 				 iph6->flow_lbl[0] & 0x0f,
2100afeb14b4SPaul Moore 				 iph6->flow_lbl[1],
2101afeb14b4SPaul Moore 				 iph6->flow_lbl[2]);
2102afeb14b4SPaul Moore 		break;
2103afeb14b4SPaul Moore 	}
2104afeb14b4SPaul Moore }
2105afeb14b4SPaul Moore 
210668277accSPaul Moore void xfrm_audit_state_add(struct xfrm_state *x, int result,
210768277accSPaul Moore 			  u32 auid, u32 secid)
2108ab5f5e8bSJoy Latten {
2109ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2110ab5f5e8bSJoy Latten 
2111afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-add");
2112ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2113ab5f5e8bSJoy Latten 		return;
2114afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2115afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2116afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2117ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2118ab5f5e8bSJoy Latten }
2119ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2120ab5f5e8bSJoy Latten 
212168277accSPaul Moore void xfrm_audit_state_delete(struct xfrm_state *x, int result,
212268277accSPaul Moore 			     u32 auid, u32 secid)
2123ab5f5e8bSJoy Latten {
2124ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2125ab5f5e8bSJoy Latten 
2126afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-delete");
2127ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2128ab5f5e8bSJoy Latten 		return;
2129afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2130afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2131afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2132ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2133ab5f5e8bSJoy Latten }
2134ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2135afeb14b4SPaul Moore 
2136afeb14b4SPaul Moore void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2137afeb14b4SPaul Moore 				      struct sk_buff *skb)
2138afeb14b4SPaul Moore {
2139afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2140afeb14b4SPaul Moore 	u32 spi;
2141afeb14b4SPaul Moore 
2142afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replay-overflow");
2143afeb14b4SPaul Moore 	if (audit_buf == NULL)
2144afeb14b4SPaul Moore 		return;
2145afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2146afeb14b4SPaul Moore 	/* don't record the sequence number because it's inherent in this kind
2147afeb14b4SPaul Moore 	 * of audit message */
2148afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2149afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2150afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2151afeb14b4SPaul Moore }
2152afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2153afeb14b4SPaul Moore 
2154afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
2155afeb14b4SPaul Moore 			     struct sk_buff *skb, __be32 net_seq)
2156afeb14b4SPaul Moore {
2157afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2158afeb14b4SPaul Moore 	u32 spi;
2159afeb14b4SPaul Moore 
2160afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replayed-pkt");
2161afeb14b4SPaul Moore 	if (audit_buf == NULL)
2162afeb14b4SPaul Moore 		return;
2163afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2164afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2165afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2166afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2167afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2168afeb14b4SPaul Moore }
2169afeb14b4SPaul Moore 
2170afeb14b4SPaul Moore void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2171afeb14b4SPaul Moore {
2172afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2173afeb14b4SPaul Moore 
2174afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2175afeb14b4SPaul Moore 	if (audit_buf == NULL)
2176afeb14b4SPaul Moore 		return;
2177afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2178afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2179afeb14b4SPaul Moore }
2180afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2181afeb14b4SPaul Moore 
2182afeb14b4SPaul Moore void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2183afeb14b4SPaul Moore 			       __be32 net_spi, __be32 net_seq)
2184afeb14b4SPaul Moore {
2185afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2186afeb14b4SPaul Moore 	u32 spi;
2187afeb14b4SPaul Moore 
2188afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2189afeb14b4SPaul Moore 	if (audit_buf == NULL)
2190afeb14b4SPaul Moore 		return;
2191afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2192afeb14b4SPaul Moore 	spi = ntohl(net_spi);
2193afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2194afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2195afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2196afeb14b4SPaul Moore }
2197afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2198afeb14b4SPaul Moore 
2199afeb14b4SPaul Moore void xfrm_audit_state_icvfail(struct xfrm_state *x,
2200afeb14b4SPaul Moore 			      struct sk_buff *skb, u8 proto)
2201afeb14b4SPaul Moore {
2202afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2203afeb14b4SPaul Moore 	__be32 net_spi;
2204afeb14b4SPaul Moore 	__be32 net_seq;
2205afeb14b4SPaul Moore 
2206afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-icv-failure");
2207afeb14b4SPaul Moore 	if (audit_buf == NULL)
2208afeb14b4SPaul Moore 		return;
2209afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2210afeb14b4SPaul Moore 	if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2211afeb14b4SPaul Moore 		u32 spi = ntohl(net_spi);
2212afeb14b4SPaul Moore 		audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2213afeb14b4SPaul Moore 				 spi, spi, ntohl(net_seq));
2214afeb14b4SPaul Moore 	}
2215afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2216afeb14b4SPaul Moore }
2217afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
2218ab5f5e8bSJoy Latten #endif /* CONFIG_AUDITSYSCALL */
2219