xref: /linux/net/key/af_key.c (revision 87536a81e1f52409b45333ce8cac415a1218163c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * net/key/af_key.c	An implementation of PF_KEYv2 sockets.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Authors:	Maxim Giryaev	<gem@asplinux.ru>
101da177e4SLinus Torvalds  *		David S. Miller	<davem@redhat.com>
111da177e4SLinus Torvalds  *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
121da177e4SLinus Torvalds  *		Kunihiro Ishiguro <kunihiro@ipinfusion.com>
131da177e4SLinus Torvalds  *		Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
141da177e4SLinus Torvalds  *		Derek Atkins <derek@ihtfp.com>
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
174fc268d2SRandy Dunlap #include <linux/capability.h>
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/socket.h>
211da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
221da177e4SLinus Torvalds #include <linux/ipsec.h>
231da177e4SLinus Torvalds #include <linux/skbuff.h>
241da177e4SLinus Torvalds #include <linux/rtnetlink.h>
251da177e4SLinus Torvalds #include <linux/in.h>
261da177e4SLinus Torvalds #include <linux/in6.h>
271da177e4SLinus Torvalds #include <linux/proc_fs.h>
281da177e4SLinus Torvalds #include <linux/init.h>
295a0e3ad6STejun Heo #include <linux/slab.h>
30457c4cbcSEric W. Biederman #include <net/net_namespace.h>
313fa87a32SAlexey Dobriyan #include <net/netns/generic.h>
321da177e4SLinus Torvalds #include <net/xfrm.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <net/sock.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
371da177e4SLinus Torvalds #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
381da177e4SLinus Torvalds 
39f99189b1SEric Dumazet static int pfkey_net_id __read_mostly;
403fa87a32SAlexey Dobriyan struct netns_pfkey {
411da177e4SLinus Torvalds 	/* List of all pfkey sockets. */
423fa87a32SAlexey Dobriyan 	struct hlist_head table;
433fa87a32SAlexey Dobriyan 	atomic_t socks_nr;
443fa87a32SAlexey Dobriyan };
457f6b9dbdSstephen hemminger static DEFINE_MUTEX(pfkey_mutex);
461da177e4SLinus Torvalds 
47bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
48e473fcb4SMathias Krause static const struct xfrm_mark dummy_mark = {0, 0};
491da177e4SLinus Torvalds struct pfkey_sock {
501da177e4SLinus Torvalds 	/* struct sock must be the first member of struct pfkey_sock */
511da177e4SLinus Torvalds 	struct sock	sk;
521da177e4SLinus Torvalds 	int		registered;
531da177e4SLinus Torvalds 	int		promisc;
5483321d6bSTimo Teras 
5583321d6bSTimo Teras 	struct {
5683321d6bSTimo Teras 		uint8_t		msg_version;
5715e47304SEric W. Biederman 		uint32_t	msg_portid;
5883321d6bSTimo Teras 		int		(*dump)(struct pfkey_sock *sk);
5983321d6bSTimo Teras 		void		(*done)(struct pfkey_sock *sk);
6083321d6bSTimo Teras 		union {
6183321d6bSTimo Teras 			struct xfrm_policy_walk	policy;
6283321d6bSTimo Teras 			struct xfrm_state_walk	state;
6383321d6bSTimo Teras 		} u;
6412a169e7SHerbert Xu 		struct sk_buff	*skb;
6583321d6bSTimo Teras 	} dump;
661da177e4SLinus Torvalds };
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	return (struct pfkey_sock *)sk;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
734c93fbb0SDavid S. Miller static int pfkey_can_dump(const struct sock *sk)
7483321d6bSTimo Teras {
7583321d6bSTimo Teras 	if (3 * atomic_read(&sk->sk_rmem_alloc) <= 2 * sk->sk_rcvbuf)
7683321d6bSTimo Teras 		return 1;
7783321d6bSTimo Teras 	return 0;
7883321d6bSTimo Teras }
7983321d6bSTimo Teras 
8005238204STimo Teras static void pfkey_terminate_dump(struct pfkey_sock *pfk)
8183321d6bSTimo Teras {
8205238204STimo Teras 	if (pfk->dump.dump) {
8312a169e7SHerbert Xu 		if (pfk->dump.skb) {
8412a169e7SHerbert Xu 			kfree_skb(pfk->dump.skb);
8512a169e7SHerbert Xu 			pfk->dump.skb = NULL;
8612a169e7SHerbert Xu 		}
8783321d6bSTimo Teras 		pfk->dump.done(pfk);
8883321d6bSTimo Teras 		pfk->dump.dump = NULL;
8983321d6bSTimo Teras 		pfk->dump.done = NULL;
9005238204STimo Teras 	}
9183321d6bSTimo Teras }
9283321d6bSTimo Teras 
931da177e4SLinus Torvalds static void pfkey_sock_destruct(struct sock *sk)
941da177e4SLinus Torvalds {
953fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
963fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
973fa87a32SAlexey Dobriyan 
9805238204STimo Teras 	pfkey_terminate_dump(pfkey_sk(sk));
991da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_receive_queue);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	if (!sock_flag(sk, SOCK_DEAD)) {
102207024b9Sstephen hemminger 		pr_err("Attempt to release alive pfkey socket: %p\n", sk);
1031da177e4SLinus Torvalds 		return;
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 
106547b792cSIlpo Järvinen 	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
107547b792cSIlpo Järvinen 	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
1081da177e4SLinus Torvalds 
1093fa87a32SAlexey Dobriyan 	atomic_dec(&net_pfkey->socks_nr);
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
11290ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds static void pfkey_insert(struct sock *sk)
1151da177e4SLinus Torvalds {
1163fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
1173fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1183fa87a32SAlexey Dobriyan 
1197f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1207f6b9dbdSstephen hemminger 	sk_add_node_rcu(sk, &net_pfkey->table);
1217f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds static void pfkey_remove(struct sock *sk)
1251da177e4SLinus Torvalds {
1267f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1277f6b9dbdSstephen hemminger 	sk_del_node_init_rcu(sk);
1287f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds static struct proto key_proto = {
1321da177e4SLinus Torvalds 	.name	  = "KEY",
1331da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
1341da177e4SLinus Torvalds 	.obj_size = sizeof(struct pfkey_sock),
1351da177e4SLinus Torvalds };
1361da177e4SLinus Torvalds 
1373f378b68SEric Paris static int pfkey_create(struct net *net, struct socket *sock, int protocol,
1383f378b68SEric Paris 			int kern)
1391da177e4SLinus Torvalds {
1403fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1411da177e4SLinus Torvalds 	struct sock *sk;
1421da177e4SLinus Torvalds 	int err;
1431da177e4SLinus Torvalds 
144df008c91SEric W. Biederman 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1451da177e4SLinus Torvalds 		return -EPERM;
1461da177e4SLinus Torvalds 	if (sock->type != SOCK_RAW)
1471da177e4SLinus Torvalds 		return -ESOCKTNOSUPPORT;
1481da177e4SLinus Torvalds 	if (protocol != PF_KEY_V2)
1491da177e4SLinus Torvalds 		return -EPROTONOSUPPORT;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	err = -ENOMEM;
1526257ff21SPavel Emelyanov 	sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto);
1531da177e4SLinus Torvalds 	if (sk == NULL)
1541da177e4SLinus Torvalds 		goto out;
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	sock->ops = &pfkey_ops;
1571da177e4SLinus Torvalds 	sock_init_data(sock, sk);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	sk->sk_family = PF_KEY;
1601da177e4SLinus Torvalds 	sk->sk_destruct = pfkey_sock_destruct;
1611da177e4SLinus Torvalds 
1623fa87a32SAlexey Dobriyan 	atomic_inc(&net_pfkey->socks_nr);
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	pfkey_insert(sk);
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	return 0;
1671da177e4SLinus Torvalds out:
1681da177e4SLinus Torvalds 	return err;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds static int pfkey_release(struct socket *sock)
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	if (!sk)
1761da177e4SLinus Torvalds 		return 0;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	pfkey_remove(sk);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	sock_orphan(sk);
1811da177e4SLinus Torvalds 	sock->sk = NULL;
1821da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_write_queue);
1837f6b9dbdSstephen hemminger 
1847f6b9dbdSstephen hemminger 	synchronize_rcu();
1851da177e4SLinus Torvalds 	sock_put(sk);
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	return 0;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
191dd0fc66fSAl Viro 			       gfp_t allocation, struct sock *sk)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	int err = -ENOBUFS;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	sock_hold(sk);
1961da177e4SLinus Torvalds 	if (*skb2 == NULL) {
1971da177e4SLinus Torvalds 		if (atomic_read(&skb->users) != 1) {
1981da177e4SLinus Torvalds 			*skb2 = skb_clone(skb, allocation);
1991da177e4SLinus Torvalds 		} else {
2001da177e4SLinus Torvalds 			*skb2 = skb;
2011da177e4SLinus Torvalds 			atomic_inc(&skb->users);
2021da177e4SLinus Torvalds 		}
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 	if (*skb2 != NULL) {
2051da177e4SLinus Torvalds 		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
2061da177e4SLinus Torvalds 			skb_set_owner_r(*skb2, sk);
2071da177e4SLinus Torvalds 			skb_queue_tail(&sk->sk_receive_queue, *skb2);
2081da177e4SLinus Torvalds 			sk->sk_data_ready(sk, (*skb2)->len);
2091da177e4SLinus Torvalds 			*skb2 = NULL;
2101da177e4SLinus Torvalds 			err = 0;
2111da177e4SLinus Torvalds 		}
2121da177e4SLinus Torvalds 	}
2131da177e4SLinus Torvalds 	sock_put(sk);
2141da177e4SLinus Torvalds 	return err;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Send SKB to all pfkey sockets matching selected criteria.  */
2181da177e4SLinus Torvalds #define BROADCAST_ALL		0
2191da177e4SLinus Torvalds #define BROADCAST_ONE		1
2201da177e4SLinus Torvalds #define BROADCAST_REGISTERED	2
2211da177e4SLinus Torvalds #define BROADCAST_PROMISC_ONLY	4
222dd0fc66fSAl Viro static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
22307fb0f17SAlexey Dobriyan 			   int broadcast_flags, struct sock *one_sk,
22407fb0f17SAlexey Dobriyan 			   struct net *net)
2251da177e4SLinus Torvalds {
2263fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
2271da177e4SLinus Torvalds 	struct sock *sk;
2281da177e4SLinus Torvalds 	struct sk_buff *skb2 = NULL;
2291da177e4SLinus Torvalds 	int err = -ESRCH;
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	/* XXX Do we need something like netlink_overrun?  I think
2321da177e4SLinus Torvalds 	 * XXX PF_KEY socket apps will not mind current behavior.
2331da177e4SLinus Torvalds 	 */
2341da177e4SLinus Torvalds 	if (!skb)
2351da177e4SLinus Torvalds 		return -ENOMEM;
2361da177e4SLinus Torvalds 
2377f6b9dbdSstephen hemminger 	rcu_read_lock();
238b67bfe0dSSasha Levin 	sk_for_each_rcu(sk, &net_pfkey->table) {
2391da177e4SLinus Torvalds 		struct pfkey_sock *pfk = pfkey_sk(sk);
2401da177e4SLinus Torvalds 		int err2;
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 		/* Yes, it means that if you are meant to receive this
2431da177e4SLinus Torvalds 		 * pfkey message you receive it twice as promiscuous
2441da177e4SLinus Torvalds 		 * socket.
2451da177e4SLinus Torvalds 		 */
2461da177e4SLinus Torvalds 		if (pfk->promisc)
2471da177e4SLinus Torvalds 			pfkey_broadcast_one(skb, &skb2, allocation, sk);
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 		/* the exact target will be processed later */
2501da177e4SLinus Torvalds 		if (sk == one_sk)
2511da177e4SLinus Torvalds 			continue;
2521da177e4SLinus Torvalds 		if (broadcast_flags != BROADCAST_ALL) {
2531da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_PROMISC_ONLY)
2541da177e4SLinus Torvalds 				continue;
2551da177e4SLinus Torvalds 			if ((broadcast_flags & BROADCAST_REGISTERED) &&
2561da177e4SLinus Torvalds 			    !pfk->registered)
2571da177e4SLinus Torvalds 				continue;
2581da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_ONE)
2591da177e4SLinus Torvalds 				continue;
2601da177e4SLinus Torvalds 		}
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 		err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk);
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 		/* Error is cleare after succecful sending to at least one
2651da177e4SLinus Torvalds 		 * registered KM */
2661da177e4SLinus Torvalds 		if ((broadcast_flags & BROADCAST_REGISTERED) && err)
2671da177e4SLinus Torvalds 			err = err2;
2681da177e4SLinus Torvalds 	}
2697f6b9dbdSstephen hemminger 	rcu_read_unlock();
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if (one_sk != NULL)
2721da177e4SLinus Torvalds 		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	kfree_skb(skb2);
2751da177e4SLinus Torvalds 	kfree_skb(skb);
2761da177e4SLinus Torvalds 	return err;
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
27905238204STimo Teras static int pfkey_do_dump(struct pfkey_sock *pfk)
28005238204STimo Teras {
28112a169e7SHerbert Xu 	struct sadb_msg *hdr;
28205238204STimo Teras 	int rc;
28305238204STimo Teras 
28405238204STimo Teras 	rc = pfk->dump.dump(pfk);
28505238204STimo Teras 	if (rc == -ENOBUFS)
28605238204STimo Teras 		return 0;
28705238204STimo Teras 
28812a169e7SHerbert Xu 	if (pfk->dump.skb) {
28912a169e7SHerbert Xu 		if (!pfkey_can_dump(&pfk->sk))
29012a169e7SHerbert Xu 			return 0;
29112a169e7SHerbert Xu 
29212a169e7SHerbert Xu 		hdr = (struct sadb_msg *) pfk->dump.skb->data;
29312a169e7SHerbert Xu 		hdr->sadb_msg_seq = 0;
29412a169e7SHerbert Xu 		hdr->sadb_msg_errno = rc;
29512a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
29607fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
29712a169e7SHerbert Xu 		pfk->dump.skb = NULL;
29812a169e7SHerbert Xu 	}
29912a169e7SHerbert Xu 
30005238204STimo Teras 	pfkey_terminate_dump(pfk);
30105238204STimo Teras 	return rc;
30205238204STimo Teras }
30305238204STimo Teras 
3044c93fbb0SDavid S. Miller static inline void pfkey_hdr_dup(struct sadb_msg *new,
3054c93fbb0SDavid S. Miller 				 const struct sadb_msg *orig)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds 	*new = *orig;
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
3104c93fbb0SDavid S. Miller static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL);
3131da177e4SLinus Torvalds 	struct sadb_msg *hdr;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	if (!skb)
3161da177e4SLinus Torvalds 		return -ENOBUFS;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	/* Woe be to the platform trying to support PFKEY yet
3191da177e4SLinus Torvalds 	 * having normal errnos outside the 1-255 range, inclusive.
3201da177e4SLinus Torvalds 	 */
3211da177e4SLinus Torvalds 	err = -err;
3221da177e4SLinus Torvalds 	if (err == ERESTARTSYS ||
3231da177e4SLinus Torvalds 	    err == ERESTARTNOHAND ||
3241da177e4SLinus Torvalds 	    err == ERESTARTNOINTR)
3251da177e4SLinus Torvalds 		err = EINTR;
3261da177e4SLinus Torvalds 	if (err >= 512)
3271da177e4SLinus Torvalds 		err = EINVAL;
32809a62660SKris Katterjohn 	BUG_ON(err <= 0 || err >= 256);
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
3311da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
3321da177e4SLinus Torvalds 	hdr->sadb_msg_errno = (uint8_t) err;
3331da177e4SLinus Torvalds 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
3341da177e4SLinus Torvalds 			     sizeof(uint64_t));
3351da177e4SLinus Torvalds 
33607fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
3418603b955SMathias Krause static const u8 sadb_ext_min_len[] = {
3421da177e4SLinus Torvalds 	[SADB_EXT_RESERVED]		= (u8) 0,
3431da177e4SLinus Torvalds 	[SADB_EXT_SA]			= (u8) sizeof(struct sadb_sa),
3441da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_CURRENT]	= (u8) sizeof(struct sadb_lifetime),
3451da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_HARD]	= (u8) sizeof(struct sadb_lifetime),
3461da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_SOFT]	= (u8) sizeof(struct sadb_lifetime),
3471da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_SRC]		= (u8) sizeof(struct sadb_address),
3481da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_DST]		= (u8) sizeof(struct sadb_address),
3491da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_PROXY]	= (u8) sizeof(struct sadb_address),
3501da177e4SLinus Torvalds 	[SADB_EXT_KEY_AUTH]		= (u8) sizeof(struct sadb_key),
3511da177e4SLinus Torvalds 	[SADB_EXT_KEY_ENCRYPT]		= (u8) sizeof(struct sadb_key),
3521da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_SRC]		= (u8) sizeof(struct sadb_ident),
3531da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_DST]		= (u8) sizeof(struct sadb_ident),
3541da177e4SLinus Torvalds 	[SADB_EXT_SENSITIVITY]		= (u8) sizeof(struct sadb_sens),
3551da177e4SLinus Torvalds 	[SADB_EXT_PROPOSAL]		= (u8) sizeof(struct sadb_prop),
3561da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_AUTH]	= (u8) sizeof(struct sadb_supported),
3571da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_ENCRYPT]	= (u8) sizeof(struct sadb_supported),
3581da177e4SLinus Torvalds 	[SADB_EXT_SPIRANGE]		= (u8) sizeof(struct sadb_spirange),
3591da177e4SLinus Torvalds 	[SADB_X_EXT_KMPRIVATE]		= (u8) sizeof(struct sadb_x_kmprivate),
3601da177e4SLinus Torvalds 	[SADB_X_EXT_POLICY]		= (u8) sizeof(struct sadb_x_policy),
3611da177e4SLinus Torvalds 	[SADB_X_EXT_SA2]		= (u8) sizeof(struct sadb_x_sa2),
3621da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_TYPE]		= (u8) sizeof(struct sadb_x_nat_t_type),
3631da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_SPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3641da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_DPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3651da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),
366df71837dSTrent Jaeger 	[SADB_X_EXT_SEC_CTX]		= (u8) sizeof(struct sadb_x_sec_ctx),
36713c1d189SArnaud Ebalard 	[SADB_X_EXT_KMADDRESS]		= (u8) sizeof(struct sadb_x_kmaddress),
3681da177e4SLinus Torvalds };
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds /* Verify sadb_address_{len,prefixlen} against sa_family.  */
3714c93fbb0SDavid S. Miller static int verify_address_len(const void *p)
3721da177e4SLinus Torvalds {
3734c93fbb0SDavid S. Miller 	const struct sadb_address *sp = p;
3744c93fbb0SDavid S. Miller 	const struct sockaddr *addr = (const struct sockaddr *)(sp + 1);
3754c93fbb0SDavid S. Miller 	const struct sockaddr_in *sin;
376dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
3774c93fbb0SDavid S. Miller 	const struct sockaddr_in6 *sin6;
3781da177e4SLinus Torvalds #endif
3791da177e4SLinus Torvalds 	int len;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	switch (addr->sa_family) {
3821da177e4SLinus Torvalds 	case AF_INET:
383356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));
3841da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
3851da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 32)
3861da177e4SLinus Torvalds 			return -EINVAL;
3871da177e4SLinus Torvalds 		break;
388dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
3891da177e4SLinus Torvalds 	case AF_INET6:
390356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t));
3911da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
3921da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 128)
3931da177e4SLinus Torvalds 			return -EINVAL;
3941da177e4SLinus Torvalds 		break;
3951da177e4SLinus Torvalds #endif
3961da177e4SLinus Torvalds 	default:
3971da177e4SLinus Torvalds 		/* It is user using kernel to keep track of security
3981da177e4SLinus Torvalds 		 * associations for another protocol, such as
3991da177e4SLinus Torvalds 		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify
4001da177e4SLinus Torvalds 		 * lengths.
4011da177e4SLinus Torvalds 		 *
4021da177e4SLinus Torvalds 		 * XXX Actually, association/policy database is not yet
4031da177e4SLinus Torvalds 		 * XXX able to cope with arbitrary sockaddr families.
4041da177e4SLinus Torvalds 		 * XXX When it can, remove this -EINVAL.  -DaveM
4051da177e4SLinus Torvalds 		 */
4061da177e4SLinus Torvalds 		return -EINVAL;
4071da177e4SLinus Torvalds 		break;
4083ff50b79SStephen Hemminger 	}
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	return 0;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4134c93fbb0SDavid S. Miller static inline int pfkey_sec_ctx_len(const struct sadb_x_sec_ctx *sec_ctx)
414df71837dSTrent Jaeger {
415356f89e1SIlpo Järvinen 	return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) +
416356f89e1SIlpo Järvinen 			    sec_ctx->sadb_x_ctx_len,
417356f89e1SIlpo Järvinen 			    sizeof(uint64_t));
418df71837dSTrent Jaeger }
419df71837dSTrent Jaeger 
4204c93fbb0SDavid S. Miller static inline int verify_sec_ctx_len(const void *p)
421df71837dSTrent Jaeger {
4224c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx = p;
423298bb621SStephen Rothwell 	int len = sec_ctx->sadb_x_ctx_len;
424df71837dSTrent Jaeger 
425298bb621SStephen Rothwell 	if (len > PAGE_SIZE)
426df71837dSTrent Jaeger 		return -EINVAL;
427df71837dSTrent Jaeger 
428df71837dSTrent Jaeger 	len = pfkey_sec_ctx_len(sec_ctx);
429df71837dSTrent Jaeger 
430df71837dSTrent Jaeger 	if (sec_ctx->sadb_x_sec_len != len)
431df71837dSTrent Jaeger 		return -EINVAL;
432df71837dSTrent Jaeger 
433df71837dSTrent Jaeger 	return 0;
434df71837dSTrent Jaeger }
435df71837dSTrent Jaeger 
436*87536a81SNikolay Aleksandrov static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx,
437*87536a81SNikolay Aleksandrov 								     gfp_t gfp)
438df71837dSTrent Jaeger {
439df71837dSTrent Jaeger 	struct xfrm_user_sec_ctx *uctx = NULL;
440df71837dSTrent Jaeger 	int ctx_size = sec_ctx->sadb_x_ctx_len;
441df71837dSTrent Jaeger 
442*87536a81SNikolay Aleksandrov 	uctx = kmalloc((sizeof(*uctx)+ctx_size), gfp);
443df71837dSTrent Jaeger 
444df71837dSTrent Jaeger 	if (!uctx)
445df71837dSTrent Jaeger 		return NULL;
446df71837dSTrent Jaeger 
447df71837dSTrent Jaeger 	uctx->len = pfkey_sec_ctx_len(sec_ctx);
448df71837dSTrent Jaeger 	uctx->exttype = sec_ctx->sadb_x_sec_exttype;
449df71837dSTrent Jaeger 	uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi;
450df71837dSTrent Jaeger 	uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg;
451df71837dSTrent Jaeger 	uctx->ctx_len = sec_ctx->sadb_x_ctx_len;
452df71837dSTrent Jaeger 	memcpy(uctx + 1, sec_ctx + 1,
453df71837dSTrent Jaeger 	       uctx->ctx_len);
454df71837dSTrent Jaeger 
455df71837dSTrent Jaeger 	return uctx;
456df71837dSTrent Jaeger }
457df71837dSTrent Jaeger 
4584c93fbb0SDavid S. Miller static int present_and_same_family(const struct sadb_address *src,
4594c93fbb0SDavid S. Miller 				   const struct sadb_address *dst)
4601da177e4SLinus Torvalds {
4614c93fbb0SDavid S. Miller 	const struct sockaddr *s_addr, *d_addr;
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	if (!src || !dst)
4641da177e4SLinus Torvalds 		return 0;
4651da177e4SLinus Torvalds 
4664c93fbb0SDavid S. Miller 	s_addr = (const struct sockaddr *)(src + 1);
4674c93fbb0SDavid S. Miller 	d_addr = (const struct sockaddr *)(dst + 1);
4681da177e4SLinus Torvalds 	if (s_addr->sa_family != d_addr->sa_family)
4691da177e4SLinus Torvalds 		return 0;
4701da177e4SLinus Torvalds 	if (s_addr->sa_family != AF_INET
471dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
4721da177e4SLinus Torvalds 	    && s_addr->sa_family != AF_INET6
4731da177e4SLinus Torvalds #endif
4741da177e4SLinus Torvalds 		)
4751da177e4SLinus Torvalds 		return 0;
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds 	return 1;
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds 
4804c93fbb0SDavid S. Miller static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void **ext_hdrs)
4811da177e4SLinus Torvalds {
4824c93fbb0SDavid S. Miller 	const char *p = (char *) hdr;
4831da177e4SLinus Torvalds 	int len = skb->len;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	len -= sizeof(*hdr);
4861da177e4SLinus Torvalds 	p += sizeof(*hdr);
4871da177e4SLinus Torvalds 	while (len > 0) {
4884c93fbb0SDavid S. Miller 		const struct sadb_ext *ehdr = (const struct sadb_ext *) p;
4891da177e4SLinus Torvalds 		uint16_t ext_type;
4901da177e4SLinus Torvalds 		int ext_len;
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 		ext_len  = ehdr->sadb_ext_len;
4931da177e4SLinus Torvalds 		ext_len *= sizeof(uint64_t);
4941da177e4SLinus Torvalds 		ext_type = ehdr->sadb_ext_type;
4951da177e4SLinus Torvalds 		if (ext_len < sizeof(uint64_t) ||
4961da177e4SLinus Torvalds 		    ext_len > len ||
4971da177e4SLinus Torvalds 		    ext_type == SADB_EXT_RESERVED)
4981da177e4SLinus Torvalds 			return -EINVAL;
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 		if (ext_type <= SADB_EXT_MAX) {
5011da177e4SLinus Torvalds 			int min = (int) sadb_ext_min_len[ext_type];
5021da177e4SLinus Torvalds 			if (ext_len < min)
5031da177e4SLinus Torvalds 				return -EINVAL;
5041da177e4SLinus Torvalds 			if (ext_hdrs[ext_type-1] != NULL)
5051da177e4SLinus Torvalds 				return -EINVAL;
5061da177e4SLinus Torvalds 			if (ext_type == SADB_EXT_ADDRESS_SRC ||
5071da177e4SLinus Torvalds 			    ext_type == SADB_EXT_ADDRESS_DST ||
5081da177e4SLinus Torvalds 			    ext_type == SADB_EXT_ADDRESS_PROXY ||
5091da177e4SLinus Torvalds 			    ext_type == SADB_X_EXT_NAT_T_OA) {
5101da177e4SLinus Torvalds 				if (verify_address_len(p))
5111da177e4SLinus Torvalds 					return -EINVAL;
5121da177e4SLinus Torvalds 			}
513df71837dSTrent Jaeger 			if (ext_type == SADB_X_EXT_SEC_CTX) {
514df71837dSTrent Jaeger 				if (verify_sec_ctx_len(p))
515df71837dSTrent Jaeger 					return -EINVAL;
516df71837dSTrent Jaeger 			}
5174c93fbb0SDavid S. Miller 			ext_hdrs[ext_type-1] = (void *) p;
5181da177e4SLinus Torvalds 		}
5191da177e4SLinus Torvalds 		p   += ext_len;
5201da177e4SLinus Torvalds 		len -= ext_len;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 	return 0;
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds static uint16_t
5271da177e4SLinus Torvalds pfkey_satype2proto(uint8_t satype)
5281da177e4SLinus Torvalds {
5291da177e4SLinus Torvalds 	switch (satype) {
5301da177e4SLinus Torvalds 	case SADB_SATYPE_UNSPEC:
5311da177e4SLinus Torvalds 		return IPSEC_PROTO_ANY;
5321da177e4SLinus Torvalds 	case SADB_SATYPE_AH:
5331da177e4SLinus Torvalds 		return IPPROTO_AH;
5341da177e4SLinus Torvalds 	case SADB_SATYPE_ESP:
5351da177e4SLinus Torvalds 		return IPPROTO_ESP;
5361da177e4SLinus Torvalds 	case SADB_X_SATYPE_IPCOMP:
5371da177e4SLinus Torvalds 		return IPPROTO_COMP;
5381da177e4SLinus Torvalds 		break;
5391da177e4SLinus Torvalds 	default:
5401da177e4SLinus Torvalds 		return 0;
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 	/* NOTREACHED */
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds static uint8_t
5461da177e4SLinus Torvalds pfkey_proto2satype(uint16_t proto)
5471da177e4SLinus Torvalds {
5481da177e4SLinus Torvalds 	switch (proto) {
5491da177e4SLinus Torvalds 	case IPPROTO_AH:
5501da177e4SLinus Torvalds 		return SADB_SATYPE_AH;
5511da177e4SLinus Torvalds 	case IPPROTO_ESP:
5521da177e4SLinus Torvalds 		return SADB_SATYPE_ESP;
5531da177e4SLinus Torvalds 	case IPPROTO_COMP:
5541da177e4SLinus Torvalds 		return SADB_X_SATYPE_IPCOMP;
5551da177e4SLinus Torvalds 		break;
5561da177e4SLinus Torvalds 	default:
5571da177e4SLinus Torvalds 		return 0;
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 	/* NOTREACHED */
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds /* BTW, this scheme means that there is no way with PFKEY2 sockets to
5631da177e4SLinus Torvalds  * say specifically 'just raw sockets' as we encode them as 255.
5641da177e4SLinus Torvalds  */
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds static uint8_t pfkey_proto_to_xfrm(uint8_t proto)
5671da177e4SLinus Torvalds {
568a02cec21SEric Dumazet 	return proto == IPSEC_PROTO_ANY ? 0 : proto;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
5721da177e4SLinus Torvalds {
573a02cec21SEric Dumazet 	return proto ? proto : IPSEC_PROTO_ANY;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
5769e8b4ed8SYOSHIFUJI Hideaki static inline int pfkey_sockaddr_len(sa_family_t family)
5779e8b4ed8SYOSHIFUJI Hideaki {
5789e8b4ed8SYOSHIFUJI Hideaki 	switch (family) {
5799e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET:
5809e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in);
581dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
5829e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET6:
5839e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in6);
5849e8b4ed8SYOSHIFUJI Hideaki #endif
5859e8b4ed8SYOSHIFUJI Hideaki 	}
5869e8b4ed8SYOSHIFUJI Hideaki 	return 0;
5879e8b4ed8SYOSHIFUJI Hideaki }
5889e8b4ed8SYOSHIFUJI Hideaki 
5895f95ac91SYOSHIFUJI Hideaki static
5905f95ac91SYOSHIFUJI Hideaki int pfkey_sockaddr_extract(const struct sockaddr *sa, xfrm_address_t *xaddr)
5911da177e4SLinus Torvalds {
5925f95ac91SYOSHIFUJI Hideaki 	switch (sa->sa_family) {
5931da177e4SLinus Torvalds 	case AF_INET:
5941da177e4SLinus Torvalds 		xaddr->a4 =
5955f95ac91SYOSHIFUJI Hideaki 			((struct sockaddr_in *)sa)->sin_addr.s_addr;
5961da177e4SLinus Torvalds 		return AF_INET;
597dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
5981da177e4SLinus Torvalds 	case AF_INET6:
5991da177e4SLinus Torvalds 		memcpy(xaddr->a6,
6005f95ac91SYOSHIFUJI Hideaki 		       &((struct sockaddr_in6 *)sa)->sin6_addr,
6011da177e4SLinus Torvalds 		       sizeof(struct in6_addr));
6021da177e4SLinus Torvalds 		return AF_INET6;
6031da177e4SLinus Torvalds #endif
6045f95ac91SYOSHIFUJI Hideaki 	}
6051da177e4SLinus Torvalds 	return 0;
6061da177e4SLinus Torvalds }
6075f95ac91SYOSHIFUJI Hideaki 
6085f95ac91SYOSHIFUJI Hideaki static
6094c93fbb0SDavid S. Miller int pfkey_sadb_addr2xfrm_addr(const struct sadb_address *addr, xfrm_address_t *xaddr)
6105f95ac91SYOSHIFUJI Hideaki {
6115f95ac91SYOSHIFUJI Hideaki 	return pfkey_sockaddr_extract((struct sockaddr *)(addr + 1),
6125f95ac91SYOSHIFUJI Hideaki 				      xaddr);
6131da177e4SLinus Torvalds }
6141da177e4SLinus Torvalds 
6154c93fbb0SDavid S. Miller static struct  xfrm_state *pfkey_xfrm_state_lookup(struct net *net, const struct sadb_msg *hdr, void * const *ext_hdrs)
6161da177e4SLinus Torvalds {
6174c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
6184c93fbb0SDavid S. Miller 	const struct sadb_address *addr;
6191da177e4SLinus Torvalds 	uint16_t proto;
6201da177e4SLinus Torvalds 	unsigned short family;
6211da177e4SLinus Torvalds 	xfrm_address_t *xaddr;
6221da177e4SLinus Torvalds 
623ea110733SJoe Perches 	sa = ext_hdrs[SADB_EXT_SA - 1];
6241da177e4SLinus Torvalds 	if (sa == NULL)
6251da177e4SLinus Torvalds 		return NULL;
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
6281da177e4SLinus Torvalds 	if (proto == 0)
6291da177e4SLinus Torvalds 		return NULL;
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	/* sadb_address_len should be checked by caller */
632ea110733SJoe Perches 	addr = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];
6331da177e4SLinus Torvalds 	if (addr == NULL)
6341da177e4SLinus Torvalds 		return NULL;
6351da177e4SLinus Torvalds 
6364c93fbb0SDavid S. Miller 	family = ((const struct sockaddr *)(addr + 1))->sa_family;
6371da177e4SLinus Torvalds 	switch (family) {
6381da177e4SLinus Torvalds 	case AF_INET:
6394c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in *)(addr + 1))->sin_addr;
6401da177e4SLinus Torvalds 		break;
641dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6421da177e4SLinus Torvalds 	case AF_INET6:
6434c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in6 *)(addr + 1))->sin6_addr;
6441da177e4SLinus Torvalds 		break;
6451da177e4SLinus Torvalds #endif
6461da177e4SLinus Torvalds 	default:
6471da177e4SLinus Torvalds 		xaddr = NULL;
6481da177e4SLinus Torvalds 	}
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	if (!xaddr)
6511da177e4SLinus Torvalds 		return NULL;
6521da177e4SLinus Torvalds 
653bd55775cSJamal Hadi Salim 	return xfrm_state_lookup(net, DUMMY_MARK, xaddr, sa->sadb_sa_spi, proto, family);
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
6579e8b4ed8SYOSHIFUJI Hideaki 
6581da177e4SLinus Torvalds static int
6591da177e4SLinus Torvalds pfkey_sockaddr_size(sa_family_t family)
6601da177e4SLinus Torvalds {
6619e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family));
6621da177e4SLinus Torvalds }
6631da177e4SLinus Torvalds 
66455569ce2SKazunori MIYAZAWA static inline int pfkey_mode_from_xfrm(int mode)
66555569ce2SKazunori MIYAZAWA {
66655569ce2SKazunori MIYAZAWA 	switch(mode) {
66755569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TRANSPORT:
66855569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TRANSPORT;
66955569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TUNNEL:
67055569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TUNNEL;
67155569ce2SKazunori MIYAZAWA 	case XFRM_MODE_BEET:
67255569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_BEET;
67355569ce2SKazunori MIYAZAWA 	default:
67455569ce2SKazunori MIYAZAWA 		return -1;
67555569ce2SKazunori MIYAZAWA 	}
67655569ce2SKazunori MIYAZAWA }
67755569ce2SKazunori MIYAZAWA 
67855569ce2SKazunori MIYAZAWA static inline int pfkey_mode_to_xfrm(int mode)
67955569ce2SKazunori MIYAZAWA {
68055569ce2SKazunori MIYAZAWA 	switch(mode) {
68155569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_ANY:	/*XXX*/
68255569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TRANSPORT:
68355569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TRANSPORT;
68455569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TUNNEL:
68555569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TUNNEL;
68655569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_BEET:
68755569ce2SKazunori MIYAZAWA 		return XFRM_MODE_BEET;
68855569ce2SKazunori MIYAZAWA 	default:
68955569ce2SKazunori MIYAZAWA 		return -1;
69055569ce2SKazunori MIYAZAWA 	}
69155569ce2SKazunori MIYAZAWA }
69255569ce2SKazunori MIYAZAWA 
693183cad12SDavid S. Miller static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port,
694e5b56652SYOSHIFUJI Hideaki 					struct sockaddr *sa,
695e5b56652SYOSHIFUJI Hideaki 					unsigned short family)
696e5b56652SYOSHIFUJI Hideaki {
697e5b56652SYOSHIFUJI Hideaki 	switch (family) {
698e5b56652SYOSHIFUJI Hideaki 	case AF_INET:
699e5b56652SYOSHIFUJI Hideaki 	    {
700e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
701e5b56652SYOSHIFUJI Hideaki 		sin->sin_family = AF_INET;
702e5b56652SYOSHIFUJI Hideaki 		sin->sin_port = port;
703e5b56652SYOSHIFUJI Hideaki 		sin->sin_addr.s_addr = xaddr->a4;
704e5b56652SYOSHIFUJI Hideaki 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
705e5b56652SYOSHIFUJI Hideaki 		return 32;
706e5b56652SYOSHIFUJI Hideaki 	    }
707dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
708e5b56652SYOSHIFUJI Hideaki 	case AF_INET6:
709e5b56652SYOSHIFUJI Hideaki 	    {
710e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
711e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_family = AF_INET6;
712e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_port = port;
713e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_flowinfo = 0;
7144e3fd7a0SAlexey Dobriyan 		sin6->sin6_addr = *(struct in6_addr *)xaddr->a6;
715e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_scope_id = 0;
716e5b56652SYOSHIFUJI Hideaki 		return 128;
717e5b56652SYOSHIFUJI Hideaki 	    }
718e5b56652SYOSHIFUJI Hideaki #endif
719e5b56652SYOSHIFUJI Hideaki 	}
720e5b56652SYOSHIFUJI Hideaki 	return 0;
721e5b56652SYOSHIFUJI Hideaki }
722e5b56652SYOSHIFUJI Hideaki 
7234c93fbb0SDavid S. Miller static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
724050f009eSHerbert Xu 					      int add_keys, int hsc)
7251da177e4SLinus Torvalds {
7261da177e4SLinus Torvalds 	struct sk_buff *skb;
7271da177e4SLinus Torvalds 	struct sadb_msg *hdr;
7281da177e4SLinus Torvalds 	struct sadb_sa *sa;
7291da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
7301da177e4SLinus Torvalds 	struct sadb_address *addr;
7311da177e4SLinus Torvalds 	struct sadb_key *key;
7321da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
733df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
734df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
735df71837dSTrent Jaeger 	int ctx_size = 0;
7361da177e4SLinus Torvalds 	int size;
7371da177e4SLinus Torvalds 	int auth_key_size = 0;
7381da177e4SLinus Torvalds 	int encrypt_key_size = 0;
7391da177e4SLinus Torvalds 	int sockaddr_size;
7401da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
74155569ce2SKazunori MIYAZAWA 	int mode;
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds 	/* address family check */
7441da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
7451da177e4SLinus Torvalds 	if (!sockaddr_size)
7461da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
7491da177e4SLinus Torvalds 	   key(AE), (identity(SD),) (sensitivity)> */
7501da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
7511da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime) +
7521da177e4SLinus Torvalds 		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
7531da177e4SLinus Torvalds 		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
7541da177e4SLinus Torvalds 			sizeof(struct sadb_address)*2 +
7551da177e4SLinus Torvalds 				sockaddr_size*2 +
7561da177e4SLinus Torvalds 					sizeof(struct sadb_x_sa2);
757df71837dSTrent Jaeger 
758df71837dSTrent Jaeger 	if ((xfrm_ctx = x->security)) {
759df71837dSTrent Jaeger 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
760df71837dSTrent Jaeger 		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
761df71837dSTrent Jaeger 	}
762df71837dSTrent Jaeger 
7631da177e4SLinus Torvalds 	/* identity & sensitivity */
76470e94e66SYOSHIFUJI Hideaki / 吉藤英明 	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family))
7651da177e4SLinus Torvalds 		size += sizeof(struct sadb_address) + sockaddr_size;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	if (add_keys) {
7681da177e4SLinus Torvalds 		if (x->aalg && x->aalg->alg_key_len) {
7691da177e4SLinus Torvalds 			auth_key_size =
7701da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
7711da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + auth_key_size;
7721da177e4SLinus Torvalds 		}
7731da177e4SLinus Torvalds 		if (x->ealg && x->ealg->alg_key_len) {
7741da177e4SLinus Torvalds 			encrypt_key_size =
7751da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
7761da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + encrypt_key_size;
7771da177e4SLinus Torvalds 		}
7781da177e4SLinus Torvalds 	}
7791da177e4SLinus Torvalds 	if (x->encap)
7801da177e4SLinus Torvalds 		natt = x->encap;
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
7831da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_type);
7841da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
7851da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
7861da177e4SLinus Torvalds 	}
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
7891da177e4SLinus Torvalds 	if (skb == NULL)
7901da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	/* call should fill header later */
7931da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
7941da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
7951da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	/* sa */
7981da177e4SLinus Torvalds 	sa = (struct sadb_sa *)  skb_put(skb, sizeof(struct sadb_sa));
7991da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
8001da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
8011da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
8021da177e4SLinus Torvalds 	sa->sadb_sa_replay = x->props.replay_window;
8034f09f0bbSHerbert Xu 	switch (x->km.state) {
8044f09f0bbSHerbert Xu 	case XFRM_STATE_VALID:
8054f09f0bbSHerbert Xu 		sa->sadb_sa_state = x->km.dying ?
8064f09f0bbSHerbert Xu 			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
8074f09f0bbSHerbert Xu 		break;
8084f09f0bbSHerbert Xu 	case XFRM_STATE_ACQ:
8091da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
8104f09f0bbSHerbert Xu 		break;
8114f09f0bbSHerbert Xu 	default:
8121da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_DEAD;
8134f09f0bbSHerbert Xu 		break;
8144f09f0bbSHerbert Xu 	}
8151da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
8161da177e4SLinus Torvalds 	if (x->aalg) {
8171da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
8187e50f84cSJussi Kivilinna 		sa->sadb_sa_auth = (a && a->pfkey_supported) ?
8197e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8201da177e4SLinus Torvalds 	}
8211da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
8221da177e4SLinus Torvalds 	BUG_ON(x->ealg && x->calg);
8231da177e4SLinus Torvalds 	if (x->ealg) {
8241da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
8257e50f84cSJussi Kivilinna 		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
8267e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8271da177e4SLinus Torvalds 	}
8281da177e4SLinus Torvalds 	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
8291da177e4SLinus Torvalds 	if (x->calg) {
8301da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
8317e50f84cSJussi Kivilinna 		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
8327e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8331da177e4SLinus Torvalds 	}
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
8361da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_NOECN)
8371da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
8381da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
8391da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
840dd87147eSHerbert Xu 	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
841dd87147eSHerbert Xu 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds 	/* hard time */
8441da177e4SLinus Torvalds 	if (hsc & 2) {
8451da177e4SLinus Torvalds 		lifetime = (struct sadb_lifetime *)  skb_put(skb,
8461da177e4SLinus Torvalds 							     sizeof(struct sadb_lifetime));
8471da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8481da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8491da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
8501da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
8511da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
8521da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
8531da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
8541da177e4SLinus Torvalds 	}
8551da177e4SLinus Torvalds 	/* soft time */
8561da177e4SLinus Torvalds 	if (hsc & 1) {
8571da177e4SLinus Torvalds 		lifetime = (struct sadb_lifetime *)  skb_put(skb,
8581da177e4SLinus Torvalds 							     sizeof(struct sadb_lifetime));
8591da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8601da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8611da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
8621da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
8631da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
8641da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
8651da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 	/* current time */
8681da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
8691da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
8701da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
8711da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8721da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
8731da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = x->curlft.packets;
8741da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
8751da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
8761da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
8771da177e4SLinus Torvalds 	/* src address */
8781da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
8791da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
8801da177e4SLinus Torvalds 	addr->sadb_address_len =
8811da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
8821da177e4SLinus Torvalds 			sizeof(uint64_t);
8831da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
8841da177e4SLinus Torvalds 	/* "if the ports are non-zero, then the sadb_address_proto field,
8851da177e4SLinus Torvalds 	   normally zero, MUST be filled in with the transport
8861da177e4SLinus Torvalds 	   protocol's number." - RFC2367 */
8871da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
8881da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
8891da177e4SLinus Torvalds 
890e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
891e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
892e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
893e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
894e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
8951da177e4SLinus Torvalds 		BUG();
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	/* dst address */
8981da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
8991da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
9001da177e4SLinus Torvalds 	addr->sadb_address_len =
9011da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
9021da177e4SLinus Torvalds 			sizeof(uint64_t);
9031da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
9041da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
9051da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
9061da177e4SLinus Torvalds 
907e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
908e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
909e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
910e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
911e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
9121da177e4SLinus Torvalds 		BUG();
9131da177e4SLinus Torvalds 
91470e94e66SYOSHIFUJI Hideaki / 吉藤英明 	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr,
915e5b56652SYOSHIFUJI Hideaki 			     x->props.family)) {
916e5b56652SYOSHIFUJI Hideaki 		addr = (struct sadb_address*) skb_put(skb,
917e5b56652SYOSHIFUJI Hideaki 			sizeof(struct sadb_address)+sockaddr_size);
918e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_len =
919e5b56652SYOSHIFUJI Hideaki 			(sizeof(struct sadb_address)+sockaddr_size)/
920e5b56652SYOSHIFUJI Hideaki 			sizeof(uint64_t);
921e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
922e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_proto =
923e5b56652SYOSHIFUJI Hideaki 			pfkey_proto_from_xfrm(x->sel.proto);
924e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
925e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_reserved = 0;
926e5b56652SYOSHIFUJI Hideaki 
927e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
928e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
929e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
930e5b56652SYOSHIFUJI Hideaki 	}
931e5b56652SYOSHIFUJI Hideaki 
9321da177e4SLinus Torvalds 	/* auth key */
9331da177e4SLinus Torvalds 	if (add_keys && auth_key_size) {
9341da177e4SLinus Torvalds 		key = (struct sadb_key *) skb_put(skb,
9351da177e4SLinus Torvalds 						  sizeof(struct sadb_key)+auth_key_size);
9361da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
9371da177e4SLinus Torvalds 			sizeof(uint64_t);
9381da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
9391da177e4SLinus Torvalds 		key->sadb_key_bits = x->aalg->alg_key_len;
9401da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9411da177e4SLinus Torvalds 		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
9421da177e4SLinus Torvalds 	}
9431da177e4SLinus Torvalds 	/* encrypt key */
9441da177e4SLinus Torvalds 	if (add_keys && encrypt_key_size) {
9451da177e4SLinus Torvalds 		key = (struct sadb_key *) skb_put(skb,
9461da177e4SLinus Torvalds 						  sizeof(struct sadb_key)+encrypt_key_size);
9471da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) +
9481da177e4SLinus Torvalds 				     encrypt_key_size) / sizeof(uint64_t);
9491da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
9501da177e4SLinus Torvalds 		key->sadb_key_bits = x->ealg->alg_key_len;
9511da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9521da177e4SLinus Torvalds 		memcpy(key + 1, x->ealg->alg_key,
9531da177e4SLinus Torvalds 		       (x->ealg->alg_key_len+7)/8);
9541da177e4SLinus Torvalds 	}
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 	/* sa */
9571da177e4SLinus Torvalds 	sa2 = (struct sadb_x_sa2 *)  skb_put(skb, sizeof(struct sadb_x_sa2));
9581da177e4SLinus Torvalds 	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
9591da177e4SLinus Torvalds 	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
96055569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
96155569ce2SKazunori MIYAZAWA 		kfree_skb(skb);
96255569ce2SKazunori MIYAZAWA 		return ERR_PTR(-EINVAL);
96355569ce2SKazunori MIYAZAWA 	}
96455569ce2SKazunori MIYAZAWA 	sa2->sadb_x_sa2_mode = mode;
9651da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved1 = 0;
9661da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved2 = 0;
9671da177e4SLinus Torvalds 	sa2->sadb_x_sa2_sequence = 0;
9681da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reqid = x->props.reqid;
9691da177e4SLinus Torvalds 
9701da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
9711da177e4SLinus Torvalds 		struct sadb_x_nat_t_type *n_type;
9721da177e4SLinus Torvalds 		struct sadb_x_nat_t_port *n_port;
9731da177e4SLinus Torvalds 
9741da177e4SLinus Torvalds 		/* type */
9751da177e4SLinus Torvalds 		n_type = (struct sadb_x_nat_t_type*) skb_put(skb, sizeof(*n_type));
9761da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
9771da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
9781da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_type = natt->encap_type;
9791da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[0] = 0;
9801da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[1] = 0;
9811da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[2] = 0;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 		/* source port */
9841da177e4SLinus Torvalds 		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
9851da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
9861da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
9871da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
9881da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 		/* dest port */
9911da177e4SLinus Torvalds 		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
9921da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
9931da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
9941da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
9951da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
9961da177e4SLinus Torvalds 	}
9971da177e4SLinus Torvalds 
998df71837dSTrent Jaeger 	/* security context */
999df71837dSTrent Jaeger 	if (xfrm_ctx) {
1000df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
1001df71837dSTrent Jaeger 				sizeof(struct sadb_x_sec_ctx) + ctx_size);
1002df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len =
1003df71837dSTrent Jaeger 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
1004df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
1005df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
1006df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
1007df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
1008df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
1009df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
1010df71837dSTrent Jaeger 	}
1011df71837dSTrent Jaeger 
10121da177e4SLinus Torvalds 	return skb;
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
1015050f009eSHerbert Xu 
10164c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg(const struct xfrm_state *x)
1017050f009eSHerbert Xu {
1018050f009eSHerbert Xu 	struct sk_buff *skb;
1019050f009eSHerbert Xu 
1020050f009eSHerbert Xu 	skb = __pfkey_xfrm_state2msg(x, 1, 3);
1021050f009eSHerbert Xu 
1022050f009eSHerbert Xu 	return skb;
1023050f009eSHerbert Xu }
1024050f009eSHerbert Xu 
10254c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg_expire(const struct xfrm_state *x,
1026050f009eSHerbert Xu 							  int hsc)
1027050f009eSHerbert Xu {
1028050f009eSHerbert Xu 	return __pfkey_xfrm_state2msg(x, 0, hsc);
1029050f009eSHerbert Xu }
1030050f009eSHerbert Xu 
103107fb0f17SAlexey Dobriyan static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
10324c93fbb0SDavid S. Miller 						const struct sadb_msg *hdr,
10334c93fbb0SDavid S. Miller 						void * const *ext_hdrs)
10341da177e4SLinus Torvalds {
10351da177e4SLinus Torvalds 	struct xfrm_state *x;
10364c93fbb0SDavid S. Miller 	const struct sadb_lifetime *lifetime;
10374c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
10384c93fbb0SDavid S. Miller 	const struct sadb_key *key;
10394c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx;
10401da177e4SLinus Torvalds 	uint16_t proto;
10411da177e4SLinus Torvalds 	int err;
10421da177e4SLinus Torvalds 
10431da177e4SLinus Torvalds 
1044ea110733SJoe Perches 	sa = ext_hdrs[SADB_EXT_SA - 1];
10451da177e4SLinus Torvalds 	if (!sa ||
10461da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
10471da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
10481da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10491da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_ESP &&
10501da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_ENCRYPT-1])
10511da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10521da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_AH &&
10531da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_AUTH-1])
10541da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10551da177e4SLinus Torvalds 	if (!!ext_hdrs[SADB_EXT_LIFETIME_HARD-1] !=
10561da177e4SLinus Torvalds 	    !!ext_hdrs[SADB_EXT_LIFETIME_SOFT-1])
10571da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
10601da177e4SLinus Torvalds 	if (proto == 0)
10611da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10621da177e4SLinus Torvalds 
10631da177e4SLinus Torvalds 	/* default error is no buffer space */
10641da177e4SLinus Torvalds 	err = -ENOBUFS;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	/* RFC2367:
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds    Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_ADD message.
10691da177e4SLinus Torvalds    SADB_SASTATE_LARVAL SAs are created by SADB_GETSPI and it is not
10701da177e4SLinus Torvalds    sensible to add a new SA in the DYING or SADB_SASTATE_DEAD state.
10711da177e4SLinus Torvalds    Therefore, the sadb_sa_state field of all submitted SAs MUST be
10721da177e4SLinus Torvalds    SADB_SASTATE_MATURE and the kernel MUST return an error if this is
10731da177e4SLinus Torvalds    not true.
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 	   However, KAME setkey always uses SADB_SASTATE_LARVAL.
10761da177e4SLinus Torvalds 	   Hence, we have to _ignore_ sadb_sa_state, which is also reasonable.
10771da177e4SLinus Torvalds 	 */
10781da177e4SLinus Torvalds 	if (sa->sadb_sa_auth > SADB_AALG_MAX ||
10791da177e4SLinus Torvalds 	    (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP &&
10801da177e4SLinus Torvalds 	     sa->sadb_sa_encrypt > SADB_X_CALG_MAX) ||
10811da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt > SADB_EALG_MAX)
10821da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
1083ea110733SJoe Perches 	key = ext_hdrs[SADB_EXT_KEY_AUTH - 1];
10841da177e4SLinus Torvalds 	if (key != NULL &&
10851da177e4SLinus Torvalds 	    sa->sadb_sa_auth != SADB_X_AALG_NULL &&
10861da177e4SLinus Torvalds 	    ((key->sadb_key_bits+7) / 8 == 0 ||
10871da177e4SLinus Torvalds 	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
10881da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10891da177e4SLinus Torvalds 	key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
10901da177e4SLinus Torvalds 	if (key != NULL &&
10911da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt != SADB_EALG_NULL &&
10921da177e4SLinus Torvalds 	    ((key->sadb_key_bits+7) / 8 == 0 ||
10931da177e4SLinus Torvalds 	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
10941da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10951da177e4SLinus Torvalds 
109607fb0f17SAlexey Dobriyan 	x = xfrm_state_alloc(net);
10971da177e4SLinus Torvalds 	if (x == NULL)
10981da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 	x->id.proto = proto;
11011da177e4SLinus Torvalds 	x->id.spi = sa->sadb_sa_spi;
110233fce60dSFan Du 	x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay,
110333fce60dSFan Du 					(sizeof(x->replay.bitmap) * 8));
11041da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
11051da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_NOECN;
11061da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
11071da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_DECAP_DSCP;
1108dd87147eSHerbert Xu 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
1109dd87147eSHerbert Xu 		x->props.flags |= XFRM_STATE_NOPMTUDISC;
11101da177e4SLinus Torvalds 
1111ea110733SJoe Perches 	lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1];
11121da177e4SLinus Torvalds 	if (lifetime != NULL) {
11131da177e4SLinus Torvalds 		x->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11141da177e4SLinus Torvalds 		x->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11151da177e4SLinus Torvalds 		x->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11161da177e4SLinus Torvalds 		x->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11171da177e4SLinus Torvalds 	}
1118ea110733SJoe Perches 	lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT - 1];
11191da177e4SLinus Torvalds 	if (lifetime != NULL) {
11201da177e4SLinus Torvalds 		x->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11211da177e4SLinus Torvalds 		x->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11221da177e4SLinus Torvalds 		x->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11231da177e4SLinus Torvalds 		x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11241da177e4SLinus Torvalds 	}
1125df71837dSTrent Jaeger 
1126ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
1127df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
1128*87536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
1129df71837dSTrent Jaeger 
1130df71837dSTrent Jaeger 		if (!uctx)
1131df71837dSTrent Jaeger 			goto out;
1132df71837dSTrent Jaeger 
1133df71837dSTrent Jaeger 		err = security_xfrm_state_alloc(x, uctx);
1134df71837dSTrent Jaeger 		kfree(uctx);
1135df71837dSTrent Jaeger 
1136df71837dSTrent Jaeger 		if (err)
1137df71837dSTrent Jaeger 			goto out;
1138df71837dSTrent Jaeger 	}
1139df71837dSTrent Jaeger 
1140ea110733SJoe Perches 	key = ext_hdrs[SADB_EXT_KEY_AUTH - 1];
11411da177e4SLinus Torvalds 	if (sa->sadb_sa_auth) {
11421da177e4SLinus Torvalds 		int keysize = 0;
11431da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth);
11447e50f84cSJussi Kivilinna 		if (!a || !a->pfkey_supported) {
11451da177e4SLinus Torvalds 			err = -ENOSYS;
11461da177e4SLinus Torvalds 			goto out;
11471da177e4SLinus Torvalds 		}
11481da177e4SLinus Torvalds 		if (key)
11491da177e4SLinus Torvalds 			keysize = (key->sadb_key_bits + 7) / 8;
11501da177e4SLinus Torvalds 		x->aalg = kmalloc(sizeof(*x->aalg) + keysize, GFP_KERNEL);
11511da177e4SLinus Torvalds 		if (!x->aalg)
11521da177e4SLinus Torvalds 			goto out;
11531da177e4SLinus Torvalds 		strcpy(x->aalg->alg_name, a->name);
11541da177e4SLinus Torvalds 		x->aalg->alg_key_len = 0;
11551da177e4SLinus Torvalds 		if (key) {
11561da177e4SLinus Torvalds 			x->aalg->alg_key_len = key->sadb_key_bits;
11571da177e4SLinus Torvalds 			memcpy(x->aalg->alg_key, key+1, keysize);
11581da177e4SLinus Torvalds 		}
1159c20a66f4SMartin Willi 		x->aalg->alg_trunc_len = a->uinfo.auth.icv_truncbits;
11601da177e4SLinus Torvalds 		x->props.aalgo = sa->sadb_sa_auth;
11611da177e4SLinus Torvalds 		/* x->algo.flags = sa->sadb_sa_flags; */
11621da177e4SLinus Torvalds 	}
11631da177e4SLinus Torvalds 	if (sa->sadb_sa_encrypt) {
11641da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) {
11651da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt);
11667e50f84cSJussi Kivilinna 			if (!a || !a->pfkey_supported) {
11671da177e4SLinus Torvalds 				err = -ENOSYS;
11681da177e4SLinus Torvalds 				goto out;
11691da177e4SLinus Torvalds 			}
11701da177e4SLinus Torvalds 			x->calg = kmalloc(sizeof(*x->calg), GFP_KERNEL);
11711da177e4SLinus Torvalds 			if (!x->calg)
11721da177e4SLinus Torvalds 				goto out;
11731da177e4SLinus Torvalds 			strcpy(x->calg->alg_name, a->name);
11741da177e4SLinus Torvalds 			x->props.calgo = sa->sadb_sa_encrypt;
11751da177e4SLinus Torvalds 		} else {
11761da177e4SLinus Torvalds 			int keysize = 0;
11771da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt);
11787e50f84cSJussi Kivilinna 			if (!a || !a->pfkey_supported) {
11791da177e4SLinus Torvalds 				err = -ENOSYS;
11801da177e4SLinus Torvalds 				goto out;
11811da177e4SLinus Torvalds 			}
11821da177e4SLinus Torvalds 			key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
11831da177e4SLinus Torvalds 			if (key)
11841da177e4SLinus Torvalds 				keysize = (key->sadb_key_bits + 7) / 8;
11851da177e4SLinus Torvalds 			x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL);
11861da177e4SLinus Torvalds 			if (!x->ealg)
11871da177e4SLinus Torvalds 				goto out;
11881da177e4SLinus Torvalds 			strcpy(x->ealg->alg_name, a->name);
11891da177e4SLinus Torvalds 			x->ealg->alg_key_len = 0;
11901da177e4SLinus Torvalds 			if (key) {
11911da177e4SLinus Torvalds 				x->ealg->alg_key_len = key->sadb_key_bits;
11921da177e4SLinus Torvalds 				memcpy(x->ealg->alg_key, key+1, keysize);
11931da177e4SLinus Torvalds 			}
11941da177e4SLinus Torvalds 			x->props.ealgo = sa->sadb_sa_encrypt;
11951da177e4SLinus Torvalds 		}
11961da177e4SLinus Torvalds 	}
11971da177e4SLinus Torvalds 	/* x->algo.flags = sa->sadb_sa_flags; */
11981da177e4SLinus Torvalds 
11991da177e4SLinus Torvalds 	x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
12001da177e4SLinus Torvalds 						    &x->props.saddr);
12011da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1],
12021da177e4SLinus Torvalds 				  &x->id.daddr);
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_SA2-1]) {
12054c93fbb0SDavid S. Miller 		const struct sadb_x_sa2 *sa2 = ext_hdrs[SADB_X_EXT_SA2-1];
120655569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
120755569ce2SKazunori MIYAZAWA 		if (mode < 0) {
120855569ce2SKazunori MIYAZAWA 			err = -EINVAL;
120955569ce2SKazunori MIYAZAWA 			goto out;
121055569ce2SKazunori MIYAZAWA 		}
121155569ce2SKazunori MIYAZAWA 		x->props.mode = mode;
12121da177e4SLinus Torvalds 		x->props.reqid = sa2->sadb_x_sa2_reqid;
12131da177e4SLinus Torvalds 	}
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds 	if (ext_hdrs[SADB_EXT_ADDRESS_PROXY-1]) {
12164c93fbb0SDavid S. Miller 		const struct sadb_address *addr = ext_hdrs[SADB_EXT_ADDRESS_PROXY-1];
12171da177e4SLinus Torvalds 
12181da177e4SLinus Torvalds 		/* Nobody uses this, but we try. */
12191da177e4SLinus Torvalds 		x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
12201da177e4SLinus Torvalds 		x->sel.prefixlen_s = addr->sadb_address_prefixlen;
12211da177e4SLinus Torvalds 	}
12221da177e4SLinus Torvalds 
12234da51056SKazunori MIYAZAWA 	if (!x->sel.family)
12244a4b6271SJoy Latten 		x->sel.family = x->props.family;
12254a4b6271SJoy Latten 
12261da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
12274c93fbb0SDavid S. Miller 		const struct sadb_x_nat_t_type* n_type;
12281da177e4SLinus Torvalds 		struct xfrm_encap_tmpl *natt;
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 		x->encap = kmalloc(sizeof(*x->encap), GFP_KERNEL);
12311da177e4SLinus Torvalds 		if (!x->encap)
12321da177e4SLinus Torvalds 			goto out;
12331da177e4SLinus Torvalds 
12341da177e4SLinus Torvalds 		natt = x->encap;
12351da177e4SLinus Torvalds 		n_type = ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1];
12361da177e4SLinus Torvalds 		natt->encap_type = n_type->sadb_x_nat_t_type_type;
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1]) {
12394c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12401da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1];
12411da177e4SLinus Torvalds 			natt->encap_sport = n_port->sadb_x_nat_t_port_port;
12421da177e4SLinus Torvalds 		}
12431da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1]) {
12444c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12451da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1];
12461da177e4SLinus Torvalds 			natt->encap_dport = n_port->sadb_x_nat_t_port_port;
12471da177e4SLinus Torvalds 		}
1248a8d694c6STimo Teras 		memset(&natt->encap_oa, 0, sizeof(natt->encap_oa));
12491da177e4SLinus Torvalds 	}
12501da177e4SLinus Torvalds 
125172cb6962SHerbert Xu 	err = xfrm_init_state(x);
125272cb6962SHerbert Xu 	if (err)
12531da177e4SLinus Torvalds 		goto out;
125472cb6962SHerbert Xu 
12551da177e4SLinus Torvalds 	x->km.seq = hdr->sadb_msg_seq;
12561da177e4SLinus Torvalds 	return x;
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds out:
12591da177e4SLinus Torvalds 	x->km.state = XFRM_STATE_DEAD;
12601da177e4SLinus Torvalds 	xfrm_state_put(x);
12611da177e4SLinus Torvalds 	return ERR_PTR(err);
12621da177e4SLinus Torvalds }
12631da177e4SLinus Torvalds 
12644c93fbb0SDavid S. Miller static int pfkey_reserved(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
12651da177e4SLinus Torvalds {
12661da177e4SLinus Torvalds 	return -EOPNOTSUPP;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds 
12694c93fbb0SDavid S. Miller static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
12701da177e4SLinus Torvalds {
127107fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
12721da177e4SLinus Torvalds 	struct sk_buff *resp_skb;
12731da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
12741da177e4SLinus Torvalds 	struct sadb_address *saddr, *daddr;
12751da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
1276658b219eSHerbert Xu 	struct sadb_spirange *range;
12771da177e4SLinus Torvalds 	struct xfrm_state *x = NULL;
127855569ce2SKazunori MIYAZAWA 	int mode;
1279658b219eSHerbert Xu 	int err;
1280658b219eSHerbert Xu 	u32 min_spi, max_spi;
12811da177e4SLinus Torvalds 	u32 reqid;
12821da177e4SLinus Torvalds 	u8 proto;
12831da177e4SLinus Torvalds 	unsigned short family;
12841da177e4SLinus Torvalds 	xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
12871da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
12881da177e4SLinus Torvalds 		return -EINVAL;
12891da177e4SLinus Torvalds 
12901da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
12911da177e4SLinus Torvalds 	if (proto == 0)
12921da177e4SLinus Torvalds 		return -EINVAL;
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	if ((sa2 = ext_hdrs[SADB_X_EXT_SA2-1]) != NULL) {
129555569ce2SKazunori MIYAZAWA 		mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
129655569ce2SKazunori MIYAZAWA 		if (mode < 0)
129755569ce2SKazunori MIYAZAWA 			return -EINVAL;
12981da177e4SLinus Torvalds 		reqid = sa2->sadb_x_sa2_reqid;
12991da177e4SLinus Torvalds 	} else {
13001da177e4SLinus Torvalds 		mode = 0;
13011da177e4SLinus Torvalds 		reqid = 0;
13021da177e4SLinus Torvalds 	}
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
13051da177e4SLinus Torvalds 	daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 	family = ((struct sockaddr *)(saddr + 1))->sa_family;
13081da177e4SLinus Torvalds 	switch (family) {
13091da177e4SLinus Torvalds 	case AF_INET:
13101da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
13111da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
13121da177e4SLinus Torvalds 		break;
1313dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
13141da177e4SLinus Torvalds 	case AF_INET6:
13151da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
13161da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
13171da177e4SLinus Torvalds 		break;
13181da177e4SLinus Torvalds #endif
13191da177e4SLinus Torvalds 	}
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq) {
1322bd55775cSJamal Hadi Salim 		x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
132370e94e66SYOSHIFUJI Hideaki / 吉藤英明 		if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) {
13241da177e4SLinus Torvalds 			xfrm_state_put(x);
13251da177e4SLinus Torvalds 			x = NULL;
13261da177e4SLinus Torvalds 		}
13271da177e4SLinus Torvalds 	}
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 	if (!x)
1330bd55775cSJamal Hadi Salim 		x = xfrm_find_acq(net, &dummy_mark, mode, reqid, proto, xdaddr, xsaddr, 1, family);
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	if (x == NULL)
13331da177e4SLinus Torvalds 		return -ENOENT;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	min_spi = 0x100;
13361da177e4SLinus Torvalds 	max_spi = 0x0fffffff;
1337658b219eSHerbert Xu 
1338658b219eSHerbert Xu 	range = ext_hdrs[SADB_EXT_SPIRANGE-1];
1339658b219eSHerbert Xu 	if (range) {
1340658b219eSHerbert Xu 		min_spi = range->sadb_spirange_min;
1341658b219eSHerbert Xu 		max_spi = range->sadb_spirange_max;
13421da177e4SLinus Torvalds 	}
1343658b219eSHerbert Xu 
1344776e9dd9SFan Du 	err = verify_spi_info(x->id.proto, min_spi, max_spi);
1345776e9dd9SFan Du 	if (err) {
1346776e9dd9SFan Du 		xfrm_state_put(x);
1347776e9dd9SFan Du 		return err;
1348776e9dd9SFan Du 	}
1349776e9dd9SFan Du 
1350658b219eSHerbert Xu 	err = xfrm_alloc_spi(x, min_spi, max_spi);
1351050f009eSHerbert Xu 	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x);
13521da177e4SLinus Torvalds 
13531da177e4SLinus Torvalds 	if (IS_ERR(resp_skb)) {
13541da177e4SLinus Torvalds 		xfrm_state_put(x);
13551da177e4SLinus Torvalds 		return  PTR_ERR(resp_skb);
13561da177e4SLinus Torvalds 	}
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) resp_skb->data;
13591da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
13601da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_GETSPI;
13611da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
13621da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
13631da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
13641da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
13651da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	xfrm_state_put(x);
13681da177e4SLinus Torvalds 
136907fb0f17SAlexey Dobriyan 	pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 	return 0;
13721da177e4SLinus Torvalds }
13731da177e4SLinus Torvalds 
13744c93fbb0SDavid S. Miller static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
13751da177e4SLinus Torvalds {
137607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
13771da177e4SLinus Torvalds 	struct xfrm_state *x;
13781da177e4SLinus Torvalds 
13791da177e4SLinus Torvalds 	if (hdr->sadb_msg_len != sizeof(struct sadb_msg)/8)
13801da177e4SLinus Torvalds 		return -EOPNOTSUPP;
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
13831da177e4SLinus Torvalds 		return 0;
13841da177e4SLinus Torvalds 
1385bd55775cSJamal Hadi Salim 	x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
13861da177e4SLinus Torvalds 	if (x == NULL)
13871da177e4SLinus Torvalds 		return 0;
13881da177e4SLinus Torvalds 
13891da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
13905b8ef341SSteffen Klassert 	if (x->km.state == XFRM_STATE_ACQ)
13911da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_ERROR;
13925b8ef341SSteffen Klassert 
13931da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
13941da177e4SLinus Torvalds 	xfrm_state_put(x);
13951da177e4SLinus Torvalds 	return 0;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds 
139826b15dadSJamal Hadi Salim static inline int event2poltype(int event)
139926b15dadSJamal Hadi Salim {
140026b15dadSJamal Hadi Salim 	switch (event) {
1401f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
140226b15dadSJamal Hadi Salim 		return SADB_X_SPDDELETE;
1403f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
140426b15dadSJamal Hadi Salim 		return SADB_X_SPDADD;
1405f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
140626b15dadSJamal Hadi Salim 		return SADB_X_SPDUPDATE;
1407f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
140826b15dadSJamal Hadi Salim 	//	return SADB_X_SPDEXPIRE;
140926b15dadSJamal Hadi Salim 	default:
1410207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", event);
141126b15dadSJamal Hadi Salim 		break;
141226b15dadSJamal Hadi Salim 	}
141326b15dadSJamal Hadi Salim 
141426b15dadSJamal Hadi Salim 	return 0;
141526b15dadSJamal Hadi Salim }
141626b15dadSJamal Hadi Salim 
141726b15dadSJamal Hadi Salim static inline int event2keytype(int event)
141826b15dadSJamal Hadi Salim {
141926b15dadSJamal Hadi Salim 	switch (event) {
1420f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
142126b15dadSJamal Hadi Salim 		return SADB_DELETE;
1422f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
142326b15dadSJamal Hadi Salim 		return SADB_ADD;
1424f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
142526b15dadSJamal Hadi Salim 		return SADB_UPDATE;
1426f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
142726b15dadSJamal Hadi Salim 		return SADB_EXPIRE;
142826b15dadSJamal Hadi Salim 	default:
1429207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", event);
143026b15dadSJamal Hadi Salim 		break;
143126b15dadSJamal Hadi Salim 	}
143226b15dadSJamal Hadi Salim 
143326b15dadSJamal Hadi Salim 	return 0;
143426b15dadSJamal Hadi Salim }
143526b15dadSJamal Hadi Salim 
143626b15dadSJamal Hadi Salim /* ADD/UPD/DEL */
1437214e005bSDavid S. Miller static int key_notify_sa(struct xfrm_state *x, const struct km_event *c)
143826b15dadSJamal Hadi Salim {
143926b15dadSJamal Hadi Salim 	struct sk_buff *skb;
144026b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
144126b15dadSJamal Hadi Salim 
1442050f009eSHerbert Xu 	skb = pfkey_xfrm_state2msg(x);
144326b15dadSJamal Hadi Salim 
144426b15dadSJamal Hadi Salim 	if (IS_ERR(skb))
144526b15dadSJamal Hadi Salim 		return PTR_ERR(skb);
144626b15dadSJamal Hadi Salim 
144726b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb->data;
144826b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
144926b15dadSJamal Hadi Salim 	hdr->sadb_msg_type = event2keytype(c->event);
145026b15dadSJamal Hadi Salim 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
145126b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = 0;
145226b15dadSJamal Hadi Salim 	hdr->sadb_msg_reserved = 0;
145326b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
145415e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
145526b15dadSJamal Hadi Salim 
145607fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
145726b15dadSJamal Hadi Salim 
145826b15dadSJamal Hadi Salim 	return 0;
145926b15dadSJamal Hadi Salim }
14601da177e4SLinus Torvalds 
14614c93fbb0SDavid S. Miller static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
14621da177e4SLinus Torvalds {
146307fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
14641da177e4SLinus Torvalds 	struct xfrm_state *x;
14651da177e4SLinus Torvalds 	int err;
146626b15dadSJamal Hadi Salim 	struct km_event c;
14671da177e4SLinus Torvalds 
146807fb0f17SAlexey Dobriyan 	x = pfkey_msg2xfrm_state(net, hdr, ext_hdrs);
14691da177e4SLinus Torvalds 	if (IS_ERR(x))
14701da177e4SLinus Torvalds 		return PTR_ERR(x);
14711da177e4SLinus Torvalds 
147226b15dadSJamal Hadi Salim 	xfrm_state_hold(x);
14731da177e4SLinus Torvalds 	if (hdr->sadb_msg_type == SADB_ADD)
14741da177e4SLinus Torvalds 		err = xfrm_state_add(x);
14751da177e4SLinus Torvalds 	else
14761da177e4SLinus Torvalds 		err = xfrm_state_update(x);
14771da177e4SLinus Torvalds 
1478ab5f5e8bSJoy Latten 	xfrm_audit_state_add(x, err ? 0 : 1,
14792532386fSEric Paris 			     audit_get_loginuid(current),
14802532386fSEric Paris 			     audit_get_sessionid(current), 0);
1481161a09e7SJoy Latten 
14821da177e4SLinus Torvalds 	if (err < 0) {
14831da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
148421380b81SHerbert Xu 		__xfrm_state_put(x);
14857d6dfe1fSPatrick McHardy 		goto out;
14861da177e4SLinus Torvalds 	}
14871da177e4SLinus Torvalds 
148826b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_ADD)
1489f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWSA;
149026b15dadSJamal Hadi Salim 	else
1491f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDSA;
149226b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
149315e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
149426b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
14957d6dfe1fSPatrick McHardy out:
149626b15dadSJamal Hadi Salim 	xfrm_state_put(x);
149726b15dadSJamal Hadi Salim 	return err;
14981da177e4SLinus Torvalds }
14991da177e4SLinus Torvalds 
15004c93fbb0SDavid S. Miller static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
15011da177e4SLinus Torvalds {
150207fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
15031da177e4SLinus Torvalds 	struct xfrm_state *x;
150426b15dadSJamal Hadi Salim 	struct km_event c;
150526b15dadSJamal Hadi Salim 	int err;
15061da177e4SLinus Torvalds 
15071da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15081da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15091da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15101da177e4SLinus Torvalds 		return -EINVAL;
15111da177e4SLinus Torvalds 
151207fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15131da177e4SLinus Torvalds 	if (x == NULL)
15141da177e4SLinus Torvalds 		return -ESRCH;
15151da177e4SLinus Torvalds 
1516c8c05a8eSCatherine Zhang 	if ((err = security_xfrm_state_delete(x)))
1517c8c05a8eSCatherine Zhang 		goto out;
1518c8c05a8eSCatherine Zhang 
15191da177e4SLinus Torvalds 	if (xfrm_state_kern(x)) {
1520c8c05a8eSCatherine Zhang 		err = -EPERM;
1521c8c05a8eSCatherine Zhang 		goto out;
15221da177e4SLinus Torvalds 	}
15231da177e4SLinus Torvalds 
152426b15dadSJamal Hadi Salim 	err = xfrm_state_delete(x);
1525161a09e7SJoy Latten 
1526c8c05a8eSCatherine Zhang 	if (err < 0)
1527c8c05a8eSCatherine Zhang 		goto out;
152826b15dadSJamal Hadi Salim 
152926b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
153015e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
1531f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELSA;
153226b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
1533c8c05a8eSCatherine Zhang out:
1534ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
15352532386fSEric Paris 				audit_get_loginuid(current),
15362532386fSEric Paris 				audit_get_sessionid(current), 0);
15371da177e4SLinus Torvalds 	xfrm_state_put(x);
15381da177e4SLinus Torvalds 
153926b15dadSJamal Hadi Salim 	return err;
15401da177e4SLinus Torvalds }
15411da177e4SLinus Torvalds 
15424c93fbb0SDavid S. Miller static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
15431da177e4SLinus Torvalds {
154407fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
15451da177e4SLinus Torvalds 	__u8 proto;
15461da177e4SLinus Torvalds 	struct sk_buff *out_skb;
15471da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
15481da177e4SLinus Torvalds 	struct xfrm_state *x;
15491da177e4SLinus Torvalds 
15501da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15511da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15521da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15531da177e4SLinus Torvalds 		return -EINVAL;
15541da177e4SLinus Torvalds 
155507fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15561da177e4SLinus Torvalds 	if (x == NULL)
15571da177e4SLinus Torvalds 		return -ESRCH;
15581da177e4SLinus Torvalds 
1559050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
15601da177e4SLinus Torvalds 	proto = x->id.proto;
15611da177e4SLinus Torvalds 	xfrm_state_put(x);
15621da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
15631da177e4SLinus Torvalds 		return  PTR_ERR(out_skb);
15641da177e4SLinus Torvalds 
15651da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
15661da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
1567435000beSCharles Hardin 	out_hdr->sadb_msg_type = SADB_GET;
15681da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
15691da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
15701da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
15711da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
15721da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
157307fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
15741da177e4SLinus Torvalds 
15751da177e4SLinus Torvalds 	return 0;
15761da177e4SLinus Torvalds }
15771da177e4SLinus Torvalds 
15784c93fbb0SDavid S. Miller static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig,
1579dd0fc66fSAl Viro 					      gfp_t allocation)
15801da177e4SLinus Torvalds {
15811da177e4SLinus Torvalds 	struct sk_buff *skb;
15821da177e4SLinus Torvalds 	struct sadb_msg *hdr;
15831da177e4SLinus Torvalds 	int len, auth_len, enc_len, i;
15841da177e4SLinus Torvalds 
15857e50f84cSJussi Kivilinna 	auth_len = xfrm_count_pfkey_auth_supported();
15861da177e4SLinus Torvalds 	if (auth_len) {
15871da177e4SLinus Torvalds 		auth_len *= sizeof(struct sadb_alg);
15881da177e4SLinus Torvalds 		auth_len += sizeof(struct sadb_supported);
15891da177e4SLinus Torvalds 	}
15901da177e4SLinus Torvalds 
15917e50f84cSJussi Kivilinna 	enc_len = xfrm_count_pfkey_enc_supported();
15921da177e4SLinus Torvalds 	if (enc_len) {
15931da177e4SLinus Torvalds 		enc_len *= sizeof(struct sadb_alg);
15941da177e4SLinus Torvalds 		enc_len += sizeof(struct sadb_supported);
15951da177e4SLinus Torvalds 	}
15961da177e4SLinus Torvalds 
15971da177e4SLinus Torvalds 	len = enc_len + auth_len + sizeof(struct sadb_msg);
15981da177e4SLinus Torvalds 
15991da177e4SLinus Torvalds 	skb = alloc_skb(len + 16, allocation);
16001da177e4SLinus Torvalds 	if (!skb)
16011da177e4SLinus Torvalds 		goto out_put_algs;
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(*hdr));
16041da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
16051da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
16061da177e4SLinus Torvalds 	hdr->sadb_msg_len = len / sizeof(uint64_t);
16071da177e4SLinus Torvalds 
16081da177e4SLinus Torvalds 	if (auth_len) {
16091da177e4SLinus Torvalds 		struct sadb_supported *sp;
16101da177e4SLinus Torvalds 		struct sadb_alg *ap;
16111da177e4SLinus Torvalds 
16121da177e4SLinus Torvalds 		sp = (struct sadb_supported *) skb_put(skb, auth_len);
16131da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16141da177e4SLinus Torvalds 
16151da177e4SLinus Torvalds 		sp->sadb_supported_len = auth_len / sizeof(uint64_t);
16161da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
16171da177e4SLinus Torvalds 
16181da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16191da177e4SLinus Torvalds 			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
16201da177e4SLinus Torvalds 			if (!aalg)
16211da177e4SLinus Torvalds 				break;
16227e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
16237e50f84cSJussi Kivilinna 				continue;
16241da177e4SLinus Torvalds 			if (aalg->available)
16251da177e4SLinus Torvalds 				*ap++ = aalg->desc;
16261da177e4SLinus Torvalds 		}
16271da177e4SLinus Torvalds 	}
16281da177e4SLinus Torvalds 
16291da177e4SLinus Torvalds 	if (enc_len) {
16301da177e4SLinus Torvalds 		struct sadb_supported *sp;
16311da177e4SLinus Torvalds 		struct sadb_alg *ap;
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 		sp = (struct sadb_supported *) skb_put(skb, enc_len);
16341da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16351da177e4SLinus Torvalds 
16361da177e4SLinus Torvalds 		sp->sadb_supported_len = enc_len / sizeof(uint64_t);
16371da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16401da177e4SLinus Torvalds 			struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
16411da177e4SLinus Torvalds 			if (!ealg)
16421da177e4SLinus Torvalds 				break;
16437e50f84cSJussi Kivilinna 			if (!ealg->pfkey_supported)
16447e50f84cSJussi Kivilinna 				continue;
16451da177e4SLinus Torvalds 			if (ealg->available)
16461da177e4SLinus Torvalds 				*ap++ = ealg->desc;
16471da177e4SLinus Torvalds 		}
16481da177e4SLinus Torvalds 	}
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds out_put_algs:
16511da177e4SLinus Torvalds 	return skb;
16521da177e4SLinus Torvalds }
16531da177e4SLinus Torvalds 
16544c93fbb0SDavid S. Miller static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
16551da177e4SLinus Torvalds {
16561da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
16571da177e4SLinus Torvalds 	struct sk_buff *supp_skb;
16581da177e4SLinus Torvalds 
16591da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype > SADB_SATYPE_MAX)
16601da177e4SLinus Torvalds 		return -EINVAL;
16611da177e4SLinus Torvalds 
16621da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) {
16631da177e4SLinus Torvalds 		if (pfk->registered&(1<<hdr->sadb_msg_satype))
16641da177e4SLinus Torvalds 			return -EEXIST;
16651da177e4SLinus Torvalds 		pfk->registered |= (1<<hdr->sadb_msg_satype);
16661da177e4SLinus Torvalds 	}
16671da177e4SLinus Torvalds 
16681da177e4SLinus Torvalds 	xfrm_probe_algs();
16691da177e4SLinus Torvalds 
16701da177e4SLinus Torvalds 	supp_skb = compose_sadb_supported(hdr, GFP_KERNEL);
16711da177e4SLinus Torvalds 	if (!supp_skb) {
16721da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
16731da177e4SLinus Torvalds 			pfk->registered &= ~(1<<hdr->sadb_msg_satype);
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 		return -ENOBUFS;
16761da177e4SLinus Torvalds 	}
16771da177e4SLinus Torvalds 
167807fb0f17SAlexey Dobriyan 	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk, sock_net(sk));
16791da177e4SLinus Torvalds 
16801da177e4SLinus Torvalds 	return 0;
16811da177e4SLinus Torvalds }
16821da177e4SLinus Torvalds 
16834c93fbb0SDavid S. Miller static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr)
16848be987d7SJamal Hadi Salim {
16858be987d7SJamal Hadi Salim 	struct sk_buff *skb;
16868be987d7SJamal Hadi Salim 	struct sadb_msg *hdr;
16878be987d7SJamal Hadi Salim 
16888be987d7SJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
16898be987d7SJamal Hadi Salim 	if (!skb)
16908be987d7SJamal Hadi Salim 		return -ENOBUFS;
16918be987d7SJamal Hadi Salim 
16928be987d7SJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
16938be987d7SJamal Hadi Salim 	memcpy(hdr, ihdr, sizeof(struct sadb_msg));
16948be987d7SJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
16958be987d7SJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
16968be987d7SJamal Hadi Salim 
16978be987d7SJamal Hadi Salim 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
16988be987d7SJamal Hadi Salim }
16998be987d7SJamal Hadi Salim 
1700214e005bSDavid S. Miller static int key_notify_sa_flush(const struct km_event *c)
170126b15dadSJamal Hadi Salim {
170226b15dadSJamal Hadi Salim 	struct sk_buff *skb;
170326b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
170426b15dadSJamal Hadi Salim 
170526b15dadSJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
170626b15dadSJamal Hadi Salim 	if (!skb)
170726b15dadSJamal Hadi Salim 		return -ENOBUFS;
170826b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
1709bf08867fSHerbert Xu 	hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto);
1710151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_FLUSH;
171126b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
171215e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
171326b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
171426b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
171526b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
1716a5cc68f3SMathias Krause 	hdr->sadb_msg_reserved = 0;
171726b15dadSJamal Hadi Salim 
171807fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
171926b15dadSJamal Hadi Salim 
172026b15dadSJamal Hadi Salim 	return 0;
172126b15dadSJamal Hadi Salim }
172226b15dadSJamal Hadi Salim 
17234c93fbb0SDavid S. Miller static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
17241da177e4SLinus Torvalds {
172507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
172695c96174SEric Dumazet 	unsigned int proto;
172726b15dadSJamal Hadi Salim 	struct km_event c;
1728161a09e7SJoy Latten 	struct xfrm_audit audit_info;
17298be987d7SJamal Hadi Salim 	int err, err2;
17301da177e4SLinus Torvalds 
17311da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
17321da177e4SLinus Torvalds 	if (proto == 0)
17331da177e4SLinus Torvalds 		return -EINVAL;
17341da177e4SLinus Torvalds 
17350c11b942SAl Viro 	audit_info.loginuid = audit_get_loginuid(current);
17362532386fSEric Paris 	audit_info.sessionid = audit_get_sessionid(current);
1737161a09e7SJoy Latten 	audit_info.secid = 0;
173807fb0f17SAlexey Dobriyan 	err = xfrm_state_flush(net, proto, &audit_info);
17398be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
17409e64cc95SJamal Hadi Salim 	if (err || err2) {
17419e64cc95SJamal Hadi Salim 		if (err == -ESRCH) /* empty table - go quietly */
17429e64cc95SJamal Hadi Salim 			err = 0;
17438be987d7SJamal Hadi Salim 		return err ? err : err2;
17449e64cc95SJamal Hadi Salim 	}
17458be987d7SJamal Hadi Salim 
1746bf08867fSHerbert Xu 	c.data.proto = proto;
174726b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
174815e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
1749f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHSA;
175007fb0f17SAlexey Dobriyan 	c.net = net;
175126b15dadSJamal Hadi Salim 	km_state_notify(NULL, &c);
17521da177e4SLinus Torvalds 
17531da177e4SLinus Torvalds 	return 0;
17541da177e4SLinus Torvalds }
17551da177e4SLinus Torvalds 
17561da177e4SLinus Torvalds static int dump_sa(struct xfrm_state *x, int count, void *ptr)
17571da177e4SLinus Torvalds {
175883321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
17591da177e4SLinus Torvalds 	struct sk_buff *out_skb;
17601da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
17611da177e4SLinus Torvalds 
176283321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
176383321d6bSTimo Teras 		return -ENOBUFS;
176483321d6bSTimo Teras 
1765050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
17661da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
17671da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
17681da177e4SLinus Torvalds 
17691da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
177083321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
17711da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_DUMP;
17721da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
17731da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
17741da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
177512a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
177615e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
177712a169e7SHerbert Xu 
177812a169e7SHerbert Xu 	if (pfk->dump.skb)
177912a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
178007fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
178112a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
178212a169e7SHerbert Xu 
17831da177e4SLinus Torvalds 	return 0;
17841da177e4SLinus Torvalds }
17851da177e4SLinus Torvalds 
178683321d6bSTimo Teras static int pfkey_dump_sa(struct pfkey_sock *pfk)
178783321d6bSTimo Teras {
178807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
178907fb0f17SAlexey Dobriyan 	return xfrm_state_walk(net, &pfk->dump.u.state, dump_sa, (void *) pfk);
179083321d6bSTimo Teras }
179183321d6bSTimo Teras 
179283321d6bSTimo Teras static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
179383321d6bSTimo Teras {
1794283bc9f3SFan Du 	struct net *net = sock_net(&pfk->sk);
1795283bc9f3SFan Du 
1796283bc9f3SFan Du 	xfrm_state_walk_done(&pfk->dump.u.state, net);
179783321d6bSTimo Teras }
179883321d6bSTimo Teras 
17994c93fbb0SDavid S. Miller static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
18001da177e4SLinus Torvalds {
18011da177e4SLinus Torvalds 	u8 proto;
180283321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
180383321d6bSTimo Teras 
180483321d6bSTimo Teras 	if (pfk->dump.dump != NULL)
180583321d6bSTimo Teras 		return -EBUSY;
18061da177e4SLinus Torvalds 
18071da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
18081da177e4SLinus Torvalds 	if (proto == 0)
18091da177e4SLinus Torvalds 		return -EINVAL;
18101da177e4SLinus Torvalds 
181183321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
181215e47304SEric W. Biederman 	pfk->dump.msg_portid = hdr->sadb_msg_pid;
181383321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sa;
181483321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sa_done;
181583321d6bSTimo Teras 	xfrm_state_walk_init(&pfk->dump.u.state, proto);
18164c563f76STimo Teras 
181783321d6bSTimo Teras 	return pfkey_do_dump(pfk);
18181da177e4SLinus Torvalds }
18191da177e4SLinus Torvalds 
18204c93fbb0SDavid S. Miller static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
18211da177e4SLinus Torvalds {
18221da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
18231da177e4SLinus Torvalds 	int satype = hdr->sadb_msg_satype;
18244c93fbb0SDavid S. Miller 	bool reset_errno = false;
18251da177e4SLinus Torvalds 
18261da177e4SLinus Torvalds 	if (hdr->sadb_msg_len == (sizeof(*hdr) / sizeof(uint64_t))) {
18274c93fbb0SDavid S. Miller 		reset_errno = true;
18281da177e4SLinus Torvalds 		if (satype != 0 && satype != 1)
18291da177e4SLinus Torvalds 			return -EINVAL;
18301da177e4SLinus Torvalds 		pfk->promisc = satype;
18311da177e4SLinus Torvalds 	}
18324c93fbb0SDavid S. Miller 	if (reset_errno && skb_cloned(skb))
18334c93fbb0SDavid S. Miller 		skb = skb_copy(skb, GFP_KERNEL);
18344c93fbb0SDavid S. Miller 	else
18354c93fbb0SDavid S. Miller 		skb = skb_clone(skb, GFP_KERNEL);
18364c93fbb0SDavid S. Miller 
18374c93fbb0SDavid S. Miller 	if (reset_errno && skb) {
18384c93fbb0SDavid S. Miller 		struct sadb_msg *new_hdr = (struct sadb_msg *) skb->data;
18394c93fbb0SDavid S. Miller 		new_hdr->sadb_msg_errno = 0;
18404c93fbb0SDavid S. Miller 	}
18414c93fbb0SDavid S. Miller 
18424c93fbb0SDavid S. Miller 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
18431da177e4SLinus Torvalds 	return 0;
18441da177e4SLinus Torvalds }
18451da177e4SLinus Torvalds 
18461da177e4SLinus Torvalds static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
18471da177e4SLinus Torvalds {
18481da177e4SLinus Torvalds 	int i;
18491da177e4SLinus Torvalds 	u32 reqid = *(u32*)ptr;
18501da177e4SLinus Torvalds 
18511da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
18521da177e4SLinus Torvalds 		if (xp->xfrm_vec[i].reqid == reqid)
18531da177e4SLinus Torvalds 			return -EEXIST;
18541da177e4SLinus Torvalds 	}
18551da177e4SLinus Torvalds 	return 0;
18561da177e4SLinus Torvalds }
18571da177e4SLinus Torvalds 
185807fb0f17SAlexey Dobriyan static u32 gen_reqid(struct net *net)
18591da177e4SLinus Torvalds {
18604c563f76STimo Teras 	struct xfrm_policy_walk walk;
18611da177e4SLinus Torvalds 	u32 start;
18624c563f76STimo Teras 	int rc;
18631da177e4SLinus Torvalds 	static u32 reqid = IPSEC_MANUAL_REQID_MAX;
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds 	start = reqid;
18661da177e4SLinus Torvalds 	do {
18671da177e4SLinus Torvalds 		++reqid;
18681da177e4SLinus Torvalds 		if (reqid == 0)
18691da177e4SLinus Torvalds 			reqid = IPSEC_MANUAL_REQID_MAX+1;
18704c563f76STimo Teras 		xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
187107fb0f17SAlexey Dobriyan 		rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid);
1872283bc9f3SFan Du 		xfrm_policy_walk_done(&walk, net);
18734c563f76STimo Teras 		if (rc != -EEXIST)
18741da177e4SLinus Torvalds 			return reqid;
18751da177e4SLinus Torvalds 	} while (reqid != start);
18761da177e4SLinus Torvalds 	return 0;
18771da177e4SLinus Torvalds }
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds static int
18801da177e4SLinus Torvalds parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
18811da177e4SLinus Torvalds {
188207fb0f17SAlexey Dobriyan 	struct net *net = xp_net(xp);
18831da177e4SLinus Torvalds 	struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
188455569ce2SKazunori MIYAZAWA 	int mode;
18851da177e4SLinus Torvalds 
18861da177e4SLinus Torvalds 	if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
18871da177e4SLinus Torvalds 		return -ELOOP;
18881da177e4SLinus Torvalds 
18891da177e4SLinus Torvalds 	if (rq->sadb_x_ipsecrequest_mode == 0)
18901da177e4SLinus Torvalds 		return -EINVAL;
18911da177e4SLinus Torvalds 
18921da177e4SLinus Torvalds 	t->id.proto = rq->sadb_x_ipsecrequest_proto; /* XXX check proto */
189355569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0)
189455569ce2SKazunori MIYAZAWA 		return -EINVAL;
189555569ce2SKazunori MIYAZAWA 	t->mode = mode;
18961da177e4SLinus Torvalds 	if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE)
18971da177e4SLinus Torvalds 		t->optional = 1;
18981da177e4SLinus Torvalds 	else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) {
18991da177e4SLinus Torvalds 		t->reqid = rq->sadb_x_ipsecrequest_reqid;
19001da177e4SLinus Torvalds 		if (t->reqid > IPSEC_MANUAL_REQID_MAX)
19011da177e4SLinus Torvalds 			t->reqid = 0;
190207fb0f17SAlexey Dobriyan 		if (!t->reqid && !(t->reqid = gen_reqid(net)))
19031da177e4SLinus Torvalds 			return -ENOBUFS;
19041da177e4SLinus Torvalds 	}
19051da177e4SLinus Torvalds 
19061da177e4SLinus Torvalds 	/* addresses present only in tunnel mode */
19077e49e6deSMasahide NAKAMURA 	if (t->mode == XFRM_MODE_TUNNEL) {
19085f95ac91SYOSHIFUJI Hideaki 		u8 *sa = (u8 *) (rq + 1);
19095f95ac91SYOSHIFUJI Hideaki 		int family, socklen;
19105f95ac91SYOSHIFUJI Hideaki 
19115f95ac91SYOSHIFUJI Hideaki 		family = pfkey_sockaddr_extract((struct sockaddr *)sa,
19125f95ac91SYOSHIFUJI Hideaki 						&t->saddr);
19135f95ac91SYOSHIFUJI Hideaki 		if (!family)
19141da177e4SLinus Torvalds 			return -EINVAL;
19155f95ac91SYOSHIFUJI Hideaki 
19165f95ac91SYOSHIFUJI Hideaki 		socklen = pfkey_sockaddr_len(family);
19175f95ac91SYOSHIFUJI Hideaki 		if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen),
19185f95ac91SYOSHIFUJI Hideaki 					   &t->id.daddr) != family)
19191da177e4SLinus Torvalds 			return -EINVAL;
19205f95ac91SYOSHIFUJI Hideaki 		t->encap_family = family;
19212718aa7cSMiika Komu 	} else
19222718aa7cSMiika Komu 		t->encap_family = xp->family;
19232718aa7cSMiika Komu 
19241da177e4SLinus Torvalds 	/* No way to set this via kame pfkey */
1925c5d18e98SHerbert Xu 	t->allalgs = 1;
19261da177e4SLinus Torvalds 	xp->xfrm_nr++;
19271da177e4SLinus Torvalds 	return 0;
19281da177e4SLinus Torvalds }
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds static int
19311da177e4SLinus Torvalds parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
19321da177e4SLinus Torvalds {
19331da177e4SLinus Torvalds 	int err;
19341da177e4SLinus Torvalds 	int len = pol->sadb_x_policy_len*8 - sizeof(struct sadb_x_policy);
19351da177e4SLinus Torvalds 	struct sadb_x_ipsecrequest *rq = (void*)(pol+1);
19361da177e4SLinus Torvalds 
1937f674e72fSDan Carpenter 	if (pol->sadb_x_policy_len * 8 < sizeof(struct sadb_x_policy))
1938f674e72fSDan Carpenter 		return -EINVAL;
1939f674e72fSDan Carpenter 
19401da177e4SLinus Torvalds 	while (len >= sizeof(struct sadb_x_ipsecrequest)) {
19411da177e4SLinus Torvalds 		if ((err = parse_ipsecrequest(xp, rq)) < 0)
19421da177e4SLinus Torvalds 			return err;
19431da177e4SLinus Torvalds 		len -= rq->sadb_x_ipsecrequest_len;
19441da177e4SLinus Torvalds 		rq = (void*)((u8*)rq + rq->sadb_x_ipsecrequest_len);
19451da177e4SLinus Torvalds 	}
19461da177e4SLinus Torvalds 	return 0;
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
19494c93fbb0SDavid S. Miller static inline int pfkey_xfrm_policy2sec_ctx_size(const struct xfrm_policy *xp)
1950df71837dSTrent Jaeger {
1951df71837dSTrent Jaeger   struct xfrm_sec_ctx *xfrm_ctx = xp->security;
1952df71837dSTrent Jaeger 
1953df71837dSTrent Jaeger 	if (xfrm_ctx) {
1954df71837dSTrent Jaeger 		int len = sizeof(struct sadb_x_sec_ctx);
1955df71837dSTrent Jaeger 		len += xfrm_ctx->ctx_len;
1956df71837dSTrent Jaeger 		return PFKEY_ALIGN8(len);
1957df71837dSTrent Jaeger 	}
1958df71837dSTrent Jaeger 	return 0;
1959df71837dSTrent Jaeger }
1960df71837dSTrent Jaeger 
19614c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg_size(const struct xfrm_policy *xp)
19621da177e4SLinus Torvalds {
19634c93fbb0SDavid S. Miller 	const struct xfrm_tmpl *t;
19641da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
19652718aa7cSMiika Komu 	int socklen = 0;
19662718aa7cSMiika Komu 	int i;
19672718aa7cSMiika Komu 
19682718aa7cSMiika Komu 	for (i=0; i<xp->xfrm_nr; i++) {
19692718aa7cSMiika Komu 		t = xp->xfrm_vec + i;
19709e8b4ed8SYOSHIFUJI Hideaki 		socklen += pfkey_sockaddr_len(t->encap_family);
19712718aa7cSMiika Komu 	}
19721da177e4SLinus Torvalds 
19731da177e4SLinus Torvalds 	return sizeof(struct sadb_msg) +
19741da177e4SLinus Torvalds 		(sizeof(struct sadb_lifetime) * 3) +
19751da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
19761da177e4SLinus Torvalds 		(sockaddr_size * 2) +
19771da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy) +
19782718aa7cSMiika Komu 		(xp->xfrm_nr * sizeof(struct sadb_x_ipsecrequest)) +
19792718aa7cSMiika Komu 		(socklen * 2) +
1980df71837dSTrent Jaeger 		pfkey_xfrm_policy2sec_ctx_size(xp);
19811da177e4SLinus Torvalds }
19821da177e4SLinus Torvalds 
19834c93fbb0SDavid S. Miller static struct sk_buff * pfkey_xfrm_policy2msg_prep(const struct xfrm_policy *xp)
19841da177e4SLinus Torvalds {
19851da177e4SLinus Torvalds 	struct sk_buff *skb;
19861da177e4SLinus Torvalds 	int size;
19871da177e4SLinus Torvalds 
19881da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
19891da177e4SLinus Torvalds 
19901da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
19911da177e4SLinus Torvalds 	if (skb == NULL)
19921da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
19931da177e4SLinus Torvalds 
19941da177e4SLinus Torvalds 	return skb;
19951da177e4SLinus Torvalds }
19961da177e4SLinus Torvalds 
19974c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg(struct sk_buff *skb, const struct xfrm_policy *xp, int dir)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	struct sadb_msg *hdr;
20001da177e4SLinus Torvalds 	struct sadb_address *addr;
20011da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
20021da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
2003df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
2004df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
20051da177e4SLinus Torvalds 	int i;
20061da177e4SLinus Torvalds 	int size;
20071da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
20089e8b4ed8SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(xp->family);
20091da177e4SLinus Torvalds 
20101da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds 	/* call should fill header later */
20131da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
20141da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
20151da177e4SLinus Torvalds 
20161da177e4SLinus Torvalds 	/* src address */
20171da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
20181da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
20191da177e4SLinus Torvalds 	addr->sadb_address_len =
20201da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
20211da177e4SLinus Torvalds 			sizeof(uint64_t);
20221da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
20231da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
20241da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
20251da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2026e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(&xp->selector.saddr,
2027e5b56652SYOSHIFUJI Hideaki 				 xp->selector.sport,
2028e5b56652SYOSHIFUJI Hideaki 				 (struct sockaddr *) (addr + 1),
2029e5b56652SYOSHIFUJI Hideaki 				 xp->family))
20301da177e4SLinus Torvalds 		BUG();
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds 	/* dst address */
20331da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
20341da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
20351da177e4SLinus Torvalds 	addr->sadb_address_len =
20361da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
20371da177e4SLinus Torvalds 			sizeof(uint64_t);
20381da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
20391da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
20401da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
20411da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2042e5b56652SYOSHIFUJI Hideaki 
2043e5b56652SYOSHIFUJI Hideaki 	pfkey_sockaddr_fill(&xp->selector.daddr, xp->selector.dport,
2044e5b56652SYOSHIFUJI Hideaki 			    (struct sockaddr *) (addr + 1),
2045e5b56652SYOSHIFUJI Hideaki 			    xp->family);
20461da177e4SLinus Torvalds 
20471da177e4SLinus Torvalds 	/* hard time */
20481da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20491da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20501da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20511da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20521da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
20531da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.hard_packet_limit);
20541da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.hard_byte_limit);
20551da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.hard_add_expires_seconds;
20561da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.hard_use_expires_seconds;
20571da177e4SLinus Torvalds 	/* soft time */
20581da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20591da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20601da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20611da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20621da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
20631da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.soft_packet_limit);
20641da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.soft_byte_limit);
20651da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.soft_add_expires_seconds;
20661da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.soft_use_expires_seconds;
20671da177e4SLinus Torvalds 	/* current time */
20681da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20691da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20701da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20711da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20721da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
20731da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = xp->curlft.packets;
20741da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = xp->curlft.bytes;
20751da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->curlft.add_time;
20761da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->curlft.use_time;
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds 	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
20791da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
20801da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
20811da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_DISCARD;
20821da177e4SLinus Torvalds 	if (xp->action == XFRM_POLICY_ALLOW) {
20831da177e4SLinus Torvalds 		if (xp->xfrm_nr)
20841da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
20851da177e4SLinus Torvalds 		else
20861da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_NONE;
20871da177e4SLinus Torvalds 	}
20881da177e4SLinus Torvalds 	pol->sadb_x_policy_dir = dir+1;
2089ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
20901da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
20911da177e4SLinus Torvalds 	pol->sadb_x_policy_priority = xp->priority;
20921da177e4SLinus Torvalds 
20931da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
20944c93fbb0SDavid S. Miller 		const struct xfrm_tmpl *t = xp->xfrm_vec + i;
20951da177e4SLinus Torvalds 		struct sadb_x_ipsecrequest *rq;
20961da177e4SLinus Torvalds 		int req_size;
209755569ce2SKazunori MIYAZAWA 		int mode;
20981da177e4SLinus Torvalds 
20991da177e4SLinus Torvalds 		req_size = sizeof(struct sadb_x_ipsecrequest);
2100e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2101e5b56652SYOSHIFUJI Hideaki 			socklen = pfkey_sockaddr_len(t->encap_family);
2102e5b56652SYOSHIFUJI Hideaki 			req_size += socklen * 2;
2103e5b56652SYOSHIFUJI Hideaki 		} else {
21041da177e4SLinus Torvalds 			size -= 2*socklen;
2105e5b56652SYOSHIFUJI Hideaki 		}
21061da177e4SLinus Torvalds 		rq = (void*)skb_put(skb, req_size);
21071da177e4SLinus Torvalds 		pol->sadb_x_policy_len += req_size/8;
21081da177e4SLinus Torvalds 		memset(rq, 0, sizeof(*rq));
21091da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_len = req_size;
21101da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_proto = t->id.proto;
211155569ce2SKazunori MIYAZAWA 		if ((mode = pfkey_mode_from_xfrm(t->mode)) < 0)
211255569ce2SKazunori MIYAZAWA 			return -EINVAL;
2113fefaa75eSDavid S. Miller 		rq->sadb_x_ipsecrequest_mode = mode;
21141da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
21151da177e4SLinus Torvalds 		if (t->reqid)
21161da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
21171da177e4SLinus Torvalds 		if (t->optional)
21181da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
21191da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_reqid = t->reqid;
21201da177e4SLinus Torvalds 
2121e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2122e5b56652SYOSHIFUJI Hideaki 			u8 *sa = (void *)(rq + 1);
2123e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->saddr, 0,
2124e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *)sa,
2125e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
2126e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->id.daddr, 0,
2127e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *) (sa + socklen),
2128e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
21291da177e4SLinus Torvalds 		}
21301da177e4SLinus Torvalds 	}
2131df71837dSTrent Jaeger 
2132df71837dSTrent Jaeger 	/* security context */
2133df71837dSTrent Jaeger 	if ((xfrm_ctx = xp->security)) {
2134df71837dSTrent Jaeger 		int ctx_size = pfkey_xfrm_policy2sec_ctx_size(xp);
2135df71837dSTrent Jaeger 
2136df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb, ctx_size);
2137df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len = ctx_size / sizeof(uint64_t);
2138df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
2139df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
2140df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
2141df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
2142df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
2143df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
2144df71837dSTrent Jaeger 	}
2145df71837dSTrent Jaeger 
21461da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
21471da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = atomic_read(&xp->refcnt);
214855569ce2SKazunori MIYAZAWA 
214955569ce2SKazunori MIYAZAWA 	return 0;
21501da177e4SLinus Torvalds }
21511da177e4SLinus Torvalds 
2152214e005bSDavid S. Miller static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
215326b15dadSJamal Hadi Salim {
215426b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
215526b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
215626b15dadSJamal Hadi Salim 	int err;
215726b15dadSJamal Hadi Salim 
215826b15dadSJamal Hadi Salim 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
21599a127aadSDan Carpenter 	if (IS_ERR(out_skb))
21609a127aadSDan Carpenter 		return PTR_ERR(out_skb);
21619a127aadSDan Carpenter 
216255569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
216355569ce2SKazunori MIYAZAWA 	if (err < 0)
216455569ce2SKazunori MIYAZAWA 		return err;
216526b15dadSJamal Hadi Salim 
216626b15dadSJamal Hadi Salim 	out_hdr = (struct sadb_msg *) out_skb->data;
216726b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_version = PF_KEY_V2;
216826b15dadSJamal Hadi Salim 
2169f60f6b8fSHerbert Xu 	if (c->data.byid && c->event == XFRM_MSG_DELPOLICY)
217026b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = SADB_X_SPDDELETE2;
217126b15dadSJamal Hadi Salim 	else
217226b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = event2poltype(c->event);
217326b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_errno = 0;
217426b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_seq = c->seq;
217515e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = c->portid;
217607fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
217726b15dadSJamal Hadi Salim 	return 0;
217826b15dadSJamal Hadi Salim 
217926b15dadSJamal Hadi Salim }
218026b15dadSJamal Hadi Salim 
21814c93fbb0SDavid S. Miller static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
21821da177e4SLinus Torvalds {
218307fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
2184df71837dSTrent Jaeger 	int err = 0;
21851da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
21861da177e4SLinus Torvalds 	struct sadb_address *sa;
21871da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
21881da177e4SLinus Torvalds 	struct xfrm_policy *xp;
218926b15dadSJamal Hadi Salim 	struct km_event c;
2190df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
21931da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
21941da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
21951da177e4SLinus Torvalds 		return -EINVAL;
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
21981da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type > IPSEC_POLICY_IPSEC)
21991da177e4SLinus Torvalds 		return -EINVAL;
22001da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
22011da177e4SLinus Torvalds 		return -EINVAL;
22021da177e4SLinus Torvalds 
220307fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_KERNEL);
22041da177e4SLinus Torvalds 	if (xp == NULL)
22051da177e4SLinus Torvalds 		return -ENOBUFS;
22061da177e4SLinus Torvalds 
22071da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
22081da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
22091da177e4SLinus Torvalds 	xp->priority = pol->sadb_x_policy_priority;
22101da177e4SLinus Torvalds 
2211d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
22121da177e4SLinus Torvalds 	xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
22131da177e4SLinus Torvalds 	xp->selector.family = xp->family;
22141da177e4SLinus Torvalds 	xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
22151da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22161da177e4SLinus Torvalds 	xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
22171da177e4SLinus Torvalds 	if (xp->selector.sport)
22188f83f23eSAl Viro 		xp->selector.sport_mask = htons(0xffff);
22191da177e4SLinus Torvalds 
2220d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
22211da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
22221da177e4SLinus Torvalds 	xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds 	/* Amusing, we set this twice.  KAME apps appear to set same value
22251da177e4SLinus Torvalds 	 * in both addresses.
22261da177e4SLinus Torvalds 	 */
22271da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22281da177e4SLinus Torvalds 
22291da177e4SLinus Torvalds 	xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
22301da177e4SLinus Torvalds 	if (xp->selector.dport)
22318f83f23eSAl Viro 		xp->selector.dport_mask = htons(0xffff);
22321da177e4SLinus Torvalds 
2233ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
2234df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
2235*87536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
2236df71837dSTrent Jaeger 
2237df71837dSTrent Jaeger 		if (!uctx) {
2238df71837dSTrent Jaeger 			err = -ENOBUFS;
2239df71837dSTrent Jaeger 			goto out;
2240df71837dSTrent Jaeger 		}
2241df71837dSTrent Jaeger 
224203e1ad7bSPaul Moore 		err = security_xfrm_policy_alloc(&xp->security, uctx);
2243df71837dSTrent Jaeger 		kfree(uctx);
2244df71837dSTrent Jaeger 
2245df71837dSTrent Jaeger 		if (err)
2246df71837dSTrent Jaeger 			goto out;
2247df71837dSTrent Jaeger 	}
2248df71837dSTrent Jaeger 
22491da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
22501da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
22511da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
22521da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
22531da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD-1]) != NULL) {
22541da177e4SLinus Torvalds 		xp->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
22551da177e4SLinus Torvalds 		xp->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
22561da177e4SLinus Torvalds 		xp->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
22571da177e4SLinus Torvalds 		xp->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
22581da177e4SLinus Torvalds 	}
22591da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]) != NULL) {
22601da177e4SLinus Torvalds 		xp->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
22611da177e4SLinus Torvalds 		xp->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
22621da177e4SLinus Torvalds 		xp->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
22631da177e4SLinus Torvalds 		xp->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
22641da177e4SLinus Torvalds 	}
22651da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
22661da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
22671da177e4SLinus Torvalds 	    (err = parse_ipsecrequests(xp, pol)) < 0)
22681da177e4SLinus Torvalds 		goto out;
22691da177e4SLinus Torvalds 
22701da177e4SLinus Torvalds 	err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
22711da177e4SLinus Torvalds 				 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
2272df71837dSTrent Jaeger 
2273ab5f5e8bSJoy Latten 	xfrm_audit_policy_add(xp, err ? 0 : 1,
22742532386fSEric Paris 			      audit_get_loginuid(current),
22752532386fSEric Paris 			      audit_get_sessionid(current), 0);
2276161a09e7SJoy Latten 
2277df71837dSTrent Jaeger 	if (err)
2278df71837dSTrent Jaeger 		goto out;
22791da177e4SLinus Torvalds 
228026b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
2281f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDPOLICY;
228226b15dadSJamal Hadi Salim 	else
2283f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWPOLICY;
22841da177e4SLinus Torvalds 
228526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
228615e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
228726b15dadSJamal Hadi Salim 
228826b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
22891da177e4SLinus Torvalds 	xfrm_pol_put(xp);
22901da177e4SLinus Torvalds 	return 0;
22911da177e4SLinus Torvalds 
22921da177e4SLinus Torvalds out:
229312a169e7SHerbert Xu 	xp->walk.dead = 1;
229464c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
22951da177e4SLinus Torvalds 	return err;
22961da177e4SLinus Torvalds }
22971da177e4SLinus Torvalds 
22984c93fbb0SDavid S. Miller static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
22991da177e4SLinus Torvalds {
230007fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
23011da177e4SLinus Torvalds 	int err;
23021da177e4SLinus Torvalds 	struct sadb_address *sa;
23031da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
230403e1ad7bSPaul Moore 	struct xfrm_policy *xp;
23051da177e4SLinus Torvalds 	struct xfrm_selector sel;
230626b15dadSJamal Hadi Salim 	struct km_event c;
2307df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
23082db3e47eSBrian Haley 	struct xfrm_sec_ctx *pol_ctx = NULL;
23091da177e4SLinus Torvalds 
23101da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
23111da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
23121da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
23131da177e4SLinus Torvalds 		return -EINVAL;
23141da177e4SLinus Torvalds 
23151da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
23161da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
23171da177e4SLinus Torvalds 		return -EINVAL;
23181da177e4SLinus Torvalds 
23191da177e4SLinus Torvalds 	memset(&sel, 0, sizeof(sel));
23201da177e4SLinus Torvalds 
2321d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
23221da177e4SLinus Torvalds 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
23231da177e4SLinus Torvalds 	sel.prefixlen_s = sa->sadb_address_prefixlen;
23241da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23251da177e4SLinus Torvalds 	sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
23261da177e4SLinus Torvalds 	if (sel.sport)
23278f83f23eSAl Viro 		sel.sport_mask = htons(0xffff);
23281da177e4SLinus Torvalds 
2329d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
23301da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
23311da177e4SLinus Torvalds 	sel.prefixlen_d = sa->sadb_address_prefixlen;
23321da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23331da177e4SLinus Torvalds 	sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
23341da177e4SLinus Torvalds 	if (sel.dport)
23358f83f23eSAl Viro 		sel.dport_mask = htons(0xffff);
23361da177e4SLinus Torvalds 
2337ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
2338df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
2339*87536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
2340df71837dSTrent Jaeger 
2341df71837dSTrent Jaeger 		if (!uctx)
2342df71837dSTrent Jaeger 			return -ENOMEM;
2343df71837dSTrent Jaeger 
234403e1ad7bSPaul Moore 		err = security_xfrm_policy_alloc(&pol_ctx, uctx);
2345df71837dSTrent Jaeger 		kfree(uctx);
2346df71837dSTrent Jaeger 		if (err)
2347df71837dSTrent Jaeger 			return err;
23482db3e47eSBrian Haley 	}
2349df71837dSTrent Jaeger 
23508ca2e93bSJamal Hadi Salim 	xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
235103e1ad7bSPaul Moore 				   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
235203e1ad7bSPaul Moore 				   1, &err);
235303e1ad7bSPaul Moore 	security_xfrm_policy_free(pol_ctx);
23541da177e4SLinus Torvalds 	if (xp == NULL)
23551da177e4SLinus Torvalds 		return -ENOENT;
23561da177e4SLinus Torvalds 
2357ab5f5e8bSJoy Latten 	xfrm_audit_policy_delete(xp, err ? 0 : 1,
23582532386fSEric Paris 				 audit_get_loginuid(current),
23592532386fSEric Paris 				 audit_get_sessionid(current), 0);
236013fcfbb0SDavid S. Miller 
236113fcfbb0SDavid S. Miller 	if (err)
2362c8c05a8eSCatherine Zhang 		goto out;
236313fcfbb0SDavid S. Miller 
236426b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
236515e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
23661839faabSTobias Brunner 	c.data.byid = 0;
2367f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELPOLICY;
236826b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
236926b15dadSJamal Hadi Salim 
2370c8c05a8eSCatherine Zhang out:
237126b15dadSJamal Hadi Salim 	xfrm_pol_put(xp);
2372e4c17216SPaul Moore 	if (err == 0)
2373e4c17216SPaul Moore 		xfrm_garbage_collect(net);
237426b15dadSJamal Hadi Salim 	return err;
237526b15dadSJamal Hadi Salim }
237626b15dadSJamal Hadi Salim 
23774c93fbb0SDavid S. Miller static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struct sadb_msg *hdr, int dir)
237826b15dadSJamal Hadi Salim {
237926b15dadSJamal Hadi Salim 	int err;
238026b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
238126b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
238226b15dadSJamal Hadi Salim 	err = 0;
238326b15dadSJamal Hadi Salim 
23841da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
23851da177e4SLinus Torvalds 	if (IS_ERR(out_skb)) {
23861da177e4SLinus Torvalds 		err =  PTR_ERR(out_skb);
23871da177e4SLinus Torvalds 		goto out;
23881da177e4SLinus Torvalds 	}
238955569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
239055569ce2SKazunori MIYAZAWA 	if (err < 0)
239155569ce2SKazunori MIYAZAWA 		goto out;
23921da177e4SLinus Torvalds 
23931da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
23941da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
239526b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_type = hdr->sadb_msg_type;
23961da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = 0;
23971da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
23981da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
23991da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
240007fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
24011da177e4SLinus Torvalds 	err = 0;
24021da177e4SLinus Torvalds 
24031da177e4SLinus Torvalds out:
24041da177e4SLinus Torvalds 	return err;
24051da177e4SLinus Torvalds }
24061da177e4SLinus Torvalds 
240708de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
240808de61beSShinta Sugimoto static int pfkey_sockaddr_pair_size(sa_family_t family)
240908de61beSShinta Sugimoto {
24109e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
241108de61beSShinta Sugimoto }
241208de61beSShinta Sugimoto 
241313c1d189SArnaud Ebalard static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
241408de61beSShinta Sugimoto 			       xfrm_address_t *saddr, xfrm_address_t *daddr,
241508de61beSShinta Sugimoto 			       u16 *family)
241608de61beSShinta Sugimoto {
24175f95ac91SYOSHIFUJI Hideaki 	int af, socklen;
24185f95ac91SYOSHIFUJI Hideaki 
241913c1d189SArnaud Ebalard 	if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
242008de61beSShinta Sugimoto 		return -EINVAL;
242108de61beSShinta Sugimoto 
242213c1d189SArnaud Ebalard 	af = pfkey_sockaddr_extract(sa, saddr);
24235f95ac91SYOSHIFUJI Hideaki 	if (!af)
242408de61beSShinta Sugimoto 		return -EINVAL;
242508de61beSShinta Sugimoto 
24265f95ac91SYOSHIFUJI Hideaki 	socklen = pfkey_sockaddr_len(af);
242713c1d189SArnaud Ebalard 	if (pfkey_sockaddr_extract((struct sockaddr *) (((u8 *)sa) + socklen),
24285f95ac91SYOSHIFUJI Hideaki 				   daddr) != af)
24295f95ac91SYOSHIFUJI Hideaki 		return -EINVAL;
24305f95ac91SYOSHIFUJI Hideaki 
24315f95ac91SYOSHIFUJI Hideaki 	*family = af;
243208de61beSShinta Sugimoto 	return 0;
243308de61beSShinta Sugimoto }
243408de61beSShinta Sugimoto 
243508de61beSShinta Sugimoto static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
243608de61beSShinta Sugimoto 				    struct xfrm_migrate *m)
243708de61beSShinta Sugimoto {
243808de61beSShinta Sugimoto 	int err;
243908de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq2;
244055569ce2SKazunori MIYAZAWA 	int mode;
244108de61beSShinta Sugimoto 
244208de61beSShinta Sugimoto 	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
244308de61beSShinta Sugimoto 	    len < rq1->sadb_x_ipsecrequest_len)
244408de61beSShinta Sugimoto 		return -EINVAL;
244508de61beSShinta Sugimoto 
244608de61beSShinta Sugimoto 	/* old endoints */
244713c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
244813c1d189SArnaud Ebalard 				  rq1->sadb_x_ipsecrequest_len,
244913c1d189SArnaud Ebalard 				  &m->old_saddr, &m->old_daddr,
245008de61beSShinta Sugimoto 				  &m->old_family);
245108de61beSShinta Sugimoto 	if (err)
245208de61beSShinta Sugimoto 		return err;
245308de61beSShinta Sugimoto 
245408de61beSShinta Sugimoto 	rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len);
245508de61beSShinta Sugimoto 	len -= rq1->sadb_x_ipsecrequest_len;
245608de61beSShinta Sugimoto 
245708de61beSShinta Sugimoto 	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
245808de61beSShinta Sugimoto 	    len < rq2->sadb_x_ipsecrequest_len)
245908de61beSShinta Sugimoto 		return -EINVAL;
246008de61beSShinta Sugimoto 
246108de61beSShinta Sugimoto 	/* new endpoints */
246213c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
246313c1d189SArnaud Ebalard 				  rq2->sadb_x_ipsecrequest_len,
246413c1d189SArnaud Ebalard 				  &m->new_saddr, &m->new_daddr,
246508de61beSShinta Sugimoto 				  &m->new_family);
246608de61beSShinta Sugimoto 	if (err)
246708de61beSShinta Sugimoto 		return err;
246808de61beSShinta Sugimoto 
246908de61beSShinta Sugimoto 	if (rq1->sadb_x_ipsecrequest_proto != rq2->sadb_x_ipsecrequest_proto ||
247008de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_mode != rq2->sadb_x_ipsecrequest_mode ||
247108de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_reqid != rq2->sadb_x_ipsecrequest_reqid)
247208de61beSShinta Sugimoto 		return -EINVAL;
247308de61beSShinta Sugimoto 
247408de61beSShinta Sugimoto 	m->proto = rq1->sadb_x_ipsecrequest_proto;
247555569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0)
247655569ce2SKazunori MIYAZAWA 		return -EINVAL;
247755569ce2SKazunori MIYAZAWA 	m->mode = mode;
247808de61beSShinta Sugimoto 	m->reqid = rq1->sadb_x_ipsecrequest_reqid;
247908de61beSShinta Sugimoto 
248008de61beSShinta Sugimoto 	return ((int)(rq1->sadb_x_ipsecrequest_len +
248108de61beSShinta Sugimoto 		      rq2->sadb_x_ipsecrequest_len));
248208de61beSShinta Sugimoto }
248308de61beSShinta Sugimoto 
248408de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
24854c93fbb0SDavid S. Miller 			 const struct sadb_msg *hdr, void * const *ext_hdrs)
248608de61beSShinta Sugimoto {
248708de61beSShinta Sugimoto 	int i, len, ret, err = -EINVAL;
248808de61beSShinta Sugimoto 	u8 dir;
248908de61beSShinta Sugimoto 	struct sadb_address *sa;
249013c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
249108de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
249208de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
249308de61beSShinta Sugimoto 	struct xfrm_selector sel;
249408de61beSShinta Sugimoto 	struct xfrm_migrate m[XFRM_MAX_DEPTH];
249513c1d189SArnaud Ebalard 	struct xfrm_kmaddress k;
24968d549c4fSFan Du 	struct net *net = sock_net(sk);
249708de61beSShinta Sugimoto 
249808de61beSShinta Sugimoto 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1],
249908de61beSShinta Sugimoto 				     ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) ||
250008de61beSShinta Sugimoto 	    !ext_hdrs[SADB_X_EXT_POLICY - 1]) {
250108de61beSShinta Sugimoto 		err = -EINVAL;
250208de61beSShinta Sugimoto 		goto out;
250308de61beSShinta Sugimoto 	}
250408de61beSShinta Sugimoto 
250513c1d189SArnaud Ebalard 	kma = ext_hdrs[SADB_X_EXT_KMADDRESS - 1];
250608de61beSShinta Sugimoto 	pol = ext_hdrs[SADB_X_EXT_POLICY - 1];
250708de61beSShinta Sugimoto 
250808de61beSShinta Sugimoto 	if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) {
250908de61beSShinta Sugimoto 		err = -EINVAL;
251008de61beSShinta Sugimoto 		goto out;
251108de61beSShinta Sugimoto 	}
251208de61beSShinta Sugimoto 
251313c1d189SArnaud Ebalard 	if (kma) {
251413c1d189SArnaud Ebalard 		/* convert sadb_x_kmaddress to xfrm_kmaddress */
251513c1d189SArnaud Ebalard 		k.reserved = kma->sadb_x_kmaddress_reserved;
251613c1d189SArnaud Ebalard 		ret = parse_sockaddr_pair((struct sockaddr *)(kma + 1),
251713c1d189SArnaud Ebalard 					  8*(kma->sadb_x_kmaddress_len) - sizeof(*kma),
251813c1d189SArnaud Ebalard 					  &k.local, &k.remote, &k.family);
251913c1d189SArnaud Ebalard 		if (ret < 0) {
252013c1d189SArnaud Ebalard 			err = ret;
252113c1d189SArnaud Ebalard 			goto out;
252213c1d189SArnaud Ebalard 		}
252313c1d189SArnaud Ebalard 	}
252413c1d189SArnaud Ebalard 
252508de61beSShinta Sugimoto 	dir = pol->sadb_x_policy_dir - 1;
252608de61beSShinta Sugimoto 	memset(&sel, 0, sizeof(sel));
252708de61beSShinta Sugimoto 
252808de61beSShinta Sugimoto 	/* set source address info of selector */
252908de61beSShinta Sugimoto 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC - 1];
253008de61beSShinta Sugimoto 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
253108de61beSShinta Sugimoto 	sel.prefixlen_s = sa->sadb_address_prefixlen;
253208de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
253308de61beSShinta Sugimoto 	sel.sport = ((struct sockaddr_in *)(sa + 1))->sin_port;
253408de61beSShinta Sugimoto 	if (sel.sport)
2535582ee43dSAl Viro 		sel.sport_mask = htons(0xffff);
253608de61beSShinta Sugimoto 
253708de61beSShinta Sugimoto 	/* set destination address info of selector */
253808de61beSShinta Sugimoto 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1],
253908de61beSShinta Sugimoto 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
254008de61beSShinta Sugimoto 	sel.prefixlen_d = sa->sadb_address_prefixlen;
254108de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
254208de61beSShinta Sugimoto 	sel.dport = ((struct sockaddr_in *)(sa + 1))->sin_port;
254308de61beSShinta Sugimoto 	if (sel.dport)
2544582ee43dSAl Viro 		sel.dport_mask = htons(0xffff);
254508de61beSShinta Sugimoto 
254608de61beSShinta Sugimoto 	rq = (struct sadb_x_ipsecrequest *)(pol + 1);
254708de61beSShinta Sugimoto 
254808de61beSShinta Sugimoto 	/* extract ipsecrequests */
254908de61beSShinta Sugimoto 	i = 0;
255008de61beSShinta Sugimoto 	len = pol->sadb_x_policy_len * 8 - sizeof(struct sadb_x_policy);
255108de61beSShinta Sugimoto 
255208de61beSShinta Sugimoto 	while (len > 0 && i < XFRM_MAX_DEPTH) {
255308de61beSShinta Sugimoto 		ret = ipsecrequests_to_migrate(rq, len, &m[i]);
255408de61beSShinta Sugimoto 		if (ret < 0) {
255508de61beSShinta Sugimoto 			err = ret;
255608de61beSShinta Sugimoto 			goto out;
255708de61beSShinta Sugimoto 		} else {
255808de61beSShinta Sugimoto 			rq = (struct sadb_x_ipsecrequest *)((u8 *)rq + ret);
255908de61beSShinta Sugimoto 			len -= ret;
256008de61beSShinta Sugimoto 			i++;
256108de61beSShinta Sugimoto 		}
256208de61beSShinta Sugimoto 	}
256308de61beSShinta Sugimoto 
256408de61beSShinta Sugimoto 	if (!i || len > 0) {
256508de61beSShinta Sugimoto 		err = -EINVAL;
256608de61beSShinta Sugimoto 		goto out;
256708de61beSShinta Sugimoto 	}
256808de61beSShinta Sugimoto 
256913c1d189SArnaud Ebalard 	return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
25708d549c4fSFan Du 			    kma ? &k : NULL, net);
257108de61beSShinta Sugimoto 
257208de61beSShinta Sugimoto  out:
257308de61beSShinta Sugimoto 	return err;
257408de61beSShinta Sugimoto }
257508de61beSShinta Sugimoto #else
257608de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
25777f6daa63SStephen Hemminger 			 const struct sadb_msg *hdr, void * const *ext_hdrs)
257808de61beSShinta Sugimoto {
257908de61beSShinta Sugimoto 	return -ENOPROTOOPT;
258008de61beSShinta Sugimoto }
258108de61beSShinta Sugimoto #endif
258208de61beSShinta Sugimoto 
258308de61beSShinta Sugimoto 
25844c93fbb0SDavid S. Miller static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
25851da177e4SLinus Torvalds {
258607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
258777d8d7a6SHerbert Xu 	unsigned int dir;
2588215a2dd3SEric Paris 	int err = 0, delete;
25891da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
25901da177e4SLinus Torvalds 	struct xfrm_policy *xp;
259126b15dadSJamal Hadi Salim 	struct km_event c;
25921da177e4SLinus Torvalds 
25931da177e4SLinus Torvalds 	if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
25941da177e4SLinus Torvalds 		return -EINVAL;
25951da177e4SLinus Torvalds 
259677d8d7a6SHerbert Xu 	dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
259777d8d7a6SHerbert Xu 	if (dir >= XFRM_POLICY_MAX)
259877d8d7a6SHerbert Xu 		return -EINVAL;
259977d8d7a6SHerbert Xu 
2600215a2dd3SEric Paris 	delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
26018ca2e93bSJamal Hadi Salim 	xp = xfrm_policy_byid(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
2602bd55775cSJamal Hadi Salim 			      dir, pol->sadb_x_policy_id, delete, &err);
26031da177e4SLinus Torvalds 	if (xp == NULL)
26041da177e4SLinus Torvalds 		return -ENOENT;
26051da177e4SLinus Torvalds 
2606215a2dd3SEric Paris 	if (delete) {
2607ab5f5e8bSJoy Latten 		xfrm_audit_policy_delete(xp, err ? 0 : 1,
26082532386fSEric Paris 				audit_get_loginuid(current),
26092532386fSEric Paris 				audit_get_sessionid(current), 0);
26101da177e4SLinus Torvalds 
2611215a2dd3SEric Paris 		if (err)
2612215a2dd3SEric Paris 			goto out;
261326b15dadSJamal Hadi Salim 		c.seq = hdr->sadb_msg_seq;
261415e47304SEric W. Biederman 		c.portid = hdr->sadb_msg_pid;
2615bf08867fSHerbert Xu 		c.data.byid = 1;
2616f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_DELPOLICY;
261777d8d7a6SHerbert Xu 		km_policy_notify(xp, dir, &c);
261826b15dadSJamal Hadi Salim 	} else {
261977d8d7a6SHerbert Xu 		err = key_pol_get_resp(sk, xp, hdr, dir);
26201da177e4SLinus Torvalds 	}
26211da177e4SLinus Torvalds 
2622215a2dd3SEric Paris out:
26231da177e4SLinus Torvalds 	xfrm_pol_put(xp);
2624e4c17216SPaul Moore 	if (delete && err == 0)
2625e4c17216SPaul Moore 		xfrm_garbage_collect(net);
26261da177e4SLinus Torvalds 	return err;
26271da177e4SLinus Torvalds }
26281da177e4SLinus Torvalds 
26291da177e4SLinus Torvalds static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
26301da177e4SLinus Torvalds {
263183321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
26321da177e4SLinus Torvalds 	struct sk_buff *out_skb;
26331da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
263455569ce2SKazunori MIYAZAWA 	int err;
26351da177e4SLinus Torvalds 
263683321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
263783321d6bSTimo Teras 		return -ENOBUFS;
263883321d6bSTimo Teras 
26391da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
26401da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
26411da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
26421da177e4SLinus Torvalds 
264355569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
264455569ce2SKazunori MIYAZAWA 	if (err < 0)
264555569ce2SKazunori MIYAZAWA 		return err;
26461da177e4SLinus Torvalds 
26471da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
264883321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
26491da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
26501da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
26511da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
265212a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
265315e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
265412a169e7SHerbert Xu 
265512a169e7SHerbert Xu 	if (pfk->dump.skb)
265612a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
265707fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
265812a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
265912a169e7SHerbert Xu 
26601da177e4SLinus Torvalds 	return 0;
26611da177e4SLinus Torvalds }
26621da177e4SLinus Torvalds 
266383321d6bSTimo Teras static int pfkey_dump_sp(struct pfkey_sock *pfk)
266483321d6bSTimo Teras {
266507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
266607fb0f17SAlexey Dobriyan 	return xfrm_policy_walk(net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
266783321d6bSTimo Teras }
266883321d6bSTimo Teras 
266983321d6bSTimo Teras static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
267083321d6bSTimo Teras {
2671283bc9f3SFan Du 	struct net *net = sock_net((struct sock *)pfk);
2672283bc9f3SFan Du 
2673283bc9f3SFan Du 	xfrm_policy_walk_done(&pfk->dump.u.policy, net);
267483321d6bSTimo Teras }
267583321d6bSTimo Teras 
26764c93fbb0SDavid S. Miller static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
26771da177e4SLinus Torvalds {
267883321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
26791da177e4SLinus Torvalds 
268083321d6bSTimo Teras 	if (pfk->dump.dump != NULL)
268183321d6bSTimo Teras 		return -EBUSY;
26824c563f76STimo Teras 
268383321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
268415e47304SEric W. Biederman 	pfk->dump.msg_portid = hdr->sadb_msg_pid;
268583321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sp;
268683321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sp_done;
268783321d6bSTimo Teras 	xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
268883321d6bSTimo Teras 
268983321d6bSTimo Teras 	return pfkey_do_dump(pfk);
26901da177e4SLinus Torvalds }
26911da177e4SLinus Torvalds 
2692214e005bSDavid S. Miller static int key_notify_policy_flush(const struct km_event *c)
26931da177e4SLinus Torvalds {
26941da177e4SLinus Torvalds 	struct sk_buff *skb_out;
269526b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
26961da177e4SLinus Torvalds 
269726b15dadSJamal Hadi Salim 	skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
26981da177e4SLinus Torvalds 	if (!skb_out)
26991da177e4SLinus Torvalds 		return -ENOBUFS;
270026b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
2701151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_X_SPDFLUSH;
270226b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
270315e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
270426b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
270526b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
270685dfb745SNicolas Dichtel 	hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
270726b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
2708a5cc68f3SMathias Krause 	hdr->sadb_msg_reserved = 0;
270907fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
271026b15dadSJamal Hadi Salim 	return 0;
271126b15dadSJamal Hadi Salim 
271226b15dadSJamal Hadi Salim }
271326b15dadSJamal Hadi Salim 
27144c93fbb0SDavid S. Miller static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
271526b15dadSJamal Hadi Salim {
271607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
271726b15dadSJamal Hadi Salim 	struct km_event c;
2718161a09e7SJoy Latten 	struct xfrm_audit audit_info;
27198be987d7SJamal Hadi Salim 	int err, err2;
27201da177e4SLinus Torvalds 
27210c11b942SAl Viro 	audit_info.loginuid = audit_get_loginuid(current);
27222532386fSEric Paris 	audit_info.sessionid = audit_get_sessionid(current);
2723161a09e7SJoy Latten 	audit_info.secid = 0;
272407fb0f17SAlexey Dobriyan 	err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
27258be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
27262f1eb65fSJamal Hadi Salim 	if (err || err2) {
27272f1eb65fSJamal Hadi Salim 		if (err == -ESRCH) /* empty table - old silent behavior */
27282f1eb65fSJamal Hadi Salim 			return 0;
27292f1eb65fSJamal Hadi Salim 		return err;
27302f1eb65fSJamal Hadi Salim 	}
27318be987d7SJamal Hadi Salim 
2732f7b6983fSMasahide NAKAMURA 	c.data.type = XFRM_POLICY_TYPE_MAIN;
2733f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHPOLICY;
273415e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
273526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
273607fb0f17SAlexey Dobriyan 	c.net = net;
273726b15dadSJamal Hadi Salim 	km_policy_notify(NULL, 0, &c);
27381da177e4SLinus Torvalds 
27391da177e4SLinus Torvalds 	return 0;
27401da177e4SLinus Torvalds }
27411da177e4SLinus Torvalds 
27421da177e4SLinus Torvalds typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
27434c93fbb0SDavid S. Miller 			     const struct sadb_msg *hdr, void * const *ext_hdrs);
27448603b955SMathias Krause static const pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
27451da177e4SLinus Torvalds 	[SADB_RESERVED]		= pfkey_reserved,
27461da177e4SLinus Torvalds 	[SADB_GETSPI]		= pfkey_getspi,
27471da177e4SLinus Torvalds 	[SADB_UPDATE]		= pfkey_add,
27481da177e4SLinus Torvalds 	[SADB_ADD]		= pfkey_add,
27491da177e4SLinus Torvalds 	[SADB_DELETE]		= pfkey_delete,
27501da177e4SLinus Torvalds 	[SADB_GET]		= pfkey_get,
27511da177e4SLinus Torvalds 	[SADB_ACQUIRE]		= pfkey_acquire,
27521da177e4SLinus Torvalds 	[SADB_REGISTER]		= pfkey_register,
27531da177e4SLinus Torvalds 	[SADB_EXPIRE]		= NULL,
27541da177e4SLinus Torvalds 	[SADB_FLUSH]		= pfkey_flush,
27551da177e4SLinus Torvalds 	[SADB_DUMP]		= pfkey_dump,
27561da177e4SLinus Torvalds 	[SADB_X_PROMISC]	= pfkey_promisc,
27571da177e4SLinus Torvalds 	[SADB_X_PCHANGE]	= NULL,
27581da177e4SLinus Torvalds 	[SADB_X_SPDUPDATE]	= pfkey_spdadd,
27591da177e4SLinus Torvalds 	[SADB_X_SPDADD]		= pfkey_spdadd,
27601da177e4SLinus Torvalds 	[SADB_X_SPDDELETE]	= pfkey_spddelete,
27611da177e4SLinus Torvalds 	[SADB_X_SPDGET]		= pfkey_spdget,
27621da177e4SLinus Torvalds 	[SADB_X_SPDACQUIRE]	= NULL,
27631da177e4SLinus Torvalds 	[SADB_X_SPDDUMP]	= pfkey_spddump,
27641da177e4SLinus Torvalds 	[SADB_X_SPDFLUSH]	= pfkey_spdflush,
27651da177e4SLinus Torvalds 	[SADB_X_SPDSETIDX]	= pfkey_spdadd,
27661da177e4SLinus Torvalds 	[SADB_X_SPDDELETE2]	= pfkey_spdget,
276708de61beSShinta Sugimoto 	[SADB_X_MIGRATE]	= pfkey_migrate,
27681da177e4SLinus Torvalds };
27691da177e4SLinus Torvalds 
27704c93fbb0SDavid S. Miller static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr)
27711da177e4SLinus Torvalds {
27721da177e4SLinus Torvalds 	void *ext_hdrs[SADB_EXT_MAX];
27731da177e4SLinus Torvalds 	int err;
27741da177e4SLinus Torvalds 
27751da177e4SLinus Torvalds 	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
277607fb0f17SAlexey Dobriyan 			BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
27771da177e4SLinus Torvalds 
27781da177e4SLinus Torvalds 	memset(ext_hdrs, 0, sizeof(ext_hdrs));
27791da177e4SLinus Torvalds 	err = parse_exthdrs(skb, hdr, ext_hdrs);
27801da177e4SLinus Torvalds 	if (!err) {
27811da177e4SLinus Torvalds 		err = -EOPNOTSUPP;
27821da177e4SLinus Torvalds 		if (pfkey_funcs[hdr->sadb_msg_type])
27831da177e4SLinus Torvalds 			err = pfkey_funcs[hdr->sadb_msg_type](sk, skb, hdr, ext_hdrs);
27841da177e4SLinus Torvalds 	}
27851da177e4SLinus Torvalds 	return err;
27861da177e4SLinus Torvalds }
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)
27891da177e4SLinus Torvalds {
27901da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
27911da177e4SLinus Torvalds 
27921da177e4SLinus Torvalds 	if (skb->len < sizeof(*hdr)) {
27931da177e4SLinus Torvalds 		*errp = -EMSGSIZE;
27941da177e4SLinus Torvalds 	} else {
27951da177e4SLinus Torvalds 		hdr = (struct sadb_msg *) skb->data;
27961da177e4SLinus Torvalds 		if (hdr->sadb_msg_version != PF_KEY_V2 ||
27971da177e4SLinus Torvalds 		    hdr->sadb_msg_reserved != 0 ||
27981da177e4SLinus Torvalds 		    (hdr->sadb_msg_type <= SADB_RESERVED ||
27991da177e4SLinus Torvalds 		     hdr->sadb_msg_type > SADB_MAX)) {
28001da177e4SLinus Torvalds 			hdr = NULL;
28011da177e4SLinus Torvalds 			*errp = -EINVAL;
28021da177e4SLinus Torvalds 		} else if (hdr->sadb_msg_len != (skb->len /
28031da177e4SLinus Torvalds 						 sizeof(uint64_t)) ||
28041da177e4SLinus Torvalds 			   hdr->sadb_msg_len < (sizeof(struct sadb_msg) /
28051da177e4SLinus Torvalds 						sizeof(uint64_t))) {
28061da177e4SLinus Torvalds 			hdr = NULL;
28071da177e4SLinus Torvalds 			*errp = -EMSGSIZE;
28081da177e4SLinus Torvalds 		} else {
28091da177e4SLinus Torvalds 			*errp = 0;
28101da177e4SLinus Torvalds 		}
28111da177e4SLinus Torvalds 	}
28121da177e4SLinus Torvalds 	return hdr;
28131da177e4SLinus Torvalds }
28141da177e4SLinus Torvalds 
28154c93fbb0SDavid S. Miller static inline int aalg_tmpl_set(const struct xfrm_tmpl *t,
28164c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
28171da177e4SLinus Torvalds {
2818f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2819f398035fSHerbert Xu 
2820f398035fSHerbert Xu 	if (id >= sizeof(t->aalgos) * 8)
2821f398035fSHerbert Xu 		return 0;
2822f398035fSHerbert Xu 
2823f398035fSHerbert Xu 	return (t->aalgos >> id) & 1;
28241da177e4SLinus Torvalds }
28251da177e4SLinus Torvalds 
28264c93fbb0SDavid S. Miller static inline int ealg_tmpl_set(const struct xfrm_tmpl *t,
28274c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
28281da177e4SLinus Torvalds {
2829f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2830f398035fSHerbert Xu 
2831f398035fSHerbert Xu 	if (id >= sizeof(t->ealgos) * 8)
2832f398035fSHerbert Xu 		return 0;
2833f398035fSHerbert Xu 
2834f398035fSHerbert Xu 	return (t->ealgos >> id) & 1;
28351da177e4SLinus Torvalds }
28361da177e4SLinus Torvalds 
28374c93fbb0SDavid S. Miller static int count_ah_combs(const struct xfrm_tmpl *t)
28381da177e4SLinus Torvalds {
28391da177e4SLinus Torvalds 	int i, sz = 0;
28401da177e4SLinus Torvalds 
28411da177e4SLinus Torvalds 	for (i = 0; ; i++) {
28424c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
28431da177e4SLinus Torvalds 		if (!aalg)
28441da177e4SLinus Torvalds 			break;
28457e50f84cSJussi Kivilinna 		if (!aalg->pfkey_supported)
28467e50f84cSJussi Kivilinna 			continue;
28471da177e4SLinus Torvalds 		if (aalg_tmpl_set(t, aalg) && aalg->available)
28481da177e4SLinus Torvalds 			sz += sizeof(struct sadb_comb);
28491da177e4SLinus Torvalds 	}
28501da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
28511da177e4SLinus Torvalds }
28521da177e4SLinus Torvalds 
28534c93fbb0SDavid S. Miller static int count_esp_combs(const struct xfrm_tmpl *t)
28541da177e4SLinus Torvalds {
28551da177e4SLinus Torvalds 	int i, k, sz = 0;
28561da177e4SLinus Torvalds 
28571da177e4SLinus Torvalds 	for (i = 0; ; i++) {
28584c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
28591da177e4SLinus Torvalds 		if (!ealg)
28601da177e4SLinus Torvalds 			break;
28611da177e4SLinus Torvalds 
28627e50f84cSJussi Kivilinna 		if (!ealg->pfkey_supported)
28637e50f84cSJussi Kivilinna 			continue;
28647e50f84cSJussi Kivilinna 
28651da177e4SLinus Torvalds 		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
28661da177e4SLinus Torvalds 			continue;
28671da177e4SLinus Torvalds 
28681da177e4SLinus Torvalds 		for (k = 1; ; k++) {
28694c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
28701da177e4SLinus Torvalds 			if (!aalg)
28711da177e4SLinus Torvalds 				break;
28721da177e4SLinus Torvalds 
28737e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
28747e50f84cSJussi Kivilinna 				continue;
28757e50f84cSJussi Kivilinna 
28761da177e4SLinus Torvalds 			if (aalg_tmpl_set(t, aalg) && aalg->available)
28771da177e4SLinus Torvalds 				sz += sizeof(struct sadb_comb);
28781da177e4SLinus Torvalds 		}
28791da177e4SLinus Torvalds 	}
28801da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
28811da177e4SLinus Torvalds }
28821da177e4SLinus Torvalds 
28834c93fbb0SDavid S. Miller static void dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
28841da177e4SLinus Torvalds {
28851da177e4SLinus Torvalds 	struct sadb_prop *p;
28861da177e4SLinus Torvalds 	int i;
28871da177e4SLinus Torvalds 
28881da177e4SLinus Torvalds 	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
28891da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
28901da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
28911da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
28921da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
28931da177e4SLinus Torvalds 
28941da177e4SLinus Torvalds 	for (i = 0; ; i++) {
28954c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
28961da177e4SLinus Torvalds 		if (!aalg)
28971da177e4SLinus Torvalds 			break;
28981da177e4SLinus Torvalds 
28997e50f84cSJussi Kivilinna 		if (!aalg->pfkey_supported)
29007e50f84cSJussi Kivilinna 			continue;
29017e50f84cSJussi Kivilinna 
29021da177e4SLinus Torvalds 		if (aalg_tmpl_set(t, aalg) && aalg->available) {
29031da177e4SLinus Torvalds 			struct sadb_comb *c;
29041da177e4SLinus Torvalds 			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
29051da177e4SLinus Torvalds 			memset(c, 0, sizeof(*c));
29061da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
29071da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
29081da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
29091da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
29101da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
29111da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
29121da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
29131da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
29141da177e4SLinus Torvalds 		}
29151da177e4SLinus Torvalds 	}
29161da177e4SLinus Torvalds }
29171da177e4SLinus Torvalds 
29184c93fbb0SDavid S. Miller static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
29191da177e4SLinus Torvalds {
29201da177e4SLinus Torvalds 	struct sadb_prop *p;
29211da177e4SLinus Torvalds 	int i, k;
29221da177e4SLinus Torvalds 
29231da177e4SLinus Torvalds 	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
29241da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
29251da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
29261da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
29271da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
29281da177e4SLinus Torvalds 
29291da177e4SLinus Torvalds 	for (i=0; ; i++) {
29304c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
29311da177e4SLinus Torvalds 		if (!ealg)
29321da177e4SLinus Torvalds 			break;
29331da177e4SLinus Torvalds 
29347e50f84cSJussi Kivilinna 		if (!ealg->pfkey_supported)
29357e50f84cSJussi Kivilinna 			continue;
29367e50f84cSJussi Kivilinna 
29371da177e4SLinus Torvalds 		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
29381da177e4SLinus Torvalds 			continue;
29391da177e4SLinus Torvalds 
29401da177e4SLinus Torvalds 		for (k = 1; ; k++) {
29411da177e4SLinus Torvalds 			struct sadb_comb *c;
29424c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
29431da177e4SLinus Torvalds 			if (!aalg)
29441da177e4SLinus Torvalds 				break;
29457e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
29467e50f84cSJussi Kivilinna 				continue;
29471da177e4SLinus Torvalds 			if (!(aalg_tmpl_set(t, aalg) && aalg->available))
29481da177e4SLinus Torvalds 				continue;
29491da177e4SLinus Torvalds 			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
29501da177e4SLinus Torvalds 			memset(c, 0, sizeof(*c));
29511da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
29521da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
29531da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
29541da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
29551da177e4SLinus Torvalds 			c->sadb_comb_encrypt = ealg->desc.sadb_alg_id;
29561da177e4SLinus Torvalds 			c->sadb_comb_encrypt_minbits = ealg->desc.sadb_alg_minbits;
29571da177e4SLinus Torvalds 			c->sadb_comb_encrypt_maxbits = ealg->desc.sadb_alg_maxbits;
29581da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
29591da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
29601da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
29611da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
29621da177e4SLinus Torvalds 		}
29631da177e4SLinus Torvalds 	}
29641da177e4SLinus Torvalds }
29651da177e4SLinus Torvalds 
2966214e005bSDavid S. Miller static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c)
296726b15dadSJamal Hadi Salim {
296826b15dadSJamal Hadi Salim 	return 0;
296926b15dadSJamal Hadi Salim }
297026b15dadSJamal Hadi Salim 
2971214e005bSDavid S. Miller static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
29721da177e4SLinus Torvalds {
29731da177e4SLinus Torvalds 	struct sk_buff *out_skb;
29741da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
297526b15dadSJamal Hadi Salim 	int hard;
297626b15dadSJamal Hadi Salim 	int hsc;
297726b15dadSJamal Hadi Salim 
2978bf08867fSHerbert Xu 	hard = c->data.hard;
297926b15dadSJamal Hadi Salim 	if (hard)
298026b15dadSJamal Hadi Salim 		hsc = 2;
298126b15dadSJamal Hadi Salim 	else
298226b15dadSJamal Hadi Salim 		hsc = 1;
29831da177e4SLinus Torvalds 
2984050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg_expire(x, hsc);
29851da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
29861da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
29871da177e4SLinus Torvalds 
29881da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
29891da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = PF_KEY_V2;
29901da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_EXPIRE;
29911da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
29921da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
29931da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
29941da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = 0;
29951da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = 0;
29961da177e4SLinus Torvalds 
299707fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
29981da177e4SLinus Torvalds 	return 0;
29991da177e4SLinus Torvalds }
30001da177e4SLinus Torvalds 
3001214e005bSDavid S. Miller static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
300226b15dadSJamal Hadi Salim {
300307fb0f17SAlexey Dobriyan 	struct net *net = x ? xs_net(x) : c->net;
30043fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
30053fa87a32SAlexey Dobriyan 
30063fa87a32SAlexey Dobriyan 	if (atomic_read(&net_pfkey->socks_nr) == 0)
300799c6f60eSJamal Hadi Salim 		return 0;
300899c6f60eSJamal Hadi Salim 
300926b15dadSJamal Hadi Salim 	switch (c->event) {
3010f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
301126b15dadSJamal Hadi Salim 		return key_notify_sa_expire(x, c);
3012f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
3013f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
3014f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
301526b15dadSJamal Hadi Salim 		return key_notify_sa(x, c);
3016f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHSA:
301726b15dadSJamal Hadi Salim 		return key_notify_sa_flush(c);
3018d51d081dSJamal Hadi Salim 	case XFRM_MSG_NEWAE: /* not yet supported */
3019d51d081dSJamal Hadi Salim 		break;
302026b15dadSJamal Hadi Salim 	default:
3021207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", c->event);
302226b15dadSJamal Hadi Salim 		break;
302326b15dadSJamal Hadi Salim 	}
302426b15dadSJamal Hadi Salim 
302526b15dadSJamal Hadi Salim 	return 0;
302626b15dadSJamal Hadi Salim }
302726b15dadSJamal Hadi Salim 
3028214e005bSDavid S. Miller static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
302926b15dadSJamal Hadi Salim {
3030f7b6983fSMasahide NAKAMURA 	if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
3031f7b6983fSMasahide NAKAMURA 		return 0;
3032f7b6983fSMasahide NAKAMURA 
303326b15dadSJamal Hadi Salim 	switch (c->event) {
3034f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
303526b15dadSJamal Hadi Salim 		return key_notify_policy_expire(xp, c);
3036f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
3037f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
3038f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
303926b15dadSJamal Hadi Salim 		return key_notify_policy(xp, dir, c);
3040f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHPOLICY:
3041f7b6983fSMasahide NAKAMURA 		if (c->data.type != XFRM_POLICY_TYPE_MAIN)
3042f7b6983fSMasahide NAKAMURA 			break;
304326b15dadSJamal Hadi Salim 		return key_notify_policy_flush(c);
304426b15dadSJamal Hadi Salim 	default:
3045207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", c->event);
304626b15dadSJamal Hadi Salim 		break;
304726b15dadSJamal Hadi Salim 	}
304826b15dadSJamal Hadi Salim 
304926b15dadSJamal Hadi Salim 	return 0;
305026b15dadSJamal Hadi Salim }
305126b15dadSJamal Hadi Salim 
30521da177e4SLinus Torvalds static u32 get_acqseq(void)
30531da177e4SLinus Torvalds {
30541da177e4SLinus Torvalds 	u32 res;
305528aecb9dSEric Dumazet 	static atomic_t acqseq;
30561da177e4SLinus Torvalds 
305728aecb9dSEric Dumazet 	do {
305828aecb9dSEric Dumazet 		res = atomic_inc_return(&acqseq);
305928aecb9dSEric Dumazet 	} while (!res);
30601da177e4SLinus Torvalds 	return res;
30611da177e4SLinus Torvalds }
30621da177e4SLinus Torvalds 
306365e0736bSFan Du static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
30641da177e4SLinus Torvalds {
30651da177e4SLinus Torvalds 	struct sk_buff *skb;
30661da177e4SLinus Torvalds 	struct sadb_msg *hdr;
30671da177e4SLinus Torvalds 	struct sadb_address *addr;
30681da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
30691da177e4SLinus Torvalds 	int sockaddr_size;
30701da177e4SLinus Torvalds 	int size;
30714e2ba18eSVenkat Yekkirala 	struct sadb_x_sec_ctx *sec_ctx;
30724e2ba18eSVenkat Yekkirala 	struct xfrm_sec_ctx *xfrm_ctx;
30734e2ba18eSVenkat Yekkirala 	int ctx_size = 0;
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
30761da177e4SLinus Torvalds 	if (!sockaddr_size)
30771da177e4SLinus Torvalds 		return -EINVAL;
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
30801da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
30811da177e4SLinus Torvalds 		(sockaddr_size * 2) +
30821da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy);
30831da177e4SLinus Torvalds 
30841da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
30851da177e4SLinus Torvalds 		size += count_ah_combs(t);
30861da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
30871da177e4SLinus Torvalds 		size += count_esp_combs(t);
30881da177e4SLinus Torvalds 
30894e2ba18eSVenkat Yekkirala 	if ((xfrm_ctx = x->security)) {
30904e2ba18eSVenkat Yekkirala 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
30914e2ba18eSVenkat Yekkirala 		size +=  sizeof(struct sadb_x_sec_ctx) + ctx_size;
30924e2ba18eSVenkat Yekkirala 	}
30934e2ba18eSVenkat Yekkirala 
30941da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
30951da177e4SLinus Torvalds 	if (skb == NULL)
30961da177e4SLinus Torvalds 		return -ENOMEM;
30971da177e4SLinus Torvalds 
30981da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
30991da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
31001da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_ACQUIRE;
31011da177e4SLinus Torvalds 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
31021da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
31031da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
31041da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
31051da177e4SLinus Torvalds 	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
31061da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
31071da177e4SLinus Torvalds 
31081da177e4SLinus Torvalds 	/* src address */
31091da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
31101da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
31111da177e4SLinus Torvalds 	addr->sadb_address_len =
31121da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
31131da177e4SLinus Torvalds 			sizeof(uint64_t);
31141da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
31151da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
31161da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3117e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3118e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3119e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3120e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3121e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
31221da177e4SLinus Torvalds 		BUG();
31231da177e4SLinus Torvalds 
31241da177e4SLinus Torvalds 	/* dst address */
31251da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
31261da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
31271da177e4SLinus Torvalds 	addr->sadb_address_len =
31281da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
31291da177e4SLinus Torvalds 			sizeof(uint64_t);
31301da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
31311da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
31321da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3133e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3134e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
3135e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3136e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3137e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
31381da177e4SLinus Torvalds 		BUG();
31391da177e4SLinus Torvalds 
31401da177e4SLinus Torvalds 	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
31411da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
31421da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
31431da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
314465e0736bSFan Du 	pol->sadb_x_policy_dir = XFRM_POLICY_OUT + 1;
3145ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
31461da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
3147ff862a46SDan Carpenter 	pol->sadb_x_policy_priority = xp->priority;
31481da177e4SLinus Torvalds 
31491da177e4SLinus Torvalds 	/* Set sadb_comb's. */
31501da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
31511da177e4SLinus Torvalds 		dump_ah_combs(skb, t);
31521da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
31531da177e4SLinus Torvalds 		dump_esp_combs(skb, t);
31541da177e4SLinus Torvalds 
31554e2ba18eSVenkat Yekkirala 	/* security context */
31564e2ba18eSVenkat Yekkirala 	if (xfrm_ctx) {
31574e2ba18eSVenkat Yekkirala 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
31584e2ba18eSVenkat Yekkirala 				sizeof(struct sadb_x_sec_ctx) + ctx_size);
31594e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_len =
31604e2ba18eSVenkat Yekkirala 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
31614e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
31624e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
31634e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
31644e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
31654e2ba18eSVenkat Yekkirala 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
31664e2ba18eSVenkat Yekkirala 		       xfrm_ctx->ctx_len);
31674e2ba18eSVenkat Yekkirala 	}
31684e2ba18eSVenkat Yekkirala 
316907fb0f17SAlexey Dobriyan 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
31701da177e4SLinus Torvalds }
31711da177e4SLinus Torvalds 
3172cb969f07SVenkat Yekkirala static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
31731da177e4SLinus Torvalds 						u8 *data, int len, int *dir)
31741da177e4SLinus Torvalds {
317507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
31761da177e4SLinus Torvalds 	struct xfrm_policy *xp;
31771da177e4SLinus Torvalds 	struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
3178df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
31791da177e4SLinus Torvalds 
3180cb969f07SVenkat Yekkirala 	switch (sk->sk_family) {
31811da177e4SLinus Torvalds 	case AF_INET:
31821da177e4SLinus Torvalds 		if (opt != IP_IPSEC_POLICY) {
31831da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
31841da177e4SLinus Torvalds 			return NULL;
31851da177e4SLinus Torvalds 		}
31861da177e4SLinus Torvalds 		break;
3187dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
31881da177e4SLinus Torvalds 	case AF_INET6:
31891da177e4SLinus Torvalds 		if (opt != IPV6_IPSEC_POLICY) {
31901da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
31911da177e4SLinus Torvalds 			return NULL;
31921da177e4SLinus Torvalds 		}
31931da177e4SLinus Torvalds 		break;
31941da177e4SLinus Torvalds #endif
31951da177e4SLinus Torvalds 	default:
31961da177e4SLinus Torvalds 		*dir = -EINVAL;
31971da177e4SLinus Torvalds 		return NULL;
31981da177e4SLinus Torvalds 	}
31991da177e4SLinus Torvalds 
32001da177e4SLinus Torvalds 	*dir = -EINVAL;
32011da177e4SLinus Torvalds 
32021da177e4SLinus Torvalds 	if (len < sizeof(struct sadb_x_policy) ||
32031da177e4SLinus Torvalds 	    pol->sadb_x_policy_len*8 > len ||
32041da177e4SLinus Torvalds 	    pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
32051da177e4SLinus Torvalds 	    (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
32061da177e4SLinus Torvalds 		return NULL;
32071da177e4SLinus Torvalds 
320807fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_ATOMIC);
32091da177e4SLinus Torvalds 	if (xp == NULL) {
32101da177e4SLinus Torvalds 		*dir = -ENOBUFS;
32111da177e4SLinus Torvalds 		return NULL;
32121da177e4SLinus Torvalds 	}
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
32151da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
32161da177e4SLinus Torvalds 
32171da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
32181da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
32191da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
32201da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
3221cb969f07SVenkat Yekkirala 	xp->family = sk->sk_family;
32221da177e4SLinus Torvalds 
32231da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
32241da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
32251da177e4SLinus Torvalds 	    (*dir = parse_ipsecrequests(xp, pol)) < 0)
32261da177e4SLinus Torvalds 		goto out;
32271da177e4SLinus Torvalds 
3228df71837dSTrent Jaeger 	/* security context too */
3229df71837dSTrent Jaeger 	if (len >= (pol->sadb_x_policy_len*8 +
3230df71837dSTrent Jaeger 	    sizeof(struct sadb_x_sec_ctx))) {
3231df71837dSTrent Jaeger 		char *p = (char *)pol;
3232df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx;
3233df71837dSTrent Jaeger 
3234df71837dSTrent Jaeger 		p += pol->sadb_x_policy_len*8;
3235df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *)p;
3236df71837dSTrent Jaeger 		if (len < pol->sadb_x_policy_len*8 +
3237cb969f07SVenkat Yekkirala 		    sec_ctx->sadb_x_sec_len) {
3238cb969f07SVenkat Yekkirala 			*dir = -EINVAL;
3239df71837dSTrent Jaeger 			goto out;
3240cb969f07SVenkat Yekkirala 		}
3241df71837dSTrent Jaeger 		if ((*dir = verify_sec_ctx_len(p)))
3242df71837dSTrent Jaeger 			goto out;
3243*87536a81SNikolay Aleksandrov 		uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_ATOMIC);
324403e1ad7bSPaul Moore 		*dir = security_xfrm_policy_alloc(&xp->security, uctx);
3245df71837dSTrent Jaeger 		kfree(uctx);
3246df71837dSTrent Jaeger 
3247df71837dSTrent Jaeger 		if (*dir)
3248df71837dSTrent Jaeger 			goto out;
3249df71837dSTrent Jaeger 	}
3250df71837dSTrent Jaeger 
32511da177e4SLinus Torvalds 	*dir = pol->sadb_x_policy_dir-1;
32521da177e4SLinus Torvalds 	return xp;
32531da177e4SLinus Torvalds 
32541da177e4SLinus Torvalds out:
325570e90679SAlexey Dobriyan 	xp->walk.dead = 1;
325664c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
32571da177e4SLinus Torvalds 	return NULL;
32581da177e4SLinus Torvalds }
32591da177e4SLinus Torvalds 
32605d36b180SAl Viro static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
32611da177e4SLinus Torvalds {
32621da177e4SLinus Torvalds 	struct sk_buff *skb;
32631da177e4SLinus Torvalds 	struct sadb_msg *hdr;
32641da177e4SLinus Torvalds 	struct sadb_sa *sa;
32651da177e4SLinus Torvalds 	struct sadb_address *addr;
32661da177e4SLinus Torvalds 	struct sadb_x_nat_t_port *n_port;
32671da177e4SLinus Torvalds 	int sockaddr_size;
32681da177e4SLinus Torvalds 	int size;
32691da177e4SLinus Torvalds 	__u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
32701da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
32711da177e4SLinus Torvalds 
32721da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
32731da177e4SLinus Torvalds 	if (!sockaddr_size)
32741da177e4SLinus Torvalds 		return -EINVAL;
32751da177e4SLinus Torvalds 
32761da177e4SLinus Torvalds 	if (!satype)
32771da177e4SLinus Torvalds 		return -EINVAL;
32781da177e4SLinus Torvalds 
32791da177e4SLinus Torvalds 	if (!x->encap)
32801da177e4SLinus Torvalds 		return -EINVAL;
32811da177e4SLinus Torvalds 
32821da177e4SLinus Torvalds 	natt = x->encap;
32831da177e4SLinus Torvalds 
32841da177e4SLinus Torvalds 	/* Build an SADB_X_NAT_T_NEW_MAPPING message:
32851da177e4SLinus Torvalds 	 *
32861da177e4SLinus Torvalds 	 * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
32871da177e4SLinus Torvalds 	 * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
32881da177e4SLinus Torvalds 	 */
32891da177e4SLinus Torvalds 
32901da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
32911da177e4SLinus Torvalds 		sizeof(struct sadb_sa) +
32921da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
32931da177e4SLinus Torvalds 		(sockaddr_size * 2) +
32941da177e4SLinus Torvalds 		(sizeof(struct sadb_x_nat_t_port) * 2);
32951da177e4SLinus Torvalds 
32961da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
32971da177e4SLinus Torvalds 	if (skb == NULL)
32981da177e4SLinus Torvalds 		return -ENOMEM;
32991da177e4SLinus Torvalds 
33001da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
33011da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
33021da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
33031da177e4SLinus Torvalds 	hdr->sadb_msg_satype = satype;
33041da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
33051da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
33061da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
33071da177e4SLinus Torvalds 	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
33081da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
33091da177e4SLinus Torvalds 
33101da177e4SLinus Torvalds 	/* SA */
33111da177e4SLinus Torvalds 	sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
33121da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
33131da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
33141da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
33151da177e4SLinus Torvalds 	sa->sadb_sa_replay = 0;
33161da177e4SLinus Torvalds 	sa->sadb_sa_state = 0;
33171da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
33181da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
33191da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
33201da177e4SLinus Torvalds 
33211da177e4SLinus Torvalds 	/* ADDRESS_SRC (old addr) */
33221da177e4SLinus Torvalds 	addr = (struct sadb_address*)
33231da177e4SLinus Torvalds 		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
33241da177e4SLinus Torvalds 	addr->sadb_address_len =
33251da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
33261da177e4SLinus Torvalds 			sizeof(uint64_t);
33271da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
33281da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
33291da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3330e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3331e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3332e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3333e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3334e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
33351da177e4SLinus Torvalds 		BUG();
33361da177e4SLinus Torvalds 
33371da177e4SLinus Torvalds 	/* NAT_T_SPORT (old port) */
33381da177e4SLinus Torvalds 	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
33391da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
33401da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
33411da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = natt->encap_sport;
33421da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
33431da177e4SLinus Torvalds 
33441da177e4SLinus Torvalds 	/* ADDRESS_DST (new addr) */
33451da177e4SLinus Torvalds 	addr = (struct sadb_address*)
33461da177e4SLinus Torvalds 		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
33471da177e4SLinus Torvalds 	addr->sadb_address_len =
33481da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
33491da177e4SLinus Torvalds 			sizeof(uint64_t);
33501da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
33511da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
33521da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3353e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3354e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(ipaddr, 0,
3355e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3356e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3357e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
33581da177e4SLinus Torvalds 		BUG();
33591da177e4SLinus Torvalds 
33601da177e4SLinus Torvalds 	/* NAT_T_DPORT (new port) */
33611da177e4SLinus Torvalds 	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
33621da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
33631da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
33641da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = sport;
33651da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
33661da177e4SLinus Torvalds 
336707fb0f17SAlexey Dobriyan 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
33681da177e4SLinus Torvalds }
33691da177e4SLinus Torvalds 
337008de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
337108de61beSShinta Sugimoto static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
3372183cad12SDavid S. Miller 			    const struct xfrm_selector *sel)
337308de61beSShinta Sugimoto {
337408de61beSShinta Sugimoto 	struct sadb_address *addr;
337508de61beSShinta Sugimoto 	addr = (struct sadb_address *)skb_put(skb, sizeof(struct sadb_address) + sasize);
337608de61beSShinta Sugimoto 	addr->sadb_address_len = (sizeof(struct sadb_address) + sasize)/8;
337708de61beSShinta Sugimoto 	addr->sadb_address_exttype = type;
337808de61beSShinta Sugimoto 	addr->sadb_address_proto = sel->proto;
337908de61beSShinta Sugimoto 	addr->sadb_address_reserved = 0;
338008de61beSShinta Sugimoto 
338108de61beSShinta Sugimoto 	switch (type) {
338208de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_SRC:
338308de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_s;
3384e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->saddr, 0,
3385e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3386e5b56652SYOSHIFUJI Hideaki 				    sel->family);
338708de61beSShinta Sugimoto 		break;
338808de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_DST:
338908de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_d;
3390e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->daddr, 0,
3391e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3392e5b56652SYOSHIFUJI Hideaki 				    sel->family);
339308de61beSShinta Sugimoto 		break;
339408de61beSShinta Sugimoto 	default:
339508de61beSShinta Sugimoto 		return -EINVAL;
339608de61beSShinta Sugimoto 	}
339708de61beSShinta Sugimoto 
339808de61beSShinta Sugimoto 	return 0;
339908de61beSShinta Sugimoto }
340008de61beSShinta Sugimoto 
340113c1d189SArnaud Ebalard 
3402183cad12SDavid S. Miller static int set_sadb_kmaddress(struct sk_buff *skb, const struct xfrm_kmaddress *k)
340313c1d189SArnaud Ebalard {
340413c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
340513c1d189SArnaud Ebalard 	u8 *sa;
340613c1d189SArnaud Ebalard 	int family = k->family;
340713c1d189SArnaud Ebalard 	int socklen = pfkey_sockaddr_len(family);
340813c1d189SArnaud Ebalard 	int size_req;
340913c1d189SArnaud Ebalard 
341013c1d189SArnaud Ebalard 	size_req = (sizeof(struct sadb_x_kmaddress) +
341113c1d189SArnaud Ebalard 		    pfkey_sockaddr_pair_size(family));
341213c1d189SArnaud Ebalard 
341313c1d189SArnaud Ebalard 	kma = (struct sadb_x_kmaddress *)skb_put(skb, size_req);
341413c1d189SArnaud Ebalard 	memset(kma, 0, size_req);
341513c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_len = size_req / 8;
341613c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_exttype = SADB_X_EXT_KMADDRESS;
341713c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_reserved = k->reserved;
341813c1d189SArnaud Ebalard 
341913c1d189SArnaud Ebalard 	sa = (u8 *)(kma + 1);
342013c1d189SArnaud Ebalard 	if (!pfkey_sockaddr_fill(&k->local, 0, (struct sockaddr *)sa, family) ||
342113c1d189SArnaud Ebalard 	    !pfkey_sockaddr_fill(&k->remote, 0, (struct sockaddr *)(sa+socklen), family))
342213c1d189SArnaud Ebalard 		return -EINVAL;
342313c1d189SArnaud Ebalard 
342413c1d189SArnaud Ebalard 	return 0;
342513c1d189SArnaud Ebalard }
342613c1d189SArnaud Ebalard 
342708de61beSShinta Sugimoto static int set_ipsecrequest(struct sk_buff *skb,
342808de61beSShinta Sugimoto 			    uint8_t proto, uint8_t mode, int level,
342908de61beSShinta Sugimoto 			    uint32_t reqid, uint8_t family,
3430183cad12SDavid S. Miller 			    const xfrm_address_t *src, const xfrm_address_t *dst)
343108de61beSShinta Sugimoto {
343208de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
3433e5b56652SYOSHIFUJI Hideaki 	u8 *sa;
3434e5b56652SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(family);
343508de61beSShinta Sugimoto 	int size_req;
343608de61beSShinta Sugimoto 
343708de61beSShinta Sugimoto 	size_req = sizeof(struct sadb_x_ipsecrequest) +
343808de61beSShinta Sugimoto 		   pfkey_sockaddr_pair_size(family);
343908de61beSShinta Sugimoto 
344008de61beSShinta Sugimoto 	rq = (struct sadb_x_ipsecrequest *)skb_put(skb, size_req);
344108de61beSShinta Sugimoto 	memset(rq, 0, size_req);
344208de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_len = size_req;
344308de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_proto = proto;
344408de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_mode = mode;
344508de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_level = level;
344608de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_reqid = reqid;
344708de61beSShinta Sugimoto 
3448e5b56652SYOSHIFUJI Hideaki 	sa = (u8 *) (rq + 1);
3449e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(src, 0, (struct sockaddr *)sa, family) ||
3450e5b56652SYOSHIFUJI Hideaki 	    !pfkey_sockaddr_fill(dst, 0, (struct sockaddr *)(sa + socklen), family))
345108de61beSShinta Sugimoto 		return -EINVAL;
345208de61beSShinta Sugimoto 
345308de61beSShinta Sugimoto 	return 0;
345408de61beSShinta Sugimoto }
345508de61beSShinta Sugimoto #endif
345608de61beSShinta Sugimoto 
345708de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
3458183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3459183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
3460183cad12SDavid S. Miller 			      const struct xfrm_kmaddress *k)
346108de61beSShinta Sugimoto {
346208de61beSShinta Sugimoto 	int i;
346308de61beSShinta Sugimoto 	int sasize_sel;
346408de61beSShinta Sugimoto 	int size = 0;
346508de61beSShinta Sugimoto 	int size_pol = 0;
346608de61beSShinta Sugimoto 	struct sk_buff *skb;
346708de61beSShinta Sugimoto 	struct sadb_msg *hdr;
346808de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
3469183cad12SDavid S. Miller 	const struct xfrm_migrate *mp;
347008de61beSShinta Sugimoto 
347108de61beSShinta Sugimoto 	if (type != XFRM_POLICY_TYPE_MAIN)
347208de61beSShinta Sugimoto 		return 0;
347308de61beSShinta Sugimoto 
347408de61beSShinta Sugimoto 	if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH)
347508de61beSShinta Sugimoto 		return -EINVAL;
347608de61beSShinta Sugimoto 
347713c1d189SArnaud Ebalard 	if (k != NULL) {
347813c1d189SArnaud Ebalard 		/* addresses for KM */
347913c1d189SArnaud Ebalard 		size += PFKEY_ALIGN8(sizeof(struct sadb_x_kmaddress) +
348013c1d189SArnaud Ebalard 				     pfkey_sockaddr_pair_size(k->family));
348113c1d189SArnaud Ebalard 	}
348213c1d189SArnaud Ebalard 
348308de61beSShinta Sugimoto 	/* selector */
348408de61beSShinta Sugimoto 	sasize_sel = pfkey_sockaddr_size(sel->family);
348508de61beSShinta Sugimoto 	if (!sasize_sel)
348608de61beSShinta Sugimoto 		return -EINVAL;
348708de61beSShinta Sugimoto 	size += (sizeof(struct sadb_address) + sasize_sel) * 2;
348808de61beSShinta Sugimoto 
348908de61beSShinta Sugimoto 	/* policy info */
349008de61beSShinta Sugimoto 	size_pol += sizeof(struct sadb_x_policy);
349108de61beSShinta Sugimoto 
349208de61beSShinta Sugimoto 	/* ipsecrequests */
349308de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
349408de61beSShinta Sugimoto 		/* old locator pair */
349508de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
349608de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->old_family);
349708de61beSShinta Sugimoto 		/* new locator pair */
349808de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
349908de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->new_family);
350008de61beSShinta Sugimoto 	}
350108de61beSShinta Sugimoto 
350208de61beSShinta Sugimoto 	size += sizeof(struct sadb_msg) + size_pol;
350308de61beSShinta Sugimoto 
350408de61beSShinta Sugimoto 	/* alloc buffer */
350508de61beSShinta Sugimoto 	skb = alloc_skb(size, GFP_ATOMIC);
350608de61beSShinta Sugimoto 	if (skb == NULL)
350708de61beSShinta Sugimoto 		return -ENOMEM;
350808de61beSShinta Sugimoto 
350908de61beSShinta Sugimoto 	hdr = (struct sadb_msg *)skb_put(skb, sizeof(struct sadb_msg));
351008de61beSShinta Sugimoto 	hdr->sadb_msg_version = PF_KEY_V2;
351108de61beSShinta Sugimoto 	hdr->sadb_msg_type = SADB_X_MIGRATE;
351208de61beSShinta Sugimoto 	hdr->sadb_msg_satype = pfkey_proto2satype(m->proto);
351308de61beSShinta Sugimoto 	hdr->sadb_msg_len = size / 8;
351408de61beSShinta Sugimoto 	hdr->sadb_msg_errno = 0;
351508de61beSShinta Sugimoto 	hdr->sadb_msg_reserved = 0;
351608de61beSShinta Sugimoto 	hdr->sadb_msg_seq = 0;
351708de61beSShinta Sugimoto 	hdr->sadb_msg_pid = 0;
351808de61beSShinta Sugimoto 
351913c1d189SArnaud Ebalard 	/* Addresses to be used by KM for negotiation, if ext is available */
352013c1d189SArnaud Ebalard 	if (k != NULL && (set_sadb_kmaddress(skb, k) < 0))
352189eb06f1SJulia Lawall 		goto err;
352213c1d189SArnaud Ebalard 
352308de61beSShinta Sugimoto 	/* selector src */
352408de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel);
352508de61beSShinta Sugimoto 
352608de61beSShinta Sugimoto 	/* selector dst */
352708de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_DST, sel);
352808de61beSShinta Sugimoto 
352908de61beSShinta Sugimoto 	/* policy information */
353008de61beSShinta Sugimoto 	pol = (struct sadb_x_policy *)skb_put(skb, sizeof(struct sadb_x_policy));
353108de61beSShinta Sugimoto 	pol->sadb_x_policy_len = size_pol / 8;
353208de61beSShinta Sugimoto 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
353308de61beSShinta Sugimoto 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
353408de61beSShinta Sugimoto 	pol->sadb_x_policy_dir = dir + 1;
3535ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
353608de61beSShinta Sugimoto 	pol->sadb_x_policy_id = 0;
353708de61beSShinta Sugimoto 	pol->sadb_x_policy_priority = 0;
353808de61beSShinta Sugimoto 
353908de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
354008de61beSShinta Sugimoto 		/* old ipsecrequest */
354155569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_from_xfrm(mp->mode);
354255569ce2SKazunori MIYAZAWA 		if (mode < 0)
3543d4782c32SPatrick McHardy 			goto err;
354455569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
354508de61beSShinta Sugimoto 				     (mp->reqid ?  IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
354608de61beSShinta Sugimoto 				     mp->reqid, mp->old_family,
3547d4782c32SPatrick McHardy 				     &mp->old_saddr, &mp->old_daddr) < 0)
3548d4782c32SPatrick McHardy 			goto err;
354908de61beSShinta Sugimoto 
355008de61beSShinta Sugimoto 		/* new ipsecrequest */
355155569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
355208de61beSShinta Sugimoto 				     (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
355308de61beSShinta Sugimoto 				     mp->reqid, mp->new_family,
3554d4782c32SPatrick McHardy 				     &mp->new_saddr, &mp->new_daddr) < 0)
3555d4782c32SPatrick McHardy 			goto err;
355608de61beSShinta Sugimoto 	}
355708de61beSShinta Sugimoto 
355808de61beSShinta Sugimoto 	/* broadcast migrate message to sockets */
355907fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
356008de61beSShinta Sugimoto 
356108de61beSShinta Sugimoto 	return 0;
3562d4782c32SPatrick McHardy 
3563d4782c32SPatrick McHardy err:
3564d4782c32SPatrick McHardy 	kfree_skb(skb);
3565d4782c32SPatrick McHardy 	return -EINVAL;
356608de61beSShinta Sugimoto }
356708de61beSShinta Sugimoto #else
3568183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3569183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
3570183cad12SDavid S. Miller 			      const struct xfrm_kmaddress *k)
357108de61beSShinta Sugimoto {
357208de61beSShinta Sugimoto 	return -ENOPROTOOPT;
357308de61beSShinta Sugimoto }
357408de61beSShinta Sugimoto #endif
357508de61beSShinta Sugimoto 
35761da177e4SLinus Torvalds static int pfkey_sendmsg(struct kiocb *kiocb,
35771da177e4SLinus Torvalds 			 struct socket *sock, struct msghdr *msg, size_t len)
35781da177e4SLinus Torvalds {
35791da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
35801da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
35811da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
35821da177e4SLinus Torvalds 	int err;
3583283bc9f3SFan Du 	struct net *net = sock_net(sk);
35841da177e4SLinus Torvalds 
35851da177e4SLinus Torvalds 	err = -EOPNOTSUPP;
35861da177e4SLinus Torvalds 	if (msg->msg_flags & MSG_OOB)
35871da177e4SLinus Torvalds 		goto out;
35881da177e4SLinus Torvalds 
35891da177e4SLinus Torvalds 	err = -EMSGSIZE;
359095c96174SEric Dumazet 	if ((unsigned int)len > sk->sk_sndbuf - 32)
35911da177e4SLinus Torvalds 		goto out;
35921da177e4SLinus Torvalds 
35931da177e4SLinus Torvalds 	err = -ENOBUFS;
35941da177e4SLinus Torvalds 	skb = alloc_skb(len, GFP_KERNEL);
35951da177e4SLinus Torvalds 	if (skb == NULL)
35961da177e4SLinus Torvalds 		goto out;
35971da177e4SLinus Torvalds 
35981da177e4SLinus Torvalds 	err = -EFAULT;
35991da177e4SLinus Torvalds 	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len))
36001da177e4SLinus Torvalds 		goto out;
36011da177e4SLinus Torvalds 
36021da177e4SLinus Torvalds 	hdr = pfkey_get_base_msg(skb, &err);
36031da177e4SLinus Torvalds 	if (!hdr)
36041da177e4SLinus Torvalds 		goto out;
36051da177e4SLinus Torvalds 
3606283bc9f3SFan Du 	mutex_lock(&net->xfrm.xfrm_cfg_mutex);
36071da177e4SLinus Torvalds 	err = pfkey_process(sk, skb, hdr);
3608283bc9f3SFan Du 	mutex_unlock(&net->xfrm.xfrm_cfg_mutex);
36091da177e4SLinus Torvalds 
36101da177e4SLinus Torvalds out:
36111da177e4SLinus Torvalds 	if (err && hdr && pfkey_error(hdr, err, sk) == 0)
36121da177e4SLinus Torvalds 		err = 0;
36131da177e4SLinus Torvalds 	kfree_skb(skb);
36141da177e4SLinus Torvalds 
36151da177e4SLinus Torvalds 	return err ? : len;
36161da177e4SLinus Torvalds }
36171da177e4SLinus Torvalds 
36181da177e4SLinus Torvalds static int pfkey_recvmsg(struct kiocb *kiocb,
36191da177e4SLinus Torvalds 			 struct socket *sock, struct msghdr *msg, size_t len,
36201da177e4SLinus Torvalds 			 int flags)
36211da177e4SLinus Torvalds {
36221da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
362383321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
36241da177e4SLinus Torvalds 	struct sk_buff *skb;
36251da177e4SLinus Torvalds 	int copied, err;
36261da177e4SLinus Torvalds 
36271da177e4SLinus Torvalds 	err = -EINVAL;
36281da177e4SLinus Torvalds 	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
36291da177e4SLinus Torvalds 		goto out;
36301da177e4SLinus Torvalds 
36311da177e4SLinus Torvalds 	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
36321da177e4SLinus Torvalds 	if (skb == NULL)
36331da177e4SLinus Torvalds 		goto out;
36341da177e4SLinus Torvalds 
36351da177e4SLinus Torvalds 	copied = skb->len;
36361da177e4SLinus Torvalds 	if (copied > len) {
36371da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
36381da177e4SLinus Torvalds 		copied = len;
36391da177e4SLinus Torvalds 	}
36401da177e4SLinus Torvalds 
3641badff6d0SArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
36421da177e4SLinus Torvalds 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
36431da177e4SLinus Torvalds 	if (err)
36441da177e4SLinus Torvalds 		goto out_free;
36451da177e4SLinus Torvalds 
36463b885787SNeil Horman 	sock_recv_ts_and_drops(msg, sk, skb);
36471da177e4SLinus Torvalds 
36481da177e4SLinus Torvalds 	err = (flags & MSG_TRUNC) ? skb->len : copied;
36491da177e4SLinus Torvalds 
365083321d6bSTimo Teras 	if (pfk->dump.dump != NULL &&
365183321d6bSTimo Teras 	    3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
365283321d6bSTimo Teras 		pfkey_do_dump(pfk);
365383321d6bSTimo Teras 
36541da177e4SLinus Torvalds out_free:
36551da177e4SLinus Torvalds 	skb_free_datagram(sk, skb);
36561da177e4SLinus Torvalds out:
36571da177e4SLinus Torvalds 	return err;
36581da177e4SLinus Torvalds }
36591da177e4SLinus Torvalds 
366090ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops = {
36611da177e4SLinus Torvalds 	.family		=	PF_KEY,
36621da177e4SLinus Torvalds 	.owner		=	THIS_MODULE,
36631da177e4SLinus Torvalds 	/* Operations that make no sense on pfkey sockets. */
36641da177e4SLinus Torvalds 	.bind		=	sock_no_bind,
36651da177e4SLinus Torvalds 	.connect	=	sock_no_connect,
36661da177e4SLinus Torvalds 	.socketpair	=	sock_no_socketpair,
36671da177e4SLinus Torvalds 	.accept		=	sock_no_accept,
36681da177e4SLinus Torvalds 	.getname	=	sock_no_getname,
36691da177e4SLinus Torvalds 	.ioctl		=	sock_no_ioctl,
36701da177e4SLinus Torvalds 	.listen		=	sock_no_listen,
36711da177e4SLinus Torvalds 	.shutdown	=	sock_no_shutdown,
36721da177e4SLinus Torvalds 	.setsockopt	=	sock_no_setsockopt,
36731da177e4SLinus Torvalds 	.getsockopt	=	sock_no_getsockopt,
36741da177e4SLinus Torvalds 	.mmap		=	sock_no_mmap,
36751da177e4SLinus Torvalds 	.sendpage	=	sock_no_sendpage,
36761da177e4SLinus Torvalds 
36771da177e4SLinus Torvalds 	/* Now the operations that really occur. */
36781da177e4SLinus Torvalds 	.release	=	pfkey_release,
36791da177e4SLinus Torvalds 	.poll		=	datagram_poll,
36801da177e4SLinus Torvalds 	.sendmsg	=	pfkey_sendmsg,
36811da177e4SLinus Torvalds 	.recvmsg	=	pfkey_recvmsg,
36821da177e4SLinus Torvalds };
36831da177e4SLinus Torvalds 
3684ec1b4cf7SStephen Hemminger static const struct net_proto_family pfkey_family_ops = {
36851da177e4SLinus Torvalds 	.family	=	PF_KEY,
36861da177e4SLinus Torvalds 	.create	=	pfkey_create,
36871da177e4SLinus Torvalds 	.owner	=	THIS_MODULE,
36881da177e4SLinus Torvalds };
36891da177e4SLinus Torvalds 
36901da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
3691bd2f7476SPavel Emelyanov static int pfkey_seq_show(struct seq_file *f, void *v)
36921da177e4SLinus Torvalds {
369327b5b865SLi Zefan 	struct sock *s = sk_entry(v);
36941da177e4SLinus Torvalds 
3695bd2f7476SPavel Emelyanov 	if (v == SEQ_START_TOKEN)
3696bd2f7476SPavel Emelyanov 		seq_printf(f ,"sk       RefCnt Rmem   Wmem   User   Inode\n");
3697bd2f7476SPavel Emelyanov 	else
369871338aa7SDan Rosenberg 		seq_printf(f, "%pK %-6d %-6u %-6u %-6u %-6lu\n",
36991da177e4SLinus Torvalds 			       s,
37001da177e4SLinus Torvalds 			       atomic_read(&s->sk_refcnt),
370131e6d363SEric Dumazet 			       sk_rmem_alloc_get(s),
370231e6d363SEric Dumazet 			       sk_wmem_alloc_get(s),
3703a7cb5a49SEric W. Biederman 			       from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
37041da177e4SLinus Torvalds 			       sock_i_ino(s)
37051da177e4SLinus Torvalds 			       );
3706bd2f7476SPavel Emelyanov 	return 0;
37071da177e4SLinus Torvalds }
37081da177e4SLinus Torvalds 
3709bd2f7476SPavel Emelyanov static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
3710ada440e3Sstephen hemminger 	__acquires(rcu)
3711bd2f7476SPavel Emelyanov {
37127013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
37133fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
3714bd2f7476SPavel Emelyanov 
37157f6b9dbdSstephen hemminger 	rcu_read_lock();
37167f6b9dbdSstephen hemminger 	return seq_hlist_start_head_rcu(&net_pfkey->table, *ppos);
3717bd2f7476SPavel Emelyanov }
3718bd2f7476SPavel Emelyanov 
3719bd2f7476SPavel Emelyanov static void *pfkey_seq_next(struct seq_file *f, void *v, loff_t *ppos)
3720bd2f7476SPavel Emelyanov {
37217013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
37223fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
37233fa87a32SAlexey Dobriyan 
37247f6b9dbdSstephen hemminger 	return seq_hlist_next_rcu(v, &net_pfkey->table, ppos);
3725bd2f7476SPavel Emelyanov }
3726bd2f7476SPavel Emelyanov 
3727bd2f7476SPavel Emelyanov static void pfkey_seq_stop(struct seq_file *f, void *v)
3728ada440e3Sstephen hemminger 	__releases(rcu)
3729bd2f7476SPavel Emelyanov {
37307f6b9dbdSstephen hemminger 	rcu_read_unlock();
37311da177e4SLinus Torvalds }
373261145aa1SPavel Emelyanov 
373398147d52SStephen Hemminger static const struct seq_operations pfkey_seq_ops = {
3734bd2f7476SPavel Emelyanov 	.start	= pfkey_seq_start,
3735bd2f7476SPavel Emelyanov 	.next	= pfkey_seq_next,
3736bd2f7476SPavel Emelyanov 	.stop	= pfkey_seq_stop,
3737bd2f7476SPavel Emelyanov 	.show	= pfkey_seq_show,
3738bd2f7476SPavel Emelyanov };
3739bd2f7476SPavel Emelyanov 
3740bd2f7476SPavel Emelyanov static int pfkey_seq_open(struct inode *inode, struct file *file)
3741bd2f7476SPavel Emelyanov {
37427013ec30SAlexey Dobriyan 	return seq_open_net(inode, file, &pfkey_seq_ops,
37437013ec30SAlexey Dobriyan 			    sizeof(struct seq_net_private));
3744bd2f7476SPavel Emelyanov }
3745bd2f7476SPavel Emelyanov 
37465ca1b998SStephen Hemminger static const struct file_operations pfkey_proc_ops = {
3747bd2f7476SPavel Emelyanov 	.open	 = pfkey_seq_open,
3748bd2f7476SPavel Emelyanov 	.read	 = seq_read,
3749bd2f7476SPavel Emelyanov 	.llseek	 = seq_lseek,
37507013ec30SAlexey Dobriyan 	.release = seq_release_net,
3751bd2f7476SPavel Emelyanov };
3752bd2f7476SPavel Emelyanov 
37537013ec30SAlexey Dobriyan static int __net_init pfkey_init_proc(struct net *net)
375461145aa1SPavel Emelyanov {
3755bd2f7476SPavel Emelyanov 	struct proc_dir_entry *e;
3756bd2f7476SPavel Emelyanov 
3757d4beaa66SGao feng 	e = proc_create("pfkey", 0, net->proc_net, &pfkey_proc_ops);
3758bd2f7476SPavel Emelyanov 	if (e == NULL)
375961145aa1SPavel Emelyanov 		return -ENOMEM;
3760bd2f7476SPavel Emelyanov 
376161145aa1SPavel Emelyanov 	return 0;
376261145aa1SPavel Emelyanov }
376361145aa1SPavel Emelyanov 
37642c8c1e72SAlexey Dobriyan static void __net_exit pfkey_exit_proc(struct net *net)
376561145aa1SPavel Emelyanov {
3766ece31ffdSGao feng 	remove_proc_entry("pfkey", net->proc_net);
376761145aa1SPavel Emelyanov }
376861145aa1SPavel Emelyanov #else
37692c8c1e72SAlexey Dobriyan static inline int pfkey_init_proc(struct net *net)
377061145aa1SPavel Emelyanov {
377161145aa1SPavel Emelyanov 	return 0;
377261145aa1SPavel Emelyanov }
377361145aa1SPavel Emelyanov 
37742c8c1e72SAlexey Dobriyan static inline void pfkey_exit_proc(struct net *net)
377561145aa1SPavel Emelyanov {
377661145aa1SPavel Emelyanov }
37771da177e4SLinus Torvalds #endif
37781da177e4SLinus Torvalds 
37791da177e4SLinus Torvalds static struct xfrm_mgr pfkeyv2_mgr =
37801da177e4SLinus Torvalds {
37811da177e4SLinus Torvalds 	.id		= "pfkeyv2",
37821da177e4SLinus Torvalds 	.notify		= pfkey_send_notify,
37831da177e4SLinus Torvalds 	.acquire	= pfkey_send_acquire,
37841da177e4SLinus Torvalds 	.compile_policy	= pfkey_compile_policy,
37851da177e4SLinus Torvalds 	.new_mapping	= pfkey_send_new_mapping,
378626b15dadSJamal Hadi Salim 	.notify_policy	= pfkey_send_policy_notify,
378708de61beSShinta Sugimoto 	.migrate	= pfkey_send_migrate,
37881da177e4SLinus Torvalds };
37891da177e4SLinus Torvalds 
37903fa87a32SAlexey Dobriyan static int __net_init pfkey_net_init(struct net *net)
37913fa87a32SAlexey Dobriyan {
379223c049caSEric W. Biederman 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
37933fa87a32SAlexey Dobriyan 	int rv;
37943fa87a32SAlexey Dobriyan 
37953fa87a32SAlexey Dobriyan 	INIT_HLIST_HEAD(&net_pfkey->table);
37963fa87a32SAlexey Dobriyan 	atomic_set(&net_pfkey->socks_nr, 0);
37973fa87a32SAlexey Dobriyan 
379823c049caSEric W. Biederman 	rv = pfkey_init_proc(net);
379923c049caSEric W. Biederman 
38003fa87a32SAlexey Dobriyan 	return rv;
38013fa87a32SAlexey Dobriyan }
38023fa87a32SAlexey Dobriyan 
38033fa87a32SAlexey Dobriyan static void __net_exit pfkey_net_exit(struct net *net)
38043fa87a32SAlexey Dobriyan {
38053fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
38063fa87a32SAlexey Dobriyan 
38077013ec30SAlexey Dobriyan 	pfkey_exit_proc(net);
38083fa87a32SAlexey Dobriyan 	BUG_ON(!hlist_empty(&net_pfkey->table));
38093fa87a32SAlexey Dobriyan }
38103fa87a32SAlexey Dobriyan 
38113fa87a32SAlexey Dobriyan static struct pernet_operations pfkey_net_ops = {
38123fa87a32SAlexey Dobriyan 	.init = pfkey_net_init,
38133fa87a32SAlexey Dobriyan 	.exit = pfkey_net_exit,
381423c049caSEric W. Biederman 	.id   = &pfkey_net_id,
381523c049caSEric W. Biederman 	.size = sizeof(struct netns_pfkey),
38163fa87a32SAlexey Dobriyan };
38173fa87a32SAlexey Dobriyan 
38181da177e4SLinus Torvalds static void __exit ipsec_pfkey_exit(void)
38191da177e4SLinus Torvalds {
38201da177e4SLinus Torvalds 	xfrm_unregister_km(&pfkeyv2_mgr);
38211da177e4SLinus Torvalds 	sock_unregister(PF_KEY);
3822180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
38231da177e4SLinus Torvalds 	proto_unregister(&key_proto);
38241da177e4SLinus Torvalds }
38251da177e4SLinus Torvalds 
38261da177e4SLinus Torvalds static int __init ipsec_pfkey_init(void)
38271da177e4SLinus Torvalds {
38281da177e4SLinus Torvalds 	int err = proto_register(&key_proto, 0);
38291da177e4SLinus Torvalds 
38301da177e4SLinus Torvalds 	if (err != 0)
38311da177e4SLinus Torvalds 		goto out;
38321da177e4SLinus Torvalds 
3833180211b8SAlexey Dobriyan 	err = register_pernet_subsys(&pfkey_net_ops);
38341da177e4SLinus Torvalds 	if (err != 0)
38351da177e4SLinus Torvalds 		goto out_unregister_key_proto;
3836180211b8SAlexey Dobriyan 	err = sock_register(&pfkey_family_ops);
3837180211b8SAlexey Dobriyan 	if (err != 0)
3838180211b8SAlexey Dobriyan 		goto out_unregister_pernet;
38391da177e4SLinus Torvalds 	err = xfrm_register_km(&pfkeyv2_mgr);
38401da177e4SLinus Torvalds 	if (err != 0)
38417013ec30SAlexey Dobriyan 		goto out_sock_unregister;
38421da177e4SLinus Torvalds out:
38431da177e4SLinus Torvalds 	return err;
3844180211b8SAlexey Dobriyan 
38451da177e4SLinus Torvalds out_sock_unregister:
38461da177e4SLinus Torvalds 	sock_unregister(PF_KEY);
3847180211b8SAlexey Dobriyan out_unregister_pernet:
3848180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
38491da177e4SLinus Torvalds out_unregister_key_proto:
38501da177e4SLinus Torvalds 	proto_unregister(&key_proto);
38511da177e4SLinus Torvalds 	goto out;
38521da177e4SLinus Torvalds }
38531da177e4SLinus Torvalds 
38541da177e4SLinus Torvalds module_init(ipsec_pfkey_init);
38551da177e4SLinus Torvalds module_exit(ipsec_pfkey_exit);
38561da177e4SLinus Torvalds MODULE_LICENSE("GPL");
38571da177e4SLinus Torvalds MODULE_ALIAS_NETPROTO(PF_KEY);
3858