xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision 4c563f76)
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  */
53*4c563f76STimo Teras static LIST_HEAD(xfrm_state_all);
54f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly;
55f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly;
56f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly;
57f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
58f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
59f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
609d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
611da177e4SLinus Torvalds 
6217c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
6317c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
6417c2a42aSHerbert Xu 
65afeb14b4SPaul Moore #ifdef CONFIG_AUDITSYSCALL
66afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
67afeb14b4SPaul Moore 				    struct sk_buff *skb, __be32 net_seq);
68afeb14b4SPaul Moore #else
69afeb14b4SPaul Moore #define xfrm_audit_state_replay(x, s, sq)	do { ; } while (0)
70afeb14b4SPaul Moore #endif /* CONFIG_AUDITSYSCALL */
71afeb14b4SPaul Moore 
72c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
73c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
74c1969f29SDavid S. Miller 					 u32 reqid,
75a624c108SDavid S. Miller 					 unsigned short family)
76a624c108SDavid S. Miller {
77c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
78a624c108SDavid S. Miller }
79a624c108SDavid S. Miller 
80667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
81667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
8244e36b42SDavid S. Miller 					 unsigned short family)
83f034b5d4SDavid S. Miller {
84667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
85f034b5d4SDavid S. Miller }
86f034b5d4SDavid S. Miller 
872575b654SDavid S. Miller static inline unsigned int
888122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
89f034b5d4SDavid S. Miller {
90c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
91f034b5d4SDavid S. Miller }
92f034b5d4SDavid S. Miller 
93f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
94f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
95f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
96f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
97f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
98f034b5d4SDavid S. Miller {
99f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
100f034b5d4SDavid S. Miller 	struct xfrm_state *x;
101f034b5d4SDavid S. Miller 
102f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
103f034b5d4SDavid S. Miller 		unsigned int h;
104f034b5d4SDavid S. Miller 
105c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
106c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
107c1969f29SDavid S. Miller 				    nhashmask);
108f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
109f034b5d4SDavid S. Miller 
110667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
111667bbcb6SMasahide NAKAMURA 				    x->props.family,
112f034b5d4SDavid S. Miller 				    nhashmask);
113f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
114f034b5d4SDavid S. Miller 
1157b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1167b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1177b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1187b4dc360SMasahide NAKAMURA 					    nhashmask);
119f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
120f034b5d4SDavid S. Miller 		}
121f034b5d4SDavid S. Miller 	}
1227b4dc360SMasahide NAKAMURA }
123f034b5d4SDavid S. Miller 
124f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
125f034b5d4SDavid S. Miller {
126f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
127f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
128f034b5d4SDavid S. Miller }
129f034b5d4SDavid S. Miller 
130f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
131f034b5d4SDavid S. Miller 
132c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
133f034b5d4SDavid S. Miller {
134f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
135f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
136f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
137f034b5d4SDavid S. Miller 	int i;
138f034b5d4SDavid S. Miller 
139f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
140f034b5d4SDavid S. Miller 
141f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
14244e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
143f034b5d4SDavid S. Miller 	if (!ndst)
144f034b5d4SDavid S. Miller 		goto out_unlock;
14544e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
146f034b5d4SDavid S. Miller 	if (!nsrc) {
14744e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
148f034b5d4SDavid S. Miller 		goto out_unlock;
149f034b5d4SDavid S. Miller 	}
15044e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
151f034b5d4SDavid S. Miller 	if (!nspi) {
15244e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
15344e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
154f034b5d4SDavid S. Miller 		goto out_unlock;
155f034b5d4SDavid S. Miller 	}
156f034b5d4SDavid S. Miller 
157f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
158f034b5d4SDavid S. Miller 
159f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
160f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
161f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
162f034b5d4SDavid S. Miller 				   nhashmask);
163f034b5d4SDavid S. Miller 
164f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
165f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
166f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
167f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
168f034b5d4SDavid S. Miller 
169f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
170f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
171f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
172f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
173f034b5d4SDavid S. Miller 
174f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
175f034b5d4SDavid S. Miller 
176f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
17744e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
17844e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
17944e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
180f034b5d4SDavid S. Miller 
181f034b5d4SDavid S. Miller out_unlock:
182f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
183f034b5d4SDavid S. Miller }
184f034b5d4SDavid S. Miller 
185c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
186f034b5d4SDavid S. Miller 
1871da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1881da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1911da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1948f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1951da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1961da177e4SLinus Torvalds 
19753bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1981da177e4SLinus Torvalds 
199980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
20053bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
2011da177e4SLinus Torvalds 
202aa5d62ccSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
203aa5d62ccSHerbert Xu {
204aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
205aa5d62ccSHerbert Xu 	if (unlikely(family >= NPROTO))
206aa5d62ccSHerbert Xu 		return NULL;
207aa5d62ccSHerbert Xu 	write_lock_bh(&xfrm_state_afinfo_lock);
208aa5d62ccSHerbert Xu 	afinfo = xfrm_state_afinfo[family];
209aa5d62ccSHerbert Xu 	if (unlikely(!afinfo))
210aa5d62ccSHerbert Xu 		write_unlock_bh(&xfrm_state_afinfo_lock);
211aa5d62ccSHerbert Xu 	return afinfo;
212aa5d62ccSHerbert Xu }
213aa5d62ccSHerbert Xu 
214aa5d62ccSHerbert Xu static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
2159a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
216aa5d62ccSHerbert Xu {
217aa5d62ccSHerbert Xu 	write_unlock_bh(&xfrm_state_afinfo_lock);
218aa5d62ccSHerbert Xu }
219aa5d62ccSHerbert Xu 
220533cb5b0SEric Dumazet int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
221aa5d62ccSHerbert Xu {
222aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
223533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
224aa5d62ccSHerbert Xu 	int err = 0;
225aa5d62ccSHerbert Xu 
226aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
227aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
228aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
229aa5d62ccSHerbert Xu 
230aa5d62ccSHerbert Xu 	if (likely(typemap[type->proto] == NULL))
231aa5d62ccSHerbert Xu 		typemap[type->proto] = type;
232aa5d62ccSHerbert Xu 	else
233aa5d62ccSHerbert Xu 		err = -EEXIST;
234aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
235aa5d62ccSHerbert Xu 	return err;
236aa5d62ccSHerbert Xu }
237aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_type);
238aa5d62ccSHerbert Xu 
239533cb5b0SEric Dumazet int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
240aa5d62ccSHerbert Xu {
241aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
242533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
243aa5d62ccSHerbert Xu 	int err = 0;
244aa5d62ccSHerbert Xu 
245aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
246aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
247aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
248aa5d62ccSHerbert Xu 
249aa5d62ccSHerbert Xu 	if (unlikely(typemap[type->proto] != type))
250aa5d62ccSHerbert Xu 		err = -ENOENT;
251aa5d62ccSHerbert Xu 	else
252aa5d62ccSHerbert Xu 		typemap[type->proto] = NULL;
253aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
254aa5d62ccSHerbert Xu 	return err;
255aa5d62ccSHerbert Xu }
256aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_type);
257aa5d62ccSHerbert Xu 
258533cb5b0SEric Dumazet static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
259aa5d62ccSHerbert Xu {
260aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
261533cb5b0SEric Dumazet 	const struct xfrm_type **typemap;
262533cb5b0SEric Dumazet 	const struct xfrm_type *type;
263aa5d62ccSHerbert Xu 	int modload_attempted = 0;
264aa5d62ccSHerbert Xu 
265aa5d62ccSHerbert Xu retry:
266aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
267aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
268aa5d62ccSHerbert Xu 		return NULL;
269aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
270aa5d62ccSHerbert Xu 
271aa5d62ccSHerbert Xu 	type = typemap[proto];
272aa5d62ccSHerbert Xu 	if (unlikely(type && !try_module_get(type->owner)))
273aa5d62ccSHerbert Xu 		type = NULL;
274aa5d62ccSHerbert Xu 	if (!type && !modload_attempted) {
275aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
276aa5d62ccSHerbert Xu 		request_module("xfrm-type-%d-%d", family, proto);
277aa5d62ccSHerbert Xu 		modload_attempted = 1;
278aa5d62ccSHerbert Xu 		goto retry;
279aa5d62ccSHerbert Xu 	}
280aa5d62ccSHerbert Xu 
281aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
282aa5d62ccSHerbert Xu 	return type;
283aa5d62ccSHerbert Xu }
284aa5d62ccSHerbert Xu 
285533cb5b0SEric Dumazet static void xfrm_put_type(const struct xfrm_type *type)
286aa5d62ccSHerbert Xu {
287aa5d62ccSHerbert Xu 	module_put(type->owner);
288aa5d62ccSHerbert Xu }
289aa5d62ccSHerbert Xu 
290aa5d62ccSHerbert Xu int xfrm_register_mode(struct xfrm_mode *mode, int family)
291aa5d62ccSHerbert Xu {
292aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
293aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
294aa5d62ccSHerbert Xu 	int err;
295aa5d62ccSHerbert Xu 
296aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
297aa5d62ccSHerbert Xu 		return -EINVAL;
298aa5d62ccSHerbert Xu 
299aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
300aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
301aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
302aa5d62ccSHerbert Xu 
303aa5d62ccSHerbert Xu 	err = -EEXIST;
304aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
30517c2a42aSHerbert Xu 	if (modemap[mode->encap])
30617c2a42aSHerbert Xu 		goto out;
30717c2a42aSHerbert Xu 
30817c2a42aSHerbert Xu 	err = -ENOENT;
30917c2a42aSHerbert Xu 	if (!try_module_get(afinfo->owner))
31017c2a42aSHerbert Xu 		goto out;
31117c2a42aSHerbert Xu 
31217c2a42aSHerbert Xu 	mode->afinfo = afinfo;
313aa5d62ccSHerbert Xu 	modemap[mode->encap] = mode;
314aa5d62ccSHerbert Xu 	err = 0;
315aa5d62ccSHerbert Xu 
31617c2a42aSHerbert Xu out:
317aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
318aa5d62ccSHerbert Xu 	return err;
319aa5d62ccSHerbert Xu }
320aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_mode);
321aa5d62ccSHerbert Xu 
322aa5d62ccSHerbert Xu int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
323aa5d62ccSHerbert Xu {
324aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
325aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
326aa5d62ccSHerbert Xu 	int err;
327aa5d62ccSHerbert Xu 
328aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
329aa5d62ccSHerbert Xu 		return -EINVAL;
330aa5d62ccSHerbert Xu 
331aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
332aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
333aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
334aa5d62ccSHerbert Xu 
335aa5d62ccSHerbert Xu 	err = -ENOENT;
336aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
337aa5d62ccSHerbert Xu 	if (likely(modemap[mode->encap] == mode)) {
338aa5d62ccSHerbert Xu 		modemap[mode->encap] = NULL;
33917c2a42aSHerbert Xu 		module_put(mode->afinfo->owner);
340aa5d62ccSHerbert Xu 		err = 0;
341aa5d62ccSHerbert Xu 	}
342aa5d62ccSHerbert Xu 
343aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
344aa5d62ccSHerbert Xu 	return err;
345aa5d62ccSHerbert Xu }
346aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_mode);
347aa5d62ccSHerbert Xu 
348aa5d62ccSHerbert Xu static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
349aa5d62ccSHerbert Xu {
350aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
351aa5d62ccSHerbert Xu 	struct xfrm_mode *mode;
352aa5d62ccSHerbert Xu 	int modload_attempted = 0;
353aa5d62ccSHerbert Xu 
354aa5d62ccSHerbert Xu 	if (unlikely(encap >= XFRM_MODE_MAX))
355aa5d62ccSHerbert Xu 		return NULL;
356aa5d62ccSHerbert Xu 
357aa5d62ccSHerbert Xu retry:
358aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
359aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
360aa5d62ccSHerbert Xu 		return NULL;
361aa5d62ccSHerbert Xu 
362aa5d62ccSHerbert Xu 	mode = afinfo->mode_map[encap];
363aa5d62ccSHerbert Xu 	if (unlikely(mode && !try_module_get(mode->owner)))
364aa5d62ccSHerbert Xu 		mode = NULL;
365aa5d62ccSHerbert Xu 	if (!mode && !modload_attempted) {
366aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
367aa5d62ccSHerbert Xu 		request_module("xfrm-mode-%d-%d", family, encap);
368aa5d62ccSHerbert Xu 		modload_attempted = 1;
369aa5d62ccSHerbert Xu 		goto retry;
370aa5d62ccSHerbert Xu 	}
371aa5d62ccSHerbert Xu 
372aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
373aa5d62ccSHerbert Xu 	return mode;
374aa5d62ccSHerbert Xu }
375aa5d62ccSHerbert Xu 
376aa5d62ccSHerbert Xu static void xfrm_put_mode(struct xfrm_mode *mode)
377aa5d62ccSHerbert Xu {
378aa5d62ccSHerbert Xu 	module_put(mode->owner);
379aa5d62ccSHerbert Xu }
380aa5d62ccSHerbert Xu 
3811da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
3821da177e4SLinus Torvalds {
383a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
384a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
3851da177e4SLinus Torvalds 	kfree(x->aalg);
3861da177e4SLinus Torvalds 	kfree(x->ealg);
3871da177e4SLinus Torvalds 	kfree(x->calg);
3881da177e4SLinus Torvalds 	kfree(x->encap);
389060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
39013996378SHerbert Xu 	if (x->inner_mode)
39113996378SHerbert Xu 		xfrm_put_mode(x->inner_mode);
39213996378SHerbert Xu 	if (x->outer_mode)
39313996378SHerbert Xu 		xfrm_put_mode(x->outer_mode);
3941da177e4SLinus Torvalds 	if (x->type) {
3951da177e4SLinus Torvalds 		x->type->destructor(x);
3961da177e4SLinus Torvalds 		xfrm_put_type(x->type);
3971da177e4SLinus Torvalds 	}
398df71837dSTrent Jaeger 	security_xfrm_state_free(x);
3991da177e4SLinus Torvalds 	kfree(x);
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
402c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	struct xfrm_state *x;
4058f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
4068f126e37SDavid S. Miller 	struct hlist_head gc_list;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
4098f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
4108f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
4111da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
4121da177e4SLinus Torvalds 
4138f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
4141da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
4158f126e37SDavid S. Miller 
4161da177e4SLinus Torvalds 	wake_up(&km_waitq);
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
4201da177e4SLinus Torvalds {
4211da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
4221da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
4231da177e4SLinus Torvalds 	else
4241da177e4SLinus Torvalds 		return secs*HZ;
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
4309d729f72SJames Morris 	unsigned long now = get_seconds();
4311da177e4SLinus Torvalds 	long next = LONG_MAX;
4321da177e4SLinus Torvalds 	int warn = 0;
433161a09e7SJoy Latten 	int err = 0;
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	spin_lock(&x->lock);
4361da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
4371da177e4SLinus Torvalds 		goto out;
4381da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
4391da177e4SLinus Torvalds 		goto expired;
4401da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
4411da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
4421da177e4SLinus Torvalds 			x->curlft.add_time - now;
4431da177e4SLinus Torvalds 		if (tmo <= 0)
4441da177e4SLinus Torvalds 			goto expired;
4451da177e4SLinus Torvalds 		if (tmo < next)
4461da177e4SLinus Torvalds 			next = tmo;
4471da177e4SLinus Torvalds 	}
4481da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
4491da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
4501da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4511da177e4SLinus Torvalds 		if (tmo <= 0)
4521da177e4SLinus Torvalds 			goto expired;
4531da177e4SLinus Torvalds 		if (tmo < next)
4541da177e4SLinus Torvalds 			next = tmo;
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds 	if (x->km.dying)
4571da177e4SLinus Torvalds 		goto resched;
4581da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
4591da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
4601da177e4SLinus Torvalds 			x->curlft.add_time - now;
4611da177e4SLinus Torvalds 		if (tmo <= 0)
4621da177e4SLinus Torvalds 			warn = 1;
4631da177e4SLinus Torvalds 		else if (tmo < next)
4641da177e4SLinus Torvalds 			next = tmo;
4651da177e4SLinus Torvalds 	}
4661da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
4671da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
4681da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4691da177e4SLinus Torvalds 		if (tmo <= 0)
4701da177e4SLinus Torvalds 			warn = 1;
4711da177e4SLinus Torvalds 		else if (tmo < next)
4721da177e4SLinus Torvalds 			next = tmo;
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 
4754666faabSHerbert Xu 	x->km.dying = warn;
4761da177e4SLinus Torvalds 	if (warn)
47753bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
4781da177e4SLinus Torvalds resched:
479a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
480a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
481a47f0ce0SDavid S. Miller 
4821da177e4SLinus Torvalds 	goto out;
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds expired:
4851da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
4861da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
4871da177e4SLinus Torvalds 		wake_up(&km_waitq);
4881da177e4SLinus Torvalds 		next = 2;
4891da177e4SLinus Torvalds 		goto resched;
4901da177e4SLinus Torvalds 	}
491161a09e7SJoy Latten 
492161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
493161a09e7SJoy Latten 	if (!err && x->id.spi)
49453bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
4951da177e4SLinus Torvalds 
496ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
4970c11b942SAl Viro 				audit_get_loginuid(current), 0);
498161a09e7SJoy Latten 
4991da177e4SLinus Torvalds out:
5001da177e4SLinus Torvalds 	spin_unlock(&x->lock);
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
5030ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
5040ac84752SDavid S. Miller 
5051da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
5061da177e4SLinus Torvalds {
5071da177e4SLinus Torvalds 	struct xfrm_state *x;
5081da177e4SLinus Torvalds 
5090da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds 	if (x) {
5121da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
5131da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
514*4c563f76STimo Teras 		INIT_LIST_HEAD(&x->all);
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;
5281da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
5291da177e4SLinus Torvalds 	}
5301da177e4SLinus Torvalds 	return x;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
5351da177e4SLinus Torvalds {
5361da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
5371da177e4SLinus Torvalds 
538*4c563f76STimo Teras 	spin_lock_bh(&xfrm_state_lock);
539*4c563f76STimo Teras 	list_del(&x->all);
540*4c563f76STimo Teras 	spin_unlock_bh(&xfrm_state_lock);
541*4c563f76STimo Teras 
5421da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
5438f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
5441da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
5451da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
5481da177e4SLinus Torvalds 
54953bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
5501da177e4SLinus Torvalds {
55126b15dadSJamal Hadi Salim 	int err = -ESRCH;
55226b15dadSJamal Hadi Salim 
5531da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
5541da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
5551da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
5568f126e37SDavid S. Miller 		hlist_del(&x->bydst);
5578f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
558a47f0ce0SDavid S. Miller 		if (x->id.spi)
5598f126e37SDavid S. Miller 			hlist_del(&x->byspi);
560f034b5d4SDavid S. Miller 		xfrm_state_num--;
5611da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
5641da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
5651da177e4SLinus Torvalds 		 * is what we are dropping here.
5661da177e4SLinus Torvalds 		 */
5675dba4797SPatrick McHardy 		xfrm_state_put(x);
56826b15dadSJamal Hadi Salim 		err = 0;
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 
57126b15dadSJamal Hadi Salim 	return err;
57226b15dadSJamal Hadi Salim }
57353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
57426b15dadSJamal Hadi Salim 
57526b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
5761da177e4SLinus Torvalds {
57726b15dadSJamal Hadi Salim 	int err;
57826b15dadSJamal Hadi Salim 
5791da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
58026b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
5811da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
58226b15dadSJamal Hadi Salim 
58326b15dadSJamal Hadi Salim 	return err;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
5861da177e4SLinus Torvalds 
5874aa2e62cSJoy Latten #ifdef CONFIG_SECURITY_NETWORK_XFRM
5884aa2e62cSJoy Latten static inline int
5894aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
5901da177e4SLinus Torvalds {
5914aa2e62cSJoy Latten 	int i, err = 0;
5924aa2e62cSJoy Latten 
5934aa2e62cSJoy Latten 	for (i = 0; i <= xfrm_state_hmask; i++) {
5944aa2e62cSJoy Latten 		struct hlist_node *entry;
5954aa2e62cSJoy Latten 		struct xfrm_state *x;
5964aa2e62cSJoy Latten 
5974aa2e62cSJoy Latten 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
5984aa2e62cSJoy Latten 			if (xfrm_id_proto_match(x->id.proto, proto) &&
5994aa2e62cSJoy Latten 			   (err = security_xfrm_state_delete(x)) != 0) {
600ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, 0,
601ab5f5e8bSJoy Latten 							audit_info->loginuid,
602ab5f5e8bSJoy Latten 							audit_info->secid);
6034aa2e62cSJoy Latten 				return err;
6044aa2e62cSJoy Latten 			}
6054aa2e62cSJoy Latten 		}
6064aa2e62cSJoy Latten 	}
6074aa2e62cSJoy Latten 
6084aa2e62cSJoy Latten 	return err;
6094aa2e62cSJoy Latten }
6104aa2e62cSJoy Latten #else
6114aa2e62cSJoy Latten static inline int
6124aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
6134aa2e62cSJoy Latten {
6144aa2e62cSJoy Latten 	return 0;
6154aa2e62cSJoy Latten }
6164aa2e62cSJoy Latten #endif
6174aa2e62cSJoy Latten 
6184aa2e62cSJoy Latten int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
6194aa2e62cSJoy Latten {
6204aa2e62cSJoy Latten 	int i, err = 0;
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6234aa2e62cSJoy Latten 	err = xfrm_state_flush_secctx_check(proto, audit_info);
6244aa2e62cSJoy Latten 	if (err)
6254aa2e62cSJoy Latten 		goto out;
6264aa2e62cSJoy Latten 
627a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
6288f126e37SDavid S. Miller 		struct hlist_node *entry;
6298f126e37SDavid S. Miller 		struct xfrm_state *x;
6301da177e4SLinus Torvalds restart:
6318f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
6321da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
6335794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
6341da177e4SLinus Torvalds 				xfrm_state_hold(x);
6351da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
6361da177e4SLinus Torvalds 
637161a09e7SJoy Latten 				err = xfrm_state_delete(x);
638ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, err ? 0 : 1,
639ab5f5e8bSJoy Latten 							audit_info->loginuid,
640ab5f5e8bSJoy Latten 							audit_info->secid);
6411da177e4SLinus Torvalds 				xfrm_state_put(x);
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
6441da177e4SLinus Torvalds 				goto restart;
6451da177e4SLinus Torvalds 			}
6461da177e4SLinus Torvalds 		}
6471da177e4SLinus Torvalds 	}
6484aa2e62cSJoy Latten 	err = 0;
6494aa2e62cSJoy Latten 
6504aa2e62cSJoy Latten out:
6511da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6521da177e4SLinus Torvalds 	wake_up(&km_waitq);
6534aa2e62cSJoy Latten 	return err;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
6561da177e4SLinus Torvalds 
657af11e316SJamal Hadi Salim void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
65828d8909bSJamal Hadi Salim {
65928d8909bSJamal Hadi Salim 	spin_lock_bh(&xfrm_state_lock);
66028d8909bSJamal Hadi Salim 	si->sadcnt = xfrm_state_num;
66128d8909bSJamal Hadi Salim 	si->sadhcnt = xfrm_state_hmask;
66228d8909bSJamal Hadi Salim 	si->sadhmcnt = xfrm_state_hashmax;
66328d8909bSJamal Hadi Salim 	spin_unlock_bh(&xfrm_state_lock);
66428d8909bSJamal Hadi Salim }
66528d8909bSJamal Hadi Salim EXPORT_SYMBOL(xfrm_sad_getinfo);
66628d8909bSJamal Hadi Salim 
6671da177e4SLinus Torvalds static int
6681da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
6691da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
6701da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
6711da177e4SLinus Torvalds 		  unsigned short family)
6721da177e4SLinus Torvalds {
6731da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6741da177e4SLinus Torvalds 	if (!afinfo)
6751da177e4SLinus Torvalds 		return -1;
6761da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
6771da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6781da177e4SLinus Torvalds 	return 0;
6791da177e4SLinus Torvalds }
6801da177e4SLinus Torvalds 
681a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
682edcd5821SDavid S. Miller {
683edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
684edcd5821SDavid S. Miller 	struct xfrm_state *x;
6858f126e37SDavid S. Miller 	struct hlist_node *entry;
686edcd5821SDavid S. Miller 
6878f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
688edcd5821SDavid S. Miller 		if (x->props.family != family ||
689edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
690edcd5821SDavid S. Miller 		    x->id.proto     != proto)
691edcd5821SDavid S. Miller 			continue;
692edcd5821SDavid S. Miller 
693edcd5821SDavid S. Miller 		switch (family) {
694edcd5821SDavid S. Miller 		case AF_INET:
695edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
696edcd5821SDavid S. Miller 				continue;
697edcd5821SDavid S. Miller 			break;
698edcd5821SDavid S. Miller 		case AF_INET6:
699edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
700edcd5821SDavid S. Miller 					     (struct in6_addr *)
701edcd5821SDavid S. Miller 					     x->id.daddr.a6))
702edcd5821SDavid S. Miller 				continue;
703edcd5821SDavid S. Miller 			break;
7043ff50b79SStephen Hemminger 		}
705edcd5821SDavid S. Miller 
706edcd5821SDavid S. Miller 		xfrm_state_hold(x);
707edcd5821SDavid S. Miller 		return x;
708edcd5821SDavid S. Miller 	}
709edcd5821SDavid S. Miller 
710edcd5821SDavid S. Miller 	return NULL;
711edcd5821SDavid S. Miller }
712edcd5821SDavid S. Miller 
713edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
714edcd5821SDavid S. Miller {
715667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
716edcd5821SDavid S. Miller 	struct xfrm_state *x;
7178f126e37SDavid S. Miller 	struct hlist_node *entry;
718edcd5821SDavid S. Miller 
7198f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
720edcd5821SDavid S. Miller 		if (x->props.family != family ||
721edcd5821SDavid S. Miller 		    x->id.proto     != proto)
722edcd5821SDavid S. Miller 			continue;
723edcd5821SDavid S. Miller 
724edcd5821SDavid S. Miller 		switch (family) {
725edcd5821SDavid S. Miller 		case AF_INET:
726edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
727edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
728edcd5821SDavid S. Miller 				continue;
729edcd5821SDavid S. Miller 			break;
730edcd5821SDavid S. Miller 		case AF_INET6:
731edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
732edcd5821SDavid S. Miller 					     (struct in6_addr *)
733edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
734edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
735edcd5821SDavid S. Miller 					     (struct in6_addr *)
736edcd5821SDavid S. Miller 					     x->props.saddr.a6))
737edcd5821SDavid S. Miller 				continue;
738edcd5821SDavid S. Miller 			break;
7393ff50b79SStephen Hemminger 		}
740edcd5821SDavid S. Miller 
741edcd5821SDavid S. Miller 		xfrm_state_hold(x);
742edcd5821SDavid S. Miller 		return x;
743edcd5821SDavid S. Miller 	}
744edcd5821SDavid S. Miller 
745edcd5821SDavid S. Miller 	return NULL;
746edcd5821SDavid S. Miller }
747edcd5821SDavid S. Miller 
748edcd5821SDavid S. Miller static inline struct xfrm_state *
749edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
750edcd5821SDavid S. Miller {
751edcd5821SDavid S. Miller 	if (use_spi)
752edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
753edcd5821SDavid S. Miller 					   x->id.proto, family);
754edcd5821SDavid S. Miller 	else
755edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
756edcd5821SDavid S. Miller 						  &x->props.saddr,
757edcd5821SDavid S. Miller 						  x->id.proto, family);
758edcd5821SDavid S. Miller }
759edcd5821SDavid S. Miller 
7602fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
7612fab22f2SPatrick McHardy {
7622fab22f2SPatrick McHardy 	if (have_hash_collision &&
7632fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
7642fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
7652fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
7662fab22f2SPatrick McHardy }
7672fab22f2SPatrick McHardy 
7681da177e4SLinus Torvalds struct xfrm_state *
7691da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
7701da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
7711da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
7721da177e4SLinus Torvalds 		unsigned short family)
7731da177e4SLinus Torvalds {
7744bda4f25SPavel Emelyanov 	unsigned int h;
7758f126e37SDavid S. Miller 	struct hlist_node *entry;
7761da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
7771da177e4SLinus Torvalds 	int acquire_in_progress = 0;
7781da177e4SLinus Torvalds 	int error = 0;
7791da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7824bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
7838f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
7841da177e4SLinus Torvalds 		if (x->props.family == family &&
7851da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
786fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
7871da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
7881da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
7891da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
7901da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
7911da177e4SLinus Torvalds 			/* Resolution logic:
7921da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
7931da177e4SLinus Torvalds 			      Done.
7941da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 			   Entering area of "sysdeps".
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
7991da177e4SLinus Torvalds 			      it selects only session which triggered
8001da177e4SLinus Torvalds 			      previous resolution. Key manager will do
8011da177e4SLinus Torvalds 			      something to install a state with proper
8021da177e4SLinus Torvalds 			      selector.
8031da177e4SLinus Torvalds 			 */
8041da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
80548b8d783SJoakim Koskela 				if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
806e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
8071da177e4SLinus Torvalds 					continue;
8081da177e4SLinus Torvalds 				if (!best ||
8091da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
8101da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
8111da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
8121da177e4SLinus Torvalds 					best = x;
8131da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
8141da177e4SLinus Torvalds 				acquire_in_progress = 1;
8151da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
8161da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
81748b8d783SJoakim Koskela 				if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
818e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
8191da177e4SLinus Torvalds 					error = -ESRCH;
8201da177e4SLinus Torvalds 			}
8211da177e4SLinus Torvalds 		}
8221da177e4SLinus Torvalds 	}
8231da177e4SLinus Torvalds 
8241da177e4SLinus Torvalds 	x = best;
8251da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
8265c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
827edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
828edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
8291da177e4SLinus Torvalds 			xfrm_state_put(x0);
8301da177e4SLinus Torvalds 			error = -EEXIST;
8311da177e4SLinus Torvalds 			goto out;
8321da177e4SLinus Torvalds 		}
8331da177e4SLinus Torvalds 		x = xfrm_state_alloc();
8341da177e4SLinus Torvalds 		if (x == NULL) {
8351da177e4SLinus Torvalds 			error = -ENOMEM;
8361da177e4SLinus Torvalds 			goto out;
8371da177e4SLinus Torvalds 		}
8381da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
8391da177e4SLinus Torvalds 		 * to current session. */
8401da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
8411da177e4SLinus Torvalds 
842e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
843e0d1caa7SVenkat Yekkirala 		if (error) {
844e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
845e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
846e0d1caa7SVenkat Yekkirala 			x = NULL;
847e0d1caa7SVenkat Yekkirala 			goto out;
848e0d1caa7SVenkat Yekkirala 		}
849e0d1caa7SVenkat Yekkirala 
8501da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
8511da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
8528f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
853667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
8548f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
8551da177e4SLinus Torvalds 			if (x->id.spi) {
8561da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
8578f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
8581da177e4SLinus Torvalds 			}
85901e67d08SDavid S. Miller 			x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
86001e67d08SDavid S. Miller 			x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
8611da177e4SLinus Torvalds 			add_timer(&x->timer);
8622fab22f2SPatrick McHardy 			xfrm_state_num++;
8632fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
8641da177e4SLinus Torvalds 		} else {
8651da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
8661da177e4SLinus Torvalds 			xfrm_state_put(x);
8671da177e4SLinus Torvalds 			x = NULL;
8681da177e4SLinus Torvalds 			error = -ESRCH;
8691da177e4SLinus Torvalds 		}
8701da177e4SLinus Torvalds 	}
8711da177e4SLinus Torvalds out:
8721da177e4SLinus Torvalds 	if (x)
8731da177e4SLinus Torvalds 		xfrm_state_hold(x);
8741da177e4SLinus Torvalds 	else
8751da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
8761da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8771da177e4SLinus Torvalds 	return x;
8781da177e4SLinus Torvalds }
8791da177e4SLinus Torvalds 
880628529b6SJamal Hadi Salim struct xfrm_state *
881628529b6SJamal Hadi Salim xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
882628529b6SJamal Hadi Salim 		    unsigned short family, u8 mode, u8 proto, u32 reqid)
883628529b6SJamal Hadi Salim {
8844bda4f25SPavel Emelyanov 	unsigned int h;
885628529b6SJamal Hadi Salim 	struct xfrm_state *rx = NULL, *x = NULL;
886628529b6SJamal Hadi Salim 	struct hlist_node *entry;
887628529b6SJamal Hadi Salim 
888628529b6SJamal Hadi Salim 	spin_lock(&xfrm_state_lock);
8894bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, reqid, family);
890628529b6SJamal Hadi Salim 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
891628529b6SJamal Hadi Salim 		if (x->props.family == family &&
892628529b6SJamal Hadi Salim 		    x->props.reqid == reqid &&
893628529b6SJamal Hadi Salim 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
894628529b6SJamal Hadi Salim 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
895628529b6SJamal Hadi Salim 		    mode == x->props.mode &&
896628529b6SJamal Hadi Salim 		    proto == x->id.proto &&
897628529b6SJamal Hadi Salim 		    x->km.state == XFRM_STATE_VALID) {
898628529b6SJamal Hadi Salim 			rx = x;
899628529b6SJamal Hadi Salim 			break;
900628529b6SJamal Hadi Salim 		}
901628529b6SJamal Hadi Salim 	}
902628529b6SJamal Hadi Salim 
903628529b6SJamal Hadi Salim 	if (rx)
904628529b6SJamal Hadi Salim 		xfrm_state_hold(rx);
905628529b6SJamal Hadi Salim 	spin_unlock(&xfrm_state_lock);
906628529b6SJamal Hadi Salim 
907628529b6SJamal Hadi Salim 
908628529b6SJamal Hadi Salim 	return rx;
909628529b6SJamal Hadi Salim }
910628529b6SJamal Hadi Salim EXPORT_SYMBOL(xfrm_stateonly_find);
911628529b6SJamal Hadi Salim 
9121da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
9131da177e4SLinus Torvalds {
914a624c108SDavid S. Miller 	unsigned int h;
9151da177e4SLinus Torvalds 
9169d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
9179d4a706dSDavid S. Miller 
918*4c563f76STimo Teras 	list_add_tail(&x->all, &xfrm_state_all);
919*4c563f76STimo Teras 
920c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
921c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
9228f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
9231da177e4SLinus Torvalds 
924667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
9258f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
9266c44e6b7SMasahide NAKAMURA 
9277b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
9286c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
9296c44e6b7SMasahide NAKAMURA 				  x->props.family);
9301da177e4SLinus Torvalds 
9318f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
9326c44e6b7SMasahide NAKAMURA 	}
9331da177e4SLinus Torvalds 
934a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
935a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
936a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
937f8cd5488SJamal Hadi Salim 
9381da177e4SLinus Torvalds 	wake_up(&km_waitq);
939f034b5d4SDavid S. Miller 
940f034b5d4SDavid S. Miller 	xfrm_state_num++;
941f034b5d4SDavid S. Miller 
942918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
9431da177e4SLinus Torvalds }
9441da177e4SLinus Torvalds 
945c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
946c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
947c7f5ea3aSDavid S. Miller {
948c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
949c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
950c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
951c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
952c7f5ea3aSDavid S. Miller 	unsigned int h;
953c7f5ea3aSDavid S. Miller 
954c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
955c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
956c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
957c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
958c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
959c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
960c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
961c7f5ea3aSDavid S. Miller 	}
962c7f5ea3aSDavid S. Miller }
963c7f5ea3aSDavid S. Miller 
9641da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
9651da177e4SLinus Torvalds {
9661da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
967c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
9681da177e4SLinus Torvalds 	__xfrm_state_insert(x);
9691da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
9721da177e4SLinus Torvalds 
9732770834cSDavid S. Miller /* xfrm_state_lock is held */
9742770834cSDavid 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)
9752770834cSDavid S. Miller {
976c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
9778f126e37SDavid S. Miller 	struct hlist_node *entry;
9782770834cSDavid S. Miller 	struct xfrm_state *x;
9792770834cSDavid S. Miller 
9808f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
9812770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
9822770834cSDavid S. Miller 		    x->props.mode   != mode ||
9832770834cSDavid S. Miller 		    x->props.family != family ||
9842770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
98575e252d9SJoy Latten 		    x->id.spi       != 0 ||
98675e252d9SJoy Latten 		    x->id.proto	    != proto)
9872770834cSDavid S. Miller 			continue;
9882770834cSDavid S. Miller 
9892770834cSDavid S. Miller 		switch (family) {
9902770834cSDavid S. Miller 		case AF_INET:
9912770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
9922770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
9932770834cSDavid S. Miller 				continue;
9942770834cSDavid S. Miller 			break;
9952770834cSDavid S. Miller 		case AF_INET6:
9962770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
9972770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
9982770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
9992770834cSDavid S. Miller 					     x->props.saddr.a6,
10002770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
10012770834cSDavid S. Miller 				continue;
10022770834cSDavid S. Miller 			break;
10033ff50b79SStephen Hemminger 		}
10042770834cSDavid S. Miller 
10052770834cSDavid S. Miller 		xfrm_state_hold(x);
10062770834cSDavid S. Miller 		return x;
10072770834cSDavid S. Miller 	}
10082770834cSDavid S. Miller 
10092770834cSDavid S. Miller 	if (!create)
10102770834cSDavid S. Miller 		return NULL;
10112770834cSDavid S. Miller 
10122770834cSDavid S. Miller 	x = xfrm_state_alloc();
10132770834cSDavid S. Miller 	if (likely(x)) {
10142770834cSDavid S. Miller 		switch (family) {
10152770834cSDavid S. Miller 		case AF_INET:
10162770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
10172770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
10182770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
10192770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
10202770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
10212770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
10222770834cSDavid S. Miller 			break;
10232770834cSDavid S. Miller 
10242770834cSDavid S. Miller 		case AF_INET6:
10252770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
10262770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10272770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
10282770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10292770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
10302770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
10312770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
10322770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10332770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
10342770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10352770834cSDavid S. Miller 			break;
10363ff50b79SStephen Hemminger 		}
10372770834cSDavid S. Miller 
10382770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
10392770834cSDavid S. Miller 		x->id.proto = proto;
10402770834cSDavid S. Miller 		x->props.family = family;
10412770834cSDavid S. Miller 		x->props.mode = mode;
10422770834cSDavid S. Miller 		x->props.reqid = reqid;
104301e67d08SDavid S. Miller 		x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
10442770834cSDavid S. Miller 		xfrm_state_hold(x);
104501e67d08SDavid S. Miller 		x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
10462770834cSDavid S. Miller 		add_timer(&x->timer);
10478f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
1048667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
10498f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
1050918049f0SDavid S. Miller 
1051918049f0SDavid S. Miller 		xfrm_state_num++;
1052918049f0SDavid S. Miller 
1053918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
10542770834cSDavid S. Miller 	}
10552770834cSDavid S. Miller 
10562770834cSDavid S. Miller 	return x;
10572770834cSDavid S. Miller }
10582770834cSDavid S. Miller 
10591da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
10621da177e4SLinus Torvalds {
10631da177e4SLinus Torvalds 	struct xfrm_state *x1;
10641da177e4SLinus Torvalds 	int family;
10651da177e4SLinus Torvalds 	int err;
1066eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	family = x->props.family;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10711da177e4SLinus Torvalds 
1072edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
10731da177e4SLinus Torvalds 	if (x1) {
10741da177e4SLinus Torvalds 		xfrm_state_put(x1);
10751da177e4SLinus Torvalds 		x1 = NULL;
10761da177e4SLinus Torvalds 		err = -EEXIST;
10771da177e4SLinus Torvalds 		goto out;
10781da177e4SLinus Torvalds 	}
10791da177e4SLinus Torvalds 
1080eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
10811da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
108275e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
108375e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
10841da177e4SLinus Torvalds 			xfrm_state_put(x1);
10851da177e4SLinus Torvalds 			x1 = NULL;
10861da177e4SLinus Torvalds 		}
10871da177e4SLinus Torvalds 	}
10881da177e4SLinus Torvalds 
1089eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
10902770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
10912770834cSDavid S. Miller 				     x->id.proto,
10921da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
10931da177e4SLinus Torvalds 
1094c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
10951da177e4SLinus Torvalds 	__xfrm_state_insert(x);
10961da177e4SLinus Torvalds 	err = 0;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds out:
10991da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
11001da177e4SLinus Torvalds 
11011da177e4SLinus Torvalds 	if (x1) {
11021da177e4SLinus Torvalds 		xfrm_state_delete(x1);
11031da177e4SLinus Torvalds 		xfrm_state_put(x1);
11041da177e4SLinus Torvalds 	}
11051da177e4SLinus Torvalds 
11061da177e4SLinus Torvalds 	return err;
11071da177e4SLinus Torvalds }
11081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
11091da177e4SLinus Torvalds 
111080c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
11116666351dSEric Dumazet static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
111280c9abaaSShinta Sugimoto {
111380c9abaaSShinta Sugimoto 	int err = -ENOMEM;
111480c9abaaSShinta Sugimoto 	struct xfrm_state *x = xfrm_state_alloc();
111580c9abaaSShinta Sugimoto 	if (!x)
111680c9abaaSShinta Sugimoto 		goto error;
111780c9abaaSShinta Sugimoto 
111880c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
111980c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
112080c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
112180c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
112280c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
112380c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
112480c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
112580c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
112680c9abaaSShinta Sugimoto 
112780c9abaaSShinta Sugimoto 	if (orig->aalg) {
112880c9abaaSShinta Sugimoto 		x->aalg = xfrm_algo_clone(orig->aalg);
112980c9abaaSShinta Sugimoto 		if (!x->aalg)
113080c9abaaSShinta Sugimoto 			goto error;
113180c9abaaSShinta Sugimoto 	}
113280c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
113380c9abaaSShinta Sugimoto 
113480c9abaaSShinta Sugimoto 	if (orig->ealg) {
113580c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
113680c9abaaSShinta Sugimoto 		if (!x->ealg)
113780c9abaaSShinta Sugimoto 			goto error;
113880c9abaaSShinta Sugimoto 	}
113980c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
114080c9abaaSShinta Sugimoto 
114180c9abaaSShinta Sugimoto 	if (orig->calg) {
114280c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
114380c9abaaSShinta Sugimoto 		if (!x->calg)
114480c9abaaSShinta Sugimoto 			goto error;
114580c9abaaSShinta Sugimoto 	}
114680c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
114780c9abaaSShinta Sugimoto 
114880c9abaaSShinta Sugimoto 	if (orig->encap) {
114980c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
115080c9abaaSShinta Sugimoto 		if (!x->encap)
115180c9abaaSShinta Sugimoto 			goto error;
115280c9abaaSShinta Sugimoto 	}
115380c9abaaSShinta Sugimoto 
115480c9abaaSShinta Sugimoto 	if (orig->coaddr) {
115580c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
115680c9abaaSShinta Sugimoto 				    GFP_KERNEL);
115780c9abaaSShinta Sugimoto 		if (!x->coaddr)
115880c9abaaSShinta Sugimoto 			goto error;
115980c9abaaSShinta Sugimoto 	}
116080c9abaaSShinta Sugimoto 
116180c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
116280c9abaaSShinta Sugimoto 	if (err)
116380c9abaaSShinta Sugimoto 		goto error;
116480c9abaaSShinta Sugimoto 
116580c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
116680c9abaaSShinta Sugimoto 
116780c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
116880c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
116980c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
117080c9abaaSShinta Sugimoto 
117180c9abaaSShinta Sugimoto 	return x;
117280c9abaaSShinta Sugimoto 
117380c9abaaSShinta Sugimoto  error:
117480c9abaaSShinta Sugimoto 	if (errp)
117580c9abaaSShinta Sugimoto 		*errp = err;
117680c9abaaSShinta Sugimoto 	if (x) {
117780c9abaaSShinta Sugimoto 		kfree(x->aalg);
117880c9abaaSShinta Sugimoto 		kfree(x->ealg);
117980c9abaaSShinta Sugimoto 		kfree(x->calg);
118080c9abaaSShinta Sugimoto 		kfree(x->encap);
118180c9abaaSShinta Sugimoto 		kfree(x->coaddr);
118280c9abaaSShinta Sugimoto 	}
118380c9abaaSShinta Sugimoto 	kfree(x);
118480c9abaaSShinta Sugimoto 	return NULL;
118580c9abaaSShinta Sugimoto }
118680c9abaaSShinta Sugimoto 
118780c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
118880c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
118980c9abaaSShinta Sugimoto {
119080c9abaaSShinta Sugimoto 	unsigned int h;
119180c9abaaSShinta Sugimoto 	struct xfrm_state *x;
119280c9abaaSShinta Sugimoto 	struct hlist_node *entry;
119380c9abaaSShinta Sugimoto 
119480c9abaaSShinta Sugimoto 	if (m->reqid) {
119580c9abaaSShinta Sugimoto 		h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
119680c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
119780c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
119880c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
119980c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
120080c9abaaSShinta Sugimoto 				continue;
120180c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
120280c9abaaSShinta Sugimoto 				continue;
120380c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
120480c9abaaSShinta Sugimoto 					  m->old_family) ||
120580c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
120680c9abaaSShinta Sugimoto 					  m->old_family))
120780c9abaaSShinta Sugimoto 				continue;
120880c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
120980c9abaaSShinta Sugimoto 			return x;
121080c9abaaSShinta Sugimoto 		}
121180c9abaaSShinta Sugimoto 	} else {
121280c9abaaSShinta Sugimoto 		h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
121380c9abaaSShinta Sugimoto 				  m->old_family);
121480c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
121580c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
121680c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
121780c9abaaSShinta Sugimoto 				continue;
121880c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
121980c9abaaSShinta Sugimoto 					  m->old_family) ||
122080c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
122180c9abaaSShinta Sugimoto 					  m->old_family))
122280c9abaaSShinta Sugimoto 				continue;
122380c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
122480c9abaaSShinta Sugimoto 			return x;
122580c9abaaSShinta Sugimoto 		}
122680c9abaaSShinta Sugimoto 	}
122780c9abaaSShinta Sugimoto 
122880c9abaaSShinta Sugimoto 	return NULL;
122980c9abaaSShinta Sugimoto }
123080c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
123180c9abaaSShinta Sugimoto 
123280c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
123380c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
123480c9abaaSShinta Sugimoto {
123580c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
123680c9abaaSShinta Sugimoto 	int err;
123780c9abaaSShinta Sugimoto 
123880c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
123980c9abaaSShinta Sugimoto 	if (!xc)
124080c9abaaSShinta Sugimoto 		return NULL;
124180c9abaaSShinta Sugimoto 
124280c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
124380c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
124480c9abaaSShinta Sugimoto 
124580c9abaaSShinta Sugimoto 	/* add state */
124680c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
124780c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
124880c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
124980c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
125080c9abaaSShinta Sugimoto 	} else {
125180c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
125280c9abaaSShinta Sugimoto 			goto error;
125380c9abaaSShinta Sugimoto 	}
125480c9abaaSShinta Sugimoto 
125580c9abaaSShinta Sugimoto 	return xc;
125680c9abaaSShinta Sugimoto error:
125780c9abaaSShinta Sugimoto 	kfree(xc);
125880c9abaaSShinta Sugimoto 	return NULL;
125980c9abaaSShinta Sugimoto }
126080c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
126180c9abaaSShinta Sugimoto #endif
126280c9abaaSShinta Sugimoto 
12631da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
12641da177e4SLinus Torvalds {
12651da177e4SLinus Torvalds 	struct xfrm_state *x1;
12661da177e4SLinus Torvalds 	int err;
1267eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1270edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	err = -ESRCH;
12731da177e4SLinus Torvalds 	if (!x1)
12741da177e4SLinus Torvalds 		goto out;
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
12771da177e4SLinus Torvalds 		xfrm_state_put(x1);
12781da177e4SLinus Torvalds 		err = -EEXIST;
12791da177e4SLinus Torvalds 		goto out;
12801da177e4SLinus Torvalds 	}
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
12831da177e4SLinus Torvalds 		__xfrm_state_insert(x);
12841da177e4SLinus Torvalds 		x = NULL;
12851da177e4SLinus Torvalds 	}
12861da177e4SLinus Torvalds 	err = 0;
12871da177e4SLinus Torvalds 
12881da177e4SLinus Torvalds out:
12891da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds 	if (err)
12921da177e4SLinus Torvalds 		return err;
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	if (!x) {
12951da177e4SLinus Torvalds 		xfrm_state_delete(x1);
12961da177e4SLinus Torvalds 		xfrm_state_put(x1);
12971da177e4SLinus Torvalds 		return 0;
12981da177e4SLinus Torvalds 	}
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 	err = -EINVAL;
13011da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
13021da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
13031da177e4SLinus Torvalds 		if (x->encap && x1->encap)
13041da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1305060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1306060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1307060f02a3SNoriaki TAKAMIYA 		}
1308060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1309060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
13101da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
13111da177e4SLinus Torvalds 		x1->km.dying = 0;
13121da177e4SLinus Torvalds 
1313a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
13141da177e4SLinus Torvalds 		if (x1->curlft.use_time)
13151da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 		err = 0;
13181da177e4SLinus Torvalds 	}
13191da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	xfrm_state_put(x1);
13221da177e4SLinus Torvalds 
13231da177e4SLinus Torvalds 	return err;
13241da177e4SLinus Torvalds }
13251da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
13261da177e4SLinus Torvalds 
13271da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
13281da177e4SLinus Torvalds {
13291da177e4SLinus Torvalds 	if (!x->curlft.use_time)
13309d729f72SJames Morris 		x->curlft.use_time = get_seconds();
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
13331da177e4SLinus Torvalds 		return -EINVAL;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
13361da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
13374666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
1338a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
13391da177e4SLinus Torvalds 		return -EINVAL;
13401da177e4SLinus Torvalds 	}
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	if (!x->km.dying &&
13431da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
13444666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
13454666faabSHerbert Xu 		x->km.dying = 1;
134653bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
13474666faabSHerbert Xu 	}
13481da177e4SLinus Torvalds 	return 0;
13491da177e4SLinus Torvalds }
13501da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds struct xfrm_state *
1353a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
13541da177e4SLinus Torvalds 		  unsigned short family)
13551da177e4SLinus Torvalds {
13561da177e4SLinus Torvalds 	struct xfrm_state *x;
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1359edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
13601da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13611da177e4SLinus Torvalds 	return x;
13621da177e4SLinus Torvalds }
13631da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds struct xfrm_state *
1366eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1367eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1368eb2971b6SMasahide NAKAMURA {
1369eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1370eb2971b6SMasahide NAKAMURA 
1371eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1372edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
1373eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1374eb2971b6SMasahide NAKAMURA 	return x;
1375eb2971b6SMasahide NAKAMURA }
1376eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1377eb2971b6SMasahide NAKAMURA 
1378eb2971b6SMasahide NAKAMURA struct xfrm_state *
13791da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
13801da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
13811da177e4SLinus Torvalds 	      int create, unsigned short family)
13821da177e4SLinus Torvalds {
13831da177e4SLinus Torvalds 	struct xfrm_state *x;
13841da177e4SLinus Torvalds 
13851da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
13862770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
13871da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13882770834cSDavid S. Miller 
13891da177e4SLinus Torvalds 	return x;
13901da177e4SLinus Torvalds }
13911da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
13921da177e4SLinus Torvalds 
139341a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
139441a49cc3SMasahide NAKAMURA int
139541a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
139641a49cc3SMasahide NAKAMURA 	       unsigned short family)
139741a49cc3SMasahide NAKAMURA {
139841a49cc3SMasahide NAKAMURA 	int err = 0;
139941a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
140041a49cc3SMasahide NAKAMURA 	if (!afinfo)
140141a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
140241a49cc3SMasahide NAKAMURA 
140341a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
140441a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
140541a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
140641a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
140741a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
140841a49cc3SMasahide NAKAMURA 	return err;
140941a49cc3SMasahide NAKAMURA }
141041a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
141141a49cc3SMasahide NAKAMURA 
141241a49cc3SMasahide NAKAMURA int
141341a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
141441a49cc3SMasahide NAKAMURA 		unsigned short family)
141541a49cc3SMasahide NAKAMURA {
141641a49cc3SMasahide NAKAMURA 	int err = 0;
141741a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
141841a49cc3SMasahide NAKAMURA 	if (!afinfo)
141941a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
142041a49cc3SMasahide NAKAMURA 
142141a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
142241a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
142341a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
142441a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
142541a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
142641a49cc3SMasahide NAKAMURA 	return err;
142741a49cc3SMasahide NAKAMURA }
142841a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
142941a49cc3SMasahide NAKAMURA #endif
143041a49cc3SMasahide NAKAMURA 
14311da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
14321da177e4SLinus Torvalds 
14331da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
14341da177e4SLinus Torvalds {
14351da177e4SLinus Torvalds 	int i;
14361da177e4SLinus Torvalds 
1437f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
14388f126e37SDavid S. Miller 		struct hlist_node *entry;
14398f126e37SDavid S. Miller 		struct xfrm_state *x;
14408f126e37SDavid S. Miller 
14418f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
14428f126e37SDavid S. Miller 			if (x->km.seq == seq &&
14438f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
14441da177e4SLinus Torvalds 				xfrm_state_hold(x);
14451da177e4SLinus Torvalds 				return x;
14461da177e4SLinus Torvalds 			}
14471da177e4SLinus Torvalds 		}
14481da177e4SLinus Torvalds 	}
14491da177e4SLinus Torvalds 	return NULL;
14501da177e4SLinus Torvalds }
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
14531da177e4SLinus Torvalds {
14541da177e4SLinus Torvalds 	struct xfrm_state *x;
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
14571da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
14581da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14591da177e4SLinus Torvalds 	return x;
14601da177e4SLinus Torvalds }
14611da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
14641da177e4SLinus Torvalds {
14651da177e4SLinus Torvalds 	u32 res;
14661da177e4SLinus Torvalds 	static u32 acqseq;
14671da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
14681da177e4SLinus Torvalds 
14691da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
14701da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
14711da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
14721da177e4SLinus Torvalds 	return res;
14731da177e4SLinus Torvalds }
14741da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
14751da177e4SLinus Torvalds 
1476658b219eSHerbert Xu int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
14771da177e4SLinus Torvalds {
1478f034b5d4SDavid S. Miller 	unsigned int h;
14791da177e4SLinus Torvalds 	struct xfrm_state *x0;
1480658b219eSHerbert Xu 	int err = -ENOENT;
1481658b219eSHerbert Xu 	__be32 minspi = htonl(low);
1482658b219eSHerbert Xu 	__be32 maxspi = htonl(high);
14831da177e4SLinus Torvalds 
1484658b219eSHerbert Xu 	spin_lock_bh(&x->lock);
1485658b219eSHerbert Xu 	if (x->km.state == XFRM_STATE_DEAD)
1486658b219eSHerbert Xu 		goto unlock;
1487658b219eSHerbert Xu 
1488658b219eSHerbert Xu 	err = 0;
14891da177e4SLinus Torvalds 	if (x->id.spi)
1490658b219eSHerbert Xu 		goto unlock;
1491658b219eSHerbert Xu 
1492658b219eSHerbert Xu 	err = -ENOENT;
14931da177e4SLinus Torvalds 
14941da177e4SLinus Torvalds 	if (minspi == maxspi) {
14951da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
14961da177e4SLinus Torvalds 		if (x0) {
14971da177e4SLinus Torvalds 			xfrm_state_put(x0);
1498658b219eSHerbert Xu 			goto unlock;
14991da177e4SLinus Torvalds 		}
15001da177e4SLinus Torvalds 		x->id.spi = minspi;
15011da177e4SLinus Torvalds 	} else {
15021da177e4SLinus Torvalds 		u32 spi = 0;
150326977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
150426977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
15051da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
15061da177e4SLinus Torvalds 			if (x0 == NULL) {
15071da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
15081da177e4SLinus Torvalds 				break;
15091da177e4SLinus Torvalds 			}
15101da177e4SLinus Torvalds 			xfrm_state_put(x0);
15111da177e4SLinus Torvalds 		}
15121da177e4SLinus Torvalds 	}
15131da177e4SLinus Torvalds 	if (x->id.spi) {
15141da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
15151da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
15168f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
15171da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
1518658b219eSHerbert Xu 
1519658b219eSHerbert Xu 		err = 0;
15201da177e4SLinus Torvalds 	}
1521658b219eSHerbert Xu 
1522658b219eSHerbert Xu unlock:
1523658b219eSHerbert Xu 	spin_unlock_bh(&x->lock);
1524658b219eSHerbert Xu 
1525658b219eSHerbert Xu 	return err;
15261da177e4SLinus Torvalds }
15271da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
15281da177e4SLinus Torvalds 
1529*4c563f76STimo Teras int xfrm_state_walk(struct xfrm_state_walk *walk,
1530*4c563f76STimo Teras 		    int (*func)(struct xfrm_state *, int, void*),
15311da177e4SLinus Torvalds 		    void *data)
15321da177e4SLinus Torvalds {
1533*4c563f76STimo Teras 	struct xfrm_state *old, *x, *last = NULL;
15341da177e4SLinus Torvalds 	int err = 0;
15351da177e4SLinus Torvalds 
1536*4c563f76STimo Teras 	if (walk->state == NULL && walk->count != 0)
1537*4c563f76STimo Teras 		return 0;
1538*4c563f76STimo Teras 
1539*4c563f76STimo Teras 	old = x = walk->state;
1540*4c563f76STimo Teras 	walk->state = NULL;
15411da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1542*4c563f76STimo Teras 	if (x == NULL)
1543*4c563f76STimo Teras 		x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
1544*4c563f76STimo Teras 	list_for_each_entry_from(x, &xfrm_state_all, all) {
1545*4c563f76STimo Teras 		if (x->km.state == XFRM_STATE_DEAD)
1546*4c563f76STimo Teras 			continue;
1547*4c563f76STimo Teras 		if (!xfrm_id_proto_match(x->id.proto, walk->proto))
154894b9bb54SJamal Hadi Salim 			continue;
154994b9bb54SJamal Hadi Salim 		if (last) {
1550*4c563f76STimo Teras 			err = func(last, walk->count, data);
1551*4c563f76STimo Teras 			if (err) {
1552*4c563f76STimo Teras 				xfrm_state_hold(last);
1553*4c563f76STimo Teras 				walk->state = last;
155494b9bb54SJamal Hadi Salim 				goto out;
155594b9bb54SJamal Hadi Salim 			}
1556*4c563f76STimo Teras 		}
155794b9bb54SJamal Hadi Salim 		last = x;
1558*4c563f76STimo Teras 		walk->count++;
15591da177e4SLinus Torvalds 	}
1560*4c563f76STimo Teras 	if (walk->count == 0) {
15611da177e4SLinus Torvalds 		err = -ENOENT;
15621da177e4SLinus Torvalds 		goto out;
15631da177e4SLinus Torvalds 	}
1564*4c563f76STimo Teras 	if (last)
156594b9bb54SJamal Hadi Salim 		err = func(last, 0, data);
15661da177e4SLinus Torvalds out:
15671da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
1568*4c563f76STimo Teras 	if (old != NULL)
1569*4c563f76STimo Teras 		xfrm_state_put(old);
15701da177e4SLinus Torvalds 	return err;
15711da177e4SLinus Torvalds }
15721da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
15731da177e4SLinus Torvalds 
1574f8cd5488SJamal Hadi Salim 
1575f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1576f8cd5488SJamal Hadi Salim {
1577f8cd5488SJamal Hadi Salim 	struct km_event c;
1578f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1579f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1580f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1581f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1582f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1583f8cd5488SJamal Hadi Salim 	 *     and there were changes
1584f8cd5488SJamal Hadi Salim 	 *
1585f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1586f8cd5488SJamal Hadi Salim 	 */
1587f8cd5488SJamal Hadi Salim 
1588f8cd5488SJamal Hadi Salim 	switch (event) {
1589f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1590f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1591f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
15922717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
15932717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
15942717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
15952717096aSJamal Hadi Salim 			else
1596f8cd5488SJamal Hadi Salim 				return;
15972717096aSJamal Hadi Salim 		}
1598f8cd5488SJamal Hadi Salim 
1599f8cd5488SJamal Hadi Salim 		break;
1600f8cd5488SJamal Hadi Salim 
1601f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1602f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1603f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
16042717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
16052717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1606f8cd5488SJamal Hadi Salim 			return;
16072717096aSJamal Hadi Salim 		}
1608f8cd5488SJamal Hadi Salim 
1609f8cd5488SJamal Hadi Salim 		break;
1610f8cd5488SJamal Hadi Salim 	}
1611f8cd5488SJamal Hadi Salim 
1612f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1613f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1614f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1615f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1616f8cd5488SJamal Hadi Salim 
1617f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1618a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
16192717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
16202717096aSJamal Hadi Salim }
1621f8cd5488SJamal Hadi Salim 
1622f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1623f8cd5488SJamal Hadi Salim {
1624f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1625f8cd5488SJamal Hadi Salim 
1626f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1627f8cd5488SJamal Hadi Salim 
16282717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
16292717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1630f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
16312717096aSJamal Hadi Salim 		else
16322717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
16332717096aSJamal Hadi Salim 	}
1634f8cd5488SJamal Hadi Salim 
1635f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1636f8cd5488SJamal Hadi Salim }
1637f8cd5488SJamal Hadi Salim 
1638afeb14b4SPaul Moore int xfrm_replay_check(struct xfrm_state *x,
1639afeb14b4SPaul Moore 		      struct sk_buff *skb, __be32 net_seq)
16401da177e4SLinus Torvalds {
16411da177e4SLinus Torvalds 	u32 diff;
1642a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
16431da177e4SLinus Torvalds 
16441da177e4SLinus Torvalds 	if (unlikely(seq == 0))
1645afeb14b4SPaul Moore 		goto err;
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
16481da177e4SLinus Torvalds 		return 0;
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
16514c4d51a7SHerbert Xu 	if (diff >= min_t(unsigned int, x->props.replay_window,
16524c4d51a7SHerbert Xu 			  sizeof(x->replay.bitmap) * 8)) {
16531da177e4SLinus Torvalds 		x->stats.replay_window++;
1654afeb14b4SPaul Moore 		goto err;
16551da177e4SLinus Torvalds 	}
16561da177e4SLinus Torvalds 
16571da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
16581da177e4SLinus Torvalds 		x->stats.replay++;
1659afeb14b4SPaul Moore 		goto err;
16601da177e4SLinus Torvalds 	}
16611da177e4SLinus Torvalds 	return 0;
1662afeb14b4SPaul Moore 
1663afeb14b4SPaul Moore err:
1664afeb14b4SPaul Moore 	xfrm_audit_state_replay(x, skb, net_seq);
1665afeb14b4SPaul Moore 	return -EINVAL;
16661da177e4SLinus Torvalds }
16671da177e4SLinus Torvalds 
166861f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
16691da177e4SLinus Torvalds {
16701da177e4SLinus Torvalds 	u32 diff;
167161f4627bSAl Viro 	u32 seq = ntohl(net_seq);
16721da177e4SLinus Torvalds 
16731da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
16741da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
16751da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
16761da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
16771da177e4SLinus Torvalds 		else
16781da177e4SLinus Torvalds 			x->replay.bitmap = 1;
16791da177e4SLinus Torvalds 		x->replay.seq = seq;
16801da177e4SLinus Torvalds 	} else {
16811da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
16821da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
16831da177e4SLinus Torvalds 	}
1684f8cd5488SJamal Hadi Salim 
1685f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1686f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
16871da177e4SLinus Torvalds }
16881da177e4SLinus Torvalds 
1689df01812eSDenis Cheng static LIST_HEAD(xfrm_km_list);
16901da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
16911da177e4SLinus Torvalds 
169226b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
16931da177e4SLinus Torvalds {
16941da177e4SLinus Torvalds 	struct xfrm_mgr *km;
16951da177e4SLinus Torvalds 
169626b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
169726b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
169826b15dadSJamal Hadi Salim 		if (km->notify_policy)
169926b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
170026b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
170126b15dadSJamal Hadi Salim }
170226b15dadSJamal Hadi Salim 
170326b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
170426b15dadSJamal Hadi Salim {
170526b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
170626b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
170726b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
170826b15dadSJamal Hadi Salim 		if (km->notify)
170926b15dadSJamal Hadi Salim 			km->notify(x, c);
171026b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
171126b15dadSJamal Hadi Salim }
171226b15dadSJamal Hadi Salim 
171326b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
171426b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
171526b15dadSJamal Hadi Salim 
171653bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
171726b15dadSJamal Hadi Salim {
171826b15dadSJamal Hadi Salim 	struct km_event c;
171926b15dadSJamal Hadi Salim 
1720bf08867fSHerbert Xu 	c.data.hard = hard;
172153bc6b4dSJamal Hadi Salim 	c.pid = pid;
1722f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
172326b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
17241da177e4SLinus Torvalds 
17251da177e4SLinus Torvalds 	if (hard)
17261da177e4SLinus Torvalds 		wake_up(&km_waitq);
17271da177e4SLinus Torvalds }
17281da177e4SLinus Torvalds 
172953bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
173026b15dadSJamal Hadi Salim /*
173126b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
173226b15dadSJamal Hadi Salim  * We are happy with one success
173326b15dadSJamal Hadi Salim */
1734980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
17351da177e4SLinus Torvalds {
173626b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
17371da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17381da177e4SLinus Torvalds 
17391da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17401da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
174126b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
174226b15dadSJamal Hadi Salim 		if (!acqret)
174326b15dadSJamal Hadi Salim 			err = acqret;
17441da177e4SLinus Torvalds 	}
17451da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17461da177e4SLinus Torvalds 	return err;
17471da177e4SLinus Torvalds }
1748980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
17491da177e4SLinus Torvalds 
17505d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
17511da177e4SLinus Torvalds {
17521da177e4SLinus Torvalds 	int err = -EINVAL;
17531da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17541da177e4SLinus Torvalds 
17551da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17561da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
17571da177e4SLinus Torvalds 		if (km->new_mapping)
17581da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
17591da177e4SLinus Torvalds 		if (!err)
17601da177e4SLinus Torvalds 			break;
17611da177e4SLinus Torvalds 	}
17621da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17631da177e4SLinus Torvalds 	return err;
17641da177e4SLinus Torvalds }
17651da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
17661da177e4SLinus Torvalds 
17676c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
17681da177e4SLinus Torvalds {
176926b15dadSJamal Hadi Salim 	struct km_event c;
17701da177e4SLinus Torvalds 
1771bf08867fSHerbert Xu 	c.data.hard = hard;
17726c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1773f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
177426b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
17751da177e4SLinus Torvalds 
17761da177e4SLinus Torvalds 	if (hard)
17771da177e4SLinus Torvalds 		wake_up(&km_waitq);
17781da177e4SLinus Torvalds }
1779a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
17801da177e4SLinus Torvalds 
17812d60abc2SEric Dumazet #ifdef CONFIG_XFRM_MIGRATE
178280c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
178380c9abaaSShinta Sugimoto 	       struct xfrm_migrate *m, int num_migrate)
178480c9abaaSShinta Sugimoto {
178580c9abaaSShinta Sugimoto 	int err = -EINVAL;
178680c9abaaSShinta Sugimoto 	int ret;
178780c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
178880c9abaaSShinta Sugimoto 
178980c9abaaSShinta Sugimoto 	read_lock(&xfrm_km_lock);
179080c9abaaSShinta Sugimoto 	list_for_each_entry(km, &xfrm_km_list, list) {
179180c9abaaSShinta Sugimoto 		if (km->migrate) {
179280c9abaaSShinta Sugimoto 			ret = km->migrate(sel, dir, type, m, num_migrate);
179380c9abaaSShinta Sugimoto 			if (!ret)
179480c9abaaSShinta Sugimoto 				err = ret;
179580c9abaaSShinta Sugimoto 		}
179680c9abaaSShinta Sugimoto 	}
179780c9abaaSShinta Sugimoto 	read_unlock(&xfrm_km_lock);
179880c9abaaSShinta Sugimoto 	return err;
179980c9abaaSShinta Sugimoto }
180080c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
18012d60abc2SEric Dumazet #endif
180280c9abaaSShinta Sugimoto 
180397a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
180497a64b45SMasahide NAKAMURA {
180597a64b45SMasahide NAKAMURA 	int err = -EINVAL;
180697a64b45SMasahide NAKAMURA 	int ret;
180797a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
180897a64b45SMasahide NAKAMURA 
180997a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
181097a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
181197a64b45SMasahide NAKAMURA 		if (km->report) {
181297a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
181397a64b45SMasahide NAKAMURA 			if (!ret)
181497a64b45SMasahide NAKAMURA 				err = ret;
181597a64b45SMasahide NAKAMURA 		}
181697a64b45SMasahide NAKAMURA 	}
181797a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
181897a64b45SMasahide NAKAMURA 	return err;
181997a64b45SMasahide NAKAMURA }
182097a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
182197a64b45SMasahide NAKAMURA 
18221da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
18231da177e4SLinus Torvalds {
18241da177e4SLinus Torvalds 	int err;
18251da177e4SLinus Torvalds 	u8 *data;
18261da177e4SLinus Torvalds 	struct xfrm_mgr *km;
18271da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
18281da177e4SLinus Torvalds 
18291da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
18301da177e4SLinus Torvalds 		return -EMSGSIZE;
18311da177e4SLinus Torvalds 
18321da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
18331da177e4SLinus Torvalds 	if (!data)
18341da177e4SLinus Torvalds 		return -ENOMEM;
18351da177e4SLinus Torvalds 
18361da177e4SLinus Torvalds 	err = -EFAULT;
18371da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
18381da177e4SLinus Torvalds 		goto out;
18391da177e4SLinus Torvalds 
18401da177e4SLinus Torvalds 	err = -EINVAL;
18411da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
18421da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1843cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
18441da177e4SLinus Torvalds 					 optlen, &err);
18451da177e4SLinus Torvalds 		if (err >= 0)
18461da177e4SLinus Torvalds 			break;
18471da177e4SLinus Torvalds 	}
18481da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
18491da177e4SLinus Torvalds 
18501da177e4SLinus Torvalds 	if (err >= 0) {
18511da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
18521da177e4SLinus Torvalds 		xfrm_pol_put(pol);
18531da177e4SLinus Torvalds 		err = 0;
18541da177e4SLinus Torvalds 	}
18551da177e4SLinus Torvalds 
18561da177e4SLinus Torvalds out:
18571da177e4SLinus Torvalds 	kfree(data);
18581da177e4SLinus Torvalds 	return err;
18591da177e4SLinus Torvalds }
18601da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
18611da177e4SLinus Torvalds 
18621da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
18631da177e4SLinus Torvalds {
18641da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18651da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
18661da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18671da177e4SLinus Torvalds 	return 0;
18681da177e4SLinus Torvalds }
18691da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
18701da177e4SLinus Torvalds 
18711da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
18721da177e4SLinus Torvalds {
18731da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18741da177e4SLinus Torvalds 	list_del(&km->list);
18751da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18761da177e4SLinus Torvalds 	return 0;
18771da177e4SLinus Torvalds }
18781da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
18791da177e4SLinus Torvalds 
18801da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
18811da177e4SLinus Torvalds {
18821da177e4SLinus Torvalds 	int err = 0;
18831da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18841da177e4SLinus Torvalds 		return -EINVAL;
18851da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18861da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1887f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
18881da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
18891da177e4SLinus Torvalds 		err = -ENOBUFS;
1890edcd5821SDavid S. Miller 	else
18911da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1892f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
18931da177e4SLinus Torvalds 	return err;
18941da177e4SLinus Torvalds }
18951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
18981da177e4SLinus Torvalds {
18991da177e4SLinus Torvalds 	int err = 0;
19001da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
19011da177e4SLinus Torvalds 		return -EINVAL;
19021da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
19031da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1904f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
19051da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
19061da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
19071da177e4SLinus Torvalds 			err = -EINVAL;
1908edcd5821SDavid S. Miller 		else
19091da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
19101da177e4SLinus Torvalds 	}
1911f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
19121da177e4SLinus Torvalds 	return err;
19131da177e4SLinus Torvalds }
19141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
19151da177e4SLinus Torvalds 
191617c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
19171da177e4SLinus Torvalds {
19181da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
19191da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
19201da177e4SLinus Torvalds 		return NULL;
19211da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
19221da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1923546be240SHerbert Xu 	if (unlikely(!afinfo))
19241da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
19251da177e4SLinus Torvalds 	return afinfo;
19261da177e4SLinus Torvalds }
19271da177e4SLinus Torvalds 
192817c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
19299a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
19301da177e4SLinus Torvalds {
1931546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
19321da177e4SLinus Torvalds }
19331da177e4SLinus Torvalds 
19341da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
19351da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
19361da177e4SLinus Torvalds {
19371da177e4SLinus Torvalds 	if (x->tunnel) {
19381da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
19391da177e4SLinus Torvalds 
19401da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
19411da177e4SLinus Torvalds 			xfrm_state_delete(t);
19421da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
19431da177e4SLinus Torvalds 		xfrm_state_put(t);
19441da177e4SLinus Torvalds 		x->tunnel = NULL;
19451da177e4SLinus Torvalds 	}
19461da177e4SLinus Torvalds }
19471da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
19481da177e4SLinus Torvalds 
19491da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
19501da177e4SLinus Torvalds {
1951c5c25238SPatrick McHardy 	int res;
19521da177e4SLinus Torvalds 
19531da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
19541da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_VALID &&
1955c5c25238SPatrick McHardy 	    x->type && x->type->get_mtu)
1956c5c25238SPatrick McHardy 		res = x->type->get_mtu(x, mtu);
19571da177e4SLinus Torvalds 	else
195828121617SPatrick McHardy 		res = mtu - x->props.header_len;
19591da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
19601da177e4SLinus Torvalds 	return res;
19611da177e4SLinus Torvalds }
19621da177e4SLinus Torvalds 
196372cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
196472cb6962SHerbert Xu {
1965d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1966d094cd83SHerbert Xu 	int family = x->props.family;
196772cb6962SHerbert Xu 	int err;
196872cb6962SHerbert Xu 
1969d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1970d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1971d094cd83SHerbert Xu 	if (!afinfo)
1972d094cd83SHerbert Xu 		goto error;
1973d094cd83SHerbert Xu 
1974d094cd83SHerbert Xu 	err = 0;
1975d094cd83SHerbert Xu 	if (afinfo->init_flags)
1976d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1977d094cd83SHerbert Xu 
1978d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1979d094cd83SHerbert Xu 
1980d094cd83SHerbert Xu 	if (err)
1981d094cd83SHerbert Xu 		goto error;
1982d094cd83SHerbert Xu 
1983d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
198413996378SHerbert Xu 	x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
198513996378SHerbert Xu 	if (x->inner_mode == NULL)
198613996378SHerbert Xu 		goto error;
198713996378SHerbert Xu 
198813996378SHerbert Xu 	if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
198913996378SHerbert Xu 	    family != x->sel.family)
199013996378SHerbert Xu 		goto error;
199113996378SHerbert Xu 
1992d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
199372cb6962SHerbert Xu 	if (x->type == NULL)
199472cb6962SHerbert Xu 		goto error;
199572cb6962SHerbert Xu 
199672cb6962SHerbert Xu 	err = x->type->init_state(x);
199772cb6962SHerbert Xu 	if (err)
199872cb6962SHerbert Xu 		goto error;
199972cb6962SHerbert Xu 
200013996378SHerbert Xu 	x->outer_mode = xfrm_get_mode(x->props.mode, family);
200113996378SHerbert Xu 	if (x->outer_mode == NULL)
2002b59f45d0SHerbert Xu 		goto error;
2003b59f45d0SHerbert Xu 
200472cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
200572cb6962SHerbert Xu 
200672cb6962SHerbert Xu error:
200772cb6962SHerbert Xu 	return err;
200872cb6962SHerbert Xu }
200972cb6962SHerbert Xu 
201072cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds void __init xfrm_state_init(void)
20131da177e4SLinus Torvalds {
2014f034b5d4SDavid S. Miller 	unsigned int sz;
20151da177e4SLinus Torvalds 
2016f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
2017f034b5d4SDavid S. Miller 
201844e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
201944e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
202044e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
2021f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2022f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2023f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2024f034b5d4SDavid S. Miller 
2025c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
20261da177e4SLinus Torvalds }
20271da177e4SLinus Torvalds 
2028ab5f5e8bSJoy Latten #ifdef CONFIG_AUDITSYSCALL
2029cf35f43eSIlpo Järvinen static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2030ab5f5e8bSJoy Latten 				     struct audit_buffer *audit_buf)
2031ab5f5e8bSJoy Latten {
203268277accSPaul Moore 	struct xfrm_sec_ctx *ctx = x->security;
203368277accSPaul Moore 	u32 spi = ntohl(x->id.spi);
203468277accSPaul Moore 
203568277accSPaul Moore 	if (ctx)
2036ab5f5e8bSJoy Latten 		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
203768277accSPaul Moore 				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
2038ab5f5e8bSJoy Latten 
2039ab5f5e8bSJoy Latten 	switch(x->props.family) {
2040ab5f5e8bSJoy Latten 	case AF_INET:
204168277accSPaul Moore 		audit_log_format(audit_buf,
204268277accSPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2043ab5f5e8bSJoy Latten 				 NIPQUAD(x->props.saddr.a4),
2044ab5f5e8bSJoy Latten 				 NIPQUAD(x->id.daddr.a4));
2045ab5f5e8bSJoy Latten 		break;
2046ab5f5e8bSJoy Latten 	case AF_INET6:
2047ab5f5e8bSJoy Latten 		audit_log_format(audit_buf,
2048ab5f5e8bSJoy Latten 				 " src=" NIP6_FMT " dst=" NIP6_FMT,
204968277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->props.saddr.a6),
205068277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->id.daddr.a6));
2051ab5f5e8bSJoy Latten 		break;
2052ab5f5e8bSJoy Latten 	}
205368277accSPaul Moore 
205468277accSPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2055ab5f5e8bSJoy Latten }
2056ab5f5e8bSJoy Latten 
2057cf35f43eSIlpo Järvinen static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2058afeb14b4SPaul Moore 				      struct audit_buffer *audit_buf)
2059afeb14b4SPaul Moore {
2060afeb14b4SPaul Moore 	struct iphdr *iph4;
2061afeb14b4SPaul Moore 	struct ipv6hdr *iph6;
2062afeb14b4SPaul Moore 
2063afeb14b4SPaul Moore 	switch (family) {
2064afeb14b4SPaul Moore 	case AF_INET:
2065afeb14b4SPaul Moore 		iph4 = ip_hdr(skb);
2066afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2067afeb14b4SPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2068afeb14b4SPaul Moore 				 NIPQUAD(iph4->saddr),
2069afeb14b4SPaul Moore 				 NIPQUAD(iph4->daddr));
2070afeb14b4SPaul Moore 		break;
2071afeb14b4SPaul Moore 	case AF_INET6:
2072afeb14b4SPaul Moore 		iph6 = ipv6_hdr(skb);
2073afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2074afeb14b4SPaul Moore 				 " src=" NIP6_FMT " dst=" NIP6_FMT
2075afeb14b4SPaul Moore 				 " flowlbl=0x%x%x%x",
2076afeb14b4SPaul Moore 				 NIP6(iph6->saddr),
2077afeb14b4SPaul Moore 				 NIP6(iph6->daddr),
2078afeb14b4SPaul Moore 				 iph6->flow_lbl[0] & 0x0f,
2079afeb14b4SPaul Moore 				 iph6->flow_lbl[1],
2080afeb14b4SPaul Moore 				 iph6->flow_lbl[2]);
2081afeb14b4SPaul Moore 		break;
2082afeb14b4SPaul Moore 	}
2083afeb14b4SPaul Moore }
2084afeb14b4SPaul Moore 
208568277accSPaul Moore void xfrm_audit_state_add(struct xfrm_state *x, int result,
208668277accSPaul Moore 			  u32 auid, u32 secid)
2087ab5f5e8bSJoy Latten {
2088ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2089ab5f5e8bSJoy Latten 
2090afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-add");
2091ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2092ab5f5e8bSJoy Latten 		return;
2093afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2094afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2095afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2096ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2097ab5f5e8bSJoy Latten }
2098ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2099ab5f5e8bSJoy Latten 
210068277accSPaul Moore void xfrm_audit_state_delete(struct xfrm_state *x, int result,
210168277accSPaul Moore 			     u32 auid, u32 secid)
2102ab5f5e8bSJoy Latten {
2103ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2104ab5f5e8bSJoy Latten 
2105afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-delete");
2106ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2107ab5f5e8bSJoy Latten 		return;
2108afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2109afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2110afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2111ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2112ab5f5e8bSJoy Latten }
2113ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2114afeb14b4SPaul Moore 
2115afeb14b4SPaul Moore void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2116afeb14b4SPaul Moore 				      struct sk_buff *skb)
2117afeb14b4SPaul Moore {
2118afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2119afeb14b4SPaul Moore 	u32 spi;
2120afeb14b4SPaul Moore 
2121afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replay-overflow");
2122afeb14b4SPaul Moore 	if (audit_buf == NULL)
2123afeb14b4SPaul Moore 		return;
2124afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2125afeb14b4SPaul Moore 	/* don't record the sequence number because it's inherent in this kind
2126afeb14b4SPaul Moore 	 * of audit message */
2127afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2128afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2129afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2130afeb14b4SPaul Moore }
2131afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2132afeb14b4SPaul Moore 
2133afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
2134afeb14b4SPaul Moore 			     struct sk_buff *skb, __be32 net_seq)
2135afeb14b4SPaul Moore {
2136afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2137afeb14b4SPaul Moore 	u32 spi;
2138afeb14b4SPaul Moore 
2139afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replayed-pkt");
2140afeb14b4SPaul Moore 	if (audit_buf == NULL)
2141afeb14b4SPaul Moore 		return;
2142afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2143afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2144afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2145afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2146afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2147afeb14b4SPaul Moore }
2148afeb14b4SPaul Moore 
2149afeb14b4SPaul Moore void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2150afeb14b4SPaul Moore {
2151afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2152afeb14b4SPaul Moore 
2153afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2154afeb14b4SPaul Moore 	if (audit_buf == NULL)
2155afeb14b4SPaul Moore 		return;
2156afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2157afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2158afeb14b4SPaul Moore }
2159afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2160afeb14b4SPaul Moore 
2161afeb14b4SPaul Moore void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2162afeb14b4SPaul Moore 			       __be32 net_spi, __be32 net_seq)
2163afeb14b4SPaul Moore {
2164afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2165afeb14b4SPaul Moore 	u32 spi;
2166afeb14b4SPaul Moore 
2167afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2168afeb14b4SPaul Moore 	if (audit_buf == NULL)
2169afeb14b4SPaul Moore 		return;
2170afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2171afeb14b4SPaul Moore 	spi = ntohl(net_spi);
2172afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2173afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2174afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2175afeb14b4SPaul Moore }
2176afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2177afeb14b4SPaul Moore 
2178afeb14b4SPaul Moore void xfrm_audit_state_icvfail(struct xfrm_state *x,
2179afeb14b4SPaul Moore 			      struct sk_buff *skb, u8 proto)
2180afeb14b4SPaul Moore {
2181afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2182afeb14b4SPaul Moore 	__be32 net_spi;
2183afeb14b4SPaul Moore 	__be32 net_seq;
2184afeb14b4SPaul Moore 
2185afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-icv-failure");
2186afeb14b4SPaul Moore 	if (audit_buf == NULL)
2187afeb14b4SPaul Moore 		return;
2188afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2189afeb14b4SPaul Moore 	if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2190afeb14b4SPaul Moore 		u32 spi = ntohl(net_spi);
2191afeb14b4SPaul Moore 		audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2192afeb14b4SPaul Moore 				 spi, spi, ntohl(net_seq));
2193afeb14b4SPaul Moore 	}
2194afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2195afeb14b4SPaul Moore }
2196afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
2197ab5f5e8bSJoy Latten #endif /* CONFIG_AUDITSYSCALL */
2198