xref: /openbmc/linux/net/xfrm/xfrm_state.c (revision cf35f43e6e41b160d8dedd80a127210fd3be9ada)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * xfrm_state.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Changes:
51da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI
61da177e4SLinus Torvalds  * 	Kazunori MIYAZAWA @USAGI
71da177e4SLinus Torvalds  * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
81da177e4SLinus Torvalds  * 		IPv6 support
91da177e4SLinus Torvalds  * 	YOSHIFUJI Hideaki @USAGI
101da177e4SLinus Torvalds  * 		Split up af-specific functions
111da177e4SLinus Torvalds  *	Derek Atkins <derek@ihtfp.com>
121da177e4SLinus Torvalds  *		Add UDP Encapsulation
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/workqueue.h>
171da177e4SLinus Torvalds #include <net/xfrm.h>
181da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
191da177e4SLinus Torvalds #include <linux/ipsec.h>
201da177e4SLinus Torvalds #include <linux/module.h>
21f034b5d4SDavid S. Miller #include <linux/cache.h>
2268277accSPaul Moore #include <linux/audit.h>
23b5890d8bSJesper Juhl #include <asm/uaccess.h>
241da177e4SLinus Torvalds 
2544e36b42SDavid S. Miller #include "xfrm_hash.h"
2644e36b42SDavid S. Miller 
27ee857a7dSDavid S. Miller struct sock *xfrm_nl;
28ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl);
29ee857a7dSDavid S. Miller 
3001e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
31a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
32a70fcb0bSDavid S. Miller 
3301e67d08SDavid S. Miller u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
34a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
35a70fcb0bSDavid S. Miller 
3601e67d08SDavid S. Miller u32 sysctl_xfrm_acq_expires __read_mostly = 30;
3701e67d08SDavid S. Miller 
381da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables:
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
41a624c108SDavid S. Miller    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
421da177e4SLinus Torvalds       destination/tunnel endpoint. (output)
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock);
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint
481da177e4SLinus Torvalds  * of tunnel or destination of transport mode) allowed by selector.
491da177e4SLinus Torvalds  *
501da177e4SLinus Torvalds  * Main use is finding SA after policy selected tunnel or transport mode.
511da177e4SLinus Torvalds  * Also, it can be used by ah/esp icmp error handler to find offending SA.
521da177e4SLinus Torvalds  */
53f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly;
54f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly;
55f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly;
56f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly;
57f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
58f034b5d4SDavid S. Miller static unsigned int xfrm_state_num;
599d4a706dSDavid S. Miller static unsigned int xfrm_state_genid;
601da177e4SLinus Torvalds 
6117c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
6217c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
6317c2a42aSHerbert Xu 
64afeb14b4SPaul Moore #ifdef CONFIG_AUDITSYSCALL
65afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
66afeb14b4SPaul Moore 				    struct sk_buff *skb, __be32 net_seq);
67afeb14b4SPaul Moore #else
68afeb14b4SPaul Moore #define xfrm_audit_state_replay(x, s, sq)	do { ; } while (0)
69afeb14b4SPaul Moore #endif /* CONFIG_AUDITSYSCALL */
70afeb14b4SPaul Moore 
71c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
72c1969f29SDavid S. Miller 					 xfrm_address_t *saddr,
73c1969f29SDavid S. Miller 					 u32 reqid,
74a624c108SDavid S. Miller 					 unsigned short family)
75a624c108SDavid S. Miller {
76c1969f29SDavid S. Miller 	return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
77a624c108SDavid S. Miller }
78a624c108SDavid S. Miller 
79667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
80667bbcb6SMasahide NAKAMURA 					 xfrm_address_t *saddr,
8144e36b42SDavid S. Miller 					 unsigned short family)
82f034b5d4SDavid S. Miller {
83667bbcb6SMasahide NAKAMURA 	return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
84f034b5d4SDavid S. Miller }
85f034b5d4SDavid S. Miller 
862575b654SDavid S. Miller static inline unsigned int
878122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
88f034b5d4SDavid S. Miller {
89c1969f29SDavid S. Miller 	return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
90f034b5d4SDavid S. Miller }
91f034b5d4SDavid S. Miller 
92f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list,
93f034b5d4SDavid S. Miller 			       struct hlist_head *ndsttable,
94f034b5d4SDavid S. Miller 			       struct hlist_head *nsrctable,
95f034b5d4SDavid S. Miller 			       struct hlist_head *nspitable,
96f034b5d4SDavid S. Miller 			       unsigned int nhashmask)
97f034b5d4SDavid S. Miller {
98f034b5d4SDavid S. Miller 	struct hlist_node *entry, *tmp;
99f034b5d4SDavid S. Miller 	struct xfrm_state *x;
100f034b5d4SDavid S. Miller 
101f034b5d4SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, list, bydst) {
102f034b5d4SDavid S. Miller 		unsigned int h;
103f034b5d4SDavid S. Miller 
104c1969f29SDavid S. Miller 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
105c1969f29SDavid S. Miller 				    x->props.reqid, x->props.family,
106c1969f29SDavid S. Miller 				    nhashmask);
107f034b5d4SDavid S. Miller 		hlist_add_head(&x->bydst, ndsttable+h);
108f034b5d4SDavid S. Miller 
109667bbcb6SMasahide NAKAMURA 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
110667bbcb6SMasahide NAKAMURA 				    x->props.family,
111f034b5d4SDavid S. Miller 				    nhashmask);
112f034b5d4SDavid S. Miller 		hlist_add_head(&x->bysrc, nsrctable+h);
113f034b5d4SDavid S. Miller 
1147b4dc360SMasahide NAKAMURA 		if (x->id.spi) {
1157b4dc360SMasahide NAKAMURA 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
1167b4dc360SMasahide NAKAMURA 					    x->id.proto, x->props.family,
1177b4dc360SMasahide NAKAMURA 					    nhashmask);
118f034b5d4SDavid S. Miller 			hlist_add_head(&x->byspi, nspitable+h);
119f034b5d4SDavid S. Miller 		}
120f034b5d4SDavid S. Miller 	}
1217b4dc360SMasahide NAKAMURA }
122f034b5d4SDavid S. Miller 
123f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void)
124f034b5d4SDavid S. Miller {
125f034b5d4SDavid S. Miller 	return ((xfrm_state_hmask + 1) << 1) *
126f034b5d4SDavid S. Miller 		sizeof(struct hlist_head);
127f034b5d4SDavid S. Miller }
128f034b5d4SDavid S. Miller 
129f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex);
130f034b5d4SDavid S. Miller 
131c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused)
132f034b5d4SDavid S. Miller {
133f034b5d4SDavid S. Miller 	struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
134f034b5d4SDavid S. Miller 	unsigned long nsize, osize;
135f034b5d4SDavid S. Miller 	unsigned int nhashmask, ohashmask;
136f034b5d4SDavid S. Miller 	int i;
137f034b5d4SDavid S. Miller 
138f034b5d4SDavid S. Miller 	mutex_lock(&hash_resize_mutex);
139f034b5d4SDavid S. Miller 
140f034b5d4SDavid S. Miller 	nsize = xfrm_hash_new_size();
14144e36b42SDavid S. Miller 	ndst = xfrm_hash_alloc(nsize);
142f034b5d4SDavid S. Miller 	if (!ndst)
143f034b5d4SDavid S. Miller 		goto out_unlock;
14444e36b42SDavid S. Miller 	nsrc = xfrm_hash_alloc(nsize);
145f034b5d4SDavid S. Miller 	if (!nsrc) {
14644e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
147f034b5d4SDavid S. Miller 		goto out_unlock;
148f034b5d4SDavid S. Miller 	}
14944e36b42SDavid S. Miller 	nspi = xfrm_hash_alloc(nsize);
150f034b5d4SDavid S. Miller 	if (!nspi) {
15144e36b42SDavid S. Miller 		xfrm_hash_free(ndst, nsize);
15244e36b42SDavid S. Miller 		xfrm_hash_free(nsrc, nsize);
153f034b5d4SDavid S. Miller 		goto out_unlock;
154f034b5d4SDavid S. Miller 	}
155f034b5d4SDavid S. Miller 
156f034b5d4SDavid S. Miller 	spin_lock_bh(&xfrm_state_lock);
157f034b5d4SDavid S. Miller 
158f034b5d4SDavid S. Miller 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
159f034b5d4SDavid S. Miller 	for (i = xfrm_state_hmask; i >= 0; i--)
160f034b5d4SDavid S. Miller 		xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
161f034b5d4SDavid S. Miller 				   nhashmask);
162f034b5d4SDavid S. Miller 
163f034b5d4SDavid S. Miller 	odst = xfrm_state_bydst;
164f034b5d4SDavid S. Miller 	osrc = xfrm_state_bysrc;
165f034b5d4SDavid S. Miller 	ospi = xfrm_state_byspi;
166f034b5d4SDavid S. Miller 	ohashmask = xfrm_state_hmask;
167f034b5d4SDavid S. Miller 
168f034b5d4SDavid S. Miller 	xfrm_state_bydst = ndst;
169f034b5d4SDavid S. Miller 	xfrm_state_bysrc = nsrc;
170f034b5d4SDavid S. Miller 	xfrm_state_byspi = nspi;
171f034b5d4SDavid S. Miller 	xfrm_state_hmask = nhashmask;
172f034b5d4SDavid S. Miller 
173f034b5d4SDavid S. Miller 	spin_unlock_bh(&xfrm_state_lock);
174f034b5d4SDavid S. Miller 
175f034b5d4SDavid S. Miller 	osize = (ohashmask + 1) * sizeof(struct hlist_head);
17644e36b42SDavid S. Miller 	xfrm_hash_free(odst, osize);
17744e36b42SDavid S. Miller 	xfrm_hash_free(osrc, osize);
17844e36b42SDavid S. Miller 	xfrm_hash_free(ospi, osize);
179f034b5d4SDavid S. Miller 
180f034b5d4SDavid S. Miller out_unlock:
181f034b5d4SDavid S. Miller 	mutex_unlock(&hash_resize_mutex);
182f034b5d4SDavid S. Miller }
183f034b5d4SDavid S. Miller 
184c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
185f034b5d4SDavid S. Miller 
1861da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq);
1871da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
1901da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work;
1938f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list);
1941da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock);
1951da177e4SLinus Torvalds 
19653bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x);
1971da177e4SLinus Torvalds 
198980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
19953bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
2001da177e4SLinus Torvalds 
201aa5d62ccSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
202aa5d62ccSHerbert Xu {
203aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
204aa5d62ccSHerbert Xu 	if (unlikely(family >= NPROTO))
205aa5d62ccSHerbert Xu 		return NULL;
206aa5d62ccSHerbert Xu 	write_lock_bh(&xfrm_state_afinfo_lock);
207aa5d62ccSHerbert Xu 	afinfo = xfrm_state_afinfo[family];
208aa5d62ccSHerbert Xu 	if (unlikely(!afinfo))
209aa5d62ccSHerbert Xu 		write_unlock_bh(&xfrm_state_afinfo_lock);
210aa5d62ccSHerbert Xu 	return afinfo;
211aa5d62ccSHerbert Xu }
212aa5d62ccSHerbert Xu 
213aa5d62ccSHerbert Xu static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
2149a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
215aa5d62ccSHerbert Xu {
216aa5d62ccSHerbert Xu 	write_unlock_bh(&xfrm_state_afinfo_lock);
217aa5d62ccSHerbert Xu }
218aa5d62ccSHerbert Xu 
219aa5d62ccSHerbert Xu int xfrm_register_type(struct xfrm_type *type, unsigned short family)
220aa5d62ccSHerbert Xu {
221aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
222aa5d62ccSHerbert Xu 	struct xfrm_type **typemap;
223aa5d62ccSHerbert Xu 	int err = 0;
224aa5d62ccSHerbert Xu 
225aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
226aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
227aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
228aa5d62ccSHerbert Xu 
229aa5d62ccSHerbert Xu 	if (likely(typemap[type->proto] == NULL))
230aa5d62ccSHerbert Xu 		typemap[type->proto] = type;
231aa5d62ccSHerbert Xu 	else
232aa5d62ccSHerbert Xu 		err = -EEXIST;
233aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
234aa5d62ccSHerbert Xu 	return err;
235aa5d62ccSHerbert Xu }
236aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_type);
237aa5d62ccSHerbert Xu 
238aa5d62ccSHerbert Xu int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
239aa5d62ccSHerbert Xu {
240aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
241aa5d62ccSHerbert Xu 	struct xfrm_type **typemap;
242aa5d62ccSHerbert Xu 	int err = 0;
243aa5d62ccSHerbert Xu 
244aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
245aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
246aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
247aa5d62ccSHerbert Xu 
248aa5d62ccSHerbert Xu 	if (unlikely(typemap[type->proto] != type))
249aa5d62ccSHerbert Xu 		err = -ENOENT;
250aa5d62ccSHerbert Xu 	else
251aa5d62ccSHerbert Xu 		typemap[type->proto] = NULL;
252aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
253aa5d62ccSHerbert Xu 	return err;
254aa5d62ccSHerbert Xu }
255aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_type);
256aa5d62ccSHerbert Xu 
257aa5d62ccSHerbert Xu static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
258aa5d62ccSHerbert Xu {
259aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
260aa5d62ccSHerbert Xu 	struct xfrm_type **typemap;
261aa5d62ccSHerbert Xu 	struct xfrm_type *type;
262aa5d62ccSHerbert Xu 	int modload_attempted = 0;
263aa5d62ccSHerbert Xu 
264aa5d62ccSHerbert Xu retry:
265aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
266aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
267aa5d62ccSHerbert Xu 		return NULL;
268aa5d62ccSHerbert Xu 	typemap = afinfo->type_map;
269aa5d62ccSHerbert Xu 
270aa5d62ccSHerbert Xu 	type = typemap[proto];
271aa5d62ccSHerbert Xu 	if (unlikely(type && !try_module_get(type->owner)))
272aa5d62ccSHerbert Xu 		type = NULL;
273aa5d62ccSHerbert Xu 	if (!type && !modload_attempted) {
274aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
275aa5d62ccSHerbert Xu 		request_module("xfrm-type-%d-%d", family, proto);
276aa5d62ccSHerbert Xu 		modload_attempted = 1;
277aa5d62ccSHerbert Xu 		goto retry;
278aa5d62ccSHerbert Xu 	}
279aa5d62ccSHerbert Xu 
280aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
281aa5d62ccSHerbert Xu 	return type;
282aa5d62ccSHerbert Xu }
283aa5d62ccSHerbert Xu 
284aa5d62ccSHerbert Xu static void xfrm_put_type(struct xfrm_type *type)
285aa5d62ccSHerbert Xu {
286aa5d62ccSHerbert Xu 	module_put(type->owner);
287aa5d62ccSHerbert Xu }
288aa5d62ccSHerbert Xu 
289aa5d62ccSHerbert Xu int xfrm_register_mode(struct xfrm_mode *mode, int family)
290aa5d62ccSHerbert Xu {
291aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
292aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
293aa5d62ccSHerbert Xu 	int err;
294aa5d62ccSHerbert Xu 
295aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
296aa5d62ccSHerbert Xu 		return -EINVAL;
297aa5d62ccSHerbert Xu 
298aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
299aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
300aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
301aa5d62ccSHerbert Xu 
302aa5d62ccSHerbert Xu 	err = -EEXIST;
303aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
30417c2a42aSHerbert Xu 	if (modemap[mode->encap])
30517c2a42aSHerbert Xu 		goto out;
30617c2a42aSHerbert Xu 
30717c2a42aSHerbert Xu 	err = -ENOENT;
30817c2a42aSHerbert Xu 	if (!try_module_get(afinfo->owner))
30917c2a42aSHerbert Xu 		goto out;
31017c2a42aSHerbert Xu 
31117c2a42aSHerbert Xu 	mode->afinfo = afinfo;
312aa5d62ccSHerbert Xu 	modemap[mode->encap] = mode;
313aa5d62ccSHerbert Xu 	err = 0;
314aa5d62ccSHerbert Xu 
31517c2a42aSHerbert Xu out:
316aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
317aa5d62ccSHerbert Xu 	return err;
318aa5d62ccSHerbert Xu }
319aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_register_mode);
320aa5d62ccSHerbert Xu 
321aa5d62ccSHerbert Xu int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
322aa5d62ccSHerbert Xu {
323aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
324aa5d62ccSHerbert Xu 	struct xfrm_mode **modemap;
325aa5d62ccSHerbert Xu 	int err;
326aa5d62ccSHerbert Xu 
327aa5d62ccSHerbert Xu 	if (unlikely(mode->encap >= XFRM_MODE_MAX))
328aa5d62ccSHerbert Xu 		return -EINVAL;
329aa5d62ccSHerbert Xu 
330aa5d62ccSHerbert Xu 	afinfo = xfrm_state_lock_afinfo(family);
331aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
332aa5d62ccSHerbert Xu 		return -EAFNOSUPPORT;
333aa5d62ccSHerbert Xu 
334aa5d62ccSHerbert Xu 	err = -ENOENT;
335aa5d62ccSHerbert Xu 	modemap = afinfo->mode_map;
336aa5d62ccSHerbert Xu 	if (likely(modemap[mode->encap] == mode)) {
337aa5d62ccSHerbert Xu 		modemap[mode->encap] = NULL;
33817c2a42aSHerbert Xu 		module_put(mode->afinfo->owner);
339aa5d62ccSHerbert Xu 		err = 0;
340aa5d62ccSHerbert Xu 	}
341aa5d62ccSHerbert Xu 
342aa5d62ccSHerbert Xu 	xfrm_state_unlock_afinfo(afinfo);
343aa5d62ccSHerbert Xu 	return err;
344aa5d62ccSHerbert Xu }
345aa5d62ccSHerbert Xu EXPORT_SYMBOL(xfrm_unregister_mode);
346aa5d62ccSHerbert Xu 
347aa5d62ccSHerbert Xu static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
348aa5d62ccSHerbert Xu {
349aa5d62ccSHerbert Xu 	struct xfrm_state_afinfo *afinfo;
350aa5d62ccSHerbert Xu 	struct xfrm_mode *mode;
351aa5d62ccSHerbert Xu 	int modload_attempted = 0;
352aa5d62ccSHerbert Xu 
353aa5d62ccSHerbert Xu 	if (unlikely(encap >= XFRM_MODE_MAX))
354aa5d62ccSHerbert Xu 		return NULL;
355aa5d62ccSHerbert Xu 
356aa5d62ccSHerbert Xu retry:
357aa5d62ccSHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
358aa5d62ccSHerbert Xu 	if (unlikely(afinfo == NULL))
359aa5d62ccSHerbert Xu 		return NULL;
360aa5d62ccSHerbert Xu 
361aa5d62ccSHerbert Xu 	mode = afinfo->mode_map[encap];
362aa5d62ccSHerbert Xu 	if (unlikely(mode && !try_module_get(mode->owner)))
363aa5d62ccSHerbert Xu 		mode = NULL;
364aa5d62ccSHerbert Xu 	if (!mode && !modload_attempted) {
365aa5d62ccSHerbert Xu 		xfrm_state_put_afinfo(afinfo);
366aa5d62ccSHerbert Xu 		request_module("xfrm-mode-%d-%d", family, encap);
367aa5d62ccSHerbert Xu 		modload_attempted = 1;
368aa5d62ccSHerbert Xu 		goto retry;
369aa5d62ccSHerbert Xu 	}
370aa5d62ccSHerbert Xu 
371aa5d62ccSHerbert Xu 	xfrm_state_put_afinfo(afinfo);
372aa5d62ccSHerbert Xu 	return mode;
373aa5d62ccSHerbert Xu }
374aa5d62ccSHerbert Xu 
375aa5d62ccSHerbert Xu static void xfrm_put_mode(struct xfrm_mode *mode)
376aa5d62ccSHerbert Xu {
377aa5d62ccSHerbert Xu 	module_put(mode->owner);
378aa5d62ccSHerbert Xu }
379aa5d62ccSHerbert Xu 
3801da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x)
3811da177e4SLinus Torvalds {
382a47f0ce0SDavid S. Miller 	del_timer_sync(&x->timer);
383a47f0ce0SDavid S. Miller 	del_timer_sync(&x->rtimer);
3841da177e4SLinus Torvalds 	kfree(x->aalg);
3851da177e4SLinus Torvalds 	kfree(x->ealg);
3861da177e4SLinus Torvalds 	kfree(x->calg);
3871da177e4SLinus Torvalds 	kfree(x->encap);
388060f02a3SNoriaki TAKAMIYA 	kfree(x->coaddr);
38913996378SHerbert Xu 	if (x->inner_mode)
39013996378SHerbert Xu 		xfrm_put_mode(x->inner_mode);
39113996378SHerbert Xu 	if (x->outer_mode)
39213996378SHerbert Xu 		xfrm_put_mode(x->outer_mode);
3931da177e4SLinus Torvalds 	if (x->type) {
3941da177e4SLinus Torvalds 		x->type->destructor(x);
3951da177e4SLinus Torvalds 		xfrm_put_type(x->type);
3961da177e4SLinus Torvalds 	}
397df71837dSTrent Jaeger 	security_xfrm_state_free(x);
3981da177e4SLinus Torvalds 	kfree(x);
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
401c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data)
4021da177e4SLinus Torvalds {
4031da177e4SLinus Torvalds 	struct xfrm_state *x;
4048f126e37SDavid S. Miller 	struct hlist_node *entry, *tmp;
4058f126e37SDavid S. Miller 	struct hlist_head gc_list;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
4088f126e37SDavid S. Miller 	gc_list.first = xfrm_state_gc_list.first;
4098f126e37SDavid S. Miller 	INIT_HLIST_HEAD(&xfrm_state_gc_list);
4101da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
4111da177e4SLinus Torvalds 
4128f126e37SDavid S. Miller 	hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
4131da177e4SLinus Torvalds 		xfrm_state_gc_destroy(x);
4148f126e37SDavid S. Miller 
4151da177e4SLinus Torvalds 	wake_up(&km_waitq);
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds 	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
4211da177e4SLinus Torvalds 		return MAX_SCHEDULE_TIMEOUT-1;
4221da177e4SLinus Torvalds 	else
4231da177e4SLinus Torvalds 		return secs*HZ;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	struct xfrm_state *x = (struct xfrm_state*)data;
4299d729f72SJames Morris 	unsigned long now = get_seconds();
4301da177e4SLinus Torvalds 	long next = LONG_MAX;
4311da177e4SLinus Torvalds 	int warn = 0;
432161a09e7SJoy Latten 	int err = 0;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	spin_lock(&x->lock);
4351da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_DEAD)
4361da177e4SLinus Torvalds 		goto out;
4371da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_EXPIRED)
4381da177e4SLinus Torvalds 		goto expired;
4391da177e4SLinus Torvalds 	if (x->lft.hard_add_expires_seconds) {
4401da177e4SLinus Torvalds 		long tmo = x->lft.hard_add_expires_seconds +
4411da177e4SLinus Torvalds 			x->curlft.add_time - now;
4421da177e4SLinus Torvalds 		if (tmo <= 0)
4431da177e4SLinus Torvalds 			goto expired;
4441da177e4SLinus Torvalds 		if (tmo < next)
4451da177e4SLinus Torvalds 			next = tmo;
4461da177e4SLinus Torvalds 	}
4471da177e4SLinus Torvalds 	if (x->lft.hard_use_expires_seconds) {
4481da177e4SLinus Torvalds 		long tmo = x->lft.hard_use_expires_seconds +
4491da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4501da177e4SLinus Torvalds 		if (tmo <= 0)
4511da177e4SLinus Torvalds 			goto expired;
4521da177e4SLinus Torvalds 		if (tmo < next)
4531da177e4SLinus Torvalds 			next = tmo;
4541da177e4SLinus Torvalds 	}
4551da177e4SLinus Torvalds 	if (x->km.dying)
4561da177e4SLinus Torvalds 		goto resched;
4571da177e4SLinus Torvalds 	if (x->lft.soft_add_expires_seconds) {
4581da177e4SLinus Torvalds 		long tmo = x->lft.soft_add_expires_seconds +
4591da177e4SLinus Torvalds 			x->curlft.add_time - now;
4601da177e4SLinus Torvalds 		if (tmo <= 0)
4611da177e4SLinus Torvalds 			warn = 1;
4621da177e4SLinus Torvalds 		else if (tmo < next)
4631da177e4SLinus Torvalds 			next = tmo;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 	if (x->lft.soft_use_expires_seconds) {
4661da177e4SLinus Torvalds 		long tmo = x->lft.soft_use_expires_seconds +
4671da177e4SLinus Torvalds 			(x->curlft.use_time ? : now) - now;
4681da177e4SLinus Torvalds 		if (tmo <= 0)
4691da177e4SLinus Torvalds 			warn = 1;
4701da177e4SLinus Torvalds 		else if (tmo < next)
4711da177e4SLinus Torvalds 			next = tmo;
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 
4744666faabSHerbert Xu 	x->km.dying = warn;
4751da177e4SLinus Torvalds 	if (warn)
47653bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
4771da177e4SLinus Torvalds resched:
478a47f0ce0SDavid S. Miller 	if (next != LONG_MAX)
479a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies + make_jiffies(next));
480a47f0ce0SDavid S. Miller 
4811da177e4SLinus Torvalds 	goto out;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds expired:
4841da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
4851da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_EXPIRED;
4861da177e4SLinus Torvalds 		wake_up(&km_waitq);
4871da177e4SLinus Torvalds 		next = 2;
4881da177e4SLinus Torvalds 		goto resched;
4891da177e4SLinus Torvalds 	}
490161a09e7SJoy Latten 
491161a09e7SJoy Latten 	err = __xfrm_state_delete(x);
492161a09e7SJoy Latten 	if (!err && x->id.spi)
49353bc6b4dSJamal Hadi Salim 		km_state_expired(x, 1, 0);
4941da177e4SLinus Torvalds 
495ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
496ab5f5e8bSJoy Latten 				audit_get_loginuid(current->audit_context), 0);
497161a09e7SJoy Latten 
4981da177e4SLinus Torvalds out:
4991da177e4SLinus Torvalds 	spin_unlock(&x->lock);
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds 
5020ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data);
5030ac84752SDavid S. Miller 
5041da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds 	struct xfrm_state *x;
5071da177e4SLinus Torvalds 
5080da974f4SPanagiotis Issaris 	x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	if (x) {
5111da177e4SLinus Torvalds 		atomic_set(&x->refcnt, 1);
5121da177e4SLinus Torvalds 		atomic_set(&x->tunnel_users, 0);
5138f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bydst);
5148f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->bysrc);
5158f126e37SDavid S. Miller 		INIT_HLIST_NODE(&x->byspi);
516b24b8a24SPavel Emelyanov 		setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
517b24b8a24SPavel Emelyanov 		setup_timer(&x->rtimer, xfrm_replay_timer_handler,
518b24b8a24SPavel Emelyanov 				(unsigned long)x);
5199d729f72SJames Morris 		x->curlft.add_time = get_seconds();
5201da177e4SLinus Torvalds 		x->lft.soft_byte_limit = XFRM_INF;
5211da177e4SLinus Torvalds 		x->lft.soft_packet_limit = XFRM_INF;
5221da177e4SLinus Torvalds 		x->lft.hard_byte_limit = XFRM_INF;
5231da177e4SLinus Torvalds 		x->lft.hard_packet_limit = XFRM_INF;
524f8cd5488SJamal Hadi Salim 		x->replay_maxage = 0;
525f8cd5488SJamal Hadi Salim 		x->replay_maxdiff = 0;
5261da177e4SLinus Torvalds 		spin_lock_init(&x->lock);
5271da177e4SLinus Torvalds 	}
5281da177e4SLinus Torvalds 	return x;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc);
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x)
5331da177e4SLinus Torvalds {
5341da177e4SLinus Torvalds 	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_gc_lock);
5378f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, &xfrm_state_gc_list);
5381da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_gc_lock);
5391da177e4SLinus Torvalds 	schedule_work(&xfrm_state_gc_work);
5401da177e4SLinus Torvalds }
5411da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy);
5421da177e4SLinus Torvalds 
54353bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x)
5441da177e4SLinus Torvalds {
54526b15dadSJamal Hadi Salim 	int err = -ESRCH;
54626b15dadSJamal Hadi Salim 
5471da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_DEAD) {
5481da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
5491da177e4SLinus Torvalds 		spin_lock(&xfrm_state_lock);
5508f126e37SDavid S. Miller 		hlist_del(&x->bydst);
5518f126e37SDavid S. Miller 		hlist_del(&x->bysrc);
552a47f0ce0SDavid S. Miller 		if (x->id.spi)
5538f126e37SDavid S. Miller 			hlist_del(&x->byspi);
554f034b5d4SDavid S. Miller 		xfrm_state_num--;
5551da177e4SLinus Torvalds 		spin_unlock(&xfrm_state_lock);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 		/* All xfrm_state objects are created by xfrm_state_alloc.
5581da177e4SLinus Torvalds 		 * The xfrm_state_alloc call gives a reference, and that
5591da177e4SLinus Torvalds 		 * is what we are dropping here.
5601da177e4SLinus Torvalds 		 */
5615dba4797SPatrick McHardy 		xfrm_state_put(x);
56226b15dadSJamal Hadi Salim 		err = 0;
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds 
56526b15dadSJamal Hadi Salim 	return err;
56626b15dadSJamal Hadi Salim }
56753bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete);
56826b15dadSJamal Hadi Salim 
56926b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x)
5701da177e4SLinus Torvalds {
57126b15dadSJamal Hadi Salim 	int err;
57226b15dadSJamal Hadi Salim 
5731da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
57426b15dadSJamal Hadi Salim 	err = __xfrm_state_delete(x);
5751da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
57626b15dadSJamal Hadi Salim 
57726b15dadSJamal Hadi Salim 	return err;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete);
5801da177e4SLinus Torvalds 
5814aa2e62cSJoy Latten #ifdef CONFIG_SECURITY_NETWORK_XFRM
5824aa2e62cSJoy Latten static inline int
5834aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
5841da177e4SLinus Torvalds {
5854aa2e62cSJoy Latten 	int i, err = 0;
5864aa2e62cSJoy Latten 
5874aa2e62cSJoy Latten 	for (i = 0; i <= xfrm_state_hmask; i++) {
5884aa2e62cSJoy Latten 		struct hlist_node *entry;
5894aa2e62cSJoy Latten 		struct xfrm_state *x;
5904aa2e62cSJoy Latten 
5914aa2e62cSJoy Latten 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
5924aa2e62cSJoy Latten 			if (xfrm_id_proto_match(x->id.proto, proto) &&
5934aa2e62cSJoy Latten 			   (err = security_xfrm_state_delete(x)) != 0) {
594ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, 0,
595ab5f5e8bSJoy Latten 							audit_info->loginuid,
596ab5f5e8bSJoy Latten 							audit_info->secid);
5974aa2e62cSJoy Latten 				return err;
5984aa2e62cSJoy Latten 			}
5994aa2e62cSJoy Latten 		}
6004aa2e62cSJoy Latten 	}
6014aa2e62cSJoy Latten 
6024aa2e62cSJoy Latten 	return err;
6034aa2e62cSJoy Latten }
6044aa2e62cSJoy Latten #else
6054aa2e62cSJoy Latten static inline int
6064aa2e62cSJoy Latten xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
6074aa2e62cSJoy Latten {
6084aa2e62cSJoy Latten 	return 0;
6094aa2e62cSJoy Latten }
6104aa2e62cSJoy Latten #endif
6114aa2e62cSJoy Latten 
6124aa2e62cSJoy Latten int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
6134aa2e62cSJoy Latten {
6144aa2e62cSJoy Latten 	int i, err = 0;
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
6174aa2e62cSJoy Latten 	err = xfrm_state_flush_secctx_check(proto, audit_info);
6184aa2e62cSJoy Latten 	if (err)
6194aa2e62cSJoy Latten 		goto out;
6204aa2e62cSJoy Latten 
621a9917c06SMasahide NAKAMURA 	for (i = 0; i <= xfrm_state_hmask; i++) {
6228f126e37SDavid S. Miller 		struct hlist_node *entry;
6238f126e37SDavid S. Miller 		struct xfrm_state *x;
6241da177e4SLinus Torvalds restart:
6258f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
6261da177e4SLinus Torvalds 			if (!xfrm_state_kern(x) &&
6275794708fSMasahide NAKAMURA 			    xfrm_id_proto_match(x->id.proto, proto)) {
6281da177e4SLinus Torvalds 				xfrm_state_hold(x);
6291da177e4SLinus Torvalds 				spin_unlock_bh(&xfrm_state_lock);
6301da177e4SLinus Torvalds 
631161a09e7SJoy Latten 				err = xfrm_state_delete(x);
632ab5f5e8bSJoy Latten 				xfrm_audit_state_delete(x, err ? 0 : 1,
633ab5f5e8bSJoy Latten 							audit_info->loginuid,
634ab5f5e8bSJoy Latten 							audit_info->secid);
6351da177e4SLinus Torvalds 				xfrm_state_put(x);
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 				spin_lock_bh(&xfrm_state_lock);
6381da177e4SLinus Torvalds 				goto restart;
6391da177e4SLinus Torvalds 			}
6401da177e4SLinus Torvalds 		}
6411da177e4SLinus Torvalds 	}
6424aa2e62cSJoy Latten 	err = 0;
6434aa2e62cSJoy Latten 
6444aa2e62cSJoy Latten out:
6451da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
6461da177e4SLinus Torvalds 	wake_up(&km_waitq);
6474aa2e62cSJoy Latten 	return err;
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush);
6501da177e4SLinus Torvalds 
651af11e316SJamal Hadi Salim void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
65228d8909bSJamal Hadi Salim {
65328d8909bSJamal Hadi Salim 	spin_lock_bh(&xfrm_state_lock);
65428d8909bSJamal Hadi Salim 	si->sadcnt = xfrm_state_num;
65528d8909bSJamal Hadi Salim 	si->sadhcnt = xfrm_state_hmask;
65628d8909bSJamal Hadi Salim 	si->sadhmcnt = xfrm_state_hashmax;
65728d8909bSJamal Hadi Salim 	spin_unlock_bh(&xfrm_state_lock);
65828d8909bSJamal Hadi Salim }
65928d8909bSJamal Hadi Salim EXPORT_SYMBOL(xfrm_sad_getinfo);
66028d8909bSJamal Hadi Salim 
6611da177e4SLinus Torvalds static int
6621da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
6631da177e4SLinus Torvalds 		  struct xfrm_tmpl *tmpl,
6641da177e4SLinus Torvalds 		  xfrm_address_t *daddr, xfrm_address_t *saddr,
6651da177e4SLinus Torvalds 		  unsigned short family)
6661da177e4SLinus Torvalds {
6671da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
6681da177e4SLinus Torvalds 	if (!afinfo)
6691da177e4SLinus Torvalds 		return -1;
6701da177e4SLinus Torvalds 	afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
6711da177e4SLinus Torvalds 	xfrm_state_put_afinfo(afinfo);
6721da177e4SLinus Torvalds 	return 0;
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
675a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
676edcd5821SDavid S. Miller {
677edcd5821SDavid S. Miller 	unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
678edcd5821SDavid S. Miller 	struct xfrm_state *x;
6798f126e37SDavid S. Miller 	struct hlist_node *entry;
680edcd5821SDavid S. Miller 
6818f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
682edcd5821SDavid S. Miller 		if (x->props.family != family ||
683edcd5821SDavid S. Miller 		    x->id.spi       != spi ||
684edcd5821SDavid S. Miller 		    x->id.proto     != proto)
685edcd5821SDavid S. Miller 			continue;
686edcd5821SDavid S. Miller 
687edcd5821SDavid S. Miller 		switch (family) {
688edcd5821SDavid S. Miller 		case AF_INET:
689edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4)
690edcd5821SDavid S. Miller 				continue;
691edcd5821SDavid S. Miller 			break;
692edcd5821SDavid S. Miller 		case AF_INET6:
693edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
694edcd5821SDavid S. Miller 					     (struct in6_addr *)
695edcd5821SDavid S. Miller 					     x->id.daddr.a6))
696edcd5821SDavid S. Miller 				continue;
697edcd5821SDavid S. Miller 			break;
6983ff50b79SStephen Hemminger 		}
699edcd5821SDavid S. Miller 
700edcd5821SDavid S. Miller 		xfrm_state_hold(x);
701edcd5821SDavid S. Miller 		return x;
702edcd5821SDavid S. Miller 	}
703edcd5821SDavid S. Miller 
704edcd5821SDavid S. Miller 	return NULL;
705edcd5821SDavid S. Miller }
706edcd5821SDavid S. Miller 
707edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
708edcd5821SDavid S. Miller {
709667bbcb6SMasahide NAKAMURA 	unsigned int h = xfrm_src_hash(daddr, saddr, family);
710edcd5821SDavid S. Miller 	struct xfrm_state *x;
7118f126e37SDavid S. Miller 	struct hlist_node *entry;
712edcd5821SDavid S. Miller 
7138f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
714edcd5821SDavid S. Miller 		if (x->props.family != family ||
715edcd5821SDavid S. Miller 		    x->id.proto     != proto)
716edcd5821SDavid S. Miller 			continue;
717edcd5821SDavid S. Miller 
718edcd5821SDavid S. Miller 		switch (family) {
719edcd5821SDavid S. Miller 		case AF_INET:
720edcd5821SDavid S. Miller 			if (x->id.daddr.a4 != daddr->a4 ||
721edcd5821SDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
722edcd5821SDavid S. Miller 				continue;
723edcd5821SDavid S. Miller 			break;
724edcd5821SDavid S. Miller 		case AF_INET6:
725edcd5821SDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)daddr,
726edcd5821SDavid S. Miller 					     (struct in6_addr *)
727edcd5821SDavid S. Miller 					     x->id.daddr.a6) ||
728edcd5821SDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)saddr,
729edcd5821SDavid S. Miller 					     (struct in6_addr *)
730edcd5821SDavid S. Miller 					     x->props.saddr.a6))
731edcd5821SDavid S. Miller 				continue;
732edcd5821SDavid S. Miller 			break;
7333ff50b79SStephen Hemminger 		}
734edcd5821SDavid S. Miller 
735edcd5821SDavid S. Miller 		xfrm_state_hold(x);
736edcd5821SDavid S. Miller 		return x;
737edcd5821SDavid S. Miller 	}
738edcd5821SDavid S. Miller 
739edcd5821SDavid S. Miller 	return NULL;
740edcd5821SDavid S. Miller }
741edcd5821SDavid S. Miller 
742edcd5821SDavid S. Miller static inline struct xfrm_state *
743edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
744edcd5821SDavid S. Miller {
745edcd5821SDavid S. Miller 	if (use_spi)
746edcd5821SDavid S. Miller 		return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
747edcd5821SDavid S. Miller 					   x->id.proto, family);
748edcd5821SDavid S. Miller 	else
749edcd5821SDavid S. Miller 		return __xfrm_state_lookup_byaddr(&x->id.daddr,
750edcd5821SDavid S. Miller 						  &x->props.saddr,
751edcd5821SDavid S. Miller 						  x->id.proto, family);
752edcd5821SDavid S. Miller }
753edcd5821SDavid S. Miller 
7542fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision)
7552fab22f2SPatrick McHardy {
7562fab22f2SPatrick McHardy 	if (have_hash_collision &&
7572fab22f2SPatrick McHardy 	    (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
7582fab22f2SPatrick McHardy 	    xfrm_state_num > xfrm_state_hmask)
7592fab22f2SPatrick McHardy 		schedule_work(&xfrm_hash_work);
7602fab22f2SPatrick McHardy }
7612fab22f2SPatrick McHardy 
7621da177e4SLinus Torvalds struct xfrm_state *
7631da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
7641da177e4SLinus Torvalds 		struct flowi *fl, struct xfrm_tmpl *tmpl,
7651da177e4SLinus Torvalds 		struct xfrm_policy *pol, int *err,
7661da177e4SLinus Torvalds 		unsigned short family)
7671da177e4SLinus Torvalds {
7684bda4f25SPavel Emelyanov 	unsigned int h;
7698f126e37SDavid S. Miller 	struct hlist_node *entry;
7701da177e4SLinus Torvalds 	struct xfrm_state *x, *x0;
7711da177e4SLinus Torvalds 	int acquire_in_progress = 0;
7721da177e4SLinus Torvalds 	int error = 0;
7731da177e4SLinus Torvalds 	struct xfrm_state *best = NULL;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
7764bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
7778f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
7781da177e4SLinus Torvalds 		if (x->props.family == family &&
7791da177e4SLinus Torvalds 		    x->props.reqid == tmpl->reqid &&
780fbd9a5b4SMasahide NAKAMURA 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
7811da177e4SLinus Torvalds 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
7821da177e4SLinus Torvalds 		    tmpl->mode == x->props.mode &&
7831da177e4SLinus Torvalds 		    tmpl->id.proto == x->id.proto &&
7841da177e4SLinus Torvalds 		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
7851da177e4SLinus Torvalds 			/* Resolution logic:
7861da177e4SLinus Torvalds 			   1. There is a valid state with matching selector.
7871da177e4SLinus Torvalds 			      Done.
7881da177e4SLinus Torvalds 			   2. Valid state with inappropriate selector. Skip.
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 			   Entering area of "sysdeps".
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 			   3. If state is not valid, selector is temporary,
7931da177e4SLinus Torvalds 			      it selects only session which triggered
7941da177e4SLinus Torvalds 			      previous resolution. Key manager will do
7951da177e4SLinus Torvalds 			      something to install a state with proper
7961da177e4SLinus Torvalds 			      selector.
7971da177e4SLinus Torvalds 			 */
7981da177e4SLinus Torvalds 			if (x->km.state == XFRM_STATE_VALID) {
79948b8d783SJoakim Koskela 				if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
800e0d1caa7SVenkat Yekkirala 				    !security_xfrm_state_pol_flow_match(x, pol, fl))
8011da177e4SLinus Torvalds 					continue;
8021da177e4SLinus Torvalds 				if (!best ||
8031da177e4SLinus Torvalds 				    best->km.dying > x->km.dying ||
8041da177e4SLinus Torvalds 				    (best->km.dying == x->km.dying &&
8051da177e4SLinus Torvalds 				     best->curlft.add_time < x->curlft.add_time))
8061da177e4SLinus Torvalds 					best = x;
8071da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ACQ) {
8081da177e4SLinus Torvalds 				acquire_in_progress = 1;
8091da177e4SLinus Torvalds 			} else if (x->km.state == XFRM_STATE_ERROR ||
8101da177e4SLinus Torvalds 				   x->km.state == XFRM_STATE_EXPIRED) {
81148b8d783SJoakim Koskela 				if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
812e0d1caa7SVenkat Yekkirala 				    security_xfrm_state_pol_flow_match(x, pol, fl))
8131da177e4SLinus Torvalds 					error = -ESRCH;
8141da177e4SLinus Torvalds 			}
8151da177e4SLinus Torvalds 		}
8161da177e4SLinus Torvalds 	}
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds 	x = best;
8191da177e4SLinus Torvalds 	if (!x && !error && !acquire_in_progress) {
8205c5d281aSPatrick McHardy 		if (tmpl->id.spi &&
821edcd5821SDavid S. Miller 		    (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
822edcd5821SDavid S. Miller 					      tmpl->id.proto, family)) != NULL) {
8231da177e4SLinus Torvalds 			xfrm_state_put(x0);
8241da177e4SLinus Torvalds 			error = -EEXIST;
8251da177e4SLinus Torvalds 			goto out;
8261da177e4SLinus Torvalds 		}
8271da177e4SLinus Torvalds 		x = xfrm_state_alloc();
8281da177e4SLinus Torvalds 		if (x == NULL) {
8291da177e4SLinus Torvalds 			error = -ENOMEM;
8301da177e4SLinus Torvalds 			goto out;
8311da177e4SLinus Torvalds 		}
8321da177e4SLinus Torvalds 		/* Initialize temporary selector matching only
8331da177e4SLinus Torvalds 		 * to current session. */
8341da177e4SLinus Torvalds 		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
8351da177e4SLinus Torvalds 
836e0d1caa7SVenkat Yekkirala 		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
837e0d1caa7SVenkat Yekkirala 		if (error) {
838e0d1caa7SVenkat Yekkirala 			x->km.state = XFRM_STATE_DEAD;
839e0d1caa7SVenkat Yekkirala 			xfrm_state_put(x);
840e0d1caa7SVenkat Yekkirala 			x = NULL;
841e0d1caa7SVenkat Yekkirala 			goto out;
842e0d1caa7SVenkat Yekkirala 		}
843e0d1caa7SVenkat Yekkirala 
8441da177e4SLinus Torvalds 		if (km_query(x, tmpl, pol) == 0) {
8451da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_ACQ;
8468f126e37SDavid S. Miller 			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
847667bbcb6SMasahide NAKAMURA 			h = xfrm_src_hash(daddr, saddr, family);
8488f126e37SDavid S. Miller 			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
8491da177e4SLinus Torvalds 			if (x->id.spi) {
8501da177e4SLinus Torvalds 				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
8518f126e37SDavid S. Miller 				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
8521da177e4SLinus Torvalds 			}
85301e67d08SDavid S. Miller 			x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
85401e67d08SDavid S. Miller 			x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
8551da177e4SLinus Torvalds 			add_timer(&x->timer);
8562fab22f2SPatrick McHardy 			xfrm_state_num++;
8572fab22f2SPatrick McHardy 			xfrm_hash_grow_check(x->bydst.next != NULL);
8581da177e4SLinus Torvalds 		} else {
8591da177e4SLinus Torvalds 			x->km.state = XFRM_STATE_DEAD;
8601da177e4SLinus Torvalds 			xfrm_state_put(x);
8611da177e4SLinus Torvalds 			x = NULL;
8621da177e4SLinus Torvalds 			error = -ESRCH;
8631da177e4SLinus Torvalds 		}
8641da177e4SLinus Torvalds 	}
8651da177e4SLinus Torvalds out:
8661da177e4SLinus Torvalds 	if (x)
8671da177e4SLinus Torvalds 		xfrm_state_hold(x);
8681da177e4SLinus Torvalds 	else
8691da177e4SLinus Torvalds 		*err = acquire_in_progress ? -EAGAIN : error;
8701da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
8711da177e4SLinus Torvalds 	return x;
8721da177e4SLinus Torvalds }
8731da177e4SLinus Torvalds 
874628529b6SJamal Hadi Salim struct xfrm_state *
875628529b6SJamal Hadi Salim xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
876628529b6SJamal Hadi Salim 		    unsigned short family, u8 mode, u8 proto, u32 reqid)
877628529b6SJamal Hadi Salim {
8784bda4f25SPavel Emelyanov 	unsigned int h;
879628529b6SJamal Hadi Salim 	struct xfrm_state *rx = NULL, *x = NULL;
880628529b6SJamal Hadi Salim 	struct hlist_node *entry;
881628529b6SJamal Hadi Salim 
882628529b6SJamal Hadi Salim 	spin_lock(&xfrm_state_lock);
8834bda4f25SPavel Emelyanov 	h = xfrm_dst_hash(daddr, saddr, reqid, family);
884628529b6SJamal Hadi Salim 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
885628529b6SJamal Hadi Salim 		if (x->props.family == family &&
886628529b6SJamal Hadi Salim 		    x->props.reqid == reqid &&
887628529b6SJamal Hadi Salim 		    !(x->props.flags & XFRM_STATE_WILDRECV) &&
888628529b6SJamal Hadi Salim 		    xfrm_state_addr_check(x, daddr, saddr, family) &&
889628529b6SJamal Hadi Salim 		    mode == x->props.mode &&
890628529b6SJamal Hadi Salim 		    proto == x->id.proto &&
891628529b6SJamal Hadi Salim 		    x->km.state == XFRM_STATE_VALID) {
892628529b6SJamal Hadi Salim 			rx = x;
893628529b6SJamal Hadi Salim 			break;
894628529b6SJamal Hadi Salim 		}
895628529b6SJamal Hadi Salim 	}
896628529b6SJamal Hadi Salim 
897628529b6SJamal Hadi Salim 	if (rx)
898628529b6SJamal Hadi Salim 		xfrm_state_hold(rx);
899628529b6SJamal Hadi Salim 	spin_unlock(&xfrm_state_lock);
900628529b6SJamal Hadi Salim 
901628529b6SJamal Hadi Salim 
902628529b6SJamal Hadi Salim 	return rx;
903628529b6SJamal Hadi Salim }
904628529b6SJamal Hadi Salim EXPORT_SYMBOL(xfrm_stateonly_find);
905628529b6SJamal Hadi Salim 
9061da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x)
9071da177e4SLinus Torvalds {
908a624c108SDavid S. Miller 	unsigned int h;
9091da177e4SLinus Torvalds 
9109d4a706dSDavid S. Miller 	x->genid = ++xfrm_state_genid;
9119d4a706dSDavid S. Miller 
912c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
913c1969f29SDavid S. Miller 			  x->props.reqid, x->props.family);
9148f126e37SDavid S. Miller 	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
9151da177e4SLinus Torvalds 
916667bbcb6SMasahide NAKAMURA 	h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
9178f126e37SDavid S. Miller 	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
9186c44e6b7SMasahide NAKAMURA 
9197b4dc360SMasahide NAKAMURA 	if (x->id.spi) {
9206c44e6b7SMasahide NAKAMURA 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
9216c44e6b7SMasahide NAKAMURA 				  x->props.family);
9221da177e4SLinus Torvalds 
9238f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
9246c44e6b7SMasahide NAKAMURA 	}
9251da177e4SLinus Torvalds 
926a47f0ce0SDavid S. Miller 	mod_timer(&x->timer, jiffies + HZ);
927a47f0ce0SDavid S. Miller 	if (x->replay_maxage)
928a47f0ce0SDavid S. Miller 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
929f8cd5488SJamal Hadi Salim 
9301da177e4SLinus Torvalds 	wake_up(&km_waitq);
931f034b5d4SDavid S. Miller 
932f034b5d4SDavid S. Miller 	xfrm_state_num++;
933f034b5d4SDavid S. Miller 
934918049f0SDavid S. Miller 	xfrm_hash_grow_check(x->bydst.next != NULL);
9351da177e4SLinus Torvalds }
9361da177e4SLinus Torvalds 
937c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */
938c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
939c7f5ea3aSDavid S. Miller {
940c7f5ea3aSDavid S. Miller 	unsigned short family = xnew->props.family;
941c7f5ea3aSDavid S. Miller 	u32 reqid = xnew->props.reqid;
942c7f5ea3aSDavid S. Miller 	struct xfrm_state *x;
943c7f5ea3aSDavid S. Miller 	struct hlist_node *entry;
944c7f5ea3aSDavid S. Miller 	unsigned int h;
945c7f5ea3aSDavid S. Miller 
946c1969f29SDavid S. Miller 	h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
947c7f5ea3aSDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
948c7f5ea3aSDavid S. Miller 		if (x->props.family	== family &&
949c7f5ea3aSDavid S. Miller 		    x->props.reqid	== reqid &&
950c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
951c1969f29SDavid S. Miller 		    !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))
952c7f5ea3aSDavid S. Miller 			x->genid = xfrm_state_genid;
953c7f5ea3aSDavid S. Miller 	}
954c7f5ea3aSDavid S. Miller }
955c7f5ea3aSDavid S. Miller 
9561da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x)
9571da177e4SLinus Torvalds {
9581da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
959c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
9601da177e4SLinus Torvalds 	__xfrm_state_insert(x);
9611da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
9621da177e4SLinus Torvalds }
9631da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert);
9641da177e4SLinus Torvalds 
9652770834cSDavid S. Miller /* xfrm_state_lock is held */
9662770834cSDavid 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)
9672770834cSDavid S. Miller {
968c1969f29SDavid S. Miller 	unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
9698f126e37SDavid S. Miller 	struct hlist_node *entry;
9702770834cSDavid S. Miller 	struct xfrm_state *x;
9712770834cSDavid S. Miller 
9728f126e37SDavid S. Miller 	hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
9732770834cSDavid S. Miller 		if (x->props.reqid  != reqid ||
9742770834cSDavid S. Miller 		    x->props.mode   != mode ||
9752770834cSDavid S. Miller 		    x->props.family != family ||
9762770834cSDavid S. Miller 		    x->km.state     != XFRM_STATE_ACQ ||
97775e252d9SJoy Latten 		    x->id.spi       != 0 ||
97875e252d9SJoy Latten 		    x->id.proto	    != proto)
9792770834cSDavid S. Miller 			continue;
9802770834cSDavid S. Miller 
9812770834cSDavid S. Miller 		switch (family) {
9822770834cSDavid S. Miller 		case AF_INET:
9832770834cSDavid S. Miller 			if (x->id.daddr.a4    != daddr->a4 ||
9842770834cSDavid S. Miller 			    x->props.saddr.a4 != saddr->a4)
9852770834cSDavid S. Miller 				continue;
9862770834cSDavid S. Miller 			break;
9872770834cSDavid S. Miller 		case AF_INET6:
9882770834cSDavid S. Miller 			if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
9892770834cSDavid S. Miller 					     (struct in6_addr *)daddr) ||
9902770834cSDavid S. Miller 			    !ipv6_addr_equal((struct in6_addr *)
9912770834cSDavid S. Miller 					     x->props.saddr.a6,
9922770834cSDavid S. Miller 					     (struct in6_addr *)saddr))
9932770834cSDavid S. Miller 				continue;
9942770834cSDavid S. Miller 			break;
9953ff50b79SStephen Hemminger 		}
9962770834cSDavid S. Miller 
9972770834cSDavid S. Miller 		xfrm_state_hold(x);
9982770834cSDavid S. Miller 		return x;
9992770834cSDavid S. Miller 	}
10002770834cSDavid S. Miller 
10012770834cSDavid S. Miller 	if (!create)
10022770834cSDavid S. Miller 		return NULL;
10032770834cSDavid S. Miller 
10042770834cSDavid S. Miller 	x = xfrm_state_alloc();
10052770834cSDavid S. Miller 	if (likely(x)) {
10062770834cSDavid S. Miller 		switch (family) {
10072770834cSDavid S. Miller 		case AF_INET:
10082770834cSDavid S. Miller 			x->sel.daddr.a4 = daddr->a4;
10092770834cSDavid S. Miller 			x->sel.saddr.a4 = saddr->a4;
10102770834cSDavid S. Miller 			x->sel.prefixlen_d = 32;
10112770834cSDavid S. Miller 			x->sel.prefixlen_s = 32;
10122770834cSDavid S. Miller 			x->props.saddr.a4 = saddr->a4;
10132770834cSDavid S. Miller 			x->id.daddr.a4 = daddr->a4;
10142770834cSDavid S. Miller 			break;
10152770834cSDavid S. Miller 
10162770834cSDavid S. Miller 		case AF_INET6:
10172770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6,
10182770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10192770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6,
10202770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10212770834cSDavid S. Miller 			x->sel.prefixlen_d = 128;
10222770834cSDavid S. Miller 			x->sel.prefixlen_s = 128;
10232770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6,
10242770834cSDavid S. Miller 				       (struct in6_addr *)saddr);
10252770834cSDavid S. Miller 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
10262770834cSDavid S. Miller 				       (struct in6_addr *)daddr);
10272770834cSDavid S. Miller 			break;
10283ff50b79SStephen Hemminger 		}
10292770834cSDavid S. Miller 
10302770834cSDavid S. Miller 		x->km.state = XFRM_STATE_ACQ;
10312770834cSDavid S. Miller 		x->id.proto = proto;
10322770834cSDavid S. Miller 		x->props.family = family;
10332770834cSDavid S. Miller 		x->props.mode = mode;
10342770834cSDavid S. Miller 		x->props.reqid = reqid;
103501e67d08SDavid S. Miller 		x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
10362770834cSDavid S. Miller 		xfrm_state_hold(x);
103701e67d08SDavid S. Miller 		x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
10382770834cSDavid S. Miller 		add_timer(&x->timer);
10398f126e37SDavid S. Miller 		hlist_add_head(&x->bydst, xfrm_state_bydst+h);
1040667bbcb6SMasahide NAKAMURA 		h = xfrm_src_hash(daddr, saddr, family);
10418f126e37SDavid S. Miller 		hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
1042918049f0SDavid S. Miller 
1043918049f0SDavid S. Miller 		xfrm_state_num++;
1044918049f0SDavid S. Miller 
1045918049f0SDavid S. Miller 		xfrm_hash_grow_check(x->bydst.next != NULL);
10462770834cSDavid S. Miller 	}
10472770834cSDavid S. Miller 
10482770834cSDavid S. Miller 	return x;
10492770834cSDavid S. Miller }
10502770834cSDavid S. Miller 
10511da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x)
10541da177e4SLinus Torvalds {
10551da177e4SLinus Torvalds 	struct xfrm_state *x1;
10561da177e4SLinus Torvalds 	int family;
10571da177e4SLinus Torvalds 	int err;
1058eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
10591da177e4SLinus Torvalds 
10601da177e4SLinus Torvalds 	family = x->props.family;
10611da177e4SLinus Torvalds 
10621da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
10631da177e4SLinus Torvalds 
1064edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, family);
10651da177e4SLinus Torvalds 	if (x1) {
10661da177e4SLinus Torvalds 		xfrm_state_put(x1);
10671da177e4SLinus Torvalds 		x1 = NULL;
10681da177e4SLinus Torvalds 		err = -EEXIST;
10691da177e4SLinus Torvalds 		goto out;
10701da177e4SLinus Torvalds 	}
10711da177e4SLinus Torvalds 
1072eb2971b6SMasahide NAKAMURA 	if (use_spi && x->km.seq) {
10731da177e4SLinus Torvalds 		x1 = __xfrm_find_acq_byseq(x->km.seq);
107475e252d9SJoy Latten 		if (x1 && ((x1->id.proto != x->id.proto) ||
107575e252d9SJoy Latten 		    xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
10761da177e4SLinus Torvalds 			xfrm_state_put(x1);
10771da177e4SLinus Torvalds 			x1 = NULL;
10781da177e4SLinus Torvalds 		}
10791da177e4SLinus Torvalds 	}
10801da177e4SLinus Torvalds 
1081eb2971b6SMasahide NAKAMURA 	if (use_spi && !x1)
10822770834cSDavid S. Miller 		x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
10832770834cSDavid S. Miller 				     x->id.proto,
10841da177e4SLinus Torvalds 				     &x->id.daddr, &x->props.saddr, 0);
10851da177e4SLinus Torvalds 
1086c7f5ea3aSDavid S. Miller 	__xfrm_state_bump_genids(x);
10871da177e4SLinus Torvalds 	__xfrm_state_insert(x);
10881da177e4SLinus Torvalds 	err = 0;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds out:
10911da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	if (x1) {
10941da177e4SLinus Torvalds 		xfrm_state_delete(x1);
10951da177e4SLinus Torvalds 		xfrm_state_put(x1);
10961da177e4SLinus Torvalds 	}
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	return err;
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add);
11011da177e4SLinus Torvalds 
110280c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
110380c9abaaSShinta Sugimoto struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
110480c9abaaSShinta Sugimoto {
110580c9abaaSShinta Sugimoto 	int err = -ENOMEM;
110680c9abaaSShinta Sugimoto 	struct xfrm_state *x = xfrm_state_alloc();
110780c9abaaSShinta Sugimoto 	if (!x)
110880c9abaaSShinta Sugimoto 		goto error;
110980c9abaaSShinta Sugimoto 
111080c9abaaSShinta Sugimoto 	memcpy(&x->id, &orig->id, sizeof(x->id));
111180c9abaaSShinta Sugimoto 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
111280c9abaaSShinta Sugimoto 	memcpy(&x->lft, &orig->lft, sizeof(x->lft));
111380c9abaaSShinta Sugimoto 	x->props.mode = orig->props.mode;
111480c9abaaSShinta Sugimoto 	x->props.replay_window = orig->props.replay_window;
111580c9abaaSShinta Sugimoto 	x->props.reqid = orig->props.reqid;
111680c9abaaSShinta Sugimoto 	x->props.family = orig->props.family;
111780c9abaaSShinta Sugimoto 	x->props.saddr = orig->props.saddr;
111880c9abaaSShinta Sugimoto 
111980c9abaaSShinta Sugimoto 	if (orig->aalg) {
112080c9abaaSShinta Sugimoto 		x->aalg = xfrm_algo_clone(orig->aalg);
112180c9abaaSShinta Sugimoto 		if (!x->aalg)
112280c9abaaSShinta Sugimoto 			goto error;
112380c9abaaSShinta Sugimoto 	}
112480c9abaaSShinta Sugimoto 	x->props.aalgo = orig->props.aalgo;
112580c9abaaSShinta Sugimoto 
112680c9abaaSShinta Sugimoto 	if (orig->ealg) {
112780c9abaaSShinta Sugimoto 		x->ealg = xfrm_algo_clone(orig->ealg);
112880c9abaaSShinta Sugimoto 		if (!x->ealg)
112980c9abaaSShinta Sugimoto 			goto error;
113080c9abaaSShinta Sugimoto 	}
113180c9abaaSShinta Sugimoto 	x->props.ealgo = orig->props.ealgo;
113280c9abaaSShinta Sugimoto 
113380c9abaaSShinta Sugimoto 	if (orig->calg) {
113480c9abaaSShinta Sugimoto 		x->calg = xfrm_algo_clone(orig->calg);
113580c9abaaSShinta Sugimoto 		if (!x->calg)
113680c9abaaSShinta Sugimoto 			goto error;
113780c9abaaSShinta Sugimoto 	}
113880c9abaaSShinta Sugimoto 	x->props.calgo = orig->props.calgo;
113980c9abaaSShinta Sugimoto 
114080c9abaaSShinta Sugimoto 	if (orig->encap) {
114180c9abaaSShinta Sugimoto 		x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL);
114280c9abaaSShinta Sugimoto 		if (!x->encap)
114380c9abaaSShinta Sugimoto 			goto error;
114480c9abaaSShinta Sugimoto 	}
114580c9abaaSShinta Sugimoto 
114680c9abaaSShinta Sugimoto 	if (orig->coaddr) {
114780c9abaaSShinta Sugimoto 		x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
114880c9abaaSShinta Sugimoto 				    GFP_KERNEL);
114980c9abaaSShinta Sugimoto 		if (!x->coaddr)
115080c9abaaSShinta Sugimoto 			goto error;
115180c9abaaSShinta Sugimoto 	}
115280c9abaaSShinta Sugimoto 
115380c9abaaSShinta Sugimoto 	err = xfrm_init_state(x);
115480c9abaaSShinta Sugimoto 	if (err)
115580c9abaaSShinta Sugimoto 		goto error;
115680c9abaaSShinta Sugimoto 
115780c9abaaSShinta Sugimoto 	x->props.flags = orig->props.flags;
115880c9abaaSShinta Sugimoto 
115980c9abaaSShinta Sugimoto 	x->curlft.add_time = orig->curlft.add_time;
116080c9abaaSShinta Sugimoto 	x->km.state = orig->km.state;
116180c9abaaSShinta Sugimoto 	x->km.seq = orig->km.seq;
116280c9abaaSShinta Sugimoto 
116380c9abaaSShinta Sugimoto 	return x;
116480c9abaaSShinta Sugimoto 
116580c9abaaSShinta Sugimoto  error:
116680c9abaaSShinta Sugimoto 	if (errp)
116780c9abaaSShinta Sugimoto 		*errp = err;
116880c9abaaSShinta Sugimoto 	if (x) {
116980c9abaaSShinta Sugimoto 		kfree(x->aalg);
117080c9abaaSShinta Sugimoto 		kfree(x->ealg);
117180c9abaaSShinta Sugimoto 		kfree(x->calg);
117280c9abaaSShinta Sugimoto 		kfree(x->encap);
117380c9abaaSShinta Sugimoto 		kfree(x->coaddr);
117480c9abaaSShinta Sugimoto 	}
117580c9abaaSShinta Sugimoto 	kfree(x);
117680c9abaaSShinta Sugimoto 	return NULL;
117780c9abaaSShinta Sugimoto }
117880c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_clone);
117980c9abaaSShinta Sugimoto 
118080c9abaaSShinta Sugimoto /* xfrm_state_lock is held */
118180c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
118280c9abaaSShinta Sugimoto {
118380c9abaaSShinta Sugimoto 	unsigned int h;
118480c9abaaSShinta Sugimoto 	struct xfrm_state *x;
118580c9abaaSShinta Sugimoto 	struct hlist_node *entry;
118680c9abaaSShinta Sugimoto 
118780c9abaaSShinta Sugimoto 	if (m->reqid) {
118880c9abaaSShinta Sugimoto 		h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
118980c9abaaSShinta Sugimoto 				  m->reqid, m->old_family);
119080c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
119180c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
119280c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
119380c9abaaSShinta Sugimoto 				continue;
119480c9abaaSShinta Sugimoto 			if (m->reqid && x->props.reqid != m->reqid)
119580c9abaaSShinta Sugimoto 				continue;
119680c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
119780c9abaaSShinta Sugimoto 					  m->old_family) ||
119880c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
119980c9abaaSShinta Sugimoto 					  m->old_family))
120080c9abaaSShinta Sugimoto 				continue;
120180c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
120280c9abaaSShinta Sugimoto 			return x;
120380c9abaaSShinta Sugimoto 		}
120480c9abaaSShinta Sugimoto 	} else {
120580c9abaaSShinta Sugimoto 		h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
120680c9abaaSShinta Sugimoto 				  m->old_family);
120780c9abaaSShinta Sugimoto 		hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
120880c9abaaSShinta Sugimoto 			if (x->props.mode != m->mode ||
120980c9abaaSShinta Sugimoto 			    x->id.proto != m->proto)
121080c9abaaSShinta Sugimoto 				continue;
121180c9abaaSShinta Sugimoto 			if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr,
121280c9abaaSShinta Sugimoto 					  m->old_family) ||
121380c9abaaSShinta Sugimoto 			    xfrm_addr_cmp(&x->props.saddr, &m->old_saddr,
121480c9abaaSShinta Sugimoto 					  m->old_family))
121580c9abaaSShinta Sugimoto 				continue;
121680c9abaaSShinta Sugimoto 			xfrm_state_hold(x);
121780c9abaaSShinta Sugimoto 			return x;
121880c9abaaSShinta Sugimoto 		}
121980c9abaaSShinta Sugimoto 	}
122080c9abaaSShinta Sugimoto 
122180c9abaaSShinta Sugimoto 	return NULL;
122280c9abaaSShinta Sugimoto }
122380c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find);
122480c9abaaSShinta Sugimoto 
122580c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
122680c9abaaSShinta Sugimoto 				       struct xfrm_migrate *m)
122780c9abaaSShinta Sugimoto {
122880c9abaaSShinta Sugimoto 	struct xfrm_state *xc;
122980c9abaaSShinta Sugimoto 	int err;
123080c9abaaSShinta Sugimoto 
123180c9abaaSShinta Sugimoto 	xc = xfrm_state_clone(x, &err);
123280c9abaaSShinta Sugimoto 	if (!xc)
123380c9abaaSShinta Sugimoto 		return NULL;
123480c9abaaSShinta Sugimoto 
123580c9abaaSShinta Sugimoto 	memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
123680c9abaaSShinta Sugimoto 	memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
123780c9abaaSShinta Sugimoto 
123880c9abaaSShinta Sugimoto 	/* add state */
123980c9abaaSShinta Sugimoto 	if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) {
124080c9abaaSShinta Sugimoto 		/* a care is needed when the destination address of the
124180c9abaaSShinta Sugimoto 		   state is to be updated as it is a part of triplet */
124280c9abaaSShinta Sugimoto 		xfrm_state_insert(xc);
124380c9abaaSShinta Sugimoto 	} else {
124480c9abaaSShinta Sugimoto 		if ((err = xfrm_state_add(xc)) < 0)
124580c9abaaSShinta Sugimoto 			goto error;
124680c9abaaSShinta Sugimoto 	}
124780c9abaaSShinta Sugimoto 
124880c9abaaSShinta Sugimoto 	return xc;
124980c9abaaSShinta Sugimoto error:
125080c9abaaSShinta Sugimoto 	kfree(xc);
125180c9abaaSShinta Sugimoto 	return NULL;
125280c9abaaSShinta Sugimoto }
125380c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate);
125480c9abaaSShinta Sugimoto #endif
125580c9abaaSShinta Sugimoto 
12561da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x)
12571da177e4SLinus Torvalds {
12581da177e4SLinus Torvalds 	struct xfrm_state *x1;
12591da177e4SLinus Torvalds 	int err;
1260eb2971b6SMasahide NAKAMURA 	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1263edcd5821SDavid S. Miller 	x1 = __xfrm_state_locate(x, use_spi, x->props.family);
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 	err = -ESRCH;
12661da177e4SLinus Torvalds 	if (!x1)
12671da177e4SLinus Torvalds 		goto out;
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	if (xfrm_state_kern(x1)) {
12701da177e4SLinus Torvalds 		xfrm_state_put(x1);
12711da177e4SLinus Torvalds 		err = -EEXIST;
12721da177e4SLinus Torvalds 		goto out;
12731da177e4SLinus Torvalds 	}
12741da177e4SLinus Torvalds 
12751da177e4SLinus Torvalds 	if (x1->km.state == XFRM_STATE_ACQ) {
12761da177e4SLinus Torvalds 		__xfrm_state_insert(x);
12771da177e4SLinus Torvalds 		x = NULL;
12781da177e4SLinus Torvalds 	}
12791da177e4SLinus Torvalds 	err = 0;
12801da177e4SLinus Torvalds 
12811da177e4SLinus Torvalds out:
12821da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	if (err)
12851da177e4SLinus Torvalds 		return err;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 	if (!x) {
12881da177e4SLinus Torvalds 		xfrm_state_delete(x1);
12891da177e4SLinus Torvalds 		xfrm_state_put(x1);
12901da177e4SLinus Torvalds 		return 0;
12911da177e4SLinus Torvalds 	}
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 	err = -EINVAL;
12941da177e4SLinus Torvalds 	spin_lock_bh(&x1->lock);
12951da177e4SLinus Torvalds 	if (likely(x1->km.state == XFRM_STATE_VALID)) {
12961da177e4SLinus Torvalds 		if (x->encap && x1->encap)
12971da177e4SLinus Torvalds 			memcpy(x1->encap, x->encap, sizeof(*x1->encap));
1298060f02a3SNoriaki TAKAMIYA 		if (x->coaddr && x1->coaddr) {
1299060f02a3SNoriaki TAKAMIYA 			memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
1300060f02a3SNoriaki TAKAMIYA 		}
1301060f02a3SNoriaki TAKAMIYA 		if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
1302060f02a3SNoriaki TAKAMIYA 			memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
13031da177e4SLinus Torvalds 		memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
13041da177e4SLinus Torvalds 		x1->km.dying = 0;
13051da177e4SLinus Torvalds 
1306a47f0ce0SDavid S. Miller 		mod_timer(&x1->timer, jiffies + HZ);
13071da177e4SLinus Torvalds 		if (x1->curlft.use_time)
13081da177e4SLinus Torvalds 			xfrm_state_check_expire(x1);
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds 		err = 0;
13111da177e4SLinus Torvalds 	}
13121da177e4SLinus Torvalds 	spin_unlock_bh(&x1->lock);
13131da177e4SLinus Torvalds 
13141da177e4SLinus Torvalds 	xfrm_state_put(x1);
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds 	return err;
13171da177e4SLinus Torvalds }
13181da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update);
13191da177e4SLinus Torvalds 
13201da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x)
13211da177e4SLinus Torvalds {
13221da177e4SLinus Torvalds 	if (!x->curlft.use_time)
13239d729f72SJames Morris 		x->curlft.use_time = get_seconds();
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	if (x->km.state != XFRM_STATE_VALID)
13261da177e4SLinus Torvalds 		return -EINVAL;
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds 	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
13291da177e4SLinus Torvalds 	    x->curlft.packets >= x->lft.hard_packet_limit) {
13304666faabSHerbert Xu 		x->km.state = XFRM_STATE_EXPIRED;
1331a47f0ce0SDavid S. Miller 		mod_timer(&x->timer, jiffies);
13321da177e4SLinus Torvalds 		return -EINVAL;
13331da177e4SLinus Torvalds 	}
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	if (!x->km.dying &&
13361da177e4SLinus Torvalds 	    (x->curlft.bytes >= x->lft.soft_byte_limit ||
13374666faabSHerbert Xu 	     x->curlft.packets >= x->lft.soft_packet_limit)) {
13384666faabSHerbert Xu 		x->km.dying = 1;
133953bc6b4dSJamal Hadi Salim 		km_state_expired(x, 0, 0);
13404666faabSHerbert Xu 	}
13411da177e4SLinus Torvalds 	return 0;
13421da177e4SLinus Torvalds }
13431da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire);
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds struct xfrm_state *
1346a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
13471da177e4SLinus Torvalds 		  unsigned short family)
13481da177e4SLinus Torvalds {
13491da177e4SLinus Torvalds 	struct xfrm_state *x;
13501da177e4SLinus Torvalds 
13511da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1352edcd5821SDavid S. Miller 	x = __xfrm_state_lookup(daddr, spi, proto, family);
13531da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13541da177e4SLinus Torvalds 	return x;
13551da177e4SLinus Torvalds }
13561da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup);
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds struct xfrm_state *
1359eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
1360eb2971b6SMasahide NAKAMURA 			 u8 proto, unsigned short family)
1361eb2971b6SMasahide NAKAMURA {
1362eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x;
1363eb2971b6SMasahide NAKAMURA 
1364eb2971b6SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
1365edcd5821SDavid S. Miller 	x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
1366eb2971b6SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
1367eb2971b6SMasahide NAKAMURA 	return x;
1368eb2971b6SMasahide NAKAMURA }
1369eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
1370eb2971b6SMasahide NAKAMURA 
1371eb2971b6SMasahide NAKAMURA struct xfrm_state *
13721da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
13731da177e4SLinus Torvalds 	      xfrm_address_t *daddr, xfrm_address_t *saddr,
13741da177e4SLinus Torvalds 	      int create, unsigned short family)
13751da177e4SLinus Torvalds {
13761da177e4SLinus Torvalds 	struct xfrm_state *x;
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
13792770834cSDavid S. Miller 	x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
13801da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
13812770834cSDavid S. Miller 
13821da177e4SLinus Torvalds 	return x;
13831da177e4SLinus Torvalds }
13841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq);
13851da177e4SLinus Torvalds 
138641a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
138741a49cc3SMasahide NAKAMURA int
138841a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
138941a49cc3SMasahide NAKAMURA 	       unsigned short family)
139041a49cc3SMasahide NAKAMURA {
139141a49cc3SMasahide NAKAMURA 	int err = 0;
139241a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
139341a49cc3SMasahide NAKAMURA 	if (!afinfo)
139441a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
139541a49cc3SMasahide NAKAMURA 
139641a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
139741a49cc3SMasahide NAKAMURA 	if (afinfo->tmpl_sort)
139841a49cc3SMasahide NAKAMURA 		err = afinfo->tmpl_sort(dst, src, n);
139941a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
140041a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
140141a49cc3SMasahide NAKAMURA 	return err;
140241a49cc3SMasahide NAKAMURA }
140341a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort);
140441a49cc3SMasahide NAKAMURA 
140541a49cc3SMasahide NAKAMURA int
140641a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
140741a49cc3SMasahide NAKAMURA 		unsigned short family)
140841a49cc3SMasahide NAKAMURA {
140941a49cc3SMasahide NAKAMURA 	int err = 0;
141041a49cc3SMasahide NAKAMURA 	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
141141a49cc3SMasahide NAKAMURA 	if (!afinfo)
141241a49cc3SMasahide NAKAMURA 		return -EAFNOSUPPORT;
141341a49cc3SMasahide NAKAMURA 
141441a49cc3SMasahide NAKAMURA 	spin_lock_bh(&xfrm_state_lock);
141541a49cc3SMasahide NAKAMURA 	if (afinfo->state_sort)
141641a49cc3SMasahide NAKAMURA 		err = afinfo->state_sort(dst, src, n);
141741a49cc3SMasahide NAKAMURA 	spin_unlock_bh(&xfrm_state_lock);
141841a49cc3SMasahide NAKAMURA 	xfrm_state_put_afinfo(afinfo);
141941a49cc3SMasahide NAKAMURA 	return err;
142041a49cc3SMasahide NAKAMURA }
142141a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort);
142241a49cc3SMasahide NAKAMURA #endif
142341a49cc3SMasahide NAKAMURA 
14241da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */
14251da177e4SLinus Torvalds 
14261da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
14271da177e4SLinus Torvalds {
14281da177e4SLinus Torvalds 	int i;
14291da177e4SLinus Torvalds 
1430f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
14318f126e37SDavid S. Miller 		struct hlist_node *entry;
14328f126e37SDavid S. Miller 		struct xfrm_state *x;
14338f126e37SDavid S. Miller 
14348f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
14358f126e37SDavid S. Miller 			if (x->km.seq == seq &&
14368f126e37SDavid S. Miller 			    x->km.state == XFRM_STATE_ACQ) {
14371da177e4SLinus Torvalds 				xfrm_state_hold(x);
14381da177e4SLinus Torvalds 				return x;
14391da177e4SLinus Torvalds 			}
14401da177e4SLinus Torvalds 		}
14411da177e4SLinus Torvalds 	}
14421da177e4SLinus Torvalds 	return NULL;
14431da177e4SLinus Torvalds }
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
14461da177e4SLinus Torvalds {
14471da177e4SLinus Torvalds 	struct xfrm_state *x;
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
14501da177e4SLinus Torvalds 	x = __xfrm_find_acq_byseq(seq);
14511da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
14521da177e4SLinus Torvalds 	return x;
14531da177e4SLinus Torvalds }
14541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq);
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds u32 xfrm_get_acqseq(void)
14571da177e4SLinus Torvalds {
14581da177e4SLinus Torvalds 	u32 res;
14591da177e4SLinus Torvalds 	static u32 acqseq;
14601da177e4SLinus Torvalds 	static DEFINE_SPINLOCK(acqseq_lock);
14611da177e4SLinus Torvalds 
14621da177e4SLinus Torvalds 	spin_lock_bh(&acqseq_lock);
14631da177e4SLinus Torvalds 	res = (++acqseq ? : ++acqseq);
14641da177e4SLinus Torvalds 	spin_unlock_bh(&acqseq_lock);
14651da177e4SLinus Torvalds 	return res;
14661da177e4SLinus Torvalds }
14671da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq);
14681da177e4SLinus Torvalds 
1469658b219eSHerbert Xu int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
14701da177e4SLinus Torvalds {
1471f034b5d4SDavid S. Miller 	unsigned int h;
14721da177e4SLinus Torvalds 	struct xfrm_state *x0;
1473658b219eSHerbert Xu 	int err = -ENOENT;
1474658b219eSHerbert Xu 	__be32 minspi = htonl(low);
1475658b219eSHerbert Xu 	__be32 maxspi = htonl(high);
14761da177e4SLinus Torvalds 
1477658b219eSHerbert Xu 	spin_lock_bh(&x->lock);
1478658b219eSHerbert Xu 	if (x->km.state == XFRM_STATE_DEAD)
1479658b219eSHerbert Xu 		goto unlock;
1480658b219eSHerbert Xu 
1481658b219eSHerbert Xu 	err = 0;
14821da177e4SLinus Torvalds 	if (x->id.spi)
1483658b219eSHerbert Xu 		goto unlock;
1484658b219eSHerbert Xu 
1485658b219eSHerbert Xu 	err = -ENOENT;
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds 	if (minspi == maxspi) {
14881da177e4SLinus Torvalds 		x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
14891da177e4SLinus Torvalds 		if (x0) {
14901da177e4SLinus Torvalds 			xfrm_state_put(x0);
1491658b219eSHerbert Xu 			goto unlock;
14921da177e4SLinus Torvalds 		}
14931da177e4SLinus Torvalds 		x->id.spi = minspi;
14941da177e4SLinus Torvalds 	} else {
14951da177e4SLinus Torvalds 		u32 spi = 0;
149626977b4eSAl Viro 		for (h=0; h<high-low+1; h++) {
149726977b4eSAl Viro 			spi = low + net_random()%(high-low+1);
14981da177e4SLinus Torvalds 			x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
14991da177e4SLinus Torvalds 			if (x0 == NULL) {
15001da177e4SLinus Torvalds 				x->id.spi = htonl(spi);
15011da177e4SLinus Torvalds 				break;
15021da177e4SLinus Torvalds 			}
15031da177e4SLinus Torvalds 			xfrm_state_put(x0);
15041da177e4SLinus Torvalds 		}
15051da177e4SLinus Torvalds 	}
15061da177e4SLinus Torvalds 	if (x->id.spi) {
15071da177e4SLinus Torvalds 		spin_lock_bh(&xfrm_state_lock);
15081da177e4SLinus Torvalds 		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
15098f126e37SDavid S. Miller 		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
15101da177e4SLinus Torvalds 		spin_unlock_bh(&xfrm_state_lock);
1511658b219eSHerbert Xu 
1512658b219eSHerbert Xu 		err = 0;
15131da177e4SLinus Torvalds 	}
1514658b219eSHerbert Xu 
1515658b219eSHerbert Xu unlock:
1516658b219eSHerbert Xu 	spin_unlock_bh(&x->lock);
1517658b219eSHerbert Xu 
1518658b219eSHerbert Xu 	return err;
15191da177e4SLinus Torvalds }
15201da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi);
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
15231da177e4SLinus Torvalds 		    void *data)
15241da177e4SLinus Torvalds {
15251da177e4SLinus Torvalds 	int i;
152694b9bb54SJamal Hadi Salim 	struct xfrm_state *x, *last = NULL;
15278f126e37SDavid S. Miller 	struct hlist_node *entry;
15281da177e4SLinus Torvalds 	int count = 0;
15291da177e4SLinus Torvalds 	int err = 0;
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	spin_lock_bh(&xfrm_state_lock);
1532f034b5d4SDavid S. Miller 	for (i = 0; i <= xfrm_state_hmask; i++) {
15338f126e37SDavid S. Miller 		hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
153494b9bb54SJamal Hadi Salim 			if (!xfrm_id_proto_match(x->id.proto, proto))
153594b9bb54SJamal Hadi Salim 				continue;
153694b9bb54SJamal Hadi Salim 			if (last) {
153794b9bb54SJamal Hadi Salim 				err = func(last, count, data);
153894b9bb54SJamal Hadi Salim 				if (err)
153994b9bb54SJamal Hadi Salim 					goto out;
154094b9bb54SJamal Hadi Salim 			}
154194b9bb54SJamal Hadi Salim 			last = x;
15421da177e4SLinus Torvalds 			count++;
15431da177e4SLinus Torvalds 		}
15441da177e4SLinus Torvalds 	}
15451da177e4SLinus Torvalds 	if (count == 0) {
15461da177e4SLinus Torvalds 		err = -ENOENT;
15471da177e4SLinus Torvalds 		goto out;
15481da177e4SLinus Torvalds 	}
154994b9bb54SJamal Hadi Salim 	err = func(last, 0, data);
15501da177e4SLinus Torvalds out:
15511da177e4SLinus Torvalds 	spin_unlock_bh(&xfrm_state_lock);
15521da177e4SLinus Torvalds 	return err;
15531da177e4SLinus Torvalds }
15541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk);
15551da177e4SLinus Torvalds 
1556f8cd5488SJamal Hadi Salim 
1557f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event)
1558f8cd5488SJamal Hadi Salim {
1559f8cd5488SJamal Hadi Salim 	struct km_event c;
1560f8cd5488SJamal Hadi Salim 	/* we send notify messages in case
1561f8cd5488SJamal Hadi Salim 	 *  1. we updated on of the sequence numbers, and the seqno difference
1562f8cd5488SJamal Hadi Salim 	 *     is at least x->replay_maxdiff, in this case we also update the
1563f8cd5488SJamal Hadi Salim 	 *     timeout of our timer function
1564f8cd5488SJamal Hadi Salim 	 *  2. if x->replay_maxage has elapsed since last update,
1565f8cd5488SJamal Hadi Salim 	 *     and there were changes
1566f8cd5488SJamal Hadi Salim 	 *
1567f8cd5488SJamal Hadi Salim 	 *  The state structure must be locked!
1568f8cd5488SJamal Hadi Salim 	 */
1569f8cd5488SJamal Hadi Salim 
1570f8cd5488SJamal Hadi Salim 	switch (event) {
1571f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_UPDATE:
1572f8cd5488SJamal Hadi Salim 		if (x->replay_maxdiff &&
1573f8cd5488SJamal Hadi Salim 		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
15742717096aSJamal Hadi Salim 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
15752717096aSJamal Hadi Salim 			if (x->xflags & XFRM_TIME_DEFER)
15762717096aSJamal Hadi Salim 				event = XFRM_REPLAY_TIMEOUT;
15772717096aSJamal Hadi Salim 			else
1578f8cd5488SJamal Hadi Salim 				return;
15792717096aSJamal Hadi Salim 		}
1580f8cd5488SJamal Hadi Salim 
1581f8cd5488SJamal Hadi Salim 		break;
1582f8cd5488SJamal Hadi Salim 
1583f8cd5488SJamal Hadi Salim 	case XFRM_REPLAY_TIMEOUT:
1584f8cd5488SJamal Hadi Salim 		if ((x->replay.seq == x->preplay.seq) &&
1585f8cd5488SJamal Hadi Salim 		    (x->replay.bitmap == x->preplay.bitmap) &&
15862717096aSJamal Hadi Salim 		    (x->replay.oseq == x->preplay.oseq)) {
15872717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
1588f8cd5488SJamal Hadi Salim 			return;
15892717096aSJamal Hadi Salim 		}
1590f8cd5488SJamal Hadi Salim 
1591f8cd5488SJamal Hadi Salim 		break;
1592f8cd5488SJamal Hadi Salim 	}
1593f8cd5488SJamal Hadi Salim 
1594f8cd5488SJamal Hadi Salim 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
1595f8cd5488SJamal Hadi Salim 	c.event = XFRM_MSG_NEWAE;
1596f8cd5488SJamal Hadi Salim 	c.data.aevent = event;
1597f8cd5488SJamal Hadi Salim 	km_state_notify(x, &c);
1598f8cd5488SJamal Hadi Salim 
1599f8cd5488SJamal Hadi Salim 	if (x->replay_maxage &&
1600a47f0ce0SDavid S. Miller 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
16012717096aSJamal Hadi Salim 		x->xflags &= ~XFRM_TIME_DEFER;
16022717096aSJamal Hadi Salim }
1603f8cd5488SJamal Hadi Salim 
1604f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data)
1605f8cd5488SJamal Hadi Salim {
1606f8cd5488SJamal Hadi Salim 	struct xfrm_state *x = (struct xfrm_state*)data;
1607f8cd5488SJamal Hadi Salim 
1608f8cd5488SJamal Hadi Salim 	spin_lock(&x->lock);
1609f8cd5488SJamal Hadi Salim 
16102717096aSJamal Hadi Salim 	if (x->km.state == XFRM_STATE_VALID) {
16112717096aSJamal Hadi Salim 		if (xfrm_aevent_is_on())
1612f8cd5488SJamal Hadi Salim 			xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
16132717096aSJamal Hadi Salim 		else
16142717096aSJamal Hadi Salim 			x->xflags |= XFRM_TIME_DEFER;
16152717096aSJamal Hadi Salim 	}
1616f8cd5488SJamal Hadi Salim 
1617f8cd5488SJamal Hadi Salim 	spin_unlock(&x->lock);
1618f8cd5488SJamal Hadi Salim }
1619f8cd5488SJamal Hadi Salim 
1620afeb14b4SPaul Moore int xfrm_replay_check(struct xfrm_state *x,
1621afeb14b4SPaul Moore 		      struct sk_buff *skb, __be32 net_seq)
16221da177e4SLinus Torvalds {
16231da177e4SLinus Torvalds 	u32 diff;
1624a252cc23SAl Viro 	u32 seq = ntohl(net_seq);
16251da177e4SLinus Torvalds 
16261da177e4SLinus Torvalds 	if (unlikely(seq == 0))
1627afeb14b4SPaul Moore 		goto err;
16281da177e4SLinus Torvalds 
16291da177e4SLinus Torvalds 	if (likely(seq > x->replay.seq))
16301da177e4SLinus Torvalds 		return 0;
16311da177e4SLinus Torvalds 
16321da177e4SLinus Torvalds 	diff = x->replay.seq - seq;
16334c4d51a7SHerbert Xu 	if (diff >= min_t(unsigned int, x->props.replay_window,
16344c4d51a7SHerbert Xu 			  sizeof(x->replay.bitmap) * 8)) {
16351da177e4SLinus Torvalds 		x->stats.replay_window++;
1636afeb14b4SPaul Moore 		goto err;
16371da177e4SLinus Torvalds 	}
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds 	if (x->replay.bitmap & (1U << diff)) {
16401da177e4SLinus Torvalds 		x->stats.replay++;
1641afeb14b4SPaul Moore 		goto err;
16421da177e4SLinus Torvalds 	}
16431da177e4SLinus Torvalds 	return 0;
1644afeb14b4SPaul Moore 
1645afeb14b4SPaul Moore err:
1646afeb14b4SPaul Moore 	xfrm_audit_state_replay(x, skb, net_seq);
1647afeb14b4SPaul Moore 	return -EINVAL;
16481da177e4SLinus Torvalds }
16491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check);
16501da177e4SLinus Torvalds 
165161f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
16521da177e4SLinus Torvalds {
16531da177e4SLinus Torvalds 	u32 diff;
165461f4627bSAl Viro 	u32 seq = ntohl(net_seq);
16551da177e4SLinus Torvalds 
16561da177e4SLinus Torvalds 	if (seq > x->replay.seq) {
16571da177e4SLinus Torvalds 		diff = seq - x->replay.seq;
16581da177e4SLinus Torvalds 		if (diff < x->props.replay_window)
16591da177e4SLinus Torvalds 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
16601da177e4SLinus Torvalds 		else
16611da177e4SLinus Torvalds 			x->replay.bitmap = 1;
16621da177e4SLinus Torvalds 		x->replay.seq = seq;
16631da177e4SLinus Torvalds 	} else {
16641da177e4SLinus Torvalds 		diff = x->replay.seq - seq;
16651da177e4SLinus Torvalds 		x->replay.bitmap |= (1U << diff);
16661da177e4SLinus Torvalds 	}
1667f8cd5488SJamal Hadi Salim 
1668f8cd5488SJamal Hadi Salim 	if (xfrm_aevent_is_on())
1669f8cd5488SJamal Hadi Salim 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
16701da177e4SLinus Torvalds }
16711da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance);
16721da177e4SLinus Torvalds 
1673df01812eSDenis Cheng static LIST_HEAD(xfrm_km_list);
16741da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock);
16751da177e4SLinus Torvalds 
167626b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
16771da177e4SLinus Torvalds {
16781da177e4SLinus Torvalds 	struct xfrm_mgr *km;
16791da177e4SLinus Torvalds 
168026b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
168126b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
168226b15dadSJamal Hadi Salim 		if (km->notify_policy)
168326b15dadSJamal Hadi Salim 			km->notify_policy(xp, dir, c);
168426b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
168526b15dadSJamal Hadi Salim }
168626b15dadSJamal Hadi Salim 
168726b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c)
168826b15dadSJamal Hadi Salim {
168926b15dadSJamal Hadi Salim 	struct xfrm_mgr *km;
169026b15dadSJamal Hadi Salim 	read_lock(&xfrm_km_lock);
169126b15dadSJamal Hadi Salim 	list_for_each_entry(km, &xfrm_km_list, list)
169226b15dadSJamal Hadi Salim 		if (km->notify)
169326b15dadSJamal Hadi Salim 			km->notify(x, c);
169426b15dadSJamal Hadi Salim 	read_unlock(&xfrm_km_lock);
169526b15dadSJamal Hadi Salim }
169626b15dadSJamal Hadi Salim 
169726b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify);
169826b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify);
169926b15dadSJamal Hadi Salim 
170053bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
170126b15dadSJamal Hadi Salim {
170226b15dadSJamal Hadi Salim 	struct km_event c;
170326b15dadSJamal Hadi Salim 
1704bf08867fSHerbert Xu 	c.data.hard = hard;
170553bc6b4dSJamal Hadi Salim 	c.pid = pid;
1706f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_EXPIRE;
170726b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
17081da177e4SLinus Torvalds 
17091da177e4SLinus Torvalds 	if (hard)
17101da177e4SLinus Torvalds 		wake_up(&km_waitq);
17111da177e4SLinus Torvalds }
17121da177e4SLinus Torvalds 
171353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired);
171426b15dadSJamal Hadi Salim /*
171526b15dadSJamal Hadi Salim  * We send to all registered managers regardless of failure
171626b15dadSJamal Hadi Salim  * We are happy with one success
171726b15dadSJamal Hadi Salim */
1718980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
17191da177e4SLinus Torvalds {
172026b15dadSJamal Hadi Salim 	int err = -EINVAL, acqret;
17211da177e4SLinus Torvalds 	struct xfrm_mgr *km;
17221da177e4SLinus Torvalds 
17231da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
17241da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
172526b15dadSJamal Hadi Salim 		acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT);
172626b15dadSJamal Hadi Salim 		if (!acqret)
172726b15dadSJamal Hadi Salim 			err = acqret;
17281da177e4SLinus Torvalds 	}
17291da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17301da177e4SLinus Torvalds 	return err;
17311da177e4SLinus Torvalds }
1732980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query);
17331da177e4SLinus Torvalds 
17345d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
17351da177e4SLinus Torvalds {
17361da177e4SLinus Torvalds 	int err = -EINVAL;
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) {
17411da177e4SLinus Torvalds 		if (km->new_mapping)
17421da177e4SLinus Torvalds 			err = km->new_mapping(x, ipaddr, sport);
17431da177e4SLinus Torvalds 		if (!err)
17441da177e4SLinus Torvalds 			break;
17451da177e4SLinus Torvalds 	}
17461da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
17471da177e4SLinus Torvalds 	return err;
17481da177e4SLinus Torvalds }
17491da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping);
17501da177e4SLinus Torvalds 
17516c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
17521da177e4SLinus Torvalds {
175326b15dadSJamal Hadi Salim 	struct km_event c;
17541da177e4SLinus Torvalds 
1755bf08867fSHerbert Xu 	c.data.hard = hard;
17566c5c8ca7SJamal Hadi Salim 	c.pid = pid;
1757f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_POLEXPIRE;
175826b15dadSJamal Hadi Salim 	km_policy_notify(pol, dir, &c);
17591da177e4SLinus Torvalds 
17601da177e4SLinus Torvalds 	if (hard)
17611da177e4SLinus Torvalds 		wake_up(&km_waitq);
17621da177e4SLinus Torvalds }
1763a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired);
17641da177e4SLinus Torvalds 
17652d60abc2SEric Dumazet #ifdef CONFIG_XFRM_MIGRATE
176680c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
176780c9abaaSShinta Sugimoto 	       struct xfrm_migrate *m, int num_migrate)
176880c9abaaSShinta Sugimoto {
176980c9abaaSShinta Sugimoto 	int err = -EINVAL;
177080c9abaaSShinta Sugimoto 	int ret;
177180c9abaaSShinta Sugimoto 	struct xfrm_mgr *km;
177280c9abaaSShinta Sugimoto 
177380c9abaaSShinta Sugimoto 	read_lock(&xfrm_km_lock);
177480c9abaaSShinta Sugimoto 	list_for_each_entry(km, &xfrm_km_list, list) {
177580c9abaaSShinta Sugimoto 		if (km->migrate) {
177680c9abaaSShinta Sugimoto 			ret = km->migrate(sel, dir, type, m, num_migrate);
177780c9abaaSShinta Sugimoto 			if (!ret)
177880c9abaaSShinta Sugimoto 				err = ret;
177980c9abaaSShinta Sugimoto 		}
178080c9abaaSShinta Sugimoto 	}
178180c9abaaSShinta Sugimoto 	read_unlock(&xfrm_km_lock);
178280c9abaaSShinta Sugimoto 	return err;
178380c9abaaSShinta Sugimoto }
178480c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate);
17852d60abc2SEric Dumazet #endif
178680c9abaaSShinta Sugimoto 
178797a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
178897a64b45SMasahide NAKAMURA {
178997a64b45SMasahide NAKAMURA 	int err = -EINVAL;
179097a64b45SMasahide NAKAMURA 	int ret;
179197a64b45SMasahide NAKAMURA 	struct xfrm_mgr *km;
179297a64b45SMasahide NAKAMURA 
179397a64b45SMasahide NAKAMURA 	read_lock(&xfrm_km_lock);
179497a64b45SMasahide NAKAMURA 	list_for_each_entry(km, &xfrm_km_list, list) {
179597a64b45SMasahide NAKAMURA 		if (km->report) {
179697a64b45SMasahide NAKAMURA 			ret = km->report(proto, sel, addr);
179797a64b45SMasahide NAKAMURA 			if (!ret)
179897a64b45SMasahide NAKAMURA 				err = ret;
179997a64b45SMasahide NAKAMURA 		}
180097a64b45SMasahide NAKAMURA 	}
180197a64b45SMasahide NAKAMURA 	read_unlock(&xfrm_km_lock);
180297a64b45SMasahide NAKAMURA 	return err;
180397a64b45SMasahide NAKAMURA }
180497a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report);
180597a64b45SMasahide NAKAMURA 
18061da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
18071da177e4SLinus Torvalds {
18081da177e4SLinus Torvalds 	int err;
18091da177e4SLinus Torvalds 	u8 *data;
18101da177e4SLinus Torvalds 	struct xfrm_mgr *km;
18111da177e4SLinus Torvalds 	struct xfrm_policy *pol = NULL;
18121da177e4SLinus Torvalds 
18131da177e4SLinus Torvalds 	if (optlen <= 0 || optlen > PAGE_SIZE)
18141da177e4SLinus Torvalds 		return -EMSGSIZE;
18151da177e4SLinus Torvalds 
18161da177e4SLinus Torvalds 	data = kmalloc(optlen, GFP_KERNEL);
18171da177e4SLinus Torvalds 	if (!data)
18181da177e4SLinus Torvalds 		return -ENOMEM;
18191da177e4SLinus Torvalds 
18201da177e4SLinus Torvalds 	err = -EFAULT;
18211da177e4SLinus Torvalds 	if (copy_from_user(data, optval, optlen))
18221da177e4SLinus Torvalds 		goto out;
18231da177e4SLinus Torvalds 
18241da177e4SLinus Torvalds 	err = -EINVAL;
18251da177e4SLinus Torvalds 	read_lock(&xfrm_km_lock);
18261da177e4SLinus Torvalds 	list_for_each_entry(km, &xfrm_km_list, list) {
1827cb969f07SVenkat Yekkirala 		pol = km->compile_policy(sk, optname, data,
18281da177e4SLinus Torvalds 					 optlen, &err);
18291da177e4SLinus Torvalds 		if (err >= 0)
18301da177e4SLinus Torvalds 			break;
18311da177e4SLinus Torvalds 	}
18321da177e4SLinus Torvalds 	read_unlock(&xfrm_km_lock);
18331da177e4SLinus Torvalds 
18341da177e4SLinus Torvalds 	if (err >= 0) {
18351da177e4SLinus Torvalds 		xfrm_sk_policy_insert(sk, err, pol);
18361da177e4SLinus Torvalds 		xfrm_pol_put(pol);
18371da177e4SLinus Torvalds 		err = 0;
18381da177e4SLinus Torvalds 	}
18391da177e4SLinus Torvalds 
18401da177e4SLinus Torvalds out:
18411da177e4SLinus Torvalds 	kfree(data);
18421da177e4SLinus Torvalds 	return err;
18431da177e4SLinus Torvalds }
18441da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy);
18451da177e4SLinus Torvalds 
18461da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km)
18471da177e4SLinus Torvalds {
18481da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18491da177e4SLinus Torvalds 	list_add_tail(&km->list, &xfrm_km_list);
18501da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18511da177e4SLinus Torvalds 	return 0;
18521da177e4SLinus Torvalds }
18531da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km);
18541da177e4SLinus Torvalds 
18551da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km)
18561da177e4SLinus Torvalds {
18571da177e4SLinus Torvalds 	write_lock_bh(&xfrm_km_lock);
18581da177e4SLinus Torvalds 	list_del(&km->list);
18591da177e4SLinus Torvalds 	write_unlock_bh(&xfrm_km_lock);
18601da177e4SLinus Torvalds 	return 0;
18611da177e4SLinus Torvalds }
18621da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km);
18631da177e4SLinus Torvalds 
18641da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
18651da177e4SLinus Torvalds {
18661da177e4SLinus Torvalds 	int err = 0;
18671da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18681da177e4SLinus Torvalds 		return -EINVAL;
18691da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18701da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1871f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
18721da177e4SLinus Torvalds 	if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
18731da177e4SLinus Torvalds 		err = -ENOBUFS;
1874edcd5821SDavid S. Miller 	else
18751da177e4SLinus Torvalds 		xfrm_state_afinfo[afinfo->family] = afinfo;
1876f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
18771da177e4SLinus Torvalds 	return err;
18781da177e4SLinus Torvalds }
18791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo);
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
18821da177e4SLinus Torvalds {
18831da177e4SLinus Torvalds 	int err = 0;
18841da177e4SLinus Torvalds 	if (unlikely(afinfo == NULL))
18851da177e4SLinus Torvalds 		return -EINVAL;
18861da177e4SLinus Torvalds 	if (unlikely(afinfo->family >= NPROTO))
18871da177e4SLinus Torvalds 		return -EAFNOSUPPORT;
1888f3111502SIngo Molnar 	write_lock_bh(&xfrm_state_afinfo_lock);
18891da177e4SLinus Torvalds 	if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
18901da177e4SLinus Torvalds 		if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
18911da177e4SLinus Torvalds 			err = -EINVAL;
1892edcd5821SDavid S. Miller 		else
18931da177e4SLinus Torvalds 			xfrm_state_afinfo[afinfo->family] = NULL;
18941da177e4SLinus Torvalds 	}
1895f3111502SIngo Molnar 	write_unlock_bh(&xfrm_state_afinfo_lock);
18961da177e4SLinus Torvalds 	return err;
18971da177e4SLinus Torvalds }
18981da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
18991da177e4SLinus Torvalds 
190017c2a42aSHerbert Xu static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
19011da177e4SLinus Torvalds {
19021da177e4SLinus Torvalds 	struct xfrm_state_afinfo *afinfo;
19031da177e4SLinus Torvalds 	if (unlikely(family >= NPROTO))
19041da177e4SLinus Torvalds 		return NULL;
19051da177e4SLinus Torvalds 	read_lock(&xfrm_state_afinfo_lock);
19061da177e4SLinus Torvalds 	afinfo = xfrm_state_afinfo[family];
1907546be240SHerbert Xu 	if (unlikely(!afinfo))
19081da177e4SLinus Torvalds 		read_unlock(&xfrm_state_afinfo_lock);
19091da177e4SLinus Torvalds 	return afinfo;
19101da177e4SLinus Torvalds }
19111da177e4SLinus Torvalds 
191217c2a42aSHerbert Xu static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
19139a429c49SEric Dumazet 	__releases(xfrm_state_afinfo_lock)
19141da177e4SLinus Torvalds {
1915546be240SHerbert Xu 	read_unlock(&xfrm_state_afinfo_lock);
19161da177e4SLinus Torvalds }
19171da177e4SLinus Torvalds 
19181da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
19191da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x)
19201da177e4SLinus Torvalds {
19211da177e4SLinus Torvalds 	if (x->tunnel) {
19221da177e4SLinus Torvalds 		struct xfrm_state *t = x->tunnel;
19231da177e4SLinus Torvalds 
19241da177e4SLinus Torvalds 		if (atomic_read(&t->tunnel_users) == 2)
19251da177e4SLinus Torvalds 			xfrm_state_delete(t);
19261da177e4SLinus Torvalds 		atomic_dec(&t->tunnel_users);
19271da177e4SLinus Torvalds 		xfrm_state_put(t);
19281da177e4SLinus Torvalds 		x->tunnel = NULL;
19291da177e4SLinus Torvalds 	}
19301da177e4SLinus Torvalds }
19311da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel);
19321da177e4SLinus Torvalds 
19331da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu)
19341da177e4SLinus Torvalds {
1935c5c25238SPatrick McHardy 	int res;
19361da177e4SLinus Torvalds 
19371da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
19381da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_VALID &&
1939c5c25238SPatrick McHardy 	    x->type && x->type->get_mtu)
1940c5c25238SPatrick McHardy 		res = x->type->get_mtu(x, mtu);
19411da177e4SLinus Torvalds 	else
194228121617SPatrick McHardy 		res = mtu - x->props.header_len;
19431da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
19441da177e4SLinus Torvalds 	return res;
19451da177e4SLinus Torvalds }
19461da177e4SLinus Torvalds 
194772cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x)
194872cb6962SHerbert Xu {
1949d094cd83SHerbert Xu 	struct xfrm_state_afinfo *afinfo;
1950d094cd83SHerbert Xu 	int family = x->props.family;
195172cb6962SHerbert Xu 	int err;
195272cb6962SHerbert Xu 
1953d094cd83SHerbert Xu 	err = -EAFNOSUPPORT;
1954d094cd83SHerbert Xu 	afinfo = xfrm_state_get_afinfo(family);
1955d094cd83SHerbert Xu 	if (!afinfo)
1956d094cd83SHerbert Xu 		goto error;
1957d094cd83SHerbert Xu 
1958d094cd83SHerbert Xu 	err = 0;
1959d094cd83SHerbert Xu 	if (afinfo->init_flags)
1960d094cd83SHerbert Xu 		err = afinfo->init_flags(x);
1961d094cd83SHerbert Xu 
1962d094cd83SHerbert Xu 	xfrm_state_put_afinfo(afinfo);
1963d094cd83SHerbert Xu 
1964d094cd83SHerbert Xu 	if (err)
1965d094cd83SHerbert Xu 		goto error;
1966d094cd83SHerbert Xu 
1967d094cd83SHerbert Xu 	err = -EPROTONOSUPPORT;
196813996378SHerbert Xu 	x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
196913996378SHerbert Xu 	if (x->inner_mode == NULL)
197013996378SHerbert Xu 		goto error;
197113996378SHerbert Xu 
197213996378SHerbert Xu 	if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
197313996378SHerbert Xu 	    family != x->sel.family)
197413996378SHerbert Xu 		goto error;
197513996378SHerbert Xu 
1976d094cd83SHerbert Xu 	x->type = xfrm_get_type(x->id.proto, family);
197772cb6962SHerbert Xu 	if (x->type == NULL)
197872cb6962SHerbert Xu 		goto error;
197972cb6962SHerbert Xu 
198072cb6962SHerbert Xu 	err = x->type->init_state(x);
198172cb6962SHerbert Xu 	if (err)
198272cb6962SHerbert Xu 		goto error;
198372cb6962SHerbert Xu 
198413996378SHerbert Xu 	x->outer_mode = xfrm_get_mode(x->props.mode, family);
198513996378SHerbert Xu 	if (x->outer_mode == NULL)
1986b59f45d0SHerbert Xu 		goto error;
1987b59f45d0SHerbert Xu 
198872cb6962SHerbert Xu 	x->km.state = XFRM_STATE_VALID;
198972cb6962SHerbert Xu 
199072cb6962SHerbert Xu error:
199172cb6962SHerbert Xu 	return err;
199272cb6962SHerbert Xu }
199372cb6962SHerbert Xu 
199472cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state);
19951da177e4SLinus Torvalds 
19961da177e4SLinus Torvalds void __init xfrm_state_init(void)
19971da177e4SLinus Torvalds {
1998f034b5d4SDavid S. Miller 	unsigned int sz;
19991da177e4SLinus Torvalds 
2000f034b5d4SDavid S. Miller 	sz = sizeof(struct hlist_head) * 8;
2001f034b5d4SDavid S. Miller 
200244e36b42SDavid S. Miller 	xfrm_state_bydst = xfrm_hash_alloc(sz);
200344e36b42SDavid S. Miller 	xfrm_state_bysrc = xfrm_hash_alloc(sz);
200444e36b42SDavid S. Miller 	xfrm_state_byspi = xfrm_hash_alloc(sz);
2005f034b5d4SDavid S. Miller 	if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
2006f034b5d4SDavid S. Miller 		panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
2007f034b5d4SDavid S. Miller 	xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
2008f034b5d4SDavid S. Miller 
2009c4028958SDavid Howells 	INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
20101da177e4SLinus Torvalds }
20111da177e4SLinus Torvalds 
2012ab5f5e8bSJoy Latten #ifdef CONFIG_AUDITSYSCALL
2013*cf35f43eSIlpo Järvinen static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
2014ab5f5e8bSJoy Latten 				     struct audit_buffer *audit_buf)
2015ab5f5e8bSJoy Latten {
201668277accSPaul Moore 	struct xfrm_sec_ctx *ctx = x->security;
201768277accSPaul Moore 	u32 spi = ntohl(x->id.spi);
201868277accSPaul Moore 
201968277accSPaul Moore 	if (ctx)
2020ab5f5e8bSJoy Latten 		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
202168277accSPaul Moore 				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
2022ab5f5e8bSJoy Latten 
2023ab5f5e8bSJoy Latten 	switch(x->props.family) {
2024ab5f5e8bSJoy Latten 	case AF_INET:
202568277accSPaul Moore 		audit_log_format(audit_buf,
202668277accSPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2027ab5f5e8bSJoy Latten 				 NIPQUAD(x->props.saddr.a4),
2028ab5f5e8bSJoy Latten 				 NIPQUAD(x->id.daddr.a4));
2029ab5f5e8bSJoy Latten 		break;
2030ab5f5e8bSJoy Latten 	case AF_INET6:
2031ab5f5e8bSJoy Latten 		audit_log_format(audit_buf,
2032ab5f5e8bSJoy Latten 				 " src=" NIP6_FMT " dst=" NIP6_FMT,
203368277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->props.saddr.a6),
203468277accSPaul Moore 				 NIP6(*(struct in6_addr *)x->id.daddr.a6));
2035ab5f5e8bSJoy Latten 		break;
2036ab5f5e8bSJoy Latten 	}
203768277accSPaul Moore 
203868277accSPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2039ab5f5e8bSJoy Latten }
2040ab5f5e8bSJoy Latten 
2041*cf35f43eSIlpo Järvinen static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
2042afeb14b4SPaul Moore 				      struct audit_buffer *audit_buf)
2043afeb14b4SPaul Moore {
2044afeb14b4SPaul Moore 	struct iphdr *iph4;
2045afeb14b4SPaul Moore 	struct ipv6hdr *iph6;
2046afeb14b4SPaul Moore 
2047afeb14b4SPaul Moore 	switch (family) {
2048afeb14b4SPaul Moore 	case AF_INET:
2049afeb14b4SPaul Moore 		iph4 = ip_hdr(skb);
2050afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2051afeb14b4SPaul Moore 				 " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
2052afeb14b4SPaul Moore 				 NIPQUAD(iph4->saddr),
2053afeb14b4SPaul Moore 				 NIPQUAD(iph4->daddr));
2054afeb14b4SPaul Moore 		break;
2055afeb14b4SPaul Moore 	case AF_INET6:
2056afeb14b4SPaul Moore 		iph6 = ipv6_hdr(skb);
2057afeb14b4SPaul Moore 		audit_log_format(audit_buf,
2058afeb14b4SPaul Moore 				 " src=" NIP6_FMT " dst=" NIP6_FMT
2059afeb14b4SPaul Moore 				 " flowlbl=0x%x%x%x",
2060afeb14b4SPaul Moore 				 NIP6(iph6->saddr),
2061afeb14b4SPaul Moore 				 NIP6(iph6->daddr),
2062afeb14b4SPaul Moore 				 iph6->flow_lbl[0] & 0x0f,
2063afeb14b4SPaul Moore 				 iph6->flow_lbl[1],
2064afeb14b4SPaul Moore 				 iph6->flow_lbl[2]);
2065afeb14b4SPaul Moore 		break;
2066afeb14b4SPaul Moore 	}
2067afeb14b4SPaul Moore }
2068afeb14b4SPaul Moore 
206968277accSPaul Moore void xfrm_audit_state_add(struct xfrm_state *x, int result,
207068277accSPaul Moore 			  u32 auid, u32 secid)
2071ab5f5e8bSJoy Latten {
2072ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2073ab5f5e8bSJoy Latten 
2074afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-add");
2075ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2076ab5f5e8bSJoy Latten 		return;
2077afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2078afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2079afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2080ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2081ab5f5e8bSJoy Latten }
2082ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
2083ab5f5e8bSJoy Latten 
208468277accSPaul Moore void xfrm_audit_state_delete(struct xfrm_state *x, int result,
208568277accSPaul Moore 			     u32 auid, u32 secid)
2086ab5f5e8bSJoy Latten {
2087ab5f5e8bSJoy Latten 	struct audit_buffer *audit_buf;
2088ab5f5e8bSJoy Latten 
2089afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SAD-delete");
2090ab5f5e8bSJoy Latten 	if (audit_buf == NULL)
2091ab5f5e8bSJoy Latten 		return;
2092afeb14b4SPaul Moore 	xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
2093afeb14b4SPaul Moore 	xfrm_audit_helper_sainfo(x, audit_buf);
2094afeb14b4SPaul Moore 	audit_log_format(audit_buf, " res=%u", result);
2095ab5f5e8bSJoy Latten 	audit_log_end(audit_buf);
2096ab5f5e8bSJoy Latten }
2097ab5f5e8bSJoy Latten EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
2098afeb14b4SPaul Moore 
2099afeb14b4SPaul Moore void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
2100afeb14b4SPaul Moore 				      struct sk_buff *skb)
2101afeb14b4SPaul Moore {
2102afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2103afeb14b4SPaul Moore 	u32 spi;
2104afeb14b4SPaul Moore 
2105afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replay-overflow");
2106afeb14b4SPaul Moore 	if (audit_buf == NULL)
2107afeb14b4SPaul Moore 		return;
2108afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2109afeb14b4SPaul Moore 	/* don't record the sequence number because it's inherent in this kind
2110afeb14b4SPaul Moore 	 * of audit message */
2111afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2112afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
2113afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2114afeb14b4SPaul Moore }
2115afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow);
2116afeb14b4SPaul Moore 
2117afeb14b4SPaul Moore static void xfrm_audit_state_replay(struct xfrm_state *x,
2118afeb14b4SPaul Moore 			     struct sk_buff *skb, __be32 net_seq)
2119afeb14b4SPaul Moore {
2120afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2121afeb14b4SPaul Moore 	u32 spi;
2122afeb14b4SPaul Moore 
2123afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-replayed-pkt");
2124afeb14b4SPaul Moore 	if (audit_buf == NULL)
2125afeb14b4SPaul Moore 		return;
2126afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2127afeb14b4SPaul Moore 	spi = ntohl(x->id.spi);
2128afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2129afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2130afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2131afeb14b4SPaul Moore }
2132afeb14b4SPaul Moore 
2133afeb14b4SPaul Moore void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family)
2134afeb14b4SPaul Moore {
2135afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2136afeb14b4SPaul Moore 
2137afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2138afeb14b4SPaul Moore 	if (audit_buf == NULL)
2139afeb14b4SPaul Moore 		return;
2140afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2141afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2142afeb14b4SPaul Moore }
2143afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple);
2144afeb14b4SPaul Moore 
2145afeb14b4SPaul Moore void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
2146afeb14b4SPaul Moore 			       __be32 net_spi, __be32 net_seq)
2147afeb14b4SPaul Moore {
2148afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2149afeb14b4SPaul Moore 	u32 spi;
2150afeb14b4SPaul Moore 
2151afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-notfound");
2152afeb14b4SPaul Moore 	if (audit_buf == NULL)
2153afeb14b4SPaul Moore 		return;
2154afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, family, audit_buf);
2155afeb14b4SPaul Moore 	spi = ntohl(net_spi);
2156afeb14b4SPaul Moore 	audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2157afeb14b4SPaul Moore 			 spi, spi, ntohl(net_seq));
2158afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2159afeb14b4SPaul Moore }
2160afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound);
2161afeb14b4SPaul Moore 
2162afeb14b4SPaul Moore void xfrm_audit_state_icvfail(struct xfrm_state *x,
2163afeb14b4SPaul Moore 			      struct sk_buff *skb, u8 proto)
2164afeb14b4SPaul Moore {
2165afeb14b4SPaul Moore 	struct audit_buffer *audit_buf;
2166afeb14b4SPaul Moore 	__be32 net_spi;
2167afeb14b4SPaul Moore 	__be32 net_seq;
2168afeb14b4SPaul Moore 
2169afeb14b4SPaul Moore 	audit_buf = xfrm_audit_start("SA-icv-failure");
2170afeb14b4SPaul Moore 	if (audit_buf == NULL)
2171afeb14b4SPaul Moore 		return;
2172afeb14b4SPaul Moore 	xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf);
2173afeb14b4SPaul Moore 	if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) {
2174afeb14b4SPaul Moore 		u32 spi = ntohl(net_spi);
2175afeb14b4SPaul Moore 		audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u",
2176afeb14b4SPaul Moore 				 spi, spi, ntohl(net_seq));
2177afeb14b4SPaul Moore 	}
2178afeb14b4SPaul Moore 	audit_log_end(audit_buf);
2179afeb14b4SPaul Moore }
2180afeb14b4SPaul Moore EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail);
2181ab5f5e8bSJoy Latten #endif /* CONFIG_AUDITSYSCALL */
2182