xref: /openbmc/linux/net/key/af_key.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * net/key/af_key.c	An implementation of PF_KEYv2 sockets.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Maxim Giryaev	<gem@asplinux.ru>
61da177e4SLinus Torvalds  *		David S. Miller	<davem@redhat.com>
71da177e4SLinus Torvalds  *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
81da177e4SLinus Torvalds  *		Kunihiro Ishiguro <kunihiro@ipinfusion.com>
91da177e4SLinus Torvalds  *		Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
101da177e4SLinus Torvalds  *		Derek Atkins <derek@ihtfp.com>
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
134fc268d2SRandy Dunlap #include <linux/capability.h>
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/socket.h>
171da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
181da177e4SLinus Torvalds #include <linux/ipsec.h>
191da177e4SLinus Torvalds #include <linux/skbuff.h>
201da177e4SLinus Torvalds #include <linux/rtnetlink.h>
211da177e4SLinus Torvalds #include <linux/in.h>
221da177e4SLinus Torvalds #include <linux/in6.h>
231da177e4SLinus Torvalds #include <linux/proc_fs.h>
241da177e4SLinus Torvalds #include <linux/init.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
26457c4cbcSEric W. Biederman #include <net/net_namespace.h>
273fa87a32SAlexey Dobriyan #include <net/netns/generic.h>
281da177e4SLinus Torvalds #include <net/xfrm.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <net/sock.h>
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
331da177e4SLinus Torvalds #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
341da177e4SLinus Torvalds 
35c7d03a00SAlexey Dobriyan static unsigned int pfkey_net_id __read_mostly;
363fa87a32SAlexey Dobriyan struct netns_pfkey {
371da177e4SLinus Torvalds 	/* List of all pfkey sockets. */
383fa87a32SAlexey Dobriyan 	struct hlist_head table;
393fa87a32SAlexey Dobriyan 	atomic_t socks_nr;
403fa87a32SAlexey Dobriyan };
417f6b9dbdSstephen hemminger static DEFINE_MUTEX(pfkey_mutex);
421da177e4SLinus Torvalds 
43bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
44e473fcb4SMathias Krause static const struct xfrm_mark dummy_mark = {0, 0};
451da177e4SLinus Torvalds struct pfkey_sock {
461da177e4SLinus Torvalds 	/* struct sock must be the first member of struct pfkey_sock */
471da177e4SLinus Torvalds 	struct sock	sk;
481da177e4SLinus Torvalds 	int		registered;
491da177e4SLinus Torvalds 	int		promisc;
5083321d6bSTimo Teras 
5183321d6bSTimo Teras 	struct {
5283321d6bSTimo Teras 		uint8_t		msg_version;
5315e47304SEric W. Biederman 		uint32_t	msg_portid;
5483321d6bSTimo Teras 		int		(*dump)(struct pfkey_sock *sk);
5583321d6bSTimo Teras 		void		(*done)(struct pfkey_sock *sk);
5683321d6bSTimo Teras 		union {
5783321d6bSTimo Teras 			struct xfrm_policy_walk	policy;
5883321d6bSTimo Teras 			struct xfrm_state_walk	state;
5983321d6bSTimo Teras 		} u;
6012a169e7SHerbert Xu 		struct sk_buff	*skb;
6183321d6bSTimo Teras 	} dump;
6289e357d8SYuejie Shi 	struct mutex dump_lock;
631da177e4SLinus Torvalds };
641da177e4SLinus Torvalds 
65096f41d3SHerbert Xu static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
66096f41d3SHerbert Xu 			       xfrm_address_t *saddr, xfrm_address_t *daddr,
67096f41d3SHerbert Xu 			       u16 *family);
68096f41d3SHerbert Xu 
pfkey_sk(struct sock * sk)691da177e4SLinus Torvalds static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	return (struct pfkey_sock *)sk;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
pfkey_can_dump(const struct sock * sk)744c93fbb0SDavid S. Miller static int pfkey_can_dump(const struct sock *sk)
7583321d6bSTimo Teras {
7683321d6bSTimo Teras 	if (3 * atomic_read(&sk->sk_rmem_alloc) <= 2 * sk->sk_rcvbuf)
7783321d6bSTimo Teras 		return 1;
7883321d6bSTimo Teras 	return 0;
7983321d6bSTimo Teras }
8083321d6bSTimo Teras 
pfkey_terminate_dump(struct pfkey_sock * pfk)8105238204STimo Teras static void pfkey_terminate_dump(struct pfkey_sock *pfk)
8283321d6bSTimo Teras {
8305238204STimo Teras 	if (pfk->dump.dump) {
8412a169e7SHerbert Xu 		if (pfk->dump.skb) {
8512a169e7SHerbert Xu 			kfree_skb(pfk->dump.skb);
8612a169e7SHerbert Xu 			pfk->dump.skb = NULL;
8712a169e7SHerbert Xu 		}
8883321d6bSTimo Teras 		pfk->dump.done(pfk);
8983321d6bSTimo Teras 		pfk->dump.dump = NULL;
9083321d6bSTimo Teras 		pfk->dump.done = NULL;
9105238204STimo Teras 	}
9283321d6bSTimo Teras }
9383321d6bSTimo Teras 
pfkey_sock_destruct(struct sock * sk)941da177e4SLinus Torvalds static void pfkey_sock_destruct(struct sock *sk)
951da177e4SLinus Torvalds {
963fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
973fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
983fa87a32SAlexey Dobriyan 
9905238204STimo Teras 	pfkey_terminate_dump(pfkey_sk(sk));
1001da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_receive_queue);
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	if (!sock_flag(sk, SOCK_DEAD)) {
103207024b9Sstephen hemminger 		pr_err("Attempt to release alive pfkey socket: %p\n", sk);
1041da177e4SLinus Torvalds 		return;
1051da177e4SLinus Torvalds 	}
1061da177e4SLinus Torvalds 
107547b792cSIlpo Järvinen 	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
10814afee4bSReshetova, Elena 	WARN_ON(refcount_read(&sk->sk_wmem_alloc));
1091da177e4SLinus Torvalds 
1103fa87a32SAlexey Dobriyan 	atomic_dec(&net_pfkey->socks_nr);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
11390ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops;
1141da177e4SLinus Torvalds 
pfkey_insert(struct sock * sk)1151da177e4SLinus Torvalds static void pfkey_insert(struct sock *sk)
1161da177e4SLinus Torvalds {
1173fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
1183fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1193fa87a32SAlexey Dobriyan 
1207f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1217f6b9dbdSstephen hemminger 	sk_add_node_rcu(sk, &net_pfkey->table);
1227f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
pfkey_remove(struct sock * sk)1251da177e4SLinus Torvalds static void pfkey_remove(struct sock *sk)
1261da177e4SLinus Torvalds {
1277f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1287f6b9dbdSstephen hemminger 	sk_del_node_init_rcu(sk);
1297f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds static struct proto key_proto = {
1331da177e4SLinus Torvalds 	.name	  = "KEY",
1341da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
1351da177e4SLinus Torvalds 	.obj_size = sizeof(struct pfkey_sock),
1361da177e4SLinus Torvalds };
1371da177e4SLinus Torvalds 
pfkey_create(struct net * net,struct socket * sock,int protocol,int kern)1383f378b68SEric Paris static int pfkey_create(struct net *net, struct socket *sock, int protocol,
1393f378b68SEric Paris 			int kern)
1401da177e4SLinus Torvalds {
1413fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1421da177e4SLinus Torvalds 	struct sock *sk;
14389e357d8SYuejie Shi 	struct pfkey_sock *pfk;
1441da177e4SLinus Torvalds 
145df008c91SEric W. Biederman 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1461da177e4SLinus Torvalds 		return -EPERM;
1471da177e4SLinus Torvalds 	if (sock->type != SOCK_RAW)
1481da177e4SLinus Torvalds 		return -ESOCKTNOSUPPORT;
1491da177e4SLinus Torvalds 	if (protocol != PF_KEY_V2)
1501da177e4SLinus Torvalds 		return -EPROTONOSUPPORT;
1511da177e4SLinus Torvalds 
15211aa9c28SEric W. Biederman 	sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto, kern);
1531da177e4SLinus Torvalds 	if (sk == NULL)
154a925316aSzuoqilin 		return -ENOMEM;
1551da177e4SLinus Torvalds 
15689e357d8SYuejie Shi 	pfk = pfkey_sk(sk);
15789e357d8SYuejie Shi 	mutex_init(&pfk->dump_lock);
15889e357d8SYuejie Shi 
1591da177e4SLinus Torvalds 	sock->ops = &pfkey_ops;
1601da177e4SLinus Torvalds 	sock_init_data(sock, sk);
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	sk->sk_family = PF_KEY;
1631da177e4SLinus Torvalds 	sk->sk_destruct = pfkey_sock_destruct;
1641da177e4SLinus Torvalds 
1653fa87a32SAlexey Dobriyan 	atomic_inc(&net_pfkey->socks_nr);
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	pfkey_insert(sk);
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	return 0;
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds 
pfkey_release(struct socket * sock)1721da177e4SLinus Torvalds static int pfkey_release(struct socket *sock)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	if (!sk)
1771da177e4SLinus Torvalds 		return 0;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	pfkey_remove(sk);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	sock_orphan(sk);
1821da177e4SLinus Torvalds 	sock->sk = NULL;
1831da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_write_queue);
1847f6b9dbdSstephen hemminger 
1857f6b9dbdSstephen hemminger 	synchronize_rcu();
1861da177e4SLinus Torvalds 	sock_put(sk);
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	return 0;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
pfkey_broadcast_one(struct sk_buff * skb,gfp_t allocation,struct sock * sk)191fc2d5cfdSSean Tranchetti static int pfkey_broadcast_one(struct sk_buff *skb, gfp_t allocation,
192fc2d5cfdSSean Tranchetti 			       struct sock *sk)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds 	int err = -ENOBUFS;
1951da177e4SLinus Torvalds 
196fc2d5cfdSSean Tranchetti 	if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf)
197fc2d5cfdSSean Tranchetti 		return err;
198fc2d5cfdSSean Tranchetti 
199fc2d5cfdSSean Tranchetti 	skb = skb_clone(skb, allocation);
200fc2d5cfdSSean Tranchetti 
201fc2d5cfdSSean Tranchetti 	if (skb) {
202fc2d5cfdSSean Tranchetti 		skb_set_owner_r(skb, sk);
203fc2d5cfdSSean Tranchetti 		skb_queue_tail(&sk->sk_receive_queue, skb);
204676d2369SDavid S. Miller 		sk->sk_data_ready(sk);
2051da177e4SLinus Torvalds 		err = 0;
2061da177e4SLinus Torvalds 	}
2071da177e4SLinus Torvalds 	return err;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds /* Send SKB to all pfkey sockets matching selected criteria.  */
2111da177e4SLinus Torvalds #define BROADCAST_ALL		0
2121da177e4SLinus Torvalds #define BROADCAST_ONE		1
2131da177e4SLinus Torvalds #define BROADCAST_REGISTERED	2
2141da177e4SLinus Torvalds #define BROADCAST_PROMISC_ONLY	4
pfkey_broadcast(struct sk_buff * skb,gfp_t allocation,int broadcast_flags,struct sock * one_sk,struct net * net)21536f41f8fSEric Dumazet static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
21607fb0f17SAlexey Dobriyan 			   int broadcast_flags, struct sock *one_sk,
21707fb0f17SAlexey Dobriyan 			   struct net *net)
2181da177e4SLinus Torvalds {
2193fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
2201da177e4SLinus Torvalds 	struct sock *sk;
2211da177e4SLinus Torvalds 	int err = -ESRCH;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	/* XXX Do we need something like netlink_overrun?  I think
2241da177e4SLinus Torvalds 	 * XXX PF_KEY socket apps will not mind current behavior.
2251da177e4SLinus Torvalds 	 */
2261da177e4SLinus Torvalds 	if (!skb)
2271da177e4SLinus Torvalds 		return -ENOMEM;
2281da177e4SLinus Torvalds 
2297f6b9dbdSstephen hemminger 	rcu_read_lock();
230b67bfe0dSSasha Levin 	sk_for_each_rcu(sk, &net_pfkey->table) {
2311da177e4SLinus Torvalds 		struct pfkey_sock *pfk = pfkey_sk(sk);
2321da177e4SLinus Torvalds 		int err2;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 		/* Yes, it means that if you are meant to receive this
2351da177e4SLinus Torvalds 		 * pfkey message you receive it twice as promiscuous
2361da177e4SLinus Torvalds 		 * socket.
2371da177e4SLinus Torvalds 		 */
2381da177e4SLinus Torvalds 		if (pfk->promisc)
239fc2d5cfdSSean Tranchetti 			pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 		/* the exact target will be processed later */
2421da177e4SLinus Torvalds 		if (sk == one_sk)
2431da177e4SLinus Torvalds 			continue;
2441da177e4SLinus Torvalds 		if (broadcast_flags != BROADCAST_ALL) {
2451da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_PROMISC_ONLY)
2461da177e4SLinus Torvalds 				continue;
2471da177e4SLinus Torvalds 			if ((broadcast_flags & BROADCAST_REGISTERED) &&
2481da177e4SLinus Torvalds 			    !pfk->registered)
2491da177e4SLinus Torvalds 				continue;
2501da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_ONE)
2511da177e4SLinus Torvalds 				continue;
2521da177e4SLinus Torvalds 		}
2531da177e4SLinus Torvalds 
254fc2d5cfdSSean Tranchetti 		err2 = pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
2551da177e4SLinus Torvalds 
256f6b8dec9SLi RongQing 		/* Error is cleared after successful sending to at least one
2571da177e4SLinus Torvalds 		 * registered KM */
2581da177e4SLinus Torvalds 		if ((broadcast_flags & BROADCAST_REGISTERED) && err)
2591da177e4SLinus Torvalds 			err = err2;
2601da177e4SLinus Torvalds 	}
2617f6b9dbdSstephen hemminger 	rcu_read_unlock();
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	if (one_sk != NULL)
264fc2d5cfdSSean Tranchetti 		err = pfkey_broadcast_one(skb, allocation, one_sk);
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	kfree_skb(skb);
2671da177e4SLinus Torvalds 	return err;
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
pfkey_do_dump(struct pfkey_sock * pfk)27005238204STimo Teras static int pfkey_do_dump(struct pfkey_sock *pfk)
27105238204STimo Teras {
27212a169e7SHerbert Xu 	struct sadb_msg *hdr;
27305238204STimo Teras 	int rc;
27405238204STimo Teras 
27589e357d8SYuejie Shi 	mutex_lock(&pfk->dump_lock);
27689e357d8SYuejie Shi 	if (!pfk->dump.dump) {
27789e357d8SYuejie Shi 		rc = 0;
27889e357d8SYuejie Shi 		goto out;
27989e357d8SYuejie Shi 	}
28089e357d8SYuejie Shi 
28105238204STimo Teras 	rc = pfk->dump.dump(pfk);
28289e357d8SYuejie Shi 	if (rc == -ENOBUFS) {
28389e357d8SYuejie Shi 		rc = 0;
28489e357d8SYuejie Shi 		goto out;
28589e357d8SYuejie Shi 	}
28605238204STimo Teras 
28712a169e7SHerbert Xu 	if (pfk->dump.skb) {
28889e357d8SYuejie Shi 		if (!pfkey_can_dump(&pfk->sk)) {
28989e357d8SYuejie Shi 			rc = 0;
29089e357d8SYuejie Shi 			goto out;
29189e357d8SYuejie Shi 		}
29212a169e7SHerbert Xu 
29312a169e7SHerbert Xu 		hdr = (struct sadb_msg *) pfk->dump.skb->data;
29412a169e7SHerbert Xu 		hdr->sadb_msg_seq = 0;
29512a169e7SHerbert Xu 		hdr->sadb_msg_errno = rc;
29636f41f8fSEric Dumazet 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
29707fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
29812a169e7SHerbert Xu 		pfk->dump.skb = NULL;
29912a169e7SHerbert Xu 	}
30012a169e7SHerbert Xu 
30105238204STimo Teras 	pfkey_terminate_dump(pfk);
30289e357d8SYuejie Shi 
30389e357d8SYuejie Shi out:
30489e357d8SYuejie Shi 	mutex_unlock(&pfk->dump_lock);
30505238204STimo Teras 	return rc;
30605238204STimo Teras }
30705238204STimo Teras 
pfkey_hdr_dup(struct sadb_msg * new,const struct sadb_msg * orig)3084c93fbb0SDavid S. Miller static inline void pfkey_hdr_dup(struct sadb_msg *new,
3094c93fbb0SDavid S. Miller 				 const struct sadb_msg *orig)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds 	*new = *orig;
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
pfkey_error(const struct sadb_msg * orig,int err,struct sock * sk)3144c93fbb0SDavid S. Miller static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk)
3151da177e4SLinus Torvalds {
3161da177e4SLinus Torvalds 	struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL);
3171da177e4SLinus Torvalds 	struct sadb_msg *hdr;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	if (!skb)
3201da177e4SLinus Torvalds 		return -ENOBUFS;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	/* Woe be to the platform trying to support PFKEY yet
3231da177e4SLinus Torvalds 	 * having normal errnos outside the 1-255 range, inclusive.
3241da177e4SLinus Torvalds 	 */
3251da177e4SLinus Torvalds 	err = -err;
3261da177e4SLinus Torvalds 	if (err == ERESTARTSYS ||
3271da177e4SLinus Torvalds 	    err == ERESTARTNOHAND ||
3281da177e4SLinus Torvalds 	    err == ERESTARTNOINTR)
3291da177e4SLinus Torvalds 		err = EINTR;
3301da177e4SLinus Torvalds 	if (err >= 512)
3311da177e4SLinus Torvalds 		err = EINVAL;
33209a62660SKris Katterjohn 	BUG_ON(err <= 0 || err >= 256);
3331da177e4SLinus Torvalds 
3344df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
3351da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
3361da177e4SLinus Torvalds 	hdr->sadb_msg_errno = (uint8_t) err;
3371da177e4SLinus Torvalds 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
3381da177e4SLinus Torvalds 			     sizeof(uint64_t));
3391da177e4SLinus Torvalds 
34036f41f8fSEric Dumazet 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	return 0;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
3458603b955SMathias Krause static const u8 sadb_ext_min_len[] = {
3461da177e4SLinus Torvalds 	[SADB_EXT_RESERVED]		= (u8) 0,
3471da177e4SLinus Torvalds 	[SADB_EXT_SA]			= (u8) sizeof(struct sadb_sa),
3481da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_CURRENT]	= (u8) sizeof(struct sadb_lifetime),
3491da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_HARD]	= (u8) sizeof(struct sadb_lifetime),
3501da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_SOFT]	= (u8) sizeof(struct sadb_lifetime),
3511da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_SRC]		= (u8) sizeof(struct sadb_address),
3521da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_DST]		= (u8) sizeof(struct sadb_address),
3531da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_PROXY]	= (u8) sizeof(struct sadb_address),
3541da177e4SLinus Torvalds 	[SADB_EXT_KEY_AUTH]		= (u8) sizeof(struct sadb_key),
3551da177e4SLinus Torvalds 	[SADB_EXT_KEY_ENCRYPT]		= (u8) sizeof(struct sadb_key),
3561da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_SRC]		= (u8) sizeof(struct sadb_ident),
3571da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_DST]		= (u8) sizeof(struct sadb_ident),
3581da177e4SLinus Torvalds 	[SADB_EXT_SENSITIVITY]		= (u8) sizeof(struct sadb_sens),
3591da177e4SLinus Torvalds 	[SADB_EXT_PROPOSAL]		= (u8) sizeof(struct sadb_prop),
3601da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_AUTH]	= (u8) sizeof(struct sadb_supported),
3611da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_ENCRYPT]	= (u8) sizeof(struct sadb_supported),
3621da177e4SLinus Torvalds 	[SADB_EXT_SPIRANGE]		= (u8) sizeof(struct sadb_spirange),
3631da177e4SLinus Torvalds 	[SADB_X_EXT_KMPRIVATE]		= (u8) sizeof(struct sadb_x_kmprivate),
3641da177e4SLinus Torvalds 	[SADB_X_EXT_POLICY]		= (u8) sizeof(struct sadb_x_policy),
3651da177e4SLinus Torvalds 	[SADB_X_EXT_SA2]		= (u8) sizeof(struct sadb_x_sa2),
3661da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_TYPE]		= (u8) sizeof(struct sadb_x_nat_t_type),
3671da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_SPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3681da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_DPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3691da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),
370df71837dSTrent Jaeger 	[SADB_X_EXT_SEC_CTX]		= (u8) sizeof(struct sadb_x_sec_ctx),
37113c1d189SArnaud Ebalard 	[SADB_X_EXT_KMADDRESS]		= (u8) sizeof(struct sadb_x_kmaddress),
372d2c5f658SNicolas Dichtel 	[SADB_X_EXT_FILTER]		= (u8) sizeof(struct sadb_x_filter),
3731da177e4SLinus Torvalds };
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds /* Verify sadb_address_{len,prefixlen} against sa_family.  */
verify_address_len(const void * p)3764c93fbb0SDavid S. Miller static int verify_address_len(const void *p)
3771da177e4SLinus Torvalds {
3784c93fbb0SDavid S. Miller 	const struct sadb_address *sp = p;
3794c93fbb0SDavid S. Miller 	const struct sockaddr *addr = (const struct sockaddr *)(sp + 1);
3804c93fbb0SDavid S. Miller 	const struct sockaddr_in *sin;
381dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
3824c93fbb0SDavid S. Miller 	const struct sockaddr_in6 *sin6;
3831da177e4SLinus Torvalds #endif
3841da177e4SLinus Torvalds 	int len;
3851da177e4SLinus Torvalds 
38606b335cbSEric Biggers 	if (sp->sadb_address_len <
38706b335cbSEric Biggers 	    DIV_ROUND_UP(sizeof(*sp) + offsetofend(typeof(*addr), sa_family),
38806b335cbSEric Biggers 			 sizeof(uint64_t)))
38906b335cbSEric Biggers 		return -EINVAL;
39006b335cbSEric Biggers 
3911da177e4SLinus Torvalds 	switch (addr->sa_family) {
3921da177e4SLinus Torvalds 	case AF_INET:
393356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));
3941da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
3951da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 32)
3961da177e4SLinus Torvalds 			return -EINVAL;
3971da177e4SLinus Torvalds 		break;
398dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
3991da177e4SLinus Torvalds 	case AF_INET6:
400356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t));
4011da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
4021da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 128)
4031da177e4SLinus Torvalds 			return -EINVAL;
4041da177e4SLinus Torvalds 		break;
4051da177e4SLinus Torvalds #endif
4061da177e4SLinus Torvalds 	default:
4071da177e4SLinus Torvalds 		/* It is user using kernel to keep track of security
4081da177e4SLinus Torvalds 		 * associations for another protocol, such as
4091da177e4SLinus Torvalds 		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify
4101da177e4SLinus Torvalds 		 * lengths.
4111da177e4SLinus Torvalds 		 *
4121da177e4SLinus Torvalds 		 * XXX Actually, association/policy database is not yet
4131da177e4SLinus Torvalds 		 * XXX able to cope with arbitrary sockaddr families.
4141da177e4SLinus Torvalds 		 * XXX When it can, remove this -EINVAL.  -DaveM
4151da177e4SLinus Torvalds 		 */
4161da177e4SLinus Torvalds 		return -EINVAL;
4173ff50b79SStephen Hemminger 	}
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	return 0;
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
sadb_key_len(const struct sadb_key * key)4224b66af2dSKevin Easton static inline int sadb_key_len(const struct sadb_key *key)
4234b66af2dSKevin Easton {
4244b66af2dSKevin Easton 	int key_bytes = DIV_ROUND_UP(key->sadb_key_bits, 8);
4254b66af2dSKevin Easton 
4264b66af2dSKevin Easton 	return DIV_ROUND_UP(sizeof(struct sadb_key) + key_bytes,
4274b66af2dSKevin Easton 			    sizeof(uint64_t));
4284b66af2dSKevin Easton }
4294b66af2dSKevin Easton 
verify_key_len(const void * p)4304b66af2dSKevin Easton static int verify_key_len(const void *p)
4314b66af2dSKevin Easton {
4324b66af2dSKevin Easton 	const struct sadb_key *key = p;
4334b66af2dSKevin Easton 
4344b66af2dSKevin Easton 	if (sadb_key_len(key) > key->sadb_key_len)
4354b66af2dSKevin Easton 		return -EINVAL;
4364b66af2dSKevin Easton 
4374b66af2dSKevin Easton 	return 0;
4384b66af2dSKevin Easton }
4394b66af2dSKevin Easton 
pfkey_sec_ctx_len(const struct sadb_x_sec_ctx * sec_ctx)4404c93fbb0SDavid S. Miller static inline int pfkey_sec_ctx_len(const struct sadb_x_sec_ctx *sec_ctx)
441df71837dSTrent Jaeger {
442356f89e1SIlpo Järvinen 	return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) +
443356f89e1SIlpo Järvinen 			    sec_ctx->sadb_x_ctx_len,
444356f89e1SIlpo Järvinen 			    sizeof(uint64_t));
445df71837dSTrent Jaeger }
446df71837dSTrent Jaeger 
verify_sec_ctx_len(const void * p)4474c93fbb0SDavid S. Miller static inline int verify_sec_ctx_len(const void *p)
448df71837dSTrent Jaeger {
4494c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx = p;
450298bb621SStephen Rothwell 	int len = sec_ctx->sadb_x_ctx_len;
451df71837dSTrent Jaeger 
452298bb621SStephen Rothwell 	if (len > PAGE_SIZE)
453df71837dSTrent Jaeger 		return -EINVAL;
454df71837dSTrent Jaeger 
455df71837dSTrent Jaeger 	len = pfkey_sec_ctx_len(sec_ctx);
456df71837dSTrent Jaeger 
457df71837dSTrent Jaeger 	if (sec_ctx->sadb_x_sec_len != len)
458df71837dSTrent Jaeger 		return -EINVAL;
459df71837dSTrent Jaeger 
460df71837dSTrent Jaeger 	return 0;
461df71837dSTrent Jaeger }
462df71837dSTrent Jaeger 
pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx * sec_ctx,gfp_t gfp)46387536a81SNikolay Aleksandrov static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx,
46487536a81SNikolay Aleksandrov 								     gfp_t gfp)
465df71837dSTrent Jaeger {
466df71837dSTrent Jaeger 	struct xfrm_user_sec_ctx *uctx = NULL;
467df71837dSTrent Jaeger 	int ctx_size = sec_ctx->sadb_x_ctx_len;
468df71837dSTrent Jaeger 
46987536a81SNikolay Aleksandrov 	uctx = kmalloc((sizeof(*uctx)+ctx_size), gfp);
470df71837dSTrent Jaeger 
471df71837dSTrent Jaeger 	if (!uctx)
472df71837dSTrent Jaeger 		return NULL;
473df71837dSTrent Jaeger 
474df71837dSTrent Jaeger 	uctx->len = pfkey_sec_ctx_len(sec_ctx);
475df71837dSTrent Jaeger 	uctx->exttype = sec_ctx->sadb_x_sec_exttype;
476df71837dSTrent Jaeger 	uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi;
477df71837dSTrent Jaeger 	uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg;
478df71837dSTrent Jaeger 	uctx->ctx_len = sec_ctx->sadb_x_ctx_len;
479df71837dSTrent Jaeger 	memcpy(uctx + 1, sec_ctx + 1,
480df71837dSTrent Jaeger 	       uctx->ctx_len);
481df71837dSTrent Jaeger 
482df71837dSTrent Jaeger 	return uctx;
483df71837dSTrent Jaeger }
484df71837dSTrent Jaeger 
present_and_same_family(const struct sadb_address * src,const struct sadb_address * dst)4854c93fbb0SDavid S. Miller static int present_and_same_family(const struct sadb_address *src,
4864c93fbb0SDavid S. Miller 				   const struct sadb_address *dst)
4871da177e4SLinus Torvalds {
4884c93fbb0SDavid S. Miller 	const struct sockaddr *s_addr, *d_addr;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	if (!src || !dst)
4911da177e4SLinus Torvalds 		return 0;
4921da177e4SLinus Torvalds 
4934c93fbb0SDavid S. Miller 	s_addr = (const struct sockaddr *)(src + 1);
4944c93fbb0SDavid S. Miller 	d_addr = (const struct sockaddr *)(dst + 1);
4951da177e4SLinus Torvalds 	if (s_addr->sa_family != d_addr->sa_family)
4961da177e4SLinus Torvalds 		return 0;
4971da177e4SLinus Torvalds 	if (s_addr->sa_family != AF_INET
498dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
4991da177e4SLinus Torvalds 	    && s_addr->sa_family != AF_INET6
5001da177e4SLinus Torvalds #endif
5011da177e4SLinus Torvalds 		)
5021da177e4SLinus Torvalds 		return 0;
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 	return 1;
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds 
parse_exthdrs(struct sk_buff * skb,const struct sadb_msg * hdr,void ** ext_hdrs)5074c93fbb0SDavid S. Miller static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void **ext_hdrs)
5081da177e4SLinus Torvalds {
5094c93fbb0SDavid S. Miller 	const char *p = (char *) hdr;
5101da177e4SLinus Torvalds 	int len = skb->len;
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	len -= sizeof(*hdr);
5131da177e4SLinus Torvalds 	p += sizeof(*hdr);
5141da177e4SLinus Torvalds 	while (len > 0) {
5154c93fbb0SDavid S. Miller 		const struct sadb_ext *ehdr = (const struct sadb_ext *) p;
5161da177e4SLinus Torvalds 		uint16_t ext_type;
5171da177e4SLinus Torvalds 		int ext_len;
5181da177e4SLinus Torvalds 
5194e765b49SEric Biggers 		if (len < sizeof(*ehdr))
5204e765b49SEric Biggers 			return -EINVAL;
5214e765b49SEric Biggers 
5221da177e4SLinus Torvalds 		ext_len  = ehdr->sadb_ext_len;
5231da177e4SLinus Torvalds 		ext_len *= sizeof(uint64_t);
5241da177e4SLinus Torvalds 		ext_type = ehdr->sadb_ext_type;
5251da177e4SLinus Torvalds 		if (ext_len < sizeof(uint64_t) ||
5261da177e4SLinus Torvalds 		    ext_len > len ||
5271da177e4SLinus Torvalds 		    ext_type == SADB_EXT_RESERVED)
5281da177e4SLinus Torvalds 			return -EINVAL;
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 		if (ext_type <= SADB_EXT_MAX) {
5311da177e4SLinus Torvalds 			int min = (int) sadb_ext_min_len[ext_type];
5321da177e4SLinus Torvalds 			if (ext_len < min)
5331da177e4SLinus Torvalds 				return -EINVAL;
5341da177e4SLinus Torvalds 			if (ext_hdrs[ext_type-1] != NULL)
5351da177e4SLinus Torvalds 				return -EINVAL;
5364b66af2dSKevin Easton 			switch (ext_type) {
5374b66af2dSKevin Easton 			case SADB_EXT_ADDRESS_SRC:
5384b66af2dSKevin Easton 			case SADB_EXT_ADDRESS_DST:
5394b66af2dSKevin Easton 			case SADB_EXT_ADDRESS_PROXY:
5404b66af2dSKevin Easton 			case SADB_X_EXT_NAT_T_OA:
5411da177e4SLinus Torvalds 				if (verify_address_len(p))
5421da177e4SLinus Torvalds 					return -EINVAL;
5434b66af2dSKevin Easton 				break;
5444b66af2dSKevin Easton 			case SADB_X_EXT_SEC_CTX:
545df71837dSTrent Jaeger 				if (verify_sec_ctx_len(p))
546df71837dSTrent Jaeger 					return -EINVAL;
5474b66af2dSKevin Easton 				break;
5484b66af2dSKevin Easton 			case SADB_EXT_KEY_AUTH:
5494b66af2dSKevin Easton 			case SADB_EXT_KEY_ENCRYPT:
5504b66af2dSKevin Easton 				if (verify_key_len(p))
5514b66af2dSKevin Easton 					return -EINVAL;
5524b66af2dSKevin Easton 				break;
5534b66af2dSKevin Easton 			default:
5544b66af2dSKevin Easton 				break;
555df71837dSTrent Jaeger 			}
5564c93fbb0SDavid S. Miller 			ext_hdrs[ext_type-1] = (void *) p;
5571da177e4SLinus Torvalds 		}
5581da177e4SLinus Torvalds 		p   += ext_len;
5591da177e4SLinus Torvalds 		len -= ext_len;
5601da177e4SLinus Torvalds 	}
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 	return 0;
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds static uint16_t
pfkey_satype2proto(uint8_t satype)5661da177e4SLinus Torvalds pfkey_satype2proto(uint8_t satype)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds 	switch (satype) {
5691da177e4SLinus Torvalds 	case SADB_SATYPE_UNSPEC:
5701da177e4SLinus Torvalds 		return IPSEC_PROTO_ANY;
5711da177e4SLinus Torvalds 	case SADB_SATYPE_AH:
5721da177e4SLinus Torvalds 		return IPPROTO_AH;
5731da177e4SLinus Torvalds 	case SADB_SATYPE_ESP:
5741da177e4SLinus Torvalds 		return IPPROTO_ESP;
5751da177e4SLinus Torvalds 	case SADB_X_SATYPE_IPCOMP:
5761da177e4SLinus Torvalds 		return IPPROTO_COMP;
5771da177e4SLinus Torvalds 	default:
5781da177e4SLinus Torvalds 		return 0;
5791da177e4SLinus Torvalds 	}
5801da177e4SLinus Torvalds 	/* NOTREACHED */
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds static uint8_t
pfkey_proto2satype(uint16_t proto)5841da177e4SLinus Torvalds pfkey_proto2satype(uint16_t proto)
5851da177e4SLinus Torvalds {
5861da177e4SLinus Torvalds 	switch (proto) {
5871da177e4SLinus Torvalds 	case IPPROTO_AH:
5881da177e4SLinus Torvalds 		return SADB_SATYPE_AH;
5891da177e4SLinus Torvalds 	case IPPROTO_ESP:
5901da177e4SLinus Torvalds 		return SADB_SATYPE_ESP;
5911da177e4SLinus Torvalds 	case IPPROTO_COMP:
5921da177e4SLinus Torvalds 		return SADB_X_SATYPE_IPCOMP;
5931da177e4SLinus Torvalds 	default:
5941da177e4SLinus Torvalds 		return 0;
5951da177e4SLinus Torvalds 	}
5961da177e4SLinus Torvalds 	/* NOTREACHED */
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds /* BTW, this scheme means that there is no way with PFKEY2 sockets to
6001da177e4SLinus Torvalds  * say specifically 'just raw sockets' as we encode them as 255.
6011da177e4SLinus Torvalds  */
6021da177e4SLinus Torvalds 
pfkey_proto_to_xfrm(uint8_t proto)6031da177e4SLinus Torvalds static uint8_t pfkey_proto_to_xfrm(uint8_t proto)
6041da177e4SLinus Torvalds {
605a02cec21SEric Dumazet 	return proto == IPSEC_PROTO_ANY ? 0 : proto;
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
pfkey_proto_from_xfrm(uint8_t proto)6081da177e4SLinus Torvalds static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
6091da177e4SLinus Torvalds {
610a02cec21SEric Dumazet 	return proto ? proto : IPSEC_PROTO_ANY;
6111da177e4SLinus Torvalds }
6121da177e4SLinus Torvalds 
pfkey_sockaddr_len(sa_family_t family)6139e8b4ed8SYOSHIFUJI Hideaki static inline int pfkey_sockaddr_len(sa_family_t family)
6149e8b4ed8SYOSHIFUJI Hideaki {
6159e8b4ed8SYOSHIFUJI Hideaki 	switch (family) {
6169e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET:
6179e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in);
618dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6199e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET6:
6209e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in6);
6219e8b4ed8SYOSHIFUJI Hideaki #endif
6229e8b4ed8SYOSHIFUJI Hideaki 	}
6239e8b4ed8SYOSHIFUJI Hideaki 	return 0;
6249e8b4ed8SYOSHIFUJI Hideaki }
6259e8b4ed8SYOSHIFUJI Hideaki 
6265f95ac91SYOSHIFUJI Hideaki static
pfkey_sockaddr_extract(const struct sockaddr * sa,xfrm_address_t * xaddr)6275f95ac91SYOSHIFUJI Hideaki int pfkey_sockaddr_extract(const struct sockaddr *sa, xfrm_address_t *xaddr)
6281da177e4SLinus Torvalds {
6295f95ac91SYOSHIFUJI Hideaki 	switch (sa->sa_family) {
6301da177e4SLinus Torvalds 	case AF_INET:
6311da177e4SLinus Torvalds 		xaddr->a4 =
6325f95ac91SYOSHIFUJI Hideaki 			((struct sockaddr_in *)sa)->sin_addr.s_addr;
6331da177e4SLinus Torvalds 		return AF_INET;
634dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6351da177e4SLinus Torvalds 	case AF_INET6:
6361da177e4SLinus Torvalds 		memcpy(xaddr->a6,
6375f95ac91SYOSHIFUJI Hideaki 		       &((struct sockaddr_in6 *)sa)->sin6_addr,
6381da177e4SLinus Torvalds 		       sizeof(struct in6_addr));
6391da177e4SLinus Torvalds 		return AF_INET6;
6401da177e4SLinus Torvalds #endif
6415f95ac91SYOSHIFUJI Hideaki 	}
6421da177e4SLinus Torvalds 	return 0;
6431da177e4SLinus Torvalds }
6445f95ac91SYOSHIFUJI Hideaki 
6455f95ac91SYOSHIFUJI Hideaki static
pfkey_sadb_addr2xfrm_addr(const struct sadb_address * addr,xfrm_address_t * xaddr)6464c93fbb0SDavid S. Miller int pfkey_sadb_addr2xfrm_addr(const struct sadb_address *addr, xfrm_address_t *xaddr)
6475f95ac91SYOSHIFUJI Hideaki {
6485f95ac91SYOSHIFUJI Hideaki 	return pfkey_sockaddr_extract((struct sockaddr *)(addr + 1),
6495f95ac91SYOSHIFUJI Hideaki 				      xaddr);
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
pfkey_xfrm_state_lookup(struct net * net,const struct sadb_msg * hdr,void * const * ext_hdrs)6524c93fbb0SDavid S. Miller static struct  xfrm_state *pfkey_xfrm_state_lookup(struct net *net, const struct sadb_msg *hdr, void * const *ext_hdrs)
6531da177e4SLinus Torvalds {
6544c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
6554c93fbb0SDavid S. Miller 	const struct sadb_address *addr;
6561da177e4SLinus Torvalds 	uint16_t proto;
6571da177e4SLinus Torvalds 	unsigned short family;
6581da177e4SLinus Torvalds 	xfrm_address_t *xaddr;
6591da177e4SLinus Torvalds 
660ea110733SJoe Perches 	sa = ext_hdrs[SADB_EXT_SA - 1];
6611da177e4SLinus Torvalds 	if (sa == NULL)
6621da177e4SLinus Torvalds 		return NULL;
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
6651da177e4SLinus Torvalds 	if (proto == 0)
6661da177e4SLinus Torvalds 		return NULL;
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 	/* sadb_address_len should be checked by caller */
669ea110733SJoe Perches 	addr = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];
6701da177e4SLinus Torvalds 	if (addr == NULL)
6711da177e4SLinus Torvalds 		return NULL;
6721da177e4SLinus Torvalds 
6734c93fbb0SDavid S. Miller 	family = ((const struct sockaddr *)(addr + 1))->sa_family;
6741da177e4SLinus Torvalds 	switch (family) {
6751da177e4SLinus Torvalds 	case AF_INET:
6764c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in *)(addr + 1))->sin_addr;
6771da177e4SLinus Torvalds 		break;
678dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
6791da177e4SLinus Torvalds 	case AF_INET6:
6804c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in6 *)(addr + 1))->sin6_addr;
6811da177e4SLinus Torvalds 		break;
6821da177e4SLinus Torvalds #endif
6831da177e4SLinus Torvalds 	default:
6841da177e4SLinus Torvalds 		xaddr = NULL;
6851da177e4SLinus Torvalds 	}
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 	if (!xaddr)
6881da177e4SLinus Torvalds 		return NULL;
6891da177e4SLinus Torvalds 
690bd55775cSJamal Hadi Salim 	return xfrm_state_lookup(net, DUMMY_MARK, xaddr, sa->sadb_sa_spi, proto, family);
6911da177e4SLinus Torvalds }
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
6949e8b4ed8SYOSHIFUJI Hideaki 
6951da177e4SLinus Torvalds static int
pfkey_sockaddr_size(sa_family_t family)6961da177e4SLinus Torvalds pfkey_sockaddr_size(sa_family_t family)
6971da177e4SLinus Torvalds {
6989e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family));
6991da177e4SLinus Torvalds }
7001da177e4SLinus Torvalds 
pfkey_mode_from_xfrm(int mode)70155569ce2SKazunori MIYAZAWA static inline int pfkey_mode_from_xfrm(int mode)
70255569ce2SKazunori MIYAZAWA {
70355569ce2SKazunori MIYAZAWA 	switch(mode) {
70455569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TRANSPORT:
70555569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TRANSPORT;
70655569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TUNNEL:
70755569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TUNNEL;
70855569ce2SKazunori MIYAZAWA 	case XFRM_MODE_BEET:
70955569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_BEET;
71055569ce2SKazunori MIYAZAWA 	default:
71155569ce2SKazunori MIYAZAWA 		return -1;
71255569ce2SKazunori MIYAZAWA 	}
71355569ce2SKazunori MIYAZAWA }
71455569ce2SKazunori MIYAZAWA 
pfkey_mode_to_xfrm(int mode)71555569ce2SKazunori MIYAZAWA static inline int pfkey_mode_to_xfrm(int mode)
71655569ce2SKazunori MIYAZAWA {
71755569ce2SKazunori MIYAZAWA 	switch(mode) {
71855569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_ANY:	/*XXX*/
71955569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TRANSPORT:
72055569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TRANSPORT;
72155569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TUNNEL:
72255569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TUNNEL;
72355569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_BEET:
72455569ce2SKazunori MIYAZAWA 		return XFRM_MODE_BEET;
72555569ce2SKazunori MIYAZAWA 	default:
72655569ce2SKazunori MIYAZAWA 		return -1;
72755569ce2SKazunori MIYAZAWA 	}
72855569ce2SKazunori MIYAZAWA }
72955569ce2SKazunori MIYAZAWA 
pfkey_sockaddr_fill(const xfrm_address_t * xaddr,__be16 port,struct sockaddr * sa,unsigned short family)730183cad12SDavid S. Miller static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port,
731e5b56652SYOSHIFUJI Hideaki 					struct sockaddr *sa,
732e5b56652SYOSHIFUJI Hideaki 					unsigned short family)
733e5b56652SYOSHIFUJI Hideaki {
734e5b56652SYOSHIFUJI Hideaki 	switch (family) {
735e5b56652SYOSHIFUJI Hideaki 	case AF_INET:
736e5b56652SYOSHIFUJI Hideaki 	    {
737e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
738e5b56652SYOSHIFUJI Hideaki 		sin->sin_family = AF_INET;
739e5b56652SYOSHIFUJI Hideaki 		sin->sin_port = port;
740e5b56652SYOSHIFUJI Hideaki 		sin->sin_addr.s_addr = xaddr->a4;
741e5b56652SYOSHIFUJI Hideaki 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
742e5b56652SYOSHIFUJI Hideaki 		return 32;
743e5b56652SYOSHIFUJI Hideaki 	    }
744dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
745e5b56652SYOSHIFUJI Hideaki 	case AF_INET6:
746e5b56652SYOSHIFUJI Hideaki 	    {
747e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
748e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_family = AF_INET6;
749e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_port = port;
750e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_flowinfo = 0;
75115e318bdSJiri Benc 		sin6->sin6_addr = xaddr->in6;
752e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_scope_id = 0;
753e5b56652SYOSHIFUJI Hideaki 		return 128;
754e5b56652SYOSHIFUJI Hideaki 	    }
755e5b56652SYOSHIFUJI Hideaki #endif
756e5b56652SYOSHIFUJI Hideaki 	}
757e5b56652SYOSHIFUJI Hideaki 	return 0;
758e5b56652SYOSHIFUJI Hideaki }
759e5b56652SYOSHIFUJI Hideaki 
__pfkey_xfrm_state2msg(const struct xfrm_state * x,int add_keys,int hsc)7604c93fbb0SDavid S. Miller static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
761050f009eSHerbert Xu 					      int add_keys, int hsc)
7621da177e4SLinus Torvalds {
7631da177e4SLinus Torvalds 	struct sk_buff *skb;
7641da177e4SLinus Torvalds 	struct sadb_msg *hdr;
7651da177e4SLinus Torvalds 	struct sadb_sa *sa;
7661da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
7671da177e4SLinus Torvalds 	struct sadb_address *addr;
7681da177e4SLinus Torvalds 	struct sadb_key *key;
7691da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
770df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
771df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
772df71837dSTrent Jaeger 	int ctx_size = 0;
7731da177e4SLinus Torvalds 	int size;
7741da177e4SLinus Torvalds 	int auth_key_size = 0;
7751da177e4SLinus Torvalds 	int encrypt_key_size = 0;
7761da177e4SLinus Torvalds 	int sockaddr_size;
7771da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
77855569ce2SKazunori MIYAZAWA 	int mode;
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	/* address family check */
7811da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
7821da177e4SLinus Torvalds 	if (!sockaddr_size)
7831da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
7861da177e4SLinus Torvalds 	   key(AE), (identity(SD),) (sensitivity)> */
7871da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
7881da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime) +
7891da177e4SLinus Torvalds 		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
7901da177e4SLinus Torvalds 		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
7911da177e4SLinus Torvalds 			sizeof(struct sadb_address)*2 +
7921da177e4SLinus Torvalds 				sockaddr_size*2 +
7931da177e4SLinus Torvalds 					sizeof(struct sadb_x_sa2);
794df71837dSTrent Jaeger 
795df71837dSTrent Jaeger 	if ((xfrm_ctx = x->security)) {
796df71837dSTrent Jaeger 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
797df71837dSTrent Jaeger 		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
798df71837dSTrent Jaeger 	}
799df71837dSTrent Jaeger 
8001da177e4SLinus Torvalds 	/* identity & sensitivity */
80170e94e66SYOSHIFUJI Hideaki / 吉藤英明 	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family))
8021da177e4SLinus Torvalds 		size += sizeof(struct sadb_address) + sockaddr_size;
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 	if (add_keys) {
8051da177e4SLinus Torvalds 		if (x->aalg && x->aalg->alg_key_len) {
8061da177e4SLinus Torvalds 			auth_key_size =
8071da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
8081da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + auth_key_size;
8091da177e4SLinus Torvalds 		}
8101da177e4SLinus Torvalds 		if (x->ealg && x->ealg->alg_key_len) {
8111da177e4SLinus Torvalds 			encrypt_key_size =
8121da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
8131da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + encrypt_key_size;
8141da177e4SLinus Torvalds 		}
8151da177e4SLinus Torvalds 	}
8161da177e4SLinus Torvalds 	if (x->encap)
8171da177e4SLinus Torvalds 		natt = x->encap;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
8201da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_type);
8211da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
8221da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
8231da177e4SLinus Torvalds 	}
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
8261da177e4SLinus Torvalds 	if (skb == NULL)
8271da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	/* call should fill header later */
8304df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
8311da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
8321da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
8331da177e4SLinus Torvalds 
8341da177e4SLinus Torvalds 	/* sa */
8354df864c1SJohannes Berg 	sa = skb_put(skb, sizeof(struct sadb_sa));
8361da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
8371da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
8381da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
8391da177e4SLinus Torvalds 	sa->sadb_sa_replay = x->props.replay_window;
8404f09f0bbSHerbert Xu 	switch (x->km.state) {
8414f09f0bbSHerbert Xu 	case XFRM_STATE_VALID:
8424f09f0bbSHerbert Xu 		sa->sadb_sa_state = x->km.dying ?
8434f09f0bbSHerbert Xu 			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
8444f09f0bbSHerbert Xu 		break;
8454f09f0bbSHerbert Xu 	case XFRM_STATE_ACQ:
8461da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
8474f09f0bbSHerbert Xu 		break;
8484f09f0bbSHerbert Xu 	default:
8491da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_DEAD;
8504f09f0bbSHerbert Xu 		break;
8514f09f0bbSHerbert Xu 	}
8521da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
8531da177e4SLinus Torvalds 	if (x->aalg) {
8541da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
8557e50f84cSJussi Kivilinna 		sa->sadb_sa_auth = (a && a->pfkey_supported) ?
8567e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8571da177e4SLinus Torvalds 	}
8581da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
8591da177e4SLinus Torvalds 	BUG_ON(x->ealg && x->calg);
8601da177e4SLinus Torvalds 	if (x->ealg) {
8611da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
8627e50f84cSJussi Kivilinna 		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
8637e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8641da177e4SLinus Torvalds 	}
8651da177e4SLinus Torvalds 	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
8661da177e4SLinus Torvalds 	if (x->calg) {
8671da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
8687e50f84cSJussi Kivilinna 		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
8697e50f84cSJussi Kivilinna 					a->desc.sadb_alg_id : 0;
8701da177e4SLinus Torvalds 	}
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
8731da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_NOECN)
8741da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
8751da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
8761da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
877dd87147eSHerbert Xu 	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
878dd87147eSHerbert Xu 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	/* hard time */
8811da177e4SLinus Torvalds 	if (hsc & 2) {
8824df864c1SJohannes Berg 		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
8831da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8841da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8851da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
8861da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
8871da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
8881da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
8891da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
8901da177e4SLinus Torvalds 	}
8911da177e4SLinus Torvalds 	/* soft time */
8921da177e4SLinus Torvalds 	if (hsc & 1) {
8934df864c1SJohannes Berg 		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
8941da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8951da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8961da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
8971da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
8981da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
8991da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
9001da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
9011da177e4SLinus Torvalds 	}
9021da177e4SLinus Torvalds 	/* current time */
9034df864c1SJohannes Berg 	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
9041da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
9051da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
9061da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
9071da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = x->curlft.packets;
9081da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
9091da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
9101da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
9111da177e4SLinus Torvalds 	/* src address */
9124df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
9131da177e4SLinus Torvalds 	addr->sadb_address_len =
9141da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
9151da177e4SLinus Torvalds 			sizeof(uint64_t);
9161da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
9171da177e4SLinus Torvalds 	/* "if the ports are non-zero, then the sadb_address_proto field,
9181da177e4SLinus Torvalds 	   normally zero, MUST be filled in with the transport
9191da177e4SLinus Torvalds 	   protocol's number." - RFC2367 */
9201da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
9211da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
9221da177e4SLinus Torvalds 
923e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
924e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
925e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
926e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
927de47c5d8SHariprasad Kelam 	BUG_ON(!addr->sadb_address_prefixlen);
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	/* dst address */
9304df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
9311da177e4SLinus Torvalds 	addr->sadb_address_len =
9321da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
9331da177e4SLinus Torvalds 			sizeof(uint64_t);
9341da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
9351da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
9361da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
9371da177e4SLinus Torvalds 
938e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
939e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
940e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
941e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
942de47c5d8SHariprasad Kelam 	BUG_ON(!addr->sadb_address_prefixlen);
9431da177e4SLinus Torvalds 
94470e94e66SYOSHIFUJI Hideaki / 吉藤英明 	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr,
945e5b56652SYOSHIFUJI Hideaki 			     x->props.family)) {
9464df864c1SJohannes Berg 		addr = skb_put(skb,
947e5b56652SYOSHIFUJI Hideaki 			       sizeof(struct sadb_address) + sockaddr_size);
948e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_len =
949e5b56652SYOSHIFUJI Hideaki 			(sizeof(struct sadb_address)+sockaddr_size)/
950e5b56652SYOSHIFUJI Hideaki 			sizeof(uint64_t);
951e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
952e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_proto =
953e5b56652SYOSHIFUJI Hideaki 			pfkey_proto_from_xfrm(x->sel.proto);
954e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
955e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_reserved = 0;
956e5b56652SYOSHIFUJI Hideaki 
957e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
958e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
959e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
960e5b56652SYOSHIFUJI Hideaki 	}
961e5b56652SYOSHIFUJI Hideaki 
9621da177e4SLinus Torvalds 	/* auth key */
9631da177e4SLinus Torvalds 	if (add_keys && auth_key_size) {
9644df864c1SJohannes Berg 		key = skb_put(skb, sizeof(struct sadb_key) + auth_key_size);
9651da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
9661da177e4SLinus Torvalds 			sizeof(uint64_t);
9671da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
9681da177e4SLinus Torvalds 		key->sadb_key_bits = x->aalg->alg_key_len;
9691da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9701da177e4SLinus Torvalds 		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
9711da177e4SLinus Torvalds 	}
9721da177e4SLinus Torvalds 	/* encrypt key */
9731da177e4SLinus Torvalds 	if (add_keys && encrypt_key_size) {
9744df864c1SJohannes Berg 		key = skb_put(skb, sizeof(struct sadb_key) + encrypt_key_size);
9751da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) +
9761da177e4SLinus Torvalds 				     encrypt_key_size) / sizeof(uint64_t);
9771da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
9781da177e4SLinus Torvalds 		key->sadb_key_bits = x->ealg->alg_key_len;
9791da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9801da177e4SLinus Torvalds 		memcpy(key + 1, x->ealg->alg_key,
9811da177e4SLinus Torvalds 		       (x->ealg->alg_key_len+7)/8);
9821da177e4SLinus Torvalds 	}
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	/* sa */
9854df864c1SJohannes Berg 	sa2 = skb_put(skb, sizeof(struct sadb_x_sa2));
9861da177e4SLinus Torvalds 	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
9871da177e4SLinus Torvalds 	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
98855569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
98955569ce2SKazunori MIYAZAWA 		kfree_skb(skb);
99055569ce2SKazunori MIYAZAWA 		return ERR_PTR(-EINVAL);
99155569ce2SKazunori MIYAZAWA 	}
99255569ce2SKazunori MIYAZAWA 	sa2->sadb_x_sa2_mode = mode;
9931da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved1 = 0;
9941da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved2 = 0;
9951da177e4SLinus Torvalds 	sa2->sadb_x_sa2_sequence = 0;
9961da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reqid = x->props.reqid;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
9991da177e4SLinus Torvalds 		struct sadb_x_nat_t_type *n_type;
10001da177e4SLinus Torvalds 		struct sadb_x_nat_t_port *n_port;
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds 		/* type */
10034df864c1SJohannes Berg 		n_type = skb_put(skb, sizeof(*n_type));
10041da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
10051da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
10061da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_type = natt->encap_type;
10071da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[0] = 0;
10081da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[1] = 0;
10091da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[2] = 0;
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 		/* source port */
10124df864c1SJohannes Berg 		n_port = skb_put(skb, sizeof(*n_port));
10131da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
10141da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
10151da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
10161da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds 		/* dest port */
10194df864c1SJohannes Berg 		n_port = skb_put(skb, sizeof(*n_port));
10201da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
10211da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
10221da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
10231da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
10241da177e4SLinus Torvalds 	}
10251da177e4SLinus Torvalds 
1026df71837dSTrent Jaeger 	/* security context */
1027df71837dSTrent Jaeger 	if (xfrm_ctx) {
10284df864c1SJohannes Berg 		sec_ctx = skb_put(skb,
1029df71837dSTrent Jaeger 				  sizeof(struct sadb_x_sec_ctx) + ctx_size);
1030df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len =
1031df71837dSTrent Jaeger 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
1032df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
1033df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
1034df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
1035df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
1036df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
1037df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
1038df71837dSTrent Jaeger 	}
1039df71837dSTrent Jaeger 
10401da177e4SLinus Torvalds 	return skb;
10411da177e4SLinus Torvalds }
10421da177e4SLinus Torvalds 
1043050f009eSHerbert Xu 
pfkey_xfrm_state2msg(const struct xfrm_state * x)10444c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg(const struct xfrm_state *x)
1045050f009eSHerbert Xu {
1046050f009eSHerbert Xu 	struct sk_buff *skb;
1047050f009eSHerbert Xu 
1048050f009eSHerbert Xu 	skb = __pfkey_xfrm_state2msg(x, 1, 3);
1049050f009eSHerbert Xu 
1050050f009eSHerbert Xu 	return skb;
1051050f009eSHerbert Xu }
1052050f009eSHerbert Xu 
pfkey_xfrm_state2msg_expire(const struct xfrm_state * x,int hsc)10534c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg_expire(const struct xfrm_state *x,
1054050f009eSHerbert Xu 							  int hsc)
1055050f009eSHerbert Xu {
1056050f009eSHerbert Xu 	return __pfkey_xfrm_state2msg(x, 0, hsc);
1057050f009eSHerbert Xu }
1058050f009eSHerbert Xu 
pfkey_msg2xfrm_state(struct net * net,const struct sadb_msg * hdr,void * const * ext_hdrs)105907fb0f17SAlexey Dobriyan static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
10604c93fbb0SDavid S. Miller 						const struct sadb_msg *hdr,
10614c93fbb0SDavid S. Miller 						void * const *ext_hdrs)
10621da177e4SLinus Torvalds {
10631da177e4SLinus Torvalds 	struct xfrm_state *x;
10644c93fbb0SDavid S. Miller 	const struct sadb_lifetime *lifetime;
10654c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
10664c93fbb0SDavid S. Miller 	const struct sadb_key *key;
10674c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx;
10681da177e4SLinus Torvalds 	uint16_t proto;
10691da177e4SLinus Torvalds 	int err;
10701da177e4SLinus Torvalds 
10711da177e4SLinus Torvalds 
1072ea110733SJoe Perches 	sa = ext_hdrs[SADB_EXT_SA - 1];
10731da177e4SLinus Torvalds 	if (!sa ||
10741da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
10751da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
10761da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10771da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_ESP &&
10781da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_ENCRYPT-1])
10791da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10801da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_AH &&
10811da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_AUTH-1])
10821da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10831da177e4SLinus Torvalds 	if (!!ext_hdrs[SADB_EXT_LIFETIME_HARD-1] !=
10841da177e4SLinus Torvalds 	    !!ext_hdrs[SADB_EXT_LIFETIME_SOFT-1])
10851da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
10881da177e4SLinus Torvalds 	if (proto == 0)
10891da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	/* default error is no buffer space */
10921da177e4SLinus Torvalds 	err = -ENOBUFS;
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	/* RFC2367:
10951da177e4SLinus Torvalds 
10961da177e4SLinus Torvalds    Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_ADD message.
10971da177e4SLinus Torvalds    SADB_SASTATE_LARVAL SAs are created by SADB_GETSPI and it is not
10981da177e4SLinus Torvalds    sensible to add a new SA in the DYING or SADB_SASTATE_DEAD state.
10991da177e4SLinus Torvalds    Therefore, the sadb_sa_state field of all submitted SAs MUST be
11001da177e4SLinus Torvalds    SADB_SASTATE_MATURE and the kernel MUST return an error if this is
11011da177e4SLinus Torvalds    not true.
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 	   However, KAME setkey always uses SADB_SASTATE_LARVAL.
11041da177e4SLinus Torvalds 	   Hence, we have to _ignore_ sadb_sa_state, which is also reasonable.
11051da177e4SLinus Torvalds 	 */
11061da177e4SLinus Torvalds 	if (sa->sadb_sa_auth > SADB_AALG_MAX ||
11071da177e4SLinus Torvalds 	    (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP &&
11081da177e4SLinus Torvalds 	     sa->sadb_sa_encrypt > SADB_X_CALG_MAX) ||
11091da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt > SADB_EALG_MAX)
11101da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
1111ea110733SJoe Perches 	key = ext_hdrs[SADB_EXT_KEY_AUTH - 1];
11121da177e4SLinus Torvalds 	if (key != NULL &&
11131da177e4SLinus Torvalds 	    sa->sadb_sa_auth != SADB_X_AALG_NULL &&
11144b66af2dSKevin Easton 	    key->sadb_key_bits == 0)
11151da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
11161da177e4SLinus Torvalds 	key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
11171da177e4SLinus Torvalds 	if (key != NULL &&
11181da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt != SADB_EALG_NULL &&
11194b66af2dSKevin Easton 	    key->sadb_key_bits == 0)
11201da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
11211da177e4SLinus Torvalds 
112207fb0f17SAlexey Dobriyan 	x = xfrm_state_alloc(net);
11231da177e4SLinus Torvalds 	if (x == NULL)
11241da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	x->id.proto = proto;
11271da177e4SLinus Torvalds 	x->id.spi = sa->sadb_sa_spi;
112833fce60dSFan Du 	x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay,
112933fce60dSFan Du 					(sizeof(x->replay.bitmap) * 8));
11301da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
11311da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_NOECN;
11321da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
11331da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_DECAP_DSCP;
1134dd87147eSHerbert Xu 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
1135dd87147eSHerbert Xu 		x->props.flags |= XFRM_STATE_NOPMTUDISC;
11361da177e4SLinus Torvalds 
1137ea110733SJoe Perches 	lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1];
11381da177e4SLinus Torvalds 	if (lifetime != NULL) {
11391da177e4SLinus Torvalds 		x->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11401da177e4SLinus Torvalds 		x->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11411da177e4SLinus Torvalds 		x->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11421da177e4SLinus Torvalds 		x->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11431da177e4SLinus Torvalds 	}
1144ea110733SJoe Perches 	lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT - 1];
11451da177e4SLinus Torvalds 	if (lifetime != NULL) {
11461da177e4SLinus Torvalds 		x->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11471da177e4SLinus Torvalds 		x->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11481da177e4SLinus Torvalds 		x->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11491da177e4SLinus Torvalds 		x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11501da177e4SLinus Torvalds 	}
1151df71837dSTrent Jaeger 
1152ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
1153df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
115487536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
1155df71837dSTrent Jaeger 
1156df71837dSTrent Jaeger 		if (!uctx)
1157df71837dSTrent Jaeger 			goto out;
1158df71837dSTrent Jaeger 
1159df71837dSTrent Jaeger 		err = security_xfrm_state_alloc(x, uctx);
1160df71837dSTrent Jaeger 		kfree(uctx);
1161df71837dSTrent Jaeger 
1162df71837dSTrent Jaeger 		if (err)
1163df71837dSTrent Jaeger 			goto out;
1164df71837dSTrent Jaeger 	}
1165df71837dSTrent Jaeger 
1166e747f643SDan Carpenter 	err = -ENOBUFS;
1167ea110733SJoe Perches 	key = ext_hdrs[SADB_EXT_KEY_AUTH - 1];
11681da177e4SLinus Torvalds 	if (sa->sadb_sa_auth) {
11691da177e4SLinus Torvalds 		int keysize = 0;
11701da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth);
11717e50f84cSJussi Kivilinna 		if (!a || !a->pfkey_supported) {
11721da177e4SLinus Torvalds 			err = -ENOSYS;
11731da177e4SLinus Torvalds 			goto out;
11741da177e4SLinus Torvalds 		}
11751da177e4SLinus Torvalds 		if (key)
11761da177e4SLinus Torvalds 			keysize = (key->sadb_key_bits + 7) / 8;
11771da177e4SLinus Torvalds 		x->aalg = kmalloc(sizeof(*x->aalg) + keysize, GFP_KERNEL);
11781e3d0c2cSDan Carpenter 		if (!x->aalg) {
11791e3d0c2cSDan Carpenter 			err = -ENOMEM;
11801da177e4SLinus Torvalds 			goto out;
11811e3d0c2cSDan Carpenter 		}
11821da177e4SLinus Torvalds 		strcpy(x->aalg->alg_name, a->name);
11831da177e4SLinus Torvalds 		x->aalg->alg_key_len = 0;
11841da177e4SLinus Torvalds 		if (key) {
11851da177e4SLinus Torvalds 			x->aalg->alg_key_len = key->sadb_key_bits;
11861da177e4SLinus Torvalds 			memcpy(x->aalg->alg_key, key+1, keysize);
11871da177e4SLinus Torvalds 		}
1188c20a66f4SMartin Willi 		x->aalg->alg_trunc_len = a->uinfo.auth.icv_truncbits;
11891da177e4SLinus Torvalds 		x->props.aalgo = sa->sadb_sa_auth;
11901da177e4SLinus Torvalds 		/* x->algo.flags = sa->sadb_sa_flags; */
11911da177e4SLinus Torvalds 	}
11921da177e4SLinus Torvalds 	if (sa->sadb_sa_encrypt) {
11931da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) {
11941da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt);
11957e50f84cSJussi Kivilinna 			if (!a || !a->pfkey_supported) {
11961da177e4SLinus Torvalds 				err = -ENOSYS;
11971da177e4SLinus Torvalds 				goto out;
11981da177e4SLinus Torvalds 			}
11991da177e4SLinus Torvalds 			x->calg = kmalloc(sizeof(*x->calg), GFP_KERNEL);
12001e3d0c2cSDan Carpenter 			if (!x->calg) {
12011e3d0c2cSDan Carpenter 				err = -ENOMEM;
12021da177e4SLinus Torvalds 				goto out;
12031e3d0c2cSDan Carpenter 			}
12041da177e4SLinus Torvalds 			strcpy(x->calg->alg_name, a->name);
12051da177e4SLinus Torvalds 			x->props.calgo = sa->sadb_sa_encrypt;
12061da177e4SLinus Torvalds 		} else {
12071da177e4SLinus Torvalds 			int keysize = 0;
12081da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt);
12097e50f84cSJussi Kivilinna 			if (!a || !a->pfkey_supported) {
12101da177e4SLinus Torvalds 				err = -ENOSYS;
12111da177e4SLinus Torvalds 				goto out;
12121da177e4SLinus Torvalds 			}
12131da177e4SLinus Torvalds 			key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
12141da177e4SLinus Torvalds 			if (key)
12151da177e4SLinus Torvalds 				keysize = (key->sadb_key_bits + 7) / 8;
12161da177e4SLinus Torvalds 			x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL);
12171e3d0c2cSDan Carpenter 			if (!x->ealg) {
12181e3d0c2cSDan Carpenter 				err = -ENOMEM;
12191da177e4SLinus Torvalds 				goto out;
12201e3d0c2cSDan Carpenter 			}
12211da177e4SLinus Torvalds 			strcpy(x->ealg->alg_name, a->name);
12221da177e4SLinus Torvalds 			x->ealg->alg_key_len = 0;
12231da177e4SLinus Torvalds 			if (key) {
12241da177e4SLinus Torvalds 				x->ealg->alg_key_len = key->sadb_key_bits;
12251da177e4SLinus Torvalds 				memcpy(x->ealg->alg_key, key+1, keysize);
12261da177e4SLinus Torvalds 			}
12271da177e4SLinus Torvalds 			x->props.ealgo = sa->sadb_sa_encrypt;
122869b0137fSHerbert Xu 			x->geniv = a->uinfo.encr.geniv;
12291da177e4SLinus Torvalds 		}
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds 	/* x->algo.flags = sa->sadb_sa_flags; */
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 	x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
12341da177e4SLinus Torvalds 						    &x->props.saddr);
12351da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1],
12361da177e4SLinus Torvalds 				  &x->id.daddr);
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_SA2-1]) {
12394c93fbb0SDavid S. Miller 		const struct sadb_x_sa2 *sa2 = ext_hdrs[SADB_X_EXT_SA2-1];
124055569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
124155569ce2SKazunori MIYAZAWA 		if (mode < 0) {
124255569ce2SKazunori MIYAZAWA 			err = -EINVAL;
124355569ce2SKazunori MIYAZAWA 			goto out;
124455569ce2SKazunori MIYAZAWA 		}
124555569ce2SKazunori MIYAZAWA 		x->props.mode = mode;
12461da177e4SLinus Torvalds 		x->props.reqid = sa2->sadb_x_sa2_reqid;
12471da177e4SLinus Torvalds 	}
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 	if (ext_hdrs[SADB_EXT_ADDRESS_PROXY-1]) {
12504c93fbb0SDavid S. Miller 		const struct sadb_address *addr = ext_hdrs[SADB_EXT_ADDRESS_PROXY-1];
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 		/* Nobody uses this, but we try. */
12531da177e4SLinus Torvalds 		x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
12541da177e4SLinus Torvalds 		x->sel.prefixlen_s = addr->sadb_address_prefixlen;
12551da177e4SLinus Torvalds 	}
12561da177e4SLinus Torvalds 
12574da51056SKazunori MIYAZAWA 	if (!x->sel.family)
12584a4b6271SJoy Latten 		x->sel.family = x->props.family;
12594a4b6271SJoy Latten 
12601da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
12614c93fbb0SDavid S. Miller 		const struct sadb_x_nat_t_type* n_type;
12621da177e4SLinus Torvalds 		struct xfrm_encap_tmpl *natt;
12631da177e4SLinus Torvalds 
12642f479651SHyunwoo Kim 		x->encap = kzalloc(sizeof(*x->encap), GFP_KERNEL);
12651e3d0c2cSDan Carpenter 		if (!x->encap) {
12661e3d0c2cSDan Carpenter 			err = -ENOMEM;
12671da177e4SLinus Torvalds 			goto out;
12681e3d0c2cSDan Carpenter 		}
12691da177e4SLinus Torvalds 
12701da177e4SLinus Torvalds 		natt = x->encap;
12711da177e4SLinus Torvalds 		n_type = ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1];
12721da177e4SLinus Torvalds 		natt->encap_type = n_type->sadb_x_nat_t_type_type;
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1]) {
12754c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12761da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1];
12771da177e4SLinus Torvalds 			natt->encap_sport = n_port->sadb_x_nat_t_port_port;
12781da177e4SLinus Torvalds 		}
12791da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1]) {
12804c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12811da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1];
12821da177e4SLinus Torvalds 			natt->encap_dport = n_port->sadb_x_nat_t_port_port;
12831da177e4SLinus Torvalds 		}
12841da177e4SLinus Torvalds 	}
12851da177e4SLinus Torvalds 
128672cb6962SHerbert Xu 	err = xfrm_init_state(x);
128772cb6962SHerbert Xu 	if (err)
12881da177e4SLinus Torvalds 		goto out;
128972cb6962SHerbert Xu 
12901da177e4SLinus Torvalds 	x->km.seq = hdr->sadb_msg_seq;
12911da177e4SLinus Torvalds 	return x;
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds out:
12941da177e4SLinus Torvalds 	x->km.state = XFRM_STATE_DEAD;
12951da177e4SLinus Torvalds 	xfrm_state_put(x);
12961da177e4SLinus Torvalds 	return ERR_PTR(err);
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds 
pfkey_reserved(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)12994c93fbb0SDavid S. Miller static int pfkey_reserved(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
13001da177e4SLinus Torvalds {
13011da177e4SLinus Torvalds 	return -EOPNOTSUPP;
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds 
pfkey_getspi(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)13044c93fbb0SDavid S. Miller static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
13051da177e4SLinus Torvalds {
130607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
13071da177e4SLinus Torvalds 	struct sk_buff *resp_skb;
13081da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
13091da177e4SLinus Torvalds 	struct sadb_address *saddr, *daddr;
13101da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
1311658b219eSHerbert Xu 	struct sadb_spirange *range;
13121da177e4SLinus Torvalds 	struct xfrm_state *x = NULL;
131355569ce2SKazunori MIYAZAWA 	int mode;
1314658b219eSHerbert Xu 	int err;
1315658b219eSHerbert Xu 	u32 min_spi, max_spi;
13161da177e4SLinus Torvalds 	u32 reqid;
13171da177e4SLinus Torvalds 	u8 proto;
13181da177e4SLinus Torvalds 	unsigned short family;
13191da177e4SLinus Torvalds 	xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
13221da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
13231da177e4SLinus Torvalds 		return -EINVAL;
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
13261da177e4SLinus Torvalds 	if (proto == 0)
13271da177e4SLinus Torvalds 		return -EINVAL;
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 	if ((sa2 = ext_hdrs[SADB_X_EXT_SA2-1]) != NULL) {
133055569ce2SKazunori MIYAZAWA 		mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
133155569ce2SKazunori MIYAZAWA 		if (mode < 0)
133255569ce2SKazunori MIYAZAWA 			return -EINVAL;
13331da177e4SLinus Torvalds 		reqid = sa2->sadb_x_sa2_reqid;
13341da177e4SLinus Torvalds 	} else {
13351da177e4SLinus Torvalds 		mode = 0;
13361da177e4SLinus Torvalds 		reqid = 0;
13371da177e4SLinus Torvalds 	}
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 	saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
13401da177e4SLinus Torvalds 	daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	family = ((struct sockaddr *)(saddr + 1))->sa_family;
13431da177e4SLinus Torvalds 	switch (family) {
13441da177e4SLinus Torvalds 	case AF_INET:
13451da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
13461da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
13471da177e4SLinus Torvalds 		break;
1348dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
13491da177e4SLinus Torvalds 	case AF_INET6:
13501da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
13511da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
13521da177e4SLinus Torvalds 		break;
13531da177e4SLinus Torvalds #endif
13541da177e4SLinus Torvalds 	}
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq) {
1357bd55775cSJamal Hadi Salim 		x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
135870e94e66SYOSHIFUJI Hideaki / 吉藤英明 		if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) {
13591da177e4SLinus Torvalds 			xfrm_state_put(x);
13601da177e4SLinus Torvalds 			x = NULL;
13611da177e4SLinus Torvalds 		}
13621da177e4SLinus Torvalds 	}
13631da177e4SLinus Torvalds 
13641da177e4SLinus Torvalds 	if (!x)
13657e652640SSteffen Klassert 		x = xfrm_find_acq(net, &dummy_mark, mode, reqid, 0, proto, xdaddr, xsaddr, 1, family);
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	if (x == NULL)
13681da177e4SLinus Torvalds 		return -ENOENT;
13691da177e4SLinus Torvalds 
13701da177e4SLinus Torvalds 	min_spi = 0x100;
13711da177e4SLinus Torvalds 	max_spi = 0x0fffffff;
1372658b219eSHerbert Xu 
1373658b219eSHerbert Xu 	range = ext_hdrs[SADB_EXT_SPIRANGE-1];
1374658b219eSHerbert Xu 	if (range) {
1375658b219eSHerbert Xu 		min_spi = range->sadb_spirange_min;
1376658b219eSHerbert Xu 		max_spi = range->sadb_spirange_max;
13771da177e4SLinus Torvalds 	}
1378658b219eSHerbert Xu 
1379c2dad11eSSabrina Dubroca 	err = verify_spi_info(x->id.proto, min_spi, max_spi, NULL);
1380776e9dd9SFan Du 	if (err) {
1381776e9dd9SFan Du 		xfrm_state_put(x);
1382776e9dd9SFan Du 		return err;
1383776e9dd9SFan Du 	}
1384776e9dd9SFan Du 
1385c2dad11eSSabrina Dubroca 	err = xfrm_alloc_spi(x, min_spi, max_spi, NULL);
1386050f009eSHerbert Xu 	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x);
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds 	if (IS_ERR(resp_skb)) {
13891da177e4SLinus Torvalds 		xfrm_state_put(x);
13901da177e4SLinus Torvalds 		return  PTR_ERR(resp_skb);
13911da177e4SLinus Torvalds 	}
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) resp_skb->data;
13941da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
13951da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_GETSPI;
13961da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
13971da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
13981da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
13991da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
14001da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds 	xfrm_state_put(x);
14031da177e4SLinus Torvalds 
140436f41f8fSEric Dumazet 	pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds 	return 0;
14071da177e4SLinus Torvalds }
14081da177e4SLinus Torvalds 
pfkey_acquire(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)14094c93fbb0SDavid S. Miller static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
14101da177e4SLinus Torvalds {
141107fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
14121da177e4SLinus Torvalds 	struct xfrm_state *x;
14131da177e4SLinus Torvalds 
14141da177e4SLinus Torvalds 	if (hdr->sadb_msg_len != sizeof(struct sadb_msg)/8)
14151da177e4SLinus Torvalds 		return -EOPNOTSUPP;
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
14181da177e4SLinus Torvalds 		return 0;
14191da177e4SLinus Torvalds 
1420bd55775cSJamal Hadi Salim 	x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
14211da177e4SLinus Torvalds 	if (x == NULL)
14221da177e4SLinus Torvalds 		return 0;
14231da177e4SLinus Torvalds 
14241da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
14255b8ef341SSteffen Klassert 	if (x->km.state == XFRM_STATE_ACQ)
14261da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_ERROR;
14275b8ef341SSteffen Klassert 
14281da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
14291da177e4SLinus Torvalds 	xfrm_state_put(x);
14301da177e4SLinus Torvalds 	return 0;
14311da177e4SLinus Torvalds }
14321da177e4SLinus Torvalds 
event2poltype(int event)143326b15dadSJamal Hadi Salim static inline int event2poltype(int event)
143426b15dadSJamal Hadi Salim {
143526b15dadSJamal Hadi Salim 	switch (event) {
1436f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
143726b15dadSJamal Hadi Salim 		return SADB_X_SPDDELETE;
1438f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
143926b15dadSJamal Hadi Salim 		return SADB_X_SPDADD;
1440f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
144126b15dadSJamal Hadi Salim 		return SADB_X_SPDUPDATE;
1442f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
144326b15dadSJamal Hadi Salim 	//	return SADB_X_SPDEXPIRE;
144426b15dadSJamal Hadi Salim 	default:
1445207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", event);
144626b15dadSJamal Hadi Salim 		break;
144726b15dadSJamal Hadi Salim 	}
144826b15dadSJamal Hadi Salim 
144926b15dadSJamal Hadi Salim 	return 0;
145026b15dadSJamal Hadi Salim }
145126b15dadSJamal Hadi Salim 
event2keytype(int event)145226b15dadSJamal Hadi Salim static inline int event2keytype(int event)
145326b15dadSJamal Hadi Salim {
145426b15dadSJamal Hadi Salim 	switch (event) {
1455f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
145626b15dadSJamal Hadi Salim 		return SADB_DELETE;
1457f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
145826b15dadSJamal Hadi Salim 		return SADB_ADD;
1459f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
146026b15dadSJamal Hadi Salim 		return SADB_UPDATE;
1461f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
146226b15dadSJamal Hadi Salim 		return SADB_EXPIRE;
146326b15dadSJamal Hadi Salim 	default:
1464207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", event);
146526b15dadSJamal Hadi Salim 		break;
146626b15dadSJamal Hadi Salim 	}
146726b15dadSJamal Hadi Salim 
146826b15dadSJamal Hadi Salim 	return 0;
146926b15dadSJamal Hadi Salim }
147026b15dadSJamal Hadi Salim 
147126b15dadSJamal Hadi Salim /* ADD/UPD/DEL */
key_notify_sa(struct xfrm_state * x,const struct km_event * c)1472214e005bSDavid S. Miller static int key_notify_sa(struct xfrm_state *x, const struct km_event *c)
147326b15dadSJamal Hadi Salim {
147426b15dadSJamal Hadi Salim 	struct sk_buff *skb;
147526b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
147626b15dadSJamal Hadi Salim 
1477050f009eSHerbert Xu 	skb = pfkey_xfrm_state2msg(x);
147826b15dadSJamal Hadi Salim 
147926b15dadSJamal Hadi Salim 	if (IS_ERR(skb))
148026b15dadSJamal Hadi Salim 		return PTR_ERR(skb);
148126b15dadSJamal Hadi Salim 
148226b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb->data;
148326b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
148426b15dadSJamal Hadi Salim 	hdr->sadb_msg_type = event2keytype(c->event);
148526b15dadSJamal Hadi Salim 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
148626b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = 0;
148726b15dadSJamal Hadi Salim 	hdr->sadb_msg_reserved = 0;
148826b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
148915e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
149026b15dadSJamal Hadi Salim 
149136f41f8fSEric Dumazet 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
149226b15dadSJamal Hadi Salim 
149326b15dadSJamal Hadi Salim 	return 0;
149426b15dadSJamal Hadi Salim }
14951da177e4SLinus Torvalds 
pfkey_add(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)14964c93fbb0SDavid S. Miller static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
14971da177e4SLinus Torvalds {
149807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
14991da177e4SLinus Torvalds 	struct xfrm_state *x;
15001da177e4SLinus Torvalds 	int err;
150126b15dadSJamal Hadi Salim 	struct km_event c;
15021da177e4SLinus Torvalds 
150307fb0f17SAlexey Dobriyan 	x = pfkey_msg2xfrm_state(net, hdr, ext_hdrs);
15041da177e4SLinus Torvalds 	if (IS_ERR(x))
15051da177e4SLinus Torvalds 		return PTR_ERR(x);
15061da177e4SLinus Torvalds 
150726b15dadSJamal Hadi Salim 	xfrm_state_hold(x);
15081da177e4SLinus Torvalds 	if (hdr->sadb_msg_type == SADB_ADD)
15091da177e4SLinus Torvalds 		err = xfrm_state_add(x);
15101da177e4SLinus Torvalds 	else
15111da177e4SLinus Torvalds 		err = xfrm_state_update(x);
15121da177e4SLinus Torvalds 
15132e71029eSTetsuo Handa 	xfrm_audit_state_add(x, err ? 0 : 1, true);
1514161a09e7SJoy Latten 
15151da177e4SLinus Torvalds 	if (err < 0) {
15161da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
151721380b81SHerbert Xu 		__xfrm_state_put(x);
15187d6dfe1fSPatrick McHardy 		goto out;
15191da177e4SLinus Torvalds 	}
15201da177e4SLinus Torvalds 
152126b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_ADD)
1522f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWSA;
152326b15dadSJamal Hadi Salim 	else
1524f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDSA;
152526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
152615e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
152726b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
15287d6dfe1fSPatrick McHardy out:
152926b15dadSJamal Hadi Salim 	xfrm_state_put(x);
153026b15dadSJamal Hadi Salim 	return err;
15311da177e4SLinus Torvalds }
15321da177e4SLinus Torvalds 
pfkey_delete(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)15334c93fbb0SDavid S. Miller static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
15341da177e4SLinus Torvalds {
153507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
15361da177e4SLinus Torvalds 	struct xfrm_state *x;
153726b15dadSJamal Hadi Salim 	struct km_event c;
153826b15dadSJamal Hadi Salim 	int err;
15391da177e4SLinus Torvalds 
15401da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15411da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15421da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15431da177e4SLinus Torvalds 		return -EINVAL;
15441da177e4SLinus Torvalds 
154507fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15461da177e4SLinus Torvalds 	if (x == NULL)
15471da177e4SLinus Torvalds 		return -ESRCH;
15481da177e4SLinus Torvalds 
1549c8c05a8eSCatherine Zhang 	if ((err = security_xfrm_state_delete(x)))
1550c8c05a8eSCatherine Zhang 		goto out;
1551c8c05a8eSCatherine Zhang 
15521da177e4SLinus Torvalds 	if (xfrm_state_kern(x)) {
1553c8c05a8eSCatherine Zhang 		err = -EPERM;
1554c8c05a8eSCatherine Zhang 		goto out;
15551da177e4SLinus Torvalds 	}
15561da177e4SLinus Torvalds 
155726b15dadSJamal Hadi Salim 	err = xfrm_state_delete(x);
1558161a09e7SJoy Latten 
1559c8c05a8eSCatherine Zhang 	if (err < 0)
1560c8c05a8eSCatherine Zhang 		goto out;
156126b15dadSJamal Hadi Salim 
156226b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
156315e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
1564f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELSA;
156526b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
1566c8c05a8eSCatherine Zhang out:
15672e71029eSTetsuo Handa 	xfrm_audit_state_delete(x, err ? 0 : 1, true);
15681da177e4SLinus Torvalds 	xfrm_state_put(x);
15691da177e4SLinus Torvalds 
157026b15dadSJamal Hadi Salim 	return err;
15711da177e4SLinus Torvalds }
15721da177e4SLinus Torvalds 
pfkey_get(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)15734c93fbb0SDavid S. Miller static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
15741da177e4SLinus Torvalds {
157507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
15761da177e4SLinus Torvalds 	__u8 proto;
15771da177e4SLinus Torvalds 	struct sk_buff *out_skb;
15781da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
15791da177e4SLinus Torvalds 	struct xfrm_state *x;
15801da177e4SLinus Torvalds 
15811da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15821da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15831da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15841da177e4SLinus Torvalds 		return -EINVAL;
15851da177e4SLinus Torvalds 
158607fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15871da177e4SLinus Torvalds 	if (x == NULL)
15881da177e4SLinus Torvalds 		return -ESRCH;
15891da177e4SLinus Torvalds 
1590050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
15911da177e4SLinus Torvalds 	proto = x->id.proto;
15921da177e4SLinus Torvalds 	xfrm_state_put(x);
15931da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
15941da177e4SLinus Torvalds 		return  PTR_ERR(out_skb);
15951da177e4SLinus Torvalds 
15961da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
15971da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
1598435000beSCharles Hardin 	out_hdr->sadb_msg_type = SADB_GET;
15991da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
16001da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
16011da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
16021da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
16031da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
160436f41f8fSEric Dumazet 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
16051da177e4SLinus Torvalds 
16061da177e4SLinus Torvalds 	return 0;
16071da177e4SLinus Torvalds }
16081da177e4SLinus Torvalds 
compose_sadb_supported(const struct sadb_msg * orig,gfp_t allocation)16094c93fbb0SDavid S. Miller static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig,
1610dd0fc66fSAl Viro 					      gfp_t allocation)
16111da177e4SLinus Torvalds {
16121da177e4SLinus Torvalds 	struct sk_buff *skb;
16131da177e4SLinus Torvalds 	struct sadb_msg *hdr;
16141da177e4SLinus Torvalds 	int len, auth_len, enc_len, i;
16151da177e4SLinus Torvalds 
16167e50f84cSJussi Kivilinna 	auth_len = xfrm_count_pfkey_auth_supported();
16171da177e4SLinus Torvalds 	if (auth_len) {
16181da177e4SLinus Torvalds 		auth_len *= sizeof(struct sadb_alg);
16191da177e4SLinus Torvalds 		auth_len += sizeof(struct sadb_supported);
16201da177e4SLinus Torvalds 	}
16211da177e4SLinus Torvalds 
16227e50f84cSJussi Kivilinna 	enc_len = xfrm_count_pfkey_enc_supported();
16231da177e4SLinus Torvalds 	if (enc_len) {
16241da177e4SLinus Torvalds 		enc_len *= sizeof(struct sadb_alg);
16251da177e4SLinus Torvalds 		enc_len += sizeof(struct sadb_supported);
16261da177e4SLinus Torvalds 	}
16271da177e4SLinus Torvalds 
16281da177e4SLinus Torvalds 	len = enc_len + auth_len + sizeof(struct sadb_msg);
16291da177e4SLinus Torvalds 
16301da177e4SLinus Torvalds 	skb = alloc_skb(len + 16, allocation);
16311da177e4SLinus Torvalds 	if (!skb)
16321da177e4SLinus Torvalds 		goto out_put_algs;
16331da177e4SLinus Torvalds 
16344df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(*hdr));
16351da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
16361da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
16371da177e4SLinus Torvalds 	hdr->sadb_msg_len = len / sizeof(uint64_t);
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds 	if (auth_len) {
16401da177e4SLinus Torvalds 		struct sadb_supported *sp;
16411da177e4SLinus Torvalds 		struct sadb_alg *ap;
16421da177e4SLinus Torvalds 
16434df864c1SJohannes Berg 		sp = skb_put(skb, auth_len);
16441da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16451da177e4SLinus Torvalds 
16461da177e4SLinus Torvalds 		sp->sadb_supported_len = auth_len / sizeof(uint64_t);
16471da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
16481da177e4SLinus Torvalds 
16491da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16501da177e4SLinus Torvalds 			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
16511da177e4SLinus Torvalds 			if (!aalg)
16521da177e4SLinus Torvalds 				break;
16537e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
16547e50f84cSJussi Kivilinna 				continue;
16551da177e4SLinus Torvalds 			if (aalg->available)
16561da177e4SLinus Torvalds 				*ap++ = aalg->desc;
16571da177e4SLinus Torvalds 		}
16581da177e4SLinus Torvalds 	}
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 	if (enc_len) {
16611da177e4SLinus Torvalds 		struct sadb_supported *sp;
16621da177e4SLinus Torvalds 		struct sadb_alg *ap;
16631da177e4SLinus Torvalds 
16644df864c1SJohannes Berg 		sp = skb_put(skb, enc_len);
16651da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16661da177e4SLinus Torvalds 
16671da177e4SLinus Torvalds 		sp->sadb_supported_len = enc_len / sizeof(uint64_t);
16681da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
16691da177e4SLinus Torvalds 
16701da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16711da177e4SLinus Torvalds 			struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
16721da177e4SLinus Torvalds 			if (!ealg)
16731da177e4SLinus Torvalds 				break;
16747e50f84cSJussi Kivilinna 			if (!ealg->pfkey_supported)
16757e50f84cSJussi Kivilinna 				continue;
16761da177e4SLinus Torvalds 			if (ealg->available)
16771da177e4SLinus Torvalds 				*ap++ = ealg->desc;
16781da177e4SLinus Torvalds 		}
16791da177e4SLinus Torvalds 	}
16801da177e4SLinus Torvalds 
16811da177e4SLinus Torvalds out_put_algs:
16821da177e4SLinus Torvalds 	return skb;
16831da177e4SLinus Torvalds }
16841da177e4SLinus Torvalds 
pfkey_register(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)16854c93fbb0SDavid S. Miller static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
16861da177e4SLinus Torvalds {
16871da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
16881da177e4SLinus Torvalds 	struct sk_buff *supp_skb;
16891da177e4SLinus Torvalds 
16901da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype > SADB_SATYPE_MAX)
16911da177e4SLinus Torvalds 		return -EINVAL;
16921da177e4SLinus Torvalds 
16931da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) {
16941da177e4SLinus Torvalds 		if (pfk->registered&(1<<hdr->sadb_msg_satype))
16951da177e4SLinus Torvalds 			return -EEXIST;
16961da177e4SLinus Torvalds 		pfk->registered |= (1<<hdr->sadb_msg_satype);
16971da177e4SLinus Torvalds 	}
16981da177e4SLinus Torvalds 
1699ba953a9dSHerbert Xu 	mutex_lock(&pfkey_mutex);
17001da177e4SLinus Torvalds 	xfrm_probe_algs();
17011da177e4SLinus Torvalds 
17029a564bccSHaimin Zhang 	supp_skb = compose_sadb_supported(hdr, GFP_KERNEL | __GFP_ZERO);
1703ba953a9dSHerbert Xu 	mutex_unlock(&pfkey_mutex);
1704ba953a9dSHerbert Xu 
17051da177e4SLinus Torvalds 	if (!supp_skb) {
17061da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
17071da177e4SLinus Torvalds 			pfk->registered &= ~(1<<hdr->sadb_msg_satype);
17081da177e4SLinus Torvalds 
17091da177e4SLinus Torvalds 		return -ENOBUFS;
17101da177e4SLinus Torvalds 	}
17111da177e4SLinus Torvalds 
171236f41f8fSEric Dumazet 	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk,
171336f41f8fSEric Dumazet 			sock_net(sk));
17141da177e4SLinus Torvalds 	return 0;
17151da177e4SLinus Torvalds }
17161da177e4SLinus Torvalds 
unicast_flush_resp(struct sock * sk,const struct sadb_msg * ihdr)17174c93fbb0SDavid S. Miller static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr)
17188be987d7SJamal Hadi Salim {
17198be987d7SJamal Hadi Salim 	struct sk_buff *skb;
17208be987d7SJamal Hadi Salim 	struct sadb_msg *hdr;
17218be987d7SJamal Hadi Salim 
17228be987d7SJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
17238be987d7SJamal Hadi Salim 	if (!skb)
17248be987d7SJamal Hadi Salim 		return -ENOBUFS;
17258be987d7SJamal Hadi Salim 
172659ae1d12SJohannes Berg 	hdr = skb_put_data(skb, ihdr, sizeof(struct sadb_msg));
17278be987d7SJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
17288be987d7SJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
17298be987d7SJamal Hadi Salim 
173036f41f8fSEric Dumazet 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk,
173136f41f8fSEric Dumazet 			       sock_net(sk));
17328be987d7SJamal Hadi Salim }
17338be987d7SJamal Hadi Salim 
key_notify_sa_flush(const struct km_event * c)1734214e005bSDavid S. Miller static int key_notify_sa_flush(const struct km_event *c)
173526b15dadSJamal Hadi Salim {
173626b15dadSJamal Hadi Salim 	struct sk_buff *skb;
173726b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
173826b15dadSJamal Hadi Salim 
173926b15dadSJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
174026b15dadSJamal Hadi Salim 	if (!skb)
174126b15dadSJamal Hadi Salim 		return -ENOBUFS;
17424df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
1743bf08867fSHerbert Xu 	hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto);
1744151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_FLUSH;
174526b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
174615e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
174726b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
174826b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
174926b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
1750a5cc68f3SMathias Krause 	hdr->sadb_msg_reserved = 0;
175126b15dadSJamal Hadi Salim 
175236f41f8fSEric Dumazet 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
175326b15dadSJamal Hadi Salim 
175426b15dadSJamal Hadi Salim 	return 0;
175526b15dadSJamal Hadi Salim }
175626b15dadSJamal Hadi Salim 
pfkey_flush(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)17574c93fbb0SDavid S. Miller static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
17581da177e4SLinus Torvalds {
175907fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
176095c96174SEric Dumazet 	unsigned int proto;
176126b15dadSJamal Hadi Salim 	struct km_event c;
17628be987d7SJamal Hadi Salim 	int err, err2;
17631da177e4SLinus Torvalds 
17641da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
17651da177e4SLinus Torvalds 	if (proto == 0)
17661da177e4SLinus Torvalds 		return -EINVAL;
17671da177e4SLinus Torvalds 
1768f75a2804SCong Wang 	err = xfrm_state_flush(net, proto, true, false);
17698be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
17709e64cc95SJamal Hadi Salim 	if (err || err2) {
17719e64cc95SJamal Hadi Salim 		if (err == -ESRCH) /* empty table - go quietly */
17729e64cc95SJamal Hadi Salim 			err = 0;
17738be987d7SJamal Hadi Salim 		return err ? err : err2;
17749e64cc95SJamal Hadi Salim 	}
17758be987d7SJamal Hadi Salim 
1776bf08867fSHerbert Xu 	c.data.proto = proto;
177726b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
177815e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
1779f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHSA;
178007fb0f17SAlexey Dobriyan 	c.net = net;
178126b15dadSJamal Hadi Salim 	km_state_notify(NULL, &c);
17821da177e4SLinus Torvalds 
17831da177e4SLinus Torvalds 	return 0;
17841da177e4SLinus Torvalds }
17851da177e4SLinus Torvalds 
dump_sa(struct xfrm_state * x,int count,void * ptr)17861da177e4SLinus Torvalds static int dump_sa(struct xfrm_state *x, int count, void *ptr)
17871da177e4SLinus Torvalds {
178883321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
17891da177e4SLinus Torvalds 	struct sk_buff *out_skb;
17901da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
17911da177e4SLinus Torvalds 
179283321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
179383321d6bSTimo Teras 		return -ENOBUFS;
179483321d6bSTimo Teras 
1795050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
17961da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
17971da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
17981da177e4SLinus Torvalds 
17991da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
180083321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
18011da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_DUMP;
18021da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
18031da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
18041da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
180512a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
180615e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
180712a169e7SHerbert Xu 
180812a169e7SHerbert Xu 	if (pfk->dump.skb)
180936f41f8fSEric Dumazet 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
181007fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
181112a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
181212a169e7SHerbert Xu 
18131da177e4SLinus Torvalds 	return 0;
18141da177e4SLinus Torvalds }
18151da177e4SLinus Torvalds 
pfkey_dump_sa(struct pfkey_sock * pfk)181683321d6bSTimo Teras static int pfkey_dump_sa(struct pfkey_sock *pfk)
181783321d6bSTimo Teras {
181807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
181907fb0f17SAlexey Dobriyan 	return xfrm_state_walk(net, &pfk->dump.u.state, dump_sa, (void *) pfk);
182083321d6bSTimo Teras }
182183321d6bSTimo Teras 
pfkey_dump_sa_done(struct pfkey_sock * pfk)182283321d6bSTimo Teras static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
182383321d6bSTimo Teras {
1824283bc9f3SFan Du 	struct net *net = sock_net(&pfk->sk);
1825283bc9f3SFan Du 
1826283bc9f3SFan Du 	xfrm_state_walk_done(&pfk->dump.u.state, net);
182783321d6bSTimo Teras }
182883321d6bSTimo Teras 
pfkey_dump(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)18294c93fbb0SDavid S. Miller static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
18301da177e4SLinus Torvalds {
18311da177e4SLinus Torvalds 	u8 proto;
1832870a2df4SNicolas Dichtel 	struct xfrm_address_filter *filter = NULL;
183383321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
183483321d6bSTimo Teras 
183589e357d8SYuejie Shi 	mutex_lock(&pfk->dump_lock);
183689e357d8SYuejie Shi 	if (pfk->dump.dump != NULL) {
183789e357d8SYuejie Shi 		mutex_unlock(&pfk->dump_lock);
183883321d6bSTimo Teras 		return -EBUSY;
183989e357d8SYuejie Shi 	}
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
184289e357d8SYuejie Shi 	if (proto == 0) {
184389e357d8SYuejie Shi 		mutex_unlock(&pfk->dump_lock);
18441da177e4SLinus Torvalds 		return -EINVAL;
184589e357d8SYuejie Shi 	}
18461da177e4SLinus Torvalds 
1847d3623099SNicolas Dichtel 	if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
1848d3623099SNicolas Dichtel 		struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
1849d3623099SNicolas Dichtel 
1850*75065a89SLin Ma 		if ((xfilter->sadb_x_filter_splen >
185137bd2242SMark Salyzyn 			(sizeof(xfrm_address_t) << 3)) ||
1852*75065a89SLin Ma 		    (xfilter->sadb_x_filter_dplen >
185337bd2242SMark Salyzyn 			(sizeof(xfrm_address_t) << 3))) {
185437bd2242SMark Salyzyn 			mutex_unlock(&pfk->dump_lock);
185537bd2242SMark Salyzyn 			return -EINVAL;
185637bd2242SMark Salyzyn 		}
1857d3623099SNicolas Dichtel 		filter = kmalloc(sizeof(*filter), GFP_KERNEL);
185889e357d8SYuejie Shi 		if (filter == NULL) {
185989e357d8SYuejie Shi 			mutex_unlock(&pfk->dump_lock);
1860d3623099SNicolas Dichtel 			return -ENOMEM;
186189e357d8SYuejie Shi 		}
1862d3623099SNicolas Dichtel 
1863d3623099SNicolas Dichtel 		memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
1864d3623099SNicolas Dichtel 		       sizeof(xfrm_address_t));
1865d3623099SNicolas Dichtel 		memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr,
1866d3623099SNicolas Dichtel 		       sizeof(xfrm_address_t));
1867d3623099SNicolas Dichtel 		filter->family = xfilter->sadb_x_filter_family;
1868d3623099SNicolas Dichtel 		filter->splen = xfilter->sadb_x_filter_splen;
1869d3623099SNicolas Dichtel 		filter->dplen = xfilter->sadb_x_filter_dplen;
1870d3623099SNicolas Dichtel 	}
1871d3623099SNicolas Dichtel 
187283321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
187315e47304SEric W. Biederman 	pfk->dump.msg_portid = hdr->sadb_msg_pid;
187483321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sa;
187583321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sa_done;
1876d3623099SNicolas Dichtel 	xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
187789e357d8SYuejie Shi 	mutex_unlock(&pfk->dump_lock);
18784c563f76STimo Teras 
187983321d6bSTimo Teras 	return pfkey_do_dump(pfk);
18801da177e4SLinus Torvalds }
18811da177e4SLinus Torvalds 
pfkey_promisc(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)18824c93fbb0SDavid S. Miller static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
18831da177e4SLinus Torvalds {
18841da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
18851da177e4SLinus Torvalds 	int satype = hdr->sadb_msg_satype;
18864c93fbb0SDavid S. Miller 	bool reset_errno = false;
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds 	if (hdr->sadb_msg_len == (sizeof(*hdr) / sizeof(uint64_t))) {
18894c93fbb0SDavid S. Miller 		reset_errno = true;
18901da177e4SLinus Torvalds 		if (satype != 0 && satype != 1)
18911da177e4SLinus Torvalds 			return -EINVAL;
18921da177e4SLinus Torvalds 		pfk->promisc = satype;
18931da177e4SLinus Torvalds 	}
18944c93fbb0SDavid S. Miller 	if (reset_errno && skb_cloned(skb))
18954c93fbb0SDavid S. Miller 		skb = skb_copy(skb, GFP_KERNEL);
18964c93fbb0SDavid S. Miller 	else
18974c93fbb0SDavid S. Miller 		skb = skb_clone(skb, GFP_KERNEL);
18984c93fbb0SDavid S. Miller 
18994c93fbb0SDavid S. Miller 	if (reset_errno && skb) {
19004c93fbb0SDavid S. Miller 		struct sadb_msg *new_hdr = (struct sadb_msg *) skb->data;
19014c93fbb0SDavid S. Miller 		new_hdr->sadb_msg_errno = 0;
19024c93fbb0SDavid S. Miller 	}
19034c93fbb0SDavid S. Miller 
190436f41f8fSEric Dumazet 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
19051da177e4SLinus Torvalds 	return 0;
19061da177e4SLinus Torvalds }
19071da177e4SLinus Torvalds 
check_reqid(struct xfrm_policy * xp,int dir,int count,void * ptr)19081da177e4SLinus Torvalds static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
19091da177e4SLinus Torvalds {
19101da177e4SLinus Torvalds 	int i;
19111da177e4SLinus Torvalds 	u32 reqid = *(u32*)ptr;
19121da177e4SLinus Torvalds 
19131da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
19141da177e4SLinus Torvalds 		if (xp->xfrm_vec[i].reqid == reqid)
19151da177e4SLinus Torvalds 			return -EEXIST;
19161da177e4SLinus Torvalds 	}
19171da177e4SLinus Torvalds 	return 0;
19181da177e4SLinus Torvalds }
19191da177e4SLinus Torvalds 
gen_reqid(struct net * net)192007fb0f17SAlexey Dobriyan static u32 gen_reqid(struct net *net)
19211da177e4SLinus Torvalds {
19224c563f76STimo Teras 	struct xfrm_policy_walk walk;
19231da177e4SLinus Torvalds 	u32 start;
19244c563f76STimo Teras 	int rc;
19251da177e4SLinus Torvalds 	static u32 reqid = IPSEC_MANUAL_REQID_MAX;
19261da177e4SLinus Torvalds 
19271da177e4SLinus Torvalds 	start = reqid;
19281da177e4SLinus Torvalds 	do {
19291da177e4SLinus Torvalds 		++reqid;
19301da177e4SLinus Torvalds 		if (reqid == 0)
19311da177e4SLinus Torvalds 			reqid = IPSEC_MANUAL_REQID_MAX+1;
19324c563f76STimo Teras 		xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
193307fb0f17SAlexey Dobriyan 		rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid);
1934283bc9f3SFan Du 		xfrm_policy_walk_done(&walk, net);
19354c563f76STimo Teras 		if (rc != -EEXIST)
19361da177e4SLinus Torvalds 			return reqid;
19371da177e4SLinus Torvalds 	} while (reqid != start);
19381da177e4SLinus Torvalds 	return 0;
19391da177e4SLinus Torvalds }
19401da177e4SLinus Torvalds 
19411da177e4SLinus Torvalds static int
parse_ipsecrequest(struct xfrm_policy * xp,struct sadb_x_policy * pol,struct sadb_x_ipsecrequest * rq)1942cf3128a7STobias Brunner parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_policy *pol,
1943cf3128a7STobias Brunner 		   struct sadb_x_ipsecrequest *rq)
19441da177e4SLinus Torvalds {
194507fb0f17SAlexey Dobriyan 	struct net *net = xp_net(xp);
19461da177e4SLinus Torvalds 	struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
194755569ce2SKazunori MIYAZAWA 	int mode;
19481da177e4SLinus Torvalds 
19491da177e4SLinus Torvalds 	if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
19501da177e4SLinus Torvalds 		return -ELOOP;
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 	if (rq->sadb_x_ipsecrequest_mode == 0)
19531da177e4SLinus Torvalds 		return -EINVAL;
1954dbb2483bSCong Wang 	if (!xfrm_id_proto_valid(rq->sadb_x_ipsecrequest_proto))
1955dbb2483bSCong Wang 		return -EINVAL;
19561da177e4SLinus Torvalds 
1957dbb2483bSCong Wang 	t->id.proto = rq->sadb_x_ipsecrequest_proto;
195855569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0)
195955569ce2SKazunori MIYAZAWA 		return -EINVAL;
196055569ce2SKazunori MIYAZAWA 	t->mode = mode;
1961cf3128a7STobias Brunner 	if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE) {
1962cf3128a7STobias Brunner 		if ((mode == XFRM_MODE_TUNNEL || mode == XFRM_MODE_BEET) &&
1963cf3128a7STobias Brunner 		    pol->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND)
1964cf3128a7STobias Brunner 			return -EINVAL;
19651da177e4SLinus Torvalds 		t->optional = 1;
1966cf3128a7STobias Brunner 	} else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) {
19671da177e4SLinus Torvalds 		t->reqid = rq->sadb_x_ipsecrequest_reqid;
19681da177e4SLinus Torvalds 		if (t->reqid > IPSEC_MANUAL_REQID_MAX)
19691da177e4SLinus Torvalds 			t->reqid = 0;
197007fb0f17SAlexey Dobriyan 		if (!t->reqid && !(t->reqid = gen_reqid(net)))
19711da177e4SLinus Torvalds 			return -ENOBUFS;
19721da177e4SLinus Torvalds 	}
19731da177e4SLinus Torvalds 
19741da177e4SLinus Torvalds 	/* addresses present only in tunnel mode */
19757e49e6deSMasahide NAKAMURA 	if (t->mode == XFRM_MODE_TUNNEL) {
1976096f41d3SHerbert Xu 		int err;
19775f95ac91SYOSHIFUJI Hideaki 
1978096f41d3SHerbert Xu 		err = parse_sockaddr_pair(
1979096f41d3SHerbert Xu 			(struct sockaddr *)(rq + 1),
1980096f41d3SHerbert Xu 			rq->sadb_x_ipsecrequest_len - sizeof(*rq),
1981096f41d3SHerbert Xu 			&t->saddr, &t->id.daddr, &t->encap_family);
1982096f41d3SHerbert Xu 		if (err)
1983096f41d3SHerbert Xu 			return err;
19842718aa7cSMiika Komu 	} else
19852718aa7cSMiika Komu 		t->encap_family = xp->family;
19862718aa7cSMiika Komu 
19871da177e4SLinus Torvalds 	/* No way to set this via kame pfkey */
1988c5d18e98SHerbert Xu 	t->allalgs = 1;
19891da177e4SLinus Torvalds 	xp->xfrm_nr++;
19901da177e4SLinus Torvalds 	return 0;
19911da177e4SLinus Torvalds }
19921da177e4SLinus Torvalds 
19931da177e4SLinus Torvalds static int
parse_ipsecrequests(struct xfrm_policy * xp,struct sadb_x_policy * pol)19941da177e4SLinus Torvalds parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
19951da177e4SLinus Torvalds {
19961da177e4SLinus Torvalds 	int err;
19971da177e4SLinus Torvalds 	int len = pol->sadb_x_policy_len*8 - sizeof(struct sadb_x_policy);
19981da177e4SLinus Torvalds 	struct sadb_x_ipsecrequest *rq = (void*)(pol+1);
19991da177e4SLinus Torvalds 
2000f674e72fSDan Carpenter 	if (pol->sadb_x_policy_len * 8 < sizeof(struct sadb_x_policy))
2001f674e72fSDan Carpenter 		return -EINVAL;
2002f674e72fSDan Carpenter 
2003096f41d3SHerbert Xu 	while (len >= sizeof(*rq)) {
2004096f41d3SHerbert Xu 		if (len < rq->sadb_x_ipsecrequest_len ||
2005096f41d3SHerbert Xu 		    rq->sadb_x_ipsecrequest_len < sizeof(*rq))
2006096f41d3SHerbert Xu 			return -EINVAL;
2007096f41d3SHerbert Xu 
2008cf3128a7STobias Brunner 		if ((err = parse_ipsecrequest(xp, pol, rq)) < 0)
20091da177e4SLinus Torvalds 			return err;
20101da177e4SLinus Torvalds 		len -= rq->sadb_x_ipsecrequest_len;
20111da177e4SLinus Torvalds 		rq = (void*)((u8*)rq + rq->sadb_x_ipsecrequest_len);
20121da177e4SLinus Torvalds 	}
20131da177e4SLinus Torvalds 	return 0;
20141da177e4SLinus Torvalds }
20151da177e4SLinus Torvalds 
pfkey_xfrm_policy2sec_ctx_size(const struct xfrm_policy * xp)20164c93fbb0SDavid S. Miller static inline int pfkey_xfrm_policy2sec_ctx_size(const struct xfrm_policy *xp)
2017df71837dSTrent Jaeger {
2018df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx = xp->security;
2019df71837dSTrent Jaeger 
2020df71837dSTrent Jaeger 	if (xfrm_ctx) {
2021df71837dSTrent Jaeger 		int len = sizeof(struct sadb_x_sec_ctx);
2022df71837dSTrent Jaeger 		len += xfrm_ctx->ctx_len;
2023df71837dSTrent Jaeger 		return PFKEY_ALIGN8(len);
2024df71837dSTrent Jaeger 	}
2025df71837dSTrent Jaeger 	return 0;
2026df71837dSTrent Jaeger }
2027df71837dSTrent Jaeger 
pfkey_xfrm_policy2msg_size(const struct xfrm_policy * xp)20284c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg_size(const struct xfrm_policy *xp)
20291da177e4SLinus Torvalds {
20304c93fbb0SDavid S. Miller 	const struct xfrm_tmpl *t;
20311da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
20322718aa7cSMiika Komu 	int socklen = 0;
20332718aa7cSMiika Komu 	int i;
20342718aa7cSMiika Komu 
20352718aa7cSMiika Komu 	for (i=0; i<xp->xfrm_nr; i++) {
20362718aa7cSMiika Komu 		t = xp->xfrm_vec + i;
20379e8b4ed8SYOSHIFUJI Hideaki 		socklen += pfkey_sockaddr_len(t->encap_family);
20382718aa7cSMiika Komu 	}
20391da177e4SLinus Torvalds 
20401da177e4SLinus Torvalds 	return sizeof(struct sadb_msg) +
20411da177e4SLinus Torvalds 		(sizeof(struct sadb_lifetime) * 3) +
20421da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
20431da177e4SLinus Torvalds 		(sockaddr_size * 2) +
20441da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy) +
20452718aa7cSMiika Komu 		(xp->xfrm_nr * sizeof(struct sadb_x_ipsecrequest)) +
20462718aa7cSMiika Komu 		(socklen * 2) +
2047df71837dSTrent Jaeger 		pfkey_xfrm_policy2sec_ctx_size(xp);
20481da177e4SLinus Torvalds }
20491da177e4SLinus Torvalds 
pfkey_xfrm_policy2msg_prep(const struct xfrm_policy * xp)20504c93fbb0SDavid S. Miller static struct sk_buff * pfkey_xfrm_policy2msg_prep(const struct xfrm_policy *xp)
20511da177e4SLinus Torvalds {
20521da177e4SLinus Torvalds 	struct sk_buff *skb;
20531da177e4SLinus Torvalds 	int size;
20541da177e4SLinus Torvalds 
20551da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
20561da177e4SLinus Torvalds 
20571da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
20581da177e4SLinus Torvalds 	if (skb == NULL)
20591da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
20601da177e4SLinus Torvalds 
20611da177e4SLinus Torvalds 	return skb;
20621da177e4SLinus Torvalds }
20631da177e4SLinus Torvalds 
pfkey_xfrm_policy2msg(struct sk_buff * skb,const struct xfrm_policy * xp,int dir)20644c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg(struct sk_buff *skb, const struct xfrm_policy *xp, int dir)
20651da177e4SLinus Torvalds {
20661da177e4SLinus Torvalds 	struct sadb_msg *hdr;
20671da177e4SLinus Torvalds 	struct sadb_address *addr;
20681da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
20691da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
2070df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
2071df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
20721da177e4SLinus Torvalds 	int i;
20731da177e4SLinus Torvalds 	int size;
20741da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
20759e8b4ed8SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(xp->family);
20761da177e4SLinus Torvalds 
20771da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
20781da177e4SLinus Torvalds 
20791da177e4SLinus Torvalds 	/* call should fill header later */
20804df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
20811da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
20821da177e4SLinus Torvalds 
20831da177e4SLinus Torvalds 	/* src address */
20844df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
20851da177e4SLinus Torvalds 	addr->sadb_address_len =
20861da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
20871da177e4SLinus Torvalds 			sizeof(uint64_t);
20881da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
20891da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
20901da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
20911da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2092e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(&xp->selector.saddr,
2093e5b56652SYOSHIFUJI Hideaki 				 xp->selector.sport,
2094e5b56652SYOSHIFUJI Hideaki 				 (struct sockaddr *) (addr + 1),
2095e5b56652SYOSHIFUJI Hideaki 				 xp->family))
20961da177e4SLinus Torvalds 		BUG();
20971da177e4SLinus Torvalds 
20981da177e4SLinus Torvalds 	/* dst address */
20994df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
21001da177e4SLinus Torvalds 	addr->sadb_address_len =
21011da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
21021da177e4SLinus Torvalds 			sizeof(uint64_t);
21031da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
21041da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
21051da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
21061da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2107e5b56652SYOSHIFUJI Hideaki 
2108e5b56652SYOSHIFUJI Hideaki 	pfkey_sockaddr_fill(&xp->selector.daddr, xp->selector.dport,
2109e5b56652SYOSHIFUJI Hideaki 			    (struct sockaddr *) (addr + 1),
2110e5b56652SYOSHIFUJI Hideaki 			    xp->family);
21111da177e4SLinus Torvalds 
21121da177e4SLinus Torvalds 	/* hard time */
21134df864c1SJohannes Berg 	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
21141da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
21151da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
21161da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
21171da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.hard_packet_limit);
21181da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.hard_byte_limit);
21191da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.hard_add_expires_seconds;
21201da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.hard_use_expires_seconds;
21211da177e4SLinus Torvalds 	/* soft time */
21224df864c1SJohannes Berg 	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
21231da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
21241da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
21251da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
21261da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.soft_packet_limit);
21271da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.soft_byte_limit);
21281da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.soft_add_expires_seconds;
21291da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.soft_use_expires_seconds;
21301da177e4SLinus Torvalds 	/* current time */
21314df864c1SJohannes Berg 	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
21321da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
21331da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
21341da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
21351da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = xp->curlft.packets;
21361da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = xp->curlft.bytes;
21371da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->curlft.add_time;
21381da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->curlft.use_time;
21391da177e4SLinus Torvalds 
21404df864c1SJohannes Berg 	pol = skb_put(skb, sizeof(struct sadb_x_policy));
21411da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
21421da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
21431da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_DISCARD;
21441da177e4SLinus Torvalds 	if (xp->action == XFRM_POLICY_ALLOW) {
21451da177e4SLinus Torvalds 		if (xp->xfrm_nr)
21461da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
21471da177e4SLinus Torvalds 		else
21481da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_NONE;
21491da177e4SLinus Torvalds 	}
21501da177e4SLinus Torvalds 	pol->sadb_x_policy_dir = dir+1;
2151ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
21521da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
21531da177e4SLinus Torvalds 	pol->sadb_x_policy_priority = xp->priority;
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
21564c93fbb0SDavid S. Miller 		const struct xfrm_tmpl *t = xp->xfrm_vec + i;
21571da177e4SLinus Torvalds 		struct sadb_x_ipsecrequest *rq;
21581da177e4SLinus Torvalds 		int req_size;
215955569ce2SKazunori MIYAZAWA 		int mode;
21601da177e4SLinus Torvalds 
21611da177e4SLinus Torvalds 		req_size = sizeof(struct sadb_x_ipsecrequest);
2162e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2163e5b56652SYOSHIFUJI Hideaki 			socklen = pfkey_sockaddr_len(t->encap_family);
2164e5b56652SYOSHIFUJI Hideaki 			req_size += socklen * 2;
2165e5b56652SYOSHIFUJI Hideaki 		} else {
21661da177e4SLinus Torvalds 			size -= 2*socklen;
2167e5b56652SYOSHIFUJI Hideaki 		}
21684df864c1SJohannes Berg 		rq = skb_put(skb, req_size);
21691da177e4SLinus Torvalds 		pol->sadb_x_policy_len += req_size/8;
21701da177e4SLinus Torvalds 		memset(rq, 0, sizeof(*rq));
21711da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_len = req_size;
21721da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_proto = t->id.proto;
217355569ce2SKazunori MIYAZAWA 		if ((mode = pfkey_mode_from_xfrm(t->mode)) < 0)
217455569ce2SKazunori MIYAZAWA 			return -EINVAL;
2175fefaa75eSDavid S. Miller 		rq->sadb_x_ipsecrequest_mode = mode;
21761da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
21771da177e4SLinus Torvalds 		if (t->reqid)
21781da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
21791da177e4SLinus Torvalds 		if (t->optional)
21801da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
21811da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_reqid = t->reqid;
21821da177e4SLinus Torvalds 
2183e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2184e5b56652SYOSHIFUJI Hideaki 			u8 *sa = (void *)(rq + 1);
2185e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->saddr, 0,
2186e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *)sa,
2187e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
2188e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->id.daddr, 0,
2189e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *) (sa + socklen),
2190e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
21911da177e4SLinus Torvalds 		}
21921da177e4SLinus Torvalds 	}
2193df71837dSTrent Jaeger 
2194df71837dSTrent Jaeger 	/* security context */
2195df71837dSTrent Jaeger 	if ((xfrm_ctx = xp->security)) {
2196df71837dSTrent Jaeger 		int ctx_size = pfkey_xfrm_policy2sec_ctx_size(xp);
2197df71837dSTrent Jaeger 
21984df864c1SJohannes Berg 		sec_ctx = skb_put(skb, ctx_size);
2199df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len = ctx_size / sizeof(uint64_t);
2200df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
2201df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
2202df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
2203df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
2204df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
2205df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
2206df71837dSTrent Jaeger 	}
2207df71837dSTrent Jaeger 
22081da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
2209850a6212SReshetova, Elena 	hdr->sadb_msg_reserved = refcount_read(&xp->refcnt);
221055569ce2SKazunori MIYAZAWA 
221155569ce2SKazunori MIYAZAWA 	return 0;
22121da177e4SLinus Torvalds }
22131da177e4SLinus Torvalds 
key_notify_policy(struct xfrm_policy * xp,int dir,const struct km_event * c)2214214e005bSDavid S. Miller static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
221526b15dadSJamal Hadi Salim {
221626b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
221726b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
221826b15dadSJamal Hadi Salim 	int err;
221926b15dadSJamal Hadi Salim 
222026b15dadSJamal Hadi Salim 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
22219a127aadSDan Carpenter 	if (IS_ERR(out_skb))
22229a127aadSDan Carpenter 		return PTR_ERR(out_skb);
22239a127aadSDan Carpenter 
222455569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
22251e532d2bSSteffen Klassert 	if (err < 0) {
22261e532d2bSSteffen Klassert 		kfree_skb(out_skb);
222755569ce2SKazunori MIYAZAWA 		return err;
22281e532d2bSSteffen Klassert 	}
222926b15dadSJamal Hadi Salim 
223026b15dadSJamal Hadi Salim 	out_hdr = (struct sadb_msg *) out_skb->data;
223126b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_version = PF_KEY_V2;
223226b15dadSJamal Hadi Salim 
2233f60f6b8fSHerbert Xu 	if (c->data.byid && c->event == XFRM_MSG_DELPOLICY)
223426b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = SADB_X_SPDDELETE2;
223526b15dadSJamal Hadi Salim 	else
223626b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = event2poltype(c->event);
223726b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_errno = 0;
223826b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_seq = c->seq;
223915e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = c->portid;
224036f41f8fSEric Dumazet 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
224126b15dadSJamal Hadi Salim 	return 0;
224226b15dadSJamal Hadi Salim 
224326b15dadSJamal Hadi Salim }
224426b15dadSJamal Hadi Salim 
pfkey_spdadd(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)22454c93fbb0SDavid S. Miller static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
22461da177e4SLinus Torvalds {
224707fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
2248df71837dSTrent Jaeger 	int err = 0;
22491da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
22501da177e4SLinus Torvalds 	struct sadb_address *sa;
22511da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
22521da177e4SLinus Torvalds 	struct xfrm_policy *xp;
225326b15dadSJamal Hadi Salim 	struct km_event c;
2254df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
22551da177e4SLinus Torvalds 
22561da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
22571da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
22581da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
22591da177e4SLinus Torvalds 		return -EINVAL;
22601da177e4SLinus Torvalds 
22611da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
22621da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type > IPSEC_POLICY_IPSEC)
22631da177e4SLinus Torvalds 		return -EINVAL;
22641da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
22651da177e4SLinus Torvalds 		return -EINVAL;
22661da177e4SLinus Torvalds 
226707fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_KERNEL);
22681da177e4SLinus Torvalds 	if (xp == NULL)
22691da177e4SLinus Torvalds 		return -ENOBUFS;
22701da177e4SLinus Torvalds 
22711da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
22721da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
22731da177e4SLinus Torvalds 	xp->priority = pol->sadb_x_policy_priority;
22741da177e4SLinus Torvalds 
2275d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
22761da177e4SLinus Torvalds 	xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
22771da177e4SLinus Torvalds 	xp->selector.family = xp->family;
22781da177e4SLinus Torvalds 	xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
22791da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22801da177e4SLinus Torvalds 	xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
22811da177e4SLinus Torvalds 	if (xp->selector.sport)
22828f83f23eSAl Viro 		xp->selector.sport_mask = htons(0xffff);
22831da177e4SLinus Torvalds 
2284d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
22851da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
22861da177e4SLinus Torvalds 	xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
22871da177e4SLinus Torvalds 
22881da177e4SLinus Torvalds 	/* Amusing, we set this twice.  KAME apps appear to set same value
22891da177e4SLinus Torvalds 	 * in both addresses.
22901da177e4SLinus Torvalds 	 */
22911da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22921da177e4SLinus Torvalds 
22931da177e4SLinus Torvalds 	xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
22941da177e4SLinus Torvalds 	if (xp->selector.dport)
22958f83f23eSAl Viro 		xp->selector.dport_mask = htons(0xffff);
22961da177e4SLinus Torvalds 
2297ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
2298df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
229987536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
2300df71837dSTrent Jaeger 
2301df71837dSTrent Jaeger 		if (!uctx) {
2302df71837dSTrent Jaeger 			err = -ENOBUFS;
2303df71837dSTrent Jaeger 			goto out;
2304df71837dSTrent Jaeger 		}
2305df71837dSTrent Jaeger 
230652a4c640SNikolay Aleksandrov 		err = security_xfrm_policy_alloc(&xp->security, uctx, GFP_KERNEL);
2307df71837dSTrent Jaeger 		kfree(uctx);
2308df71837dSTrent Jaeger 
2309df71837dSTrent Jaeger 		if (err)
2310df71837dSTrent Jaeger 			goto out;
2311df71837dSTrent Jaeger 	}
2312df71837dSTrent Jaeger 
23131da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
23141da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
23151da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
23161da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
23171da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD-1]) != NULL) {
23181da177e4SLinus Torvalds 		xp->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
23191da177e4SLinus Torvalds 		xp->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
23201da177e4SLinus Torvalds 		xp->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
23211da177e4SLinus Torvalds 		xp->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
23221da177e4SLinus Torvalds 	}
23231da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]) != NULL) {
23241da177e4SLinus Torvalds 		xp->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
23251da177e4SLinus Torvalds 		xp->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
23261da177e4SLinus Torvalds 		xp->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
23271da177e4SLinus Torvalds 		xp->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
23281da177e4SLinus Torvalds 	}
23291da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
23301da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
23311da177e4SLinus Torvalds 	    (err = parse_ipsecrequests(xp, pol)) < 0)
23321da177e4SLinus Torvalds 		goto out;
23331da177e4SLinus Torvalds 
23341da177e4SLinus Torvalds 	err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
23351da177e4SLinus Torvalds 				 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
2336df71837dSTrent Jaeger 
23372e71029eSTetsuo Handa 	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
2338161a09e7SJoy Latten 
2339df71837dSTrent Jaeger 	if (err)
2340df71837dSTrent Jaeger 		goto out;
23411da177e4SLinus Torvalds 
234226b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
2343f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDPOLICY;
234426b15dadSJamal Hadi Salim 	else
2345f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWPOLICY;
23461da177e4SLinus Torvalds 
234726b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
234815e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
234926b15dadSJamal Hadi Salim 
235026b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
23511da177e4SLinus Torvalds 	xfrm_pol_put(xp);
23521da177e4SLinus Torvalds 	return 0;
23531da177e4SLinus Torvalds 
23541da177e4SLinus Torvalds out:
235512a169e7SHerbert Xu 	xp->walk.dead = 1;
235664c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
23571da177e4SLinus Torvalds 	return err;
23581da177e4SLinus Torvalds }
23591da177e4SLinus Torvalds 
pfkey_spddelete(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)23604c93fbb0SDavid S. Miller static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
23611da177e4SLinus Torvalds {
236207fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
23631da177e4SLinus Torvalds 	int err;
23641da177e4SLinus Torvalds 	struct sadb_address *sa;
23651da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
236603e1ad7bSPaul Moore 	struct xfrm_policy *xp;
23671da177e4SLinus Torvalds 	struct xfrm_selector sel;
236826b15dadSJamal Hadi Salim 	struct km_event c;
2369df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
23702db3e47eSBrian Haley 	struct xfrm_sec_ctx *pol_ctx = NULL;
23711da177e4SLinus Torvalds 
23721da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
23731da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
23741da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
23751da177e4SLinus Torvalds 		return -EINVAL;
23761da177e4SLinus Torvalds 
23771da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
23781da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
23791da177e4SLinus Torvalds 		return -EINVAL;
23801da177e4SLinus Torvalds 
23811da177e4SLinus Torvalds 	memset(&sel, 0, sizeof(sel));
23821da177e4SLinus Torvalds 
2383d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
23841da177e4SLinus Torvalds 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
23851da177e4SLinus Torvalds 	sel.prefixlen_s = sa->sadb_address_prefixlen;
23861da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23871da177e4SLinus Torvalds 	sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
23881da177e4SLinus Torvalds 	if (sel.sport)
23898f83f23eSAl Viro 		sel.sport_mask = htons(0xffff);
23901da177e4SLinus Torvalds 
2391d0d79c3fSJunwei Zhang 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
23921da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
23931da177e4SLinus Torvalds 	sel.prefixlen_d = sa->sadb_address_prefixlen;
23941da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23951da177e4SLinus Torvalds 	sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
23961da177e4SLinus Torvalds 	if (sel.dport)
23978f83f23eSAl Viro 		sel.dport_mask = htons(0xffff);
23981da177e4SLinus Torvalds 
2399ea110733SJoe Perches 	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
2400df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
240187536a81SNikolay Aleksandrov 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
2402df71837dSTrent Jaeger 
2403df71837dSTrent Jaeger 		if (!uctx)
2404df71837dSTrent Jaeger 			return -ENOMEM;
2405df71837dSTrent Jaeger 
240652a4c640SNikolay Aleksandrov 		err = security_xfrm_policy_alloc(&pol_ctx, uctx, GFP_KERNEL);
2407df71837dSTrent Jaeger 		kfree(uctx);
2408df71837dSTrent Jaeger 		if (err)
2409df71837dSTrent Jaeger 			return err;
24102db3e47eSBrian Haley 	}
2411df71837dSTrent Jaeger 
24124f47e8abSXin Long 	xp = xfrm_policy_bysel_ctx(net, &dummy_mark, 0, XFRM_POLICY_TYPE_MAIN,
241303e1ad7bSPaul Moore 				   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
241403e1ad7bSPaul Moore 				   1, &err);
241503e1ad7bSPaul Moore 	security_xfrm_policy_free(pol_ctx);
24161da177e4SLinus Torvalds 	if (xp == NULL)
24171da177e4SLinus Torvalds 		return -ENOENT;
24181da177e4SLinus Torvalds 
24192e71029eSTetsuo Handa 	xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
242013fcfbb0SDavid S. Miller 
242113fcfbb0SDavid S. Miller 	if (err)
2422c8c05a8eSCatherine Zhang 		goto out;
242313fcfbb0SDavid S. Miller 
242426b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
242515e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
24261839faabSTobias Brunner 	c.data.byid = 0;
2427f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELPOLICY;
242826b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
242926b15dadSJamal Hadi Salim 
2430c8c05a8eSCatherine Zhang out:
243126b15dadSJamal Hadi Salim 	xfrm_pol_put(xp);
243226b15dadSJamal Hadi Salim 	return err;
243326b15dadSJamal Hadi Salim }
243426b15dadSJamal Hadi Salim 
key_pol_get_resp(struct sock * sk,struct xfrm_policy * xp,const struct sadb_msg * hdr,int dir)24354c93fbb0SDavid S. Miller static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struct sadb_msg *hdr, int dir)
243626b15dadSJamal Hadi Salim {
243726b15dadSJamal Hadi Salim 	int err;
243826b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
243926b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
244026b15dadSJamal Hadi Salim 	err = 0;
244126b15dadSJamal Hadi Salim 
24421da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
24431da177e4SLinus Torvalds 	if (IS_ERR(out_skb)) {
24441da177e4SLinus Torvalds 		err =  PTR_ERR(out_skb);
24451da177e4SLinus Torvalds 		goto out;
24461da177e4SLinus Torvalds 	}
244755569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
24487c80eb1cSJeremy Sowden 	if (err < 0) {
24497c80eb1cSJeremy Sowden 		kfree_skb(out_skb);
245055569ce2SKazunori MIYAZAWA 		goto out;
24517c80eb1cSJeremy Sowden 	}
24521da177e4SLinus Torvalds 
24531da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
24541da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
245526b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_type = hdr->sadb_msg_type;
24561da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = 0;
24571da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
24581da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
24591da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
246036f41f8fSEric Dumazet 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
24611da177e4SLinus Torvalds 	err = 0;
24621da177e4SLinus Torvalds 
24631da177e4SLinus Torvalds out:
24641da177e4SLinus Torvalds 	return err;
24651da177e4SLinus Torvalds }
24661da177e4SLinus Torvalds 
pfkey_sockaddr_pair_size(sa_family_t family)246708de61beSShinta Sugimoto static int pfkey_sockaddr_pair_size(sa_family_t family)
246808de61beSShinta Sugimoto {
24699e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
247008de61beSShinta Sugimoto }
247108de61beSShinta Sugimoto 
parse_sockaddr_pair(struct sockaddr * sa,int ext_len,xfrm_address_t * saddr,xfrm_address_t * daddr,u16 * family)247213c1d189SArnaud Ebalard static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
247308de61beSShinta Sugimoto 			       xfrm_address_t *saddr, xfrm_address_t *daddr,
247408de61beSShinta Sugimoto 			       u16 *family)
247508de61beSShinta Sugimoto {
24765f95ac91SYOSHIFUJI Hideaki 	int af, socklen;
24775f95ac91SYOSHIFUJI Hideaki 
2478096f41d3SHerbert Xu 	if (ext_len < 2 || ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
247908de61beSShinta Sugimoto 		return -EINVAL;
248008de61beSShinta Sugimoto 
248113c1d189SArnaud Ebalard 	af = pfkey_sockaddr_extract(sa, saddr);
24825f95ac91SYOSHIFUJI Hideaki 	if (!af)
248308de61beSShinta Sugimoto 		return -EINVAL;
248408de61beSShinta Sugimoto 
24855f95ac91SYOSHIFUJI Hideaki 	socklen = pfkey_sockaddr_len(af);
248613c1d189SArnaud Ebalard 	if (pfkey_sockaddr_extract((struct sockaddr *) (((u8 *)sa) + socklen),
24875f95ac91SYOSHIFUJI Hideaki 				   daddr) != af)
24885f95ac91SYOSHIFUJI Hideaki 		return -EINVAL;
24895f95ac91SYOSHIFUJI Hideaki 
24905f95ac91SYOSHIFUJI Hideaki 	*family = af;
249108de61beSShinta Sugimoto 	return 0;
249208de61beSShinta Sugimoto }
249308de61beSShinta Sugimoto 
2494096f41d3SHerbert Xu #ifdef CONFIG_NET_KEY_MIGRATE
ipsecrequests_to_migrate(struct sadb_x_ipsecrequest * rq1,int len,struct xfrm_migrate * m)249508de61beSShinta Sugimoto static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
249608de61beSShinta Sugimoto 				    struct xfrm_migrate *m)
249708de61beSShinta Sugimoto {
249808de61beSShinta Sugimoto 	int err;
249908de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq2;
250055569ce2SKazunori MIYAZAWA 	int mode;
250108de61beSShinta Sugimoto 
2502096f41d3SHerbert Xu 	if (len < sizeof(*rq1) ||
2503096f41d3SHerbert Xu 	    len < rq1->sadb_x_ipsecrequest_len ||
2504096f41d3SHerbert Xu 	    rq1->sadb_x_ipsecrequest_len < sizeof(*rq1))
250508de61beSShinta Sugimoto 		return -EINVAL;
250608de61beSShinta Sugimoto 
250708de61beSShinta Sugimoto 	/* old endoints */
250813c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
2509096f41d3SHerbert Xu 				  rq1->sadb_x_ipsecrequest_len - sizeof(*rq1),
251013c1d189SArnaud Ebalard 				  &m->old_saddr, &m->old_daddr,
251108de61beSShinta Sugimoto 				  &m->old_family);
251208de61beSShinta Sugimoto 	if (err)
251308de61beSShinta Sugimoto 		return err;
251408de61beSShinta Sugimoto 
251508de61beSShinta Sugimoto 	rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len);
251608de61beSShinta Sugimoto 	len -= rq1->sadb_x_ipsecrequest_len;
251708de61beSShinta Sugimoto 
2518096f41d3SHerbert Xu 	if (len <= sizeof(*rq2) ||
2519096f41d3SHerbert Xu 	    len < rq2->sadb_x_ipsecrequest_len ||
2520096f41d3SHerbert Xu 	    rq2->sadb_x_ipsecrequest_len < sizeof(*rq2))
252108de61beSShinta Sugimoto 		return -EINVAL;
252208de61beSShinta Sugimoto 
252308de61beSShinta Sugimoto 	/* new endpoints */
252413c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
2525096f41d3SHerbert Xu 				  rq2->sadb_x_ipsecrequest_len - sizeof(*rq2),
252613c1d189SArnaud Ebalard 				  &m->new_saddr, &m->new_daddr,
252708de61beSShinta Sugimoto 				  &m->new_family);
252808de61beSShinta Sugimoto 	if (err)
252908de61beSShinta Sugimoto 		return err;
253008de61beSShinta Sugimoto 
253108de61beSShinta Sugimoto 	if (rq1->sadb_x_ipsecrequest_proto != rq2->sadb_x_ipsecrequest_proto ||
253208de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_mode != rq2->sadb_x_ipsecrequest_mode ||
253308de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_reqid != rq2->sadb_x_ipsecrequest_reqid)
253408de61beSShinta Sugimoto 		return -EINVAL;
253508de61beSShinta Sugimoto 
253608de61beSShinta Sugimoto 	m->proto = rq1->sadb_x_ipsecrequest_proto;
253755569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0)
253855569ce2SKazunori MIYAZAWA 		return -EINVAL;
253955569ce2SKazunori MIYAZAWA 	m->mode = mode;
254008de61beSShinta Sugimoto 	m->reqid = rq1->sadb_x_ipsecrequest_reqid;
254108de61beSShinta Sugimoto 
254208de61beSShinta Sugimoto 	return ((int)(rq1->sadb_x_ipsecrequest_len +
254308de61beSShinta Sugimoto 		      rq2->sadb_x_ipsecrequest_len));
254408de61beSShinta Sugimoto }
254508de61beSShinta Sugimoto 
pfkey_migrate(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)254608de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
25474c93fbb0SDavid S. Miller 			 const struct sadb_msg *hdr, void * const *ext_hdrs)
254808de61beSShinta Sugimoto {
254908de61beSShinta Sugimoto 	int i, len, ret, err = -EINVAL;
255008de61beSShinta Sugimoto 	u8 dir;
255108de61beSShinta Sugimoto 	struct sadb_address *sa;
255213c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
255308de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
255408de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
255508de61beSShinta Sugimoto 	struct xfrm_selector sel;
255608de61beSShinta Sugimoto 	struct xfrm_migrate m[XFRM_MAX_DEPTH];
255713c1d189SArnaud Ebalard 	struct xfrm_kmaddress k;
25588d549c4fSFan Du 	struct net *net = sock_net(sk);
255908de61beSShinta Sugimoto 
256008de61beSShinta Sugimoto 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1],
256108de61beSShinta Sugimoto 				     ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) ||
256208de61beSShinta Sugimoto 	    !ext_hdrs[SADB_X_EXT_POLICY - 1]) {
256308de61beSShinta Sugimoto 		err = -EINVAL;
256408de61beSShinta Sugimoto 		goto out;
256508de61beSShinta Sugimoto 	}
256608de61beSShinta Sugimoto 
256713c1d189SArnaud Ebalard 	kma = ext_hdrs[SADB_X_EXT_KMADDRESS - 1];
256808de61beSShinta Sugimoto 	pol = ext_hdrs[SADB_X_EXT_POLICY - 1];
256908de61beSShinta Sugimoto 
257008de61beSShinta Sugimoto 	if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) {
257108de61beSShinta Sugimoto 		err = -EINVAL;
257208de61beSShinta Sugimoto 		goto out;
257308de61beSShinta Sugimoto 	}
257408de61beSShinta Sugimoto 
257513c1d189SArnaud Ebalard 	if (kma) {
257613c1d189SArnaud Ebalard 		/* convert sadb_x_kmaddress to xfrm_kmaddress */
257713c1d189SArnaud Ebalard 		k.reserved = kma->sadb_x_kmaddress_reserved;
257813c1d189SArnaud Ebalard 		ret = parse_sockaddr_pair((struct sockaddr *)(kma + 1),
257913c1d189SArnaud Ebalard 					  8*(kma->sadb_x_kmaddress_len) - sizeof(*kma),
258013c1d189SArnaud Ebalard 					  &k.local, &k.remote, &k.family);
258113c1d189SArnaud Ebalard 		if (ret < 0) {
258213c1d189SArnaud Ebalard 			err = ret;
258313c1d189SArnaud Ebalard 			goto out;
258413c1d189SArnaud Ebalard 		}
258513c1d189SArnaud Ebalard 	}
258613c1d189SArnaud Ebalard 
258708de61beSShinta Sugimoto 	dir = pol->sadb_x_policy_dir - 1;
258808de61beSShinta Sugimoto 	memset(&sel, 0, sizeof(sel));
258908de61beSShinta Sugimoto 
259008de61beSShinta Sugimoto 	/* set source address info of selector */
259108de61beSShinta Sugimoto 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC - 1];
259208de61beSShinta Sugimoto 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
259308de61beSShinta Sugimoto 	sel.prefixlen_s = sa->sadb_address_prefixlen;
259408de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
259508de61beSShinta Sugimoto 	sel.sport = ((struct sockaddr_in *)(sa + 1))->sin_port;
259608de61beSShinta Sugimoto 	if (sel.sport)
2597582ee43dSAl Viro 		sel.sport_mask = htons(0xffff);
259808de61beSShinta Sugimoto 
259908de61beSShinta Sugimoto 	/* set destination address info of selector */
260047162c0bSHimangi Saraogi 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];
260108de61beSShinta Sugimoto 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
260208de61beSShinta Sugimoto 	sel.prefixlen_d = sa->sadb_address_prefixlen;
260308de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
260408de61beSShinta Sugimoto 	sel.dport = ((struct sockaddr_in *)(sa + 1))->sin_port;
260508de61beSShinta Sugimoto 	if (sel.dport)
2606582ee43dSAl Viro 		sel.dport_mask = htons(0xffff);
260708de61beSShinta Sugimoto 
260808de61beSShinta Sugimoto 	rq = (struct sadb_x_ipsecrequest *)(pol + 1);
260908de61beSShinta Sugimoto 
261008de61beSShinta Sugimoto 	/* extract ipsecrequests */
261108de61beSShinta Sugimoto 	i = 0;
261208de61beSShinta Sugimoto 	len = pol->sadb_x_policy_len * 8 - sizeof(struct sadb_x_policy);
261308de61beSShinta Sugimoto 
261408de61beSShinta Sugimoto 	while (len > 0 && i < XFRM_MAX_DEPTH) {
261508de61beSShinta Sugimoto 		ret = ipsecrequests_to_migrate(rq, len, &m[i]);
261608de61beSShinta Sugimoto 		if (ret < 0) {
261708de61beSShinta Sugimoto 			err = ret;
261808de61beSShinta Sugimoto 			goto out;
261908de61beSShinta Sugimoto 		} else {
262008de61beSShinta Sugimoto 			rq = (struct sadb_x_ipsecrequest *)((u8 *)rq + ret);
262108de61beSShinta Sugimoto 			len -= ret;
262208de61beSShinta Sugimoto 			i++;
262308de61beSShinta Sugimoto 		}
262408de61beSShinta Sugimoto 	}
262508de61beSShinta Sugimoto 
262608de61beSShinta Sugimoto 	if (!i || len > 0) {
262708de61beSShinta Sugimoto 		err = -EINVAL;
262808de61beSShinta Sugimoto 		goto out;
262908de61beSShinta Sugimoto 	}
263008de61beSShinta Sugimoto 
263113c1d189SArnaud Ebalard 	return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
2632bd122403SSabrina Dubroca 			    kma ? &k : NULL, net, NULL, 0, NULL);
263308de61beSShinta Sugimoto 
263408de61beSShinta Sugimoto  out:
263508de61beSShinta Sugimoto 	return err;
263608de61beSShinta Sugimoto }
263708de61beSShinta Sugimoto #else
pfkey_migrate(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)263808de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
26397f6daa63SStephen Hemminger 			 const struct sadb_msg *hdr, void * const *ext_hdrs)
264008de61beSShinta Sugimoto {
264108de61beSShinta Sugimoto 	return -ENOPROTOOPT;
264208de61beSShinta Sugimoto }
264308de61beSShinta Sugimoto #endif
264408de61beSShinta Sugimoto 
264508de61beSShinta Sugimoto 
pfkey_spdget(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)26464c93fbb0SDavid S. Miller static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
26471da177e4SLinus Torvalds {
264807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
264977d8d7a6SHerbert Xu 	unsigned int dir;
2650215a2dd3SEric Paris 	int err = 0, delete;
26511da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
26521da177e4SLinus Torvalds 	struct xfrm_policy *xp;
265326b15dadSJamal Hadi Salim 	struct km_event c;
26541da177e4SLinus Torvalds 
26551da177e4SLinus Torvalds 	if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
26561da177e4SLinus Torvalds 		return -EINVAL;
26571da177e4SLinus Torvalds 
265877d8d7a6SHerbert Xu 	dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
265977d8d7a6SHerbert Xu 	if (dir >= XFRM_POLICY_MAX)
266077d8d7a6SHerbert Xu 		return -EINVAL;
266177d8d7a6SHerbert Xu 
2662215a2dd3SEric Paris 	delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
26634f47e8abSXin Long 	xp = xfrm_policy_byid(net, &dummy_mark, 0, XFRM_POLICY_TYPE_MAIN,
2664bd55775cSJamal Hadi Salim 			      dir, pol->sadb_x_policy_id, delete, &err);
26651da177e4SLinus Torvalds 	if (xp == NULL)
26661da177e4SLinus Torvalds 		return -ENOENT;
26671da177e4SLinus Torvalds 
2668215a2dd3SEric Paris 	if (delete) {
26692e71029eSTetsuo Handa 		xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
26701da177e4SLinus Torvalds 
2671215a2dd3SEric Paris 		if (err)
2672215a2dd3SEric Paris 			goto out;
267326b15dadSJamal Hadi Salim 		c.seq = hdr->sadb_msg_seq;
267415e47304SEric W. Biederman 		c.portid = hdr->sadb_msg_pid;
2675bf08867fSHerbert Xu 		c.data.byid = 1;
2676f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_DELPOLICY;
267777d8d7a6SHerbert Xu 		km_policy_notify(xp, dir, &c);
267826b15dadSJamal Hadi Salim 	} else {
267977d8d7a6SHerbert Xu 		err = key_pol_get_resp(sk, xp, hdr, dir);
26801da177e4SLinus Torvalds 	}
26811da177e4SLinus Torvalds 
2682215a2dd3SEric Paris out:
26831da177e4SLinus Torvalds 	xfrm_pol_put(xp);
26841da177e4SLinus Torvalds 	return err;
26851da177e4SLinus Torvalds }
26861da177e4SLinus Torvalds 
dump_sp(struct xfrm_policy * xp,int dir,int count,void * ptr)26871da177e4SLinus Torvalds static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
26881da177e4SLinus Torvalds {
268983321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
26901da177e4SLinus Torvalds 	struct sk_buff *out_skb;
26911da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
269255569ce2SKazunori MIYAZAWA 	int err;
26931da177e4SLinus Torvalds 
269483321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
269583321d6bSTimo Teras 		return -ENOBUFS;
269683321d6bSTimo Teras 
26971da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
26981da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
26991da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
27001da177e4SLinus Torvalds 
270155569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
27027c80eb1cSJeremy Sowden 	if (err < 0) {
27037c80eb1cSJeremy Sowden 		kfree_skb(out_skb);
270455569ce2SKazunori MIYAZAWA 		return err;
27057c80eb1cSJeremy Sowden 	}
27061da177e4SLinus Torvalds 
27071da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
270883321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
27091da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
27101da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
27111da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
271212a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
271315e47304SEric W. Biederman 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
271412a169e7SHerbert Xu 
271512a169e7SHerbert Xu 	if (pfk->dump.skb)
271636f41f8fSEric Dumazet 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
271707fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
271812a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
271912a169e7SHerbert Xu 
27201da177e4SLinus Torvalds 	return 0;
27211da177e4SLinus Torvalds }
27221da177e4SLinus Torvalds 
pfkey_dump_sp(struct pfkey_sock * pfk)272383321d6bSTimo Teras static int pfkey_dump_sp(struct pfkey_sock *pfk)
272483321d6bSTimo Teras {
272507fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
272607fb0f17SAlexey Dobriyan 	return xfrm_policy_walk(net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
272783321d6bSTimo Teras }
272883321d6bSTimo Teras 
pfkey_dump_sp_done(struct pfkey_sock * pfk)272983321d6bSTimo Teras static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
273083321d6bSTimo Teras {
2731283bc9f3SFan Du 	struct net *net = sock_net((struct sock *)pfk);
2732283bc9f3SFan Du 
2733283bc9f3SFan Du 	xfrm_policy_walk_done(&pfk->dump.u.policy, net);
273483321d6bSTimo Teras }
273583321d6bSTimo Teras 
pfkey_spddump(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)27364c93fbb0SDavid S. Miller static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
27371da177e4SLinus Torvalds {
273883321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
27391da177e4SLinus Torvalds 
274089e357d8SYuejie Shi 	mutex_lock(&pfk->dump_lock);
274189e357d8SYuejie Shi 	if (pfk->dump.dump != NULL) {
274289e357d8SYuejie Shi 		mutex_unlock(&pfk->dump_lock);
274383321d6bSTimo Teras 		return -EBUSY;
274489e357d8SYuejie Shi 	}
27454c563f76STimo Teras 
274683321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
274715e47304SEric W. Biederman 	pfk->dump.msg_portid = hdr->sadb_msg_pid;
274883321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sp;
274983321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sp_done;
275083321d6bSTimo Teras 	xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
275189e357d8SYuejie Shi 	mutex_unlock(&pfk->dump_lock);
275283321d6bSTimo Teras 
275383321d6bSTimo Teras 	return pfkey_do_dump(pfk);
27541da177e4SLinus Torvalds }
27551da177e4SLinus Torvalds 
key_notify_policy_flush(const struct km_event * c)2756214e005bSDavid S. Miller static int key_notify_policy_flush(const struct km_event *c)
27571da177e4SLinus Torvalds {
27581da177e4SLinus Torvalds 	struct sk_buff *skb_out;
275926b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
27601da177e4SLinus Torvalds 
276126b15dadSJamal Hadi Salim 	skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
27621da177e4SLinus Torvalds 	if (!skb_out)
27631da177e4SLinus Torvalds 		return -ENOBUFS;
27644df864c1SJohannes Berg 	hdr = skb_put(skb_out, sizeof(struct sadb_msg));
2765151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_X_SPDFLUSH;
276626b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
276715e47304SEric W. Biederman 	hdr->sadb_msg_pid = c->portid;
276826b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
276926b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
277085dfb745SNicolas Dichtel 	hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
277126b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
2772a5cc68f3SMathias Krause 	hdr->sadb_msg_reserved = 0;
277336f41f8fSEric Dumazet 	pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
277426b15dadSJamal Hadi Salim 	return 0;
277526b15dadSJamal Hadi Salim 
277626b15dadSJamal Hadi Salim }
277726b15dadSJamal Hadi Salim 
pfkey_spdflush(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr,void * const * ext_hdrs)27784c93fbb0SDavid S. Miller static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
277926b15dadSJamal Hadi Salim {
278007fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
278126b15dadSJamal Hadi Salim 	struct km_event c;
27828be987d7SJamal Hadi Salim 	int err, err2;
27831da177e4SLinus Torvalds 
27842e71029eSTetsuo Handa 	err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true);
27858be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
27862f1eb65fSJamal Hadi Salim 	if (err || err2) {
27872f1eb65fSJamal Hadi Salim 		if (err == -ESRCH) /* empty table - old silent behavior */
27882f1eb65fSJamal Hadi Salim 			return 0;
27892f1eb65fSJamal Hadi Salim 		return err;
27902f1eb65fSJamal Hadi Salim 	}
27918be987d7SJamal Hadi Salim 
2792f7b6983fSMasahide NAKAMURA 	c.data.type = XFRM_POLICY_TYPE_MAIN;
2793f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHPOLICY;
279415e47304SEric W. Biederman 	c.portid = hdr->sadb_msg_pid;
279526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
279607fb0f17SAlexey Dobriyan 	c.net = net;
279726b15dadSJamal Hadi Salim 	km_policy_notify(NULL, 0, &c);
27981da177e4SLinus Torvalds 
27991da177e4SLinus Torvalds 	return 0;
28001da177e4SLinus Torvalds }
28011da177e4SLinus Torvalds 
28021da177e4SLinus Torvalds typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
28034c93fbb0SDavid S. Miller 			     const struct sadb_msg *hdr, void * const *ext_hdrs);
28048603b955SMathias Krause static const pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
28051da177e4SLinus Torvalds 	[SADB_RESERVED]		= pfkey_reserved,
28061da177e4SLinus Torvalds 	[SADB_GETSPI]		= pfkey_getspi,
28071da177e4SLinus Torvalds 	[SADB_UPDATE]		= pfkey_add,
28081da177e4SLinus Torvalds 	[SADB_ADD]		= pfkey_add,
28091da177e4SLinus Torvalds 	[SADB_DELETE]		= pfkey_delete,
28101da177e4SLinus Torvalds 	[SADB_GET]		= pfkey_get,
28111da177e4SLinus Torvalds 	[SADB_ACQUIRE]		= pfkey_acquire,
28121da177e4SLinus Torvalds 	[SADB_REGISTER]		= pfkey_register,
28131da177e4SLinus Torvalds 	[SADB_EXPIRE]		= NULL,
28141da177e4SLinus Torvalds 	[SADB_FLUSH]		= pfkey_flush,
28151da177e4SLinus Torvalds 	[SADB_DUMP]		= pfkey_dump,
28161da177e4SLinus Torvalds 	[SADB_X_PROMISC]	= pfkey_promisc,
28171da177e4SLinus Torvalds 	[SADB_X_PCHANGE]	= NULL,
28181da177e4SLinus Torvalds 	[SADB_X_SPDUPDATE]	= pfkey_spdadd,
28191da177e4SLinus Torvalds 	[SADB_X_SPDADD]		= pfkey_spdadd,
28201da177e4SLinus Torvalds 	[SADB_X_SPDDELETE]	= pfkey_spddelete,
28211da177e4SLinus Torvalds 	[SADB_X_SPDGET]		= pfkey_spdget,
28221da177e4SLinus Torvalds 	[SADB_X_SPDACQUIRE]	= NULL,
28231da177e4SLinus Torvalds 	[SADB_X_SPDDUMP]	= pfkey_spddump,
28241da177e4SLinus Torvalds 	[SADB_X_SPDFLUSH]	= pfkey_spdflush,
28251da177e4SLinus Torvalds 	[SADB_X_SPDSETIDX]	= pfkey_spdadd,
28261da177e4SLinus Torvalds 	[SADB_X_SPDDELETE2]	= pfkey_spdget,
282708de61beSShinta Sugimoto 	[SADB_X_MIGRATE]	= pfkey_migrate,
28281da177e4SLinus Torvalds };
28291da177e4SLinus Torvalds 
pfkey_process(struct sock * sk,struct sk_buff * skb,const struct sadb_msg * hdr)28304c93fbb0SDavid S. Miller static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr)
28311da177e4SLinus Torvalds {
28321da177e4SLinus Torvalds 	void *ext_hdrs[SADB_EXT_MAX];
28331da177e4SLinus Torvalds 	int err;
28341da177e4SLinus Torvalds 
28359c90c9b3SMichal Kubecek 	/* Non-zero return value of pfkey_broadcast() does not always signal
28369c90c9b3SMichal Kubecek 	 * an error and even on an actual error we may still want to process
28379c90c9b3SMichal Kubecek 	 * the message so rather ignore the return value.
28389c90c9b3SMichal Kubecek 	 */
28399c90c9b3SMichal Kubecek 	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
284007fb0f17SAlexey Dobriyan 			BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 	memset(ext_hdrs, 0, sizeof(ext_hdrs));
28431da177e4SLinus Torvalds 	err = parse_exthdrs(skb, hdr, ext_hdrs);
28441da177e4SLinus Torvalds 	if (!err) {
28451da177e4SLinus Torvalds 		err = -EOPNOTSUPP;
28461da177e4SLinus Torvalds 		if (pfkey_funcs[hdr->sadb_msg_type])
28471da177e4SLinus Torvalds 			err = pfkey_funcs[hdr->sadb_msg_type](sk, skb, hdr, ext_hdrs);
28481da177e4SLinus Torvalds 	}
28491da177e4SLinus Torvalds 	return err;
28501da177e4SLinus Torvalds }
28511da177e4SLinus Torvalds 
pfkey_get_base_msg(struct sk_buff * skb,int * errp)28521da177e4SLinus Torvalds static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)
28531da177e4SLinus Torvalds {
28541da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
28551da177e4SLinus Torvalds 
28561da177e4SLinus Torvalds 	if (skb->len < sizeof(*hdr)) {
28571da177e4SLinus Torvalds 		*errp = -EMSGSIZE;
28581da177e4SLinus Torvalds 	} else {
28591da177e4SLinus Torvalds 		hdr = (struct sadb_msg *) skb->data;
28601da177e4SLinus Torvalds 		if (hdr->sadb_msg_version != PF_KEY_V2 ||
28611da177e4SLinus Torvalds 		    hdr->sadb_msg_reserved != 0 ||
28621da177e4SLinus Torvalds 		    (hdr->sadb_msg_type <= SADB_RESERVED ||
28631da177e4SLinus Torvalds 		     hdr->sadb_msg_type > SADB_MAX)) {
28641da177e4SLinus Torvalds 			hdr = NULL;
28651da177e4SLinus Torvalds 			*errp = -EINVAL;
28661da177e4SLinus Torvalds 		} else if (hdr->sadb_msg_len != (skb->len /
28671da177e4SLinus Torvalds 						 sizeof(uint64_t)) ||
28681da177e4SLinus Torvalds 			   hdr->sadb_msg_len < (sizeof(struct sadb_msg) /
28691da177e4SLinus Torvalds 						sizeof(uint64_t))) {
28701da177e4SLinus Torvalds 			hdr = NULL;
28711da177e4SLinus Torvalds 			*errp = -EMSGSIZE;
28721da177e4SLinus Torvalds 		} else {
28731da177e4SLinus Torvalds 			*errp = 0;
28741da177e4SLinus Torvalds 		}
28751da177e4SLinus Torvalds 	}
28761da177e4SLinus Torvalds 	return hdr;
28771da177e4SLinus Torvalds }
28781da177e4SLinus Torvalds 
aalg_tmpl_set(const struct xfrm_tmpl * t,const struct xfrm_algo_desc * d)28794c93fbb0SDavid S. Miller static inline int aalg_tmpl_set(const struct xfrm_tmpl *t,
28804c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
28811da177e4SLinus Torvalds {
2882f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2883f398035fSHerbert Xu 
2884f398035fSHerbert Xu 	if (id >= sizeof(t->aalgos) * 8)
2885f398035fSHerbert Xu 		return 0;
2886f398035fSHerbert Xu 
2887f398035fSHerbert Xu 	return (t->aalgos >> id) & 1;
28881da177e4SLinus Torvalds }
28891da177e4SLinus Torvalds 
ealg_tmpl_set(const struct xfrm_tmpl * t,const struct xfrm_algo_desc * d)28904c93fbb0SDavid S. Miller static inline int ealg_tmpl_set(const struct xfrm_tmpl *t,
28914c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
28921da177e4SLinus Torvalds {
2893f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2894f398035fSHerbert Xu 
2895f398035fSHerbert Xu 	if (id >= sizeof(t->ealgos) * 8)
2896f398035fSHerbert Xu 		return 0;
2897f398035fSHerbert Xu 
2898f398035fSHerbert Xu 	return (t->ealgos >> id) & 1;
28991da177e4SLinus Torvalds }
29001da177e4SLinus Torvalds 
count_ah_combs(const struct xfrm_tmpl * t)29014c93fbb0SDavid S. Miller static int count_ah_combs(const struct xfrm_tmpl *t)
29021da177e4SLinus Torvalds {
29031da177e4SLinus Torvalds 	int i, sz = 0;
29041da177e4SLinus Torvalds 
29051da177e4SLinus Torvalds 	for (i = 0; ; i++) {
29064c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
29071da177e4SLinus Torvalds 		if (!aalg)
29081da177e4SLinus Torvalds 			break;
29097e50f84cSJussi Kivilinna 		if (!aalg->pfkey_supported)
29107e50f84cSJussi Kivilinna 			continue;
29117f57f816SHerbert Xu 		if (aalg_tmpl_set(t, aalg))
29121da177e4SLinus Torvalds 			sz += sizeof(struct sadb_comb);
29131da177e4SLinus Torvalds 	}
29141da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
29151da177e4SLinus Torvalds }
29161da177e4SLinus Torvalds 
count_esp_combs(const struct xfrm_tmpl * t)29174c93fbb0SDavid S. Miller static int count_esp_combs(const struct xfrm_tmpl *t)
29181da177e4SLinus Torvalds {
29191da177e4SLinus Torvalds 	int i, k, sz = 0;
29201da177e4SLinus Torvalds 
29211da177e4SLinus Torvalds 	for (i = 0; ; i++) {
29224c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
29231da177e4SLinus Torvalds 		if (!ealg)
29241da177e4SLinus Torvalds 			break;
29251da177e4SLinus Torvalds 
29267e50f84cSJussi Kivilinna 		if (!ealg->pfkey_supported)
29277e50f84cSJussi Kivilinna 			continue;
29287e50f84cSJussi Kivilinna 
29297f57f816SHerbert Xu 		if (!(ealg_tmpl_set(t, ealg)))
29301da177e4SLinus Torvalds 			continue;
29311da177e4SLinus Torvalds 
29321da177e4SLinus Torvalds 		for (k = 1; ; k++) {
29334c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
29341da177e4SLinus Torvalds 			if (!aalg)
29351da177e4SLinus Torvalds 				break;
29361da177e4SLinus Torvalds 
29377e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
29387e50f84cSJussi Kivilinna 				continue;
29397e50f84cSJussi Kivilinna 
29407f57f816SHerbert Xu 			if (aalg_tmpl_set(t, aalg))
29411da177e4SLinus Torvalds 				sz += sizeof(struct sadb_comb);
29421da177e4SLinus Torvalds 		}
29431da177e4SLinus Torvalds 	}
29441da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
29451da177e4SLinus Torvalds }
29461da177e4SLinus Torvalds 
dump_ah_combs(struct sk_buff * skb,const struct xfrm_tmpl * t)29477f57f816SHerbert Xu static int dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
29481da177e4SLinus Torvalds {
29491da177e4SLinus Torvalds 	struct sadb_prop *p;
29507f57f816SHerbert Xu 	int sz = 0;
29511da177e4SLinus Torvalds 	int i;
29521da177e4SLinus Torvalds 
29534df864c1SJohannes Berg 	p = skb_put(skb, sizeof(struct sadb_prop));
29541da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
29551da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
29561da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
29571da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
29581da177e4SLinus Torvalds 
29591da177e4SLinus Torvalds 	for (i = 0; ; i++) {
29604c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
29611da177e4SLinus Torvalds 		if (!aalg)
29621da177e4SLinus Torvalds 			break;
29631da177e4SLinus Torvalds 
29647e50f84cSJussi Kivilinna 		if (!aalg->pfkey_supported)
29657e50f84cSJussi Kivilinna 			continue;
29667e50f84cSJussi Kivilinna 
29671da177e4SLinus Torvalds 		if (aalg_tmpl_set(t, aalg) && aalg->available) {
29681da177e4SLinus Torvalds 			struct sadb_comb *c;
2969b080db58SJohannes Berg 			c = skb_put_zero(skb, sizeof(struct sadb_comb));
29701da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
29711da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
29721da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
29731da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
29741da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
29751da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
29761da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
29771da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
29787f57f816SHerbert Xu 			sz += sizeof(*c);
29791da177e4SLinus Torvalds 		}
29801da177e4SLinus Torvalds 	}
29811da177e4SLinus Torvalds 
29827f57f816SHerbert Xu 	return sz + sizeof(*p);
29837f57f816SHerbert Xu }
29847f57f816SHerbert Xu 
dump_esp_combs(struct sk_buff * skb,const struct xfrm_tmpl * t)29857f57f816SHerbert Xu static int dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
29861da177e4SLinus Torvalds {
29871da177e4SLinus Torvalds 	struct sadb_prop *p;
29887f57f816SHerbert Xu 	int sz = 0;
29891da177e4SLinus Torvalds 	int i, k;
29901da177e4SLinus Torvalds 
29914df864c1SJohannes Berg 	p = skb_put(skb, sizeof(struct sadb_prop));
29921da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
29931da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
29941da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
29951da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
29961da177e4SLinus Torvalds 
29971da177e4SLinus Torvalds 	for (i=0; ; i++) {
29984c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
29991da177e4SLinus Torvalds 		if (!ealg)
30001da177e4SLinus Torvalds 			break;
30011da177e4SLinus Torvalds 
30027e50f84cSJussi Kivilinna 		if (!ealg->pfkey_supported)
30037e50f84cSJussi Kivilinna 			continue;
30047e50f84cSJussi Kivilinna 
30051da177e4SLinus Torvalds 		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
30061da177e4SLinus Torvalds 			continue;
30071da177e4SLinus Torvalds 
30081da177e4SLinus Torvalds 		for (k = 1; ; k++) {
30091da177e4SLinus Torvalds 			struct sadb_comb *c;
30104c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
30111da177e4SLinus Torvalds 			if (!aalg)
30121da177e4SLinus Torvalds 				break;
30137e50f84cSJussi Kivilinna 			if (!aalg->pfkey_supported)
30147e50f84cSJussi Kivilinna 				continue;
30151da177e4SLinus Torvalds 			if (!(aalg_tmpl_set(t, aalg) && aalg->available))
30161da177e4SLinus Torvalds 				continue;
30174df864c1SJohannes Berg 			c = skb_put(skb, sizeof(struct sadb_comb));
30181da177e4SLinus Torvalds 			memset(c, 0, sizeof(*c));
30191da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
30201da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
30211da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
30221da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
30231da177e4SLinus Torvalds 			c->sadb_comb_encrypt = ealg->desc.sadb_alg_id;
30241da177e4SLinus Torvalds 			c->sadb_comb_encrypt_minbits = ealg->desc.sadb_alg_minbits;
30251da177e4SLinus Torvalds 			c->sadb_comb_encrypt_maxbits = ealg->desc.sadb_alg_maxbits;
30261da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
30271da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
30281da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
30291da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
30307f57f816SHerbert Xu 			sz += sizeof(*c);
30311da177e4SLinus Torvalds 		}
30321da177e4SLinus Torvalds 	}
30337f57f816SHerbert Xu 
30347f57f816SHerbert Xu 	return sz + sizeof(*p);
30351da177e4SLinus Torvalds }
30361da177e4SLinus Torvalds 
key_notify_policy_expire(struct xfrm_policy * xp,const struct km_event * c)3037214e005bSDavid S. Miller static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c)
303826b15dadSJamal Hadi Salim {
303926b15dadSJamal Hadi Salim 	return 0;
304026b15dadSJamal Hadi Salim }
304126b15dadSJamal Hadi Salim 
key_notify_sa_expire(struct xfrm_state * x,const struct km_event * c)3042214e005bSDavid S. Miller static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
30431da177e4SLinus Torvalds {
30441da177e4SLinus Torvalds 	struct sk_buff *out_skb;
30451da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
304626b15dadSJamal Hadi Salim 	int hard;
304726b15dadSJamal Hadi Salim 	int hsc;
304826b15dadSJamal Hadi Salim 
3049bf08867fSHerbert Xu 	hard = c->data.hard;
305026b15dadSJamal Hadi Salim 	if (hard)
305126b15dadSJamal Hadi Salim 		hsc = 2;
305226b15dadSJamal Hadi Salim 	else
305326b15dadSJamal Hadi Salim 		hsc = 1;
30541da177e4SLinus Torvalds 
3055050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg_expire(x, hsc);
30561da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
30571da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
30581da177e4SLinus Torvalds 
30591da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
30601da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = PF_KEY_V2;
30611da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_EXPIRE;
30621da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
30631da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
30641da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
30651da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = 0;
30661da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = 0;
30671da177e4SLinus Torvalds 
306836f41f8fSEric Dumazet 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
306936f41f8fSEric Dumazet 			xs_net(x));
30701da177e4SLinus Torvalds 	return 0;
30711da177e4SLinus Torvalds }
30721da177e4SLinus Torvalds 
pfkey_send_notify(struct xfrm_state * x,const struct km_event * c)3073214e005bSDavid S. Miller static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
307426b15dadSJamal Hadi Salim {
307507fb0f17SAlexey Dobriyan 	struct net *net = x ? xs_net(x) : c->net;
30763fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
30773fa87a32SAlexey Dobriyan 
30783fa87a32SAlexey Dobriyan 	if (atomic_read(&net_pfkey->socks_nr) == 0)
307999c6f60eSJamal Hadi Salim 		return 0;
308099c6f60eSJamal Hadi Salim 
308126b15dadSJamal Hadi Salim 	switch (c->event) {
3082f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
308326b15dadSJamal Hadi Salim 		return key_notify_sa_expire(x, c);
3084f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
3085f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
3086f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
308726b15dadSJamal Hadi Salim 		return key_notify_sa(x, c);
3088f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHSA:
308926b15dadSJamal Hadi Salim 		return key_notify_sa_flush(c);
3090d51d081dSJamal Hadi Salim 	case XFRM_MSG_NEWAE: /* not yet supported */
3091d51d081dSJamal Hadi Salim 		break;
309226b15dadSJamal Hadi Salim 	default:
3093207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", c->event);
309426b15dadSJamal Hadi Salim 		break;
309526b15dadSJamal Hadi Salim 	}
309626b15dadSJamal Hadi Salim 
309726b15dadSJamal Hadi Salim 	return 0;
309826b15dadSJamal Hadi Salim }
309926b15dadSJamal Hadi Salim 
pfkey_send_policy_notify(struct xfrm_policy * xp,int dir,const struct km_event * c)3100214e005bSDavid S. Miller static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
310126b15dadSJamal Hadi Salim {
3102f7b6983fSMasahide NAKAMURA 	if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
3103f7b6983fSMasahide NAKAMURA 		return 0;
3104f7b6983fSMasahide NAKAMURA 
310526b15dadSJamal Hadi Salim 	switch (c->event) {
3106f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
310726b15dadSJamal Hadi Salim 		return key_notify_policy_expire(xp, c);
3108f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
3109f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
3110f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
311126b15dadSJamal Hadi Salim 		return key_notify_policy(xp, dir, c);
3112f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHPOLICY:
3113f7b6983fSMasahide NAKAMURA 		if (c->data.type != XFRM_POLICY_TYPE_MAIN)
3114f7b6983fSMasahide NAKAMURA 			break;
311526b15dadSJamal Hadi Salim 		return key_notify_policy_flush(c);
311626b15dadSJamal Hadi Salim 	default:
3117207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", c->event);
311826b15dadSJamal Hadi Salim 		break;
311926b15dadSJamal Hadi Salim 	}
312026b15dadSJamal Hadi Salim 
312126b15dadSJamal Hadi Salim 	return 0;
312226b15dadSJamal Hadi Salim }
312326b15dadSJamal Hadi Salim 
get_acqseq(void)31241da177e4SLinus Torvalds static u32 get_acqseq(void)
31251da177e4SLinus Torvalds {
31261da177e4SLinus Torvalds 	u32 res;
312728aecb9dSEric Dumazet 	static atomic_t acqseq;
31281da177e4SLinus Torvalds 
312928aecb9dSEric Dumazet 	do {
313028aecb9dSEric Dumazet 		res = atomic_inc_return(&acqseq);
313128aecb9dSEric Dumazet 	} while (!res);
31321da177e4SLinus Torvalds 	return res;
31331da177e4SLinus Torvalds }
31341da177e4SLinus Torvalds 
pfkey_is_alive(const struct km_event * c)31350f24558eSHoria Geanta static bool pfkey_is_alive(const struct km_event *c)
31360f24558eSHoria Geanta {
31370f24558eSHoria Geanta 	struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
31380f24558eSHoria Geanta 	struct sock *sk;
31390f24558eSHoria Geanta 	bool is_alive = false;
31400f24558eSHoria Geanta 
31410f24558eSHoria Geanta 	rcu_read_lock();
31420f24558eSHoria Geanta 	sk_for_each_rcu(sk, &net_pfkey->table) {
31430f24558eSHoria Geanta 		if (pfkey_sk(sk)->registered) {
31440f24558eSHoria Geanta 			is_alive = true;
31450f24558eSHoria Geanta 			break;
31460f24558eSHoria Geanta 		}
31470f24558eSHoria Geanta 	}
31480f24558eSHoria Geanta 	rcu_read_unlock();
31490f24558eSHoria Geanta 
31500f24558eSHoria Geanta 	return is_alive;
31510f24558eSHoria Geanta }
31520f24558eSHoria Geanta 
pfkey_send_acquire(struct xfrm_state * x,struct xfrm_tmpl * t,struct xfrm_policy * xp)315365e0736bSFan Du static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
31541da177e4SLinus Torvalds {
31551da177e4SLinus Torvalds 	struct sk_buff *skb;
31561da177e4SLinus Torvalds 	struct sadb_msg *hdr;
31571da177e4SLinus Torvalds 	struct sadb_address *addr;
31581da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
31591da177e4SLinus Torvalds 	int sockaddr_size;
31601da177e4SLinus Torvalds 	int size;
31614e2ba18eSVenkat Yekkirala 	struct sadb_x_sec_ctx *sec_ctx;
31624e2ba18eSVenkat Yekkirala 	struct xfrm_sec_ctx *xfrm_ctx;
31634e2ba18eSVenkat Yekkirala 	int ctx_size = 0;
31647f57f816SHerbert Xu 	int alg_size = 0;
31651da177e4SLinus Torvalds 
31661da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
31671da177e4SLinus Torvalds 	if (!sockaddr_size)
31681da177e4SLinus Torvalds 		return -EINVAL;
31691da177e4SLinus Torvalds 
31701da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
31711da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
31721da177e4SLinus Torvalds 		(sockaddr_size * 2) +
31731da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy);
31741da177e4SLinus Torvalds 
31751da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
31767f57f816SHerbert Xu 		alg_size = count_ah_combs(t);
31771da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
31787f57f816SHerbert Xu 		alg_size = count_esp_combs(t);
31791da177e4SLinus Torvalds 
31804e2ba18eSVenkat Yekkirala 	if ((xfrm_ctx = x->security)) {
31814e2ba18eSVenkat Yekkirala 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
31824e2ba18eSVenkat Yekkirala 		size +=  sizeof(struct sadb_x_sec_ctx) + ctx_size;
31834e2ba18eSVenkat Yekkirala 	}
31844e2ba18eSVenkat Yekkirala 
31857f57f816SHerbert Xu 	skb =  alloc_skb(size + alg_size + 16, GFP_ATOMIC);
31861da177e4SLinus Torvalds 	if (skb == NULL)
31871da177e4SLinus Torvalds 		return -ENOMEM;
31881da177e4SLinus Torvalds 
31894df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
31901da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
31911da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_ACQUIRE;
31921da177e4SLinus Torvalds 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
31931da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
31941da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
31951da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
31961da177e4SLinus Torvalds 	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
31971da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
31981da177e4SLinus Torvalds 
31991da177e4SLinus Torvalds 	/* src address */
32004df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
32011da177e4SLinus Torvalds 	addr->sadb_address_len =
32021da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
32031da177e4SLinus Torvalds 			sizeof(uint64_t);
32041da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
32051da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
32061da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3207e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3208e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3209e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3210e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3211e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
32121da177e4SLinus Torvalds 		BUG();
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds 	/* dst address */
32154df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
32161da177e4SLinus Torvalds 	addr->sadb_address_len =
32171da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
32181da177e4SLinus Torvalds 			sizeof(uint64_t);
32191da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
32201da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
32211da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3222e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3223e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
3224e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3225e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3226e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
32271da177e4SLinus Torvalds 		BUG();
32281da177e4SLinus Torvalds 
32294df864c1SJohannes Berg 	pol = skb_put(skb, sizeof(struct sadb_x_policy));
32301da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
32311da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
32321da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
323365e0736bSFan Du 	pol->sadb_x_policy_dir = XFRM_POLICY_OUT + 1;
3234ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
32351da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
3236ff862a46SDan Carpenter 	pol->sadb_x_policy_priority = xp->priority;
32371da177e4SLinus Torvalds 
32381da177e4SLinus Torvalds 	/* Set sadb_comb's. */
32397f57f816SHerbert Xu 	alg_size = 0;
32401da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
32417f57f816SHerbert Xu 		alg_size = dump_ah_combs(skb, t);
32421da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
32437f57f816SHerbert Xu 		alg_size = dump_esp_combs(skb, t);
32447f57f816SHerbert Xu 
32457f57f816SHerbert Xu 	hdr->sadb_msg_len += alg_size / 8;
32461da177e4SLinus Torvalds 
32474e2ba18eSVenkat Yekkirala 	/* security context */
32484e2ba18eSVenkat Yekkirala 	if (xfrm_ctx) {
32494df864c1SJohannes Berg 		sec_ctx = skb_put(skb,
32504e2ba18eSVenkat Yekkirala 				  sizeof(struct sadb_x_sec_ctx) + ctx_size);
32514e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_len =
32524e2ba18eSVenkat Yekkirala 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
32534e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
32544e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
32554e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
32564e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
32574e2ba18eSVenkat Yekkirala 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
32584e2ba18eSVenkat Yekkirala 		       xfrm_ctx->ctx_len);
32594e2ba18eSVenkat Yekkirala 	}
32604e2ba18eSVenkat Yekkirala 
326136f41f8fSEric Dumazet 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
326236f41f8fSEric Dumazet 			       xs_net(x));
32631da177e4SLinus Torvalds }
32641da177e4SLinus Torvalds 
pfkey_compile_policy(struct sock * sk,int opt,u8 * data,int len,int * dir)3265cb969f07SVenkat Yekkirala static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
32661da177e4SLinus Torvalds 						u8 *data, int len, int *dir)
32671da177e4SLinus Torvalds {
326807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
32691da177e4SLinus Torvalds 	struct xfrm_policy *xp;
32701da177e4SLinus Torvalds 	struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
3271df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
32721da177e4SLinus Torvalds 
3273cb969f07SVenkat Yekkirala 	switch (sk->sk_family) {
32741da177e4SLinus Torvalds 	case AF_INET:
32751da177e4SLinus Torvalds 		if (opt != IP_IPSEC_POLICY) {
32761da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
32771da177e4SLinus Torvalds 			return NULL;
32781da177e4SLinus Torvalds 		}
32791da177e4SLinus Torvalds 		break;
3280dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
32811da177e4SLinus Torvalds 	case AF_INET6:
32821da177e4SLinus Torvalds 		if (opt != IPV6_IPSEC_POLICY) {
32831da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
32841da177e4SLinus Torvalds 			return NULL;
32851da177e4SLinus Torvalds 		}
32861da177e4SLinus Torvalds 		break;
32871da177e4SLinus Torvalds #endif
32881da177e4SLinus Torvalds 	default:
32891da177e4SLinus Torvalds 		*dir = -EINVAL;
32901da177e4SLinus Torvalds 		return NULL;
32911da177e4SLinus Torvalds 	}
32921da177e4SLinus Torvalds 
32931da177e4SLinus Torvalds 	*dir = -EINVAL;
32941da177e4SLinus Torvalds 
32951da177e4SLinus Torvalds 	if (len < sizeof(struct sadb_x_policy) ||
32961da177e4SLinus Torvalds 	    pol->sadb_x_policy_len*8 > len ||
32971da177e4SLinus Torvalds 	    pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
32981da177e4SLinus Torvalds 	    (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
32991da177e4SLinus Torvalds 		return NULL;
33001da177e4SLinus Torvalds 
330107fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_ATOMIC);
33021da177e4SLinus Torvalds 	if (xp == NULL) {
33031da177e4SLinus Torvalds 		*dir = -ENOBUFS;
33041da177e4SLinus Torvalds 		return NULL;
33051da177e4SLinus Torvalds 	}
33061da177e4SLinus Torvalds 
33071da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
33081da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
33091da177e4SLinus Torvalds 
33101da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
33111da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
33121da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
33131da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
3314cb969f07SVenkat Yekkirala 	xp->family = sk->sk_family;
33151da177e4SLinus Torvalds 
33161da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
33171da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
33181da177e4SLinus Torvalds 	    (*dir = parse_ipsecrequests(xp, pol)) < 0)
33191da177e4SLinus Torvalds 		goto out;
33201da177e4SLinus Torvalds 
3321df71837dSTrent Jaeger 	/* security context too */
3322df71837dSTrent Jaeger 	if (len >= (pol->sadb_x_policy_len*8 +
3323df71837dSTrent Jaeger 	    sizeof(struct sadb_x_sec_ctx))) {
3324df71837dSTrent Jaeger 		char *p = (char *)pol;
3325df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx;
3326df71837dSTrent Jaeger 
3327df71837dSTrent Jaeger 		p += pol->sadb_x_policy_len*8;
3328df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *)p;
3329df71837dSTrent Jaeger 		if (len < pol->sadb_x_policy_len*8 +
3330d90c9024SSteffen Klassert 		    sec_ctx->sadb_x_sec_len*8) {
3331cb969f07SVenkat Yekkirala 			*dir = -EINVAL;
3332df71837dSTrent Jaeger 			goto out;
3333cb969f07SVenkat Yekkirala 		}
3334df71837dSTrent Jaeger 		if ((*dir = verify_sec_ctx_len(p)))
3335df71837dSTrent Jaeger 			goto out;
333687536a81SNikolay Aleksandrov 		uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_ATOMIC);
333752a4c640SNikolay Aleksandrov 		*dir = security_xfrm_policy_alloc(&xp->security, uctx, GFP_ATOMIC);
3338df71837dSTrent Jaeger 		kfree(uctx);
3339df71837dSTrent Jaeger 
3340df71837dSTrent Jaeger 		if (*dir)
3341df71837dSTrent Jaeger 			goto out;
3342df71837dSTrent Jaeger 	}
3343df71837dSTrent Jaeger 
33441da177e4SLinus Torvalds 	*dir = pol->sadb_x_policy_dir-1;
33451da177e4SLinus Torvalds 	return xp;
33461da177e4SLinus Torvalds 
33471da177e4SLinus Torvalds out:
334870e90679SAlexey Dobriyan 	xp->walk.dead = 1;
334964c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
33501da177e4SLinus Torvalds 	return NULL;
33511da177e4SLinus Torvalds }
33521da177e4SLinus Torvalds 
pfkey_send_new_mapping(struct xfrm_state * x,xfrm_address_t * ipaddr,__be16 sport)33535d36b180SAl Viro static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
33541da177e4SLinus Torvalds {
33551da177e4SLinus Torvalds 	struct sk_buff *skb;
33561da177e4SLinus Torvalds 	struct sadb_msg *hdr;
33571da177e4SLinus Torvalds 	struct sadb_sa *sa;
33581da177e4SLinus Torvalds 	struct sadb_address *addr;
33591da177e4SLinus Torvalds 	struct sadb_x_nat_t_port *n_port;
33601da177e4SLinus Torvalds 	int sockaddr_size;
33611da177e4SLinus Torvalds 	int size;
33621da177e4SLinus Torvalds 	__u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
33631da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
33641da177e4SLinus Torvalds 
33651da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
33661da177e4SLinus Torvalds 	if (!sockaddr_size)
33671da177e4SLinus Torvalds 		return -EINVAL;
33681da177e4SLinus Torvalds 
33691da177e4SLinus Torvalds 	if (!satype)
33701da177e4SLinus Torvalds 		return -EINVAL;
33711da177e4SLinus Torvalds 
33721da177e4SLinus Torvalds 	if (!x->encap)
33731da177e4SLinus Torvalds 		return -EINVAL;
33741da177e4SLinus Torvalds 
33751da177e4SLinus Torvalds 	natt = x->encap;
33761da177e4SLinus Torvalds 
33771da177e4SLinus Torvalds 	/* Build an SADB_X_NAT_T_NEW_MAPPING message:
33781da177e4SLinus Torvalds 	 *
33791da177e4SLinus Torvalds 	 * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
33801da177e4SLinus Torvalds 	 * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
33811da177e4SLinus Torvalds 	 */
33821da177e4SLinus Torvalds 
33831da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
33841da177e4SLinus Torvalds 		sizeof(struct sadb_sa) +
33851da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
33861da177e4SLinus Torvalds 		(sockaddr_size * 2) +
33871da177e4SLinus Torvalds 		(sizeof(struct sadb_x_nat_t_port) * 2);
33881da177e4SLinus Torvalds 
33891da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
33901da177e4SLinus Torvalds 	if (skb == NULL)
33911da177e4SLinus Torvalds 		return -ENOMEM;
33921da177e4SLinus Torvalds 
33934df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
33941da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
33951da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
33961da177e4SLinus Torvalds 	hdr->sadb_msg_satype = satype;
33971da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
33981da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
33991da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
3400b97df039SThomas Jarosch 	hdr->sadb_msg_seq = x->km.seq;
34011da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
34021da177e4SLinus Torvalds 
34031da177e4SLinus Torvalds 	/* SA */
34044df864c1SJohannes Berg 	sa = skb_put(skb, sizeof(struct sadb_sa));
34051da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
34061da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
34071da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
34081da177e4SLinus Torvalds 	sa->sadb_sa_replay = 0;
34091da177e4SLinus Torvalds 	sa->sadb_sa_state = 0;
34101da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
34111da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
34121da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
34131da177e4SLinus Torvalds 
34141da177e4SLinus Torvalds 	/* ADDRESS_SRC (old addr) */
34154df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
34161da177e4SLinus Torvalds 	addr->sadb_address_len =
34171da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
34181da177e4SLinus Torvalds 			sizeof(uint64_t);
34191da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
34201da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
34211da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3422e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3423e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3424e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3425e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3426e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
34271da177e4SLinus Torvalds 		BUG();
34281da177e4SLinus Torvalds 
34291da177e4SLinus Torvalds 	/* NAT_T_SPORT (old port) */
34304df864c1SJohannes Berg 	n_port = skb_put(skb, sizeof(*n_port));
34311da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
34321da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
34331da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = natt->encap_sport;
34341da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
34351da177e4SLinus Torvalds 
34361da177e4SLinus Torvalds 	/* ADDRESS_DST (new addr) */
34374df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
34381da177e4SLinus Torvalds 	addr->sadb_address_len =
34391da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
34401da177e4SLinus Torvalds 			sizeof(uint64_t);
34411da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
34421da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
34431da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3444e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3445e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(ipaddr, 0,
3446e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3447e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3448e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
34491da177e4SLinus Torvalds 		BUG();
34501da177e4SLinus Torvalds 
34511da177e4SLinus Torvalds 	/* NAT_T_DPORT (new port) */
34524df864c1SJohannes Berg 	n_port = skb_put(skb, sizeof(*n_port));
34531da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
34541da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
34551da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = sport;
34561da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
34571da177e4SLinus Torvalds 
345836f41f8fSEric Dumazet 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
345936f41f8fSEric Dumazet 			       xs_net(x));
34601da177e4SLinus Torvalds }
34611da177e4SLinus Torvalds 
346208de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
set_sadb_address(struct sk_buff * skb,int sasize,int type,const struct xfrm_selector * sel)346308de61beSShinta Sugimoto static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
3464183cad12SDavid S. Miller 			    const struct xfrm_selector *sel)
346508de61beSShinta Sugimoto {
346608de61beSShinta Sugimoto 	struct sadb_address *addr;
34674df864c1SJohannes Berg 	addr = skb_put(skb, sizeof(struct sadb_address) + sasize);
346808de61beSShinta Sugimoto 	addr->sadb_address_len = (sizeof(struct sadb_address) + sasize)/8;
346908de61beSShinta Sugimoto 	addr->sadb_address_exttype = type;
347008de61beSShinta Sugimoto 	addr->sadb_address_proto = sel->proto;
347108de61beSShinta Sugimoto 	addr->sadb_address_reserved = 0;
347208de61beSShinta Sugimoto 
347308de61beSShinta Sugimoto 	switch (type) {
347408de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_SRC:
347508de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_s;
3476e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->saddr, 0,
3477e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3478e5b56652SYOSHIFUJI Hideaki 				    sel->family);
347908de61beSShinta Sugimoto 		break;
348008de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_DST:
348108de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_d;
3482e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->daddr, 0,
3483e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3484e5b56652SYOSHIFUJI Hideaki 				    sel->family);
348508de61beSShinta Sugimoto 		break;
348608de61beSShinta Sugimoto 	default:
348708de61beSShinta Sugimoto 		return -EINVAL;
348808de61beSShinta Sugimoto 	}
348908de61beSShinta Sugimoto 
349008de61beSShinta Sugimoto 	return 0;
349108de61beSShinta Sugimoto }
349208de61beSShinta Sugimoto 
349313c1d189SArnaud Ebalard 
set_sadb_kmaddress(struct sk_buff * skb,const struct xfrm_kmaddress * k)3494183cad12SDavid S. Miller static int set_sadb_kmaddress(struct sk_buff *skb, const struct xfrm_kmaddress *k)
349513c1d189SArnaud Ebalard {
349613c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
349713c1d189SArnaud Ebalard 	u8 *sa;
349813c1d189SArnaud Ebalard 	int family = k->family;
349913c1d189SArnaud Ebalard 	int socklen = pfkey_sockaddr_len(family);
350013c1d189SArnaud Ebalard 	int size_req;
350113c1d189SArnaud Ebalard 
350213c1d189SArnaud Ebalard 	size_req = (sizeof(struct sadb_x_kmaddress) +
350313c1d189SArnaud Ebalard 		    pfkey_sockaddr_pair_size(family));
350413c1d189SArnaud Ebalard 
3505b080db58SJohannes Berg 	kma = skb_put_zero(skb, size_req);
350613c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_len = size_req / 8;
350713c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_exttype = SADB_X_EXT_KMADDRESS;
350813c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_reserved = k->reserved;
350913c1d189SArnaud Ebalard 
351013c1d189SArnaud Ebalard 	sa = (u8 *)(kma + 1);
351113c1d189SArnaud Ebalard 	if (!pfkey_sockaddr_fill(&k->local, 0, (struct sockaddr *)sa, family) ||
351213c1d189SArnaud Ebalard 	    !pfkey_sockaddr_fill(&k->remote, 0, (struct sockaddr *)(sa+socklen), family))
351313c1d189SArnaud Ebalard 		return -EINVAL;
351413c1d189SArnaud Ebalard 
351513c1d189SArnaud Ebalard 	return 0;
351613c1d189SArnaud Ebalard }
351713c1d189SArnaud Ebalard 
set_ipsecrequest(struct sk_buff * skb,uint8_t proto,uint8_t mode,int level,uint32_t reqid,uint8_t family,const xfrm_address_t * src,const xfrm_address_t * dst)351808de61beSShinta Sugimoto static int set_ipsecrequest(struct sk_buff *skb,
351908de61beSShinta Sugimoto 			    uint8_t proto, uint8_t mode, int level,
352008de61beSShinta Sugimoto 			    uint32_t reqid, uint8_t family,
3521183cad12SDavid S. Miller 			    const xfrm_address_t *src, const xfrm_address_t *dst)
352208de61beSShinta Sugimoto {
352308de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
3524e5b56652SYOSHIFUJI Hideaki 	u8 *sa;
3525e5b56652SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(family);
352608de61beSShinta Sugimoto 	int size_req;
352708de61beSShinta Sugimoto 
352808de61beSShinta Sugimoto 	size_req = sizeof(struct sadb_x_ipsecrequest) +
352908de61beSShinta Sugimoto 		   pfkey_sockaddr_pair_size(family);
353008de61beSShinta Sugimoto 
3531b080db58SJohannes Berg 	rq = skb_put_zero(skb, size_req);
353208de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_len = size_req;
353308de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_proto = proto;
353408de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_mode = mode;
353508de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_level = level;
353608de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_reqid = reqid;
353708de61beSShinta Sugimoto 
3538e5b56652SYOSHIFUJI Hideaki 	sa = (u8 *) (rq + 1);
3539e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(src, 0, (struct sockaddr *)sa, family) ||
3540e5b56652SYOSHIFUJI Hideaki 	    !pfkey_sockaddr_fill(dst, 0, (struct sockaddr *)(sa + socklen), family))
354108de61beSShinta Sugimoto 		return -EINVAL;
354208de61beSShinta Sugimoto 
354308de61beSShinta Sugimoto 	return 0;
354408de61beSShinta Sugimoto }
354508de61beSShinta Sugimoto #endif
354608de61beSShinta Sugimoto 
354708de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
pfkey_send_migrate(const struct xfrm_selector * sel,u8 dir,u8 type,const struct xfrm_migrate * m,int num_bundles,const struct xfrm_kmaddress * k,const struct xfrm_encap_tmpl * encap)3548183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3549183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
35508bafd730SAntony Antony 			      const struct xfrm_kmaddress *k,
35518bafd730SAntony Antony 			      const struct xfrm_encap_tmpl *encap)
355208de61beSShinta Sugimoto {
355308de61beSShinta Sugimoto 	int i;
355408de61beSShinta Sugimoto 	int sasize_sel;
355508de61beSShinta Sugimoto 	int size = 0;
355608de61beSShinta Sugimoto 	int size_pol = 0;
355708de61beSShinta Sugimoto 	struct sk_buff *skb;
355808de61beSShinta Sugimoto 	struct sadb_msg *hdr;
355908de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
3560183cad12SDavid S. Miller 	const struct xfrm_migrate *mp;
356108de61beSShinta Sugimoto 
356208de61beSShinta Sugimoto 	if (type != XFRM_POLICY_TYPE_MAIN)
356308de61beSShinta Sugimoto 		return 0;
356408de61beSShinta Sugimoto 
356508de61beSShinta Sugimoto 	if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH)
356608de61beSShinta Sugimoto 		return -EINVAL;
356708de61beSShinta Sugimoto 
356813c1d189SArnaud Ebalard 	if (k != NULL) {
356913c1d189SArnaud Ebalard 		/* addresses for KM */
357013c1d189SArnaud Ebalard 		size += PFKEY_ALIGN8(sizeof(struct sadb_x_kmaddress) +
357113c1d189SArnaud Ebalard 				     pfkey_sockaddr_pair_size(k->family));
357213c1d189SArnaud Ebalard 	}
357313c1d189SArnaud Ebalard 
357408de61beSShinta Sugimoto 	/* selector */
357508de61beSShinta Sugimoto 	sasize_sel = pfkey_sockaddr_size(sel->family);
357608de61beSShinta Sugimoto 	if (!sasize_sel)
357708de61beSShinta Sugimoto 		return -EINVAL;
357808de61beSShinta Sugimoto 	size += (sizeof(struct sadb_address) + sasize_sel) * 2;
357908de61beSShinta Sugimoto 
358008de61beSShinta Sugimoto 	/* policy info */
358108de61beSShinta Sugimoto 	size_pol += sizeof(struct sadb_x_policy);
358208de61beSShinta Sugimoto 
358308de61beSShinta Sugimoto 	/* ipsecrequests */
358408de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
358508de61beSShinta Sugimoto 		/* old locator pair */
358608de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
358708de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->old_family);
358808de61beSShinta Sugimoto 		/* new locator pair */
358908de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
359008de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->new_family);
359108de61beSShinta Sugimoto 	}
359208de61beSShinta Sugimoto 
359308de61beSShinta Sugimoto 	size += sizeof(struct sadb_msg) + size_pol;
359408de61beSShinta Sugimoto 
359508de61beSShinta Sugimoto 	/* alloc buffer */
359608de61beSShinta Sugimoto 	skb = alloc_skb(size, GFP_ATOMIC);
359708de61beSShinta Sugimoto 	if (skb == NULL)
359808de61beSShinta Sugimoto 		return -ENOMEM;
359908de61beSShinta Sugimoto 
36004df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(struct sadb_msg));
360108de61beSShinta Sugimoto 	hdr->sadb_msg_version = PF_KEY_V2;
360208de61beSShinta Sugimoto 	hdr->sadb_msg_type = SADB_X_MIGRATE;
360308de61beSShinta Sugimoto 	hdr->sadb_msg_satype = pfkey_proto2satype(m->proto);
360408de61beSShinta Sugimoto 	hdr->sadb_msg_len = size / 8;
360508de61beSShinta Sugimoto 	hdr->sadb_msg_errno = 0;
360608de61beSShinta Sugimoto 	hdr->sadb_msg_reserved = 0;
360708de61beSShinta Sugimoto 	hdr->sadb_msg_seq = 0;
360808de61beSShinta Sugimoto 	hdr->sadb_msg_pid = 0;
360908de61beSShinta Sugimoto 
361013c1d189SArnaud Ebalard 	/* Addresses to be used by KM for negotiation, if ext is available */
361113c1d189SArnaud Ebalard 	if (k != NULL && (set_sadb_kmaddress(skb, k) < 0))
361289eb06f1SJulia Lawall 		goto err;
361313c1d189SArnaud Ebalard 
361408de61beSShinta Sugimoto 	/* selector src */
361508de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel);
361608de61beSShinta Sugimoto 
361708de61beSShinta Sugimoto 	/* selector dst */
361808de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_DST, sel);
361908de61beSShinta Sugimoto 
362008de61beSShinta Sugimoto 	/* policy information */
36214df864c1SJohannes Berg 	pol = skb_put(skb, sizeof(struct sadb_x_policy));
362208de61beSShinta Sugimoto 	pol->sadb_x_policy_len = size_pol / 8;
362308de61beSShinta Sugimoto 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
362408de61beSShinta Sugimoto 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
362508de61beSShinta Sugimoto 	pol->sadb_x_policy_dir = dir + 1;
3626ff862a46SDan Carpenter 	pol->sadb_x_policy_reserved = 0;
362708de61beSShinta Sugimoto 	pol->sadb_x_policy_id = 0;
362808de61beSShinta Sugimoto 	pol->sadb_x_policy_priority = 0;
362908de61beSShinta Sugimoto 
363008de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
363108de61beSShinta Sugimoto 		/* old ipsecrequest */
363255569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_from_xfrm(mp->mode);
363355569ce2SKazunori MIYAZAWA 		if (mode < 0)
3634d4782c32SPatrick McHardy 			goto err;
363555569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
363608de61beSShinta Sugimoto 				     (mp->reqid ?  IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
363708de61beSShinta Sugimoto 				     mp->reqid, mp->old_family,
3638d4782c32SPatrick McHardy 				     &mp->old_saddr, &mp->old_daddr) < 0)
3639d4782c32SPatrick McHardy 			goto err;
364008de61beSShinta Sugimoto 
364108de61beSShinta Sugimoto 		/* new ipsecrequest */
364255569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
364308de61beSShinta Sugimoto 				     (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
364408de61beSShinta Sugimoto 				     mp->reqid, mp->new_family,
3645d4782c32SPatrick McHardy 				     &mp->new_saddr, &mp->new_daddr) < 0)
3646d4782c32SPatrick McHardy 			goto err;
364708de61beSShinta Sugimoto 	}
364808de61beSShinta Sugimoto 
364908de61beSShinta Sugimoto 	/* broadcast migrate message to sockets */
365036f41f8fSEric Dumazet 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
365108de61beSShinta Sugimoto 
365208de61beSShinta Sugimoto 	return 0;
3653d4782c32SPatrick McHardy 
3654d4782c32SPatrick McHardy err:
3655d4782c32SPatrick McHardy 	kfree_skb(skb);
3656d4782c32SPatrick McHardy 	return -EINVAL;
365708de61beSShinta Sugimoto }
365808de61beSShinta Sugimoto #else
pfkey_send_migrate(const struct xfrm_selector * sel,u8 dir,u8 type,const struct xfrm_migrate * m,int num_bundles,const struct xfrm_kmaddress * k,const struct xfrm_encap_tmpl * encap)3659183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3660183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
36618bafd730SAntony Antony 			      const struct xfrm_kmaddress *k,
36628bafd730SAntony Antony 			      const struct xfrm_encap_tmpl *encap)
366308de61beSShinta Sugimoto {
366408de61beSShinta Sugimoto 	return -ENOPROTOOPT;
366508de61beSShinta Sugimoto }
366608de61beSShinta Sugimoto #endif
366708de61beSShinta Sugimoto 
pfkey_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)36681b784140SYing Xue static int pfkey_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
36691da177e4SLinus Torvalds {
36701da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
36711da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
36721da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
36731da177e4SLinus Torvalds 	int err;
3674283bc9f3SFan Du 	struct net *net = sock_net(sk);
36751da177e4SLinus Torvalds 
36761da177e4SLinus Torvalds 	err = -EOPNOTSUPP;
36771da177e4SLinus Torvalds 	if (msg->msg_flags & MSG_OOB)
36781da177e4SLinus Torvalds 		goto out;
36791da177e4SLinus Torvalds 
36801da177e4SLinus Torvalds 	err = -EMSGSIZE;
368195c96174SEric Dumazet 	if ((unsigned int)len > sk->sk_sndbuf - 32)
36821da177e4SLinus Torvalds 		goto out;
36831da177e4SLinus Torvalds 
36841da177e4SLinus Torvalds 	err = -ENOBUFS;
36851da177e4SLinus Torvalds 	skb = alloc_skb(len, GFP_KERNEL);
36861da177e4SLinus Torvalds 	if (skb == NULL)
36871da177e4SLinus Torvalds 		goto out;
36881da177e4SLinus Torvalds 
36891da177e4SLinus Torvalds 	err = -EFAULT;
36906ce8e9ceSAl Viro 	if (memcpy_from_msg(skb_put(skb,len), msg, len))
36911da177e4SLinus Torvalds 		goto out;
36921da177e4SLinus Torvalds 
36931da177e4SLinus Torvalds 	hdr = pfkey_get_base_msg(skb, &err);
36941da177e4SLinus Torvalds 	if (!hdr)
36951da177e4SLinus Torvalds 		goto out;
36961da177e4SLinus Torvalds 
3697283bc9f3SFan Du 	mutex_lock(&net->xfrm.xfrm_cfg_mutex);
36981da177e4SLinus Torvalds 	err = pfkey_process(sk, skb, hdr);
3699283bc9f3SFan Du 	mutex_unlock(&net->xfrm.xfrm_cfg_mutex);
37001da177e4SLinus Torvalds 
37011da177e4SLinus Torvalds out:
37021da177e4SLinus Torvalds 	if (err && hdr && pfkey_error(hdr, err, sk) == 0)
37031da177e4SLinus Torvalds 		err = 0;
37041da177e4SLinus Torvalds 	kfree_skb(skb);
37051da177e4SLinus Torvalds 
37061da177e4SLinus Torvalds 	return err ? : len;
37071da177e4SLinus Torvalds }
37081da177e4SLinus Torvalds 
pfkey_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)37091b784140SYing Xue static int pfkey_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
37101da177e4SLinus Torvalds 			 int flags)
37111da177e4SLinus Torvalds {
37121da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
371383321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
37141da177e4SLinus Torvalds 	struct sk_buff *skb;
37151da177e4SLinus Torvalds 	int copied, err;
37161da177e4SLinus Torvalds 
37171da177e4SLinus Torvalds 	err = -EINVAL;
37181da177e4SLinus Torvalds 	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
37191da177e4SLinus Torvalds 		goto out;
37201da177e4SLinus Torvalds 
3721f4b41f06SOliver Hartkopp 	skb = skb_recv_datagram(sk, flags, &err);
37221da177e4SLinus Torvalds 	if (skb == NULL)
37231da177e4SLinus Torvalds 		goto out;
37241da177e4SLinus Torvalds 
37251da177e4SLinus Torvalds 	copied = skb->len;
37261da177e4SLinus Torvalds 	if (copied > len) {
37271da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
37281da177e4SLinus Torvalds 		copied = len;
37291da177e4SLinus Torvalds 	}
37301da177e4SLinus Torvalds 
3731badff6d0SArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
373251f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
37331da177e4SLinus Torvalds 	if (err)
37341da177e4SLinus Torvalds 		goto out_free;
37351da177e4SLinus Torvalds 
37366fd1d51cSErin MacNeil 	sock_recv_cmsgs(msg, sk, skb);
37371da177e4SLinus Torvalds 
37381da177e4SLinus Torvalds 	err = (flags & MSG_TRUNC) ? skb->len : copied;
37391da177e4SLinus Torvalds 
374083321d6bSTimo Teras 	if (pfk->dump.dump != NULL &&
374183321d6bSTimo Teras 	    3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
374283321d6bSTimo Teras 		pfkey_do_dump(pfk);
374383321d6bSTimo Teras 
37441da177e4SLinus Torvalds out_free:
37451da177e4SLinus Torvalds 	skb_free_datagram(sk, skb);
37461da177e4SLinus Torvalds out:
37471da177e4SLinus Torvalds 	return err;
37481da177e4SLinus Torvalds }
37491da177e4SLinus Torvalds 
375090ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops = {
37511da177e4SLinus Torvalds 	.family		=	PF_KEY,
37521da177e4SLinus Torvalds 	.owner		=	THIS_MODULE,
37531da177e4SLinus Torvalds 	/* Operations that make no sense on pfkey sockets. */
37541da177e4SLinus Torvalds 	.bind		=	sock_no_bind,
37551da177e4SLinus Torvalds 	.connect	=	sock_no_connect,
37561da177e4SLinus Torvalds 	.socketpair	=	sock_no_socketpair,
37571da177e4SLinus Torvalds 	.accept		=	sock_no_accept,
37581da177e4SLinus Torvalds 	.getname	=	sock_no_getname,
37591da177e4SLinus Torvalds 	.ioctl		=	sock_no_ioctl,
37601da177e4SLinus Torvalds 	.listen		=	sock_no_listen,
37611da177e4SLinus Torvalds 	.shutdown	=	sock_no_shutdown,
37621da177e4SLinus Torvalds 	.mmap		=	sock_no_mmap,
37631da177e4SLinus Torvalds 
37641da177e4SLinus Torvalds 	/* Now the operations that really occur. */
37651da177e4SLinus Torvalds 	.release	=	pfkey_release,
3766a11e1d43SLinus Torvalds 	.poll		=	datagram_poll,
37671da177e4SLinus Torvalds 	.sendmsg	=	pfkey_sendmsg,
37681da177e4SLinus Torvalds 	.recvmsg	=	pfkey_recvmsg,
37691da177e4SLinus Torvalds };
37701da177e4SLinus Torvalds 
3771ec1b4cf7SStephen Hemminger static const struct net_proto_family pfkey_family_ops = {
37721da177e4SLinus Torvalds 	.family	=	PF_KEY,
37731da177e4SLinus Torvalds 	.create	=	pfkey_create,
37741da177e4SLinus Torvalds 	.owner	=	THIS_MODULE,
37751da177e4SLinus Torvalds };
37761da177e4SLinus Torvalds 
37771da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
pfkey_seq_show(struct seq_file * f,void * v)3778bd2f7476SPavel Emelyanov static int pfkey_seq_show(struct seq_file *f, void *v)
37791da177e4SLinus Torvalds {
378027b5b865SLi Zefan 	struct sock *s = sk_entry(v);
37811da177e4SLinus Torvalds 
3782bd2f7476SPavel Emelyanov 	if (v == SEQ_START_TOKEN)
3783bd2f7476SPavel Emelyanov 		seq_printf(f ,"sk       RefCnt Rmem   Wmem   User   Inode\n");
3784bd2f7476SPavel Emelyanov 	else
378571338aa7SDan Rosenberg 		seq_printf(f, "%pK %-6d %-6u %-6u %-6u %-6lu\n",
37861da177e4SLinus Torvalds 			       s,
378741c6d650SReshetova, Elena 			       refcount_read(&s->sk_refcnt),
378831e6d363SEric Dumazet 			       sk_rmem_alloc_get(s),
378931e6d363SEric Dumazet 			       sk_wmem_alloc_get(s),
3790a7cb5a49SEric W. Biederman 			       from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
37911da177e4SLinus Torvalds 			       sock_i_ino(s)
37921da177e4SLinus Torvalds 			       );
3793bd2f7476SPavel Emelyanov 	return 0;
37941da177e4SLinus Torvalds }
37951da177e4SLinus Torvalds 
pfkey_seq_start(struct seq_file * f,loff_t * ppos)3796bd2f7476SPavel Emelyanov static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
3797ada440e3Sstephen hemminger 	__acquires(rcu)
3798bd2f7476SPavel Emelyanov {
37997013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
38003fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
3801bd2f7476SPavel Emelyanov 
38027f6b9dbdSstephen hemminger 	rcu_read_lock();
38037f6b9dbdSstephen hemminger 	return seq_hlist_start_head_rcu(&net_pfkey->table, *ppos);
3804bd2f7476SPavel Emelyanov }
3805bd2f7476SPavel Emelyanov 
pfkey_seq_next(struct seq_file * f,void * v,loff_t * ppos)3806bd2f7476SPavel Emelyanov static void *pfkey_seq_next(struct seq_file *f, void *v, loff_t *ppos)
3807bd2f7476SPavel Emelyanov {
38087013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
38093fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
38103fa87a32SAlexey Dobriyan 
38117f6b9dbdSstephen hemminger 	return seq_hlist_next_rcu(v, &net_pfkey->table, ppos);
3812bd2f7476SPavel Emelyanov }
3813bd2f7476SPavel Emelyanov 
pfkey_seq_stop(struct seq_file * f,void * v)3814bd2f7476SPavel Emelyanov static void pfkey_seq_stop(struct seq_file *f, void *v)
3815ada440e3Sstephen hemminger 	__releases(rcu)
3816bd2f7476SPavel Emelyanov {
38177f6b9dbdSstephen hemminger 	rcu_read_unlock();
38181da177e4SLinus Torvalds }
381961145aa1SPavel Emelyanov 
382098147d52SStephen Hemminger static const struct seq_operations pfkey_seq_ops = {
3821bd2f7476SPavel Emelyanov 	.start	= pfkey_seq_start,
3822bd2f7476SPavel Emelyanov 	.next	= pfkey_seq_next,
3823bd2f7476SPavel Emelyanov 	.stop	= pfkey_seq_stop,
3824bd2f7476SPavel Emelyanov 	.show	= pfkey_seq_show,
3825bd2f7476SPavel Emelyanov };
3826bd2f7476SPavel Emelyanov 
pfkey_init_proc(struct net * net)38277013ec30SAlexey Dobriyan static int __net_init pfkey_init_proc(struct net *net)
382861145aa1SPavel Emelyanov {
3829bd2f7476SPavel Emelyanov 	struct proc_dir_entry *e;
3830bd2f7476SPavel Emelyanov 
3831c3506372SChristoph Hellwig 	e = proc_create_net("pfkey", 0, net->proc_net, &pfkey_seq_ops,
3832c3506372SChristoph Hellwig 			sizeof(struct seq_net_private));
3833bd2f7476SPavel Emelyanov 	if (e == NULL)
383461145aa1SPavel Emelyanov 		return -ENOMEM;
3835bd2f7476SPavel Emelyanov 
383661145aa1SPavel Emelyanov 	return 0;
383761145aa1SPavel Emelyanov }
383861145aa1SPavel Emelyanov 
pfkey_exit_proc(struct net * net)38392c8c1e72SAlexey Dobriyan static void __net_exit pfkey_exit_proc(struct net *net)
384061145aa1SPavel Emelyanov {
3841ece31ffdSGao feng 	remove_proc_entry("pfkey", net->proc_net);
384261145aa1SPavel Emelyanov }
384361145aa1SPavel Emelyanov #else
pfkey_init_proc(struct net * net)38442c8c1e72SAlexey Dobriyan static inline int pfkey_init_proc(struct net *net)
384561145aa1SPavel Emelyanov {
384661145aa1SPavel Emelyanov 	return 0;
384761145aa1SPavel Emelyanov }
384861145aa1SPavel Emelyanov 
pfkey_exit_proc(struct net * net)38492c8c1e72SAlexey Dobriyan static inline void pfkey_exit_proc(struct net *net)
385061145aa1SPavel Emelyanov {
385161145aa1SPavel Emelyanov }
38521da177e4SLinus Torvalds #endif
38531da177e4SLinus Torvalds 
38541da177e4SLinus Torvalds static struct xfrm_mgr pfkeyv2_mgr =
38551da177e4SLinus Torvalds {
38561da177e4SLinus Torvalds 	.notify		= pfkey_send_notify,
38571da177e4SLinus Torvalds 	.acquire	= pfkey_send_acquire,
38581da177e4SLinus Torvalds 	.compile_policy	= pfkey_compile_policy,
38591da177e4SLinus Torvalds 	.new_mapping	= pfkey_send_new_mapping,
386026b15dadSJamal Hadi Salim 	.notify_policy	= pfkey_send_policy_notify,
386108de61beSShinta Sugimoto 	.migrate	= pfkey_send_migrate,
38620f24558eSHoria Geanta 	.is_alive	= pfkey_is_alive,
38631da177e4SLinus Torvalds };
38641da177e4SLinus Torvalds 
pfkey_net_init(struct net * net)38653fa87a32SAlexey Dobriyan static int __net_init pfkey_net_init(struct net *net)
38663fa87a32SAlexey Dobriyan {
386723c049caSEric W. Biederman 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
38683fa87a32SAlexey Dobriyan 	int rv;
38693fa87a32SAlexey Dobriyan 
38703fa87a32SAlexey Dobriyan 	INIT_HLIST_HEAD(&net_pfkey->table);
38713fa87a32SAlexey Dobriyan 	atomic_set(&net_pfkey->socks_nr, 0);
38723fa87a32SAlexey Dobriyan 
387323c049caSEric W. Biederman 	rv = pfkey_init_proc(net);
387423c049caSEric W. Biederman 
38753fa87a32SAlexey Dobriyan 	return rv;
38763fa87a32SAlexey Dobriyan }
38773fa87a32SAlexey Dobriyan 
pfkey_net_exit(struct net * net)38783fa87a32SAlexey Dobriyan static void __net_exit pfkey_net_exit(struct net *net)
38793fa87a32SAlexey Dobriyan {
38803fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
38813fa87a32SAlexey Dobriyan 
38827013ec30SAlexey Dobriyan 	pfkey_exit_proc(net);
3883663faeabSVasily Averin 	WARN_ON(!hlist_empty(&net_pfkey->table));
38843fa87a32SAlexey Dobriyan }
38853fa87a32SAlexey Dobriyan 
38863fa87a32SAlexey Dobriyan static struct pernet_operations pfkey_net_ops = {
38873fa87a32SAlexey Dobriyan 	.init = pfkey_net_init,
38883fa87a32SAlexey Dobriyan 	.exit = pfkey_net_exit,
388923c049caSEric W. Biederman 	.id   = &pfkey_net_id,
389023c049caSEric W. Biederman 	.size = sizeof(struct netns_pfkey),
38913fa87a32SAlexey Dobriyan };
38923fa87a32SAlexey Dobriyan 
ipsec_pfkey_exit(void)38931da177e4SLinus Torvalds static void __exit ipsec_pfkey_exit(void)
38941da177e4SLinus Torvalds {
38951da177e4SLinus Torvalds 	xfrm_unregister_km(&pfkeyv2_mgr);
38961da177e4SLinus Torvalds 	sock_unregister(PF_KEY);
3897180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
38981da177e4SLinus Torvalds 	proto_unregister(&key_proto);
38991da177e4SLinus Torvalds }
39001da177e4SLinus Torvalds 
ipsec_pfkey_init(void)39011da177e4SLinus Torvalds static int __init ipsec_pfkey_init(void)
39021da177e4SLinus Torvalds {
39031da177e4SLinus Torvalds 	int err = proto_register(&key_proto, 0);
39041da177e4SLinus Torvalds 
39051da177e4SLinus Torvalds 	if (err != 0)
39061da177e4SLinus Torvalds 		goto out;
39071da177e4SLinus Torvalds 
3908180211b8SAlexey Dobriyan 	err = register_pernet_subsys(&pfkey_net_ops);
39091da177e4SLinus Torvalds 	if (err != 0)
39101da177e4SLinus Torvalds 		goto out_unregister_key_proto;
3911180211b8SAlexey Dobriyan 	err = sock_register(&pfkey_family_ops);
3912180211b8SAlexey Dobriyan 	if (err != 0)
3913180211b8SAlexey Dobriyan 		goto out_unregister_pernet;
3914f41b284aSZhengchao Shao 	xfrm_register_km(&pfkeyv2_mgr);
39151da177e4SLinus Torvalds out:
39161da177e4SLinus Torvalds 	return err;
3917180211b8SAlexey Dobriyan 
3918180211b8SAlexey Dobriyan out_unregister_pernet:
3919180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
39201da177e4SLinus Torvalds out_unregister_key_proto:
39211da177e4SLinus Torvalds 	proto_unregister(&key_proto);
39221da177e4SLinus Torvalds 	goto out;
39231da177e4SLinus Torvalds }
39241da177e4SLinus Torvalds 
39251da177e4SLinus Torvalds module_init(ipsec_pfkey_init);
39261da177e4SLinus Torvalds module_exit(ipsec_pfkey_exit);
39271da177e4SLinus Torvalds MODULE_LICENSE("GPL");
39281da177e4SLinus Torvalds MODULE_ALIAS_NETPROTO(PF_KEY);
3929