xref: /linux/net/key/af_key.c (revision 4c93fbb0626080d196fb461c859b24a1feec3270)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * net/key/af_key.c	An implementation of PF_KEYv2 sockets.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Authors:	Maxim Giryaev	<gem@asplinux.ru>
101da177e4SLinus Torvalds  *		David S. Miller	<davem@redhat.com>
111da177e4SLinus Torvalds  *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
121da177e4SLinus Torvalds  *		Kunihiro Ishiguro <kunihiro@ipinfusion.com>
131da177e4SLinus Torvalds  *		Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
141da177e4SLinus Torvalds  *		Derek Atkins <derek@ihtfp.com>
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
174fc268d2SRandy Dunlap #include <linux/capability.h>
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/socket.h>
211da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
221da177e4SLinus Torvalds #include <linux/ipsec.h>
231da177e4SLinus Torvalds #include <linux/skbuff.h>
241da177e4SLinus Torvalds #include <linux/rtnetlink.h>
251da177e4SLinus Torvalds #include <linux/in.h>
261da177e4SLinus Torvalds #include <linux/in6.h>
271da177e4SLinus Torvalds #include <linux/proc_fs.h>
281da177e4SLinus Torvalds #include <linux/init.h>
295a0e3ad6STejun Heo #include <linux/slab.h>
30457c4cbcSEric W. Biederman #include <net/net_namespace.h>
313fa87a32SAlexey Dobriyan #include <net/netns/generic.h>
321da177e4SLinus Torvalds #include <net/xfrm.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <net/sock.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
371da177e4SLinus Torvalds #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
381da177e4SLinus Torvalds 
39f99189b1SEric Dumazet static int pfkey_net_id __read_mostly;
403fa87a32SAlexey Dobriyan struct netns_pfkey {
411da177e4SLinus Torvalds 	/* List of all pfkey sockets. */
423fa87a32SAlexey Dobriyan 	struct hlist_head table;
433fa87a32SAlexey Dobriyan 	atomic_t socks_nr;
443fa87a32SAlexey Dobriyan };
457f6b9dbdSstephen hemminger static DEFINE_MUTEX(pfkey_mutex);
461da177e4SLinus Torvalds 
47bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
48bd55775cSJamal Hadi Salim static struct xfrm_mark dummy_mark = {0, 0};
491da177e4SLinus Torvalds struct pfkey_sock {
501da177e4SLinus Torvalds 	/* struct sock must be the first member of struct pfkey_sock */
511da177e4SLinus Torvalds 	struct sock	sk;
521da177e4SLinus Torvalds 	int		registered;
531da177e4SLinus Torvalds 	int		promisc;
5483321d6bSTimo Teras 
5583321d6bSTimo Teras 	struct {
5683321d6bSTimo Teras 		uint8_t		msg_version;
5783321d6bSTimo Teras 		uint32_t	msg_pid;
5883321d6bSTimo Teras 		int		(*dump)(struct pfkey_sock *sk);
5983321d6bSTimo Teras 		void		(*done)(struct pfkey_sock *sk);
6083321d6bSTimo Teras 		union {
6183321d6bSTimo Teras 			struct xfrm_policy_walk	policy;
6283321d6bSTimo Teras 			struct xfrm_state_walk	state;
6383321d6bSTimo Teras 		} u;
6412a169e7SHerbert Xu 		struct sk_buff	*skb;
6583321d6bSTimo Teras 	} dump;
661da177e4SLinus Torvalds };
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	return (struct pfkey_sock *)sk;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
73*4c93fbb0SDavid S. Miller static int pfkey_can_dump(const struct sock *sk)
7483321d6bSTimo Teras {
7583321d6bSTimo Teras 	if (3 * atomic_read(&sk->sk_rmem_alloc) <= 2 * sk->sk_rcvbuf)
7683321d6bSTimo Teras 		return 1;
7783321d6bSTimo Teras 	return 0;
7883321d6bSTimo Teras }
7983321d6bSTimo Teras 
8005238204STimo Teras static void pfkey_terminate_dump(struct pfkey_sock *pfk)
8183321d6bSTimo Teras {
8205238204STimo Teras 	if (pfk->dump.dump) {
8312a169e7SHerbert Xu 		if (pfk->dump.skb) {
8412a169e7SHerbert Xu 			kfree_skb(pfk->dump.skb);
8512a169e7SHerbert Xu 			pfk->dump.skb = NULL;
8612a169e7SHerbert Xu 		}
8783321d6bSTimo Teras 		pfk->dump.done(pfk);
8883321d6bSTimo Teras 		pfk->dump.dump = NULL;
8983321d6bSTimo Teras 		pfk->dump.done = NULL;
9005238204STimo Teras 	}
9183321d6bSTimo Teras }
9283321d6bSTimo Teras 
931da177e4SLinus Torvalds static void pfkey_sock_destruct(struct sock *sk)
941da177e4SLinus Torvalds {
953fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
963fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
973fa87a32SAlexey Dobriyan 
9805238204STimo Teras 	pfkey_terminate_dump(pfkey_sk(sk));
991da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_receive_queue);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	if (!sock_flag(sk, SOCK_DEAD)) {
102207024b9Sstephen hemminger 		pr_err("Attempt to release alive pfkey socket: %p\n", sk);
1031da177e4SLinus Torvalds 		return;
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 
106547b792cSIlpo Järvinen 	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
107547b792cSIlpo Järvinen 	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
1081da177e4SLinus Torvalds 
1093fa87a32SAlexey Dobriyan 	atomic_dec(&net_pfkey->socks_nr);
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
11290ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds static void pfkey_insert(struct sock *sk)
1151da177e4SLinus Torvalds {
1163fa87a32SAlexey Dobriyan 	struct net *net = sock_net(sk);
1173fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1183fa87a32SAlexey Dobriyan 
1197f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1207f6b9dbdSstephen hemminger 	sk_add_node_rcu(sk, &net_pfkey->table);
1217f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds static void pfkey_remove(struct sock *sk)
1251da177e4SLinus Torvalds {
1267f6b9dbdSstephen hemminger 	mutex_lock(&pfkey_mutex);
1277f6b9dbdSstephen hemminger 	sk_del_node_init_rcu(sk);
1287f6b9dbdSstephen hemminger 	mutex_unlock(&pfkey_mutex);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds static struct proto key_proto = {
1321da177e4SLinus Torvalds 	.name	  = "KEY",
1331da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
1341da177e4SLinus Torvalds 	.obj_size = sizeof(struct pfkey_sock),
1351da177e4SLinus Torvalds };
1361da177e4SLinus Torvalds 
1373f378b68SEric Paris static int pfkey_create(struct net *net, struct socket *sock, int protocol,
1383f378b68SEric Paris 			int kern)
1391da177e4SLinus Torvalds {
1403fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
1411da177e4SLinus Torvalds 	struct sock *sk;
1421da177e4SLinus Torvalds 	int err;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	if (!capable(CAP_NET_ADMIN))
1451da177e4SLinus Torvalds 		return -EPERM;
1461da177e4SLinus Torvalds 	if (sock->type != SOCK_RAW)
1471da177e4SLinus Torvalds 		return -ESOCKTNOSUPPORT;
1481da177e4SLinus Torvalds 	if (protocol != PF_KEY_V2)
1491da177e4SLinus Torvalds 		return -EPROTONOSUPPORT;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	err = -ENOMEM;
1526257ff21SPavel Emelyanov 	sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto);
1531da177e4SLinus Torvalds 	if (sk == NULL)
1541da177e4SLinus Torvalds 		goto out;
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	sock->ops = &pfkey_ops;
1571da177e4SLinus Torvalds 	sock_init_data(sock, sk);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	sk->sk_family = PF_KEY;
1601da177e4SLinus Torvalds 	sk->sk_destruct = pfkey_sock_destruct;
1611da177e4SLinus Torvalds 
1623fa87a32SAlexey Dobriyan 	atomic_inc(&net_pfkey->socks_nr);
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	pfkey_insert(sk);
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	return 0;
1671da177e4SLinus Torvalds out:
1681da177e4SLinus Torvalds 	return err;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds static int pfkey_release(struct socket *sock)
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	if (!sk)
1761da177e4SLinus Torvalds 		return 0;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	pfkey_remove(sk);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	sock_orphan(sk);
1811da177e4SLinus Torvalds 	sock->sk = NULL;
1821da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_write_queue);
1837f6b9dbdSstephen hemminger 
1847f6b9dbdSstephen hemminger 	synchronize_rcu();
1851da177e4SLinus Torvalds 	sock_put(sk);
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	return 0;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
191dd0fc66fSAl Viro 			       gfp_t allocation, struct sock *sk)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	int err = -ENOBUFS;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	sock_hold(sk);
1961da177e4SLinus Torvalds 	if (*skb2 == NULL) {
1971da177e4SLinus Torvalds 		if (atomic_read(&skb->users) != 1) {
1981da177e4SLinus Torvalds 			*skb2 = skb_clone(skb, allocation);
1991da177e4SLinus Torvalds 		} else {
2001da177e4SLinus Torvalds 			*skb2 = skb;
2011da177e4SLinus Torvalds 			atomic_inc(&skb->users);
2021da177e4SLinus Torvalds 		}
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 	if (*skb2 != NULL) {
2051da177e4SLinus Torvalds 		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
2061da177e4SLinus Torvalds 			skb_orphan(*skb2);
2071da177e4SLinus Torvalds 			skb_set_owner_r(*skb2, sk);
2081da177e4SLinus Torvalds 			skb_queue_tail(&sk->sk_receive_queue, *skb2);
2091da177e4SLinus Torvalds 			sk->sk_data_ready(sk, (*skb2)->len);
2101da177e4SLinus Torvalds 			*skb2 = NULL;
2111da177e4SLinus Torvalds 			err = 0;
2121da177e4SLinus Torvalds 		}
2131da177e4SLinus Torvalds 	}
2141da177e4SLinus Torvalds 	sock_put(sk);
2151da177e4SLinus Torvalds 	return err;
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds /* Send SKB to all pfkey sockets matching selected criteria.  */
2191da177e4SLinus Torvalds #define BROADCAST_ALL		0
2201da177e4SLinus Torvalds #define BROADCAST_ONE		1
2211da177e4SLinus Torvalds #define BROADCAST_REGISTERED	2
2221da177e4SLinus Torvalds #define BROADCAST_PROMISC_ONLY	4
223dd0fc66fSAl Viro static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
22407fb0f17SAlexey Dobriyan 			   int broadcast_flags, struct sock *one_sk,
22507fb0f17SAlexey Dobriyan 			   struct net *net)
2261da177e4SLinus Torvalds {
2273fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
2281da177e4SLinus Torvalds 	struct sock *sk;
2291da177e4SLinus Torvalds 	struct hlist_node *node;
2301da177e4SLinus Torvalds 	struct sk_buff *skb2 = NULL;
2311da177e4SLinus Torvalds 	int err = -ESRCH;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/* XXX Do we need something like netlink_overrun?  I think
2341da177e4SLinus Torvalds 	 * XXX PF_KEY socket apps will not mind current behavior.
2351da177e4SLinus Torvalds 	 */
2361da177e4SLinus Torvalds 	if (!skb)
2371da177e4SLinus Torvalds 		return -ENOMEM;
2381da177e4SLinus Torvalds 
2397f6b9dbdSstephen hemminger 	rcu_read_lock();
2407f6b9dbdSstephen hemminger 	sk_for_each_rcu(sk, node, &net_pfkey->table) {
2411da177e4SLinus Torvalds 		struct pfkey_sock *pfk = pfkey_sk(sk);
2421da177e4SLinus Torvalds 		int err2;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 		/* Yes, it means that if you are meant to receive this
2451da177e4SLinus Torvalds 		 * pfkey message you receive it twice as promiscuous
2461da177e4SLinus Torvalds 		 * socket.
2471da177e4SLinus Torvalds 		 */
2481da177e4SLinus Torvalds 		if (pfk->promisc)
2491da177e4SLinus Torvalds 			pfkey_broadcast_one(skb, &skb2, allocation, sk);
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 		/* the exact target will be processed later */
2521da177e4SLinus Torvalds 		if (sk == one_sk)
2531da177e4SLinus Torvalds 			continue;
2541da177e4SLinus Torvalds 		if (broadcast_flags != BROADCAST_ALL) {
2551da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_PROMISC_ONLY)
2561da177e4SLinus Torvalds 				continue;
2571da177e4SLinus Torvalds 			if ((broadcast_flags & BROADCAST_REGISTERED) &&
2581da177e4SLinus Torvalds 			    !pfk->registered)
2591da177e4SLinus Torvalds 				continue;
2601da177e4SLinus Torvalds 			if (broadcast_flags & BROADCAST_ONE)
2611da177e4SLinus Torvalds 				continue;
2621da177e4SLinus Torvalds 		}
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 		err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk);
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 		/* Error is cleare after succecful sending to at least one
2671da177e4SLinus Torvalds 		 * registered KM */
2681da177e4SLinus Torvalds 		if ((broadcast_flags & BROADCAST_REGISTERED) && err)
2691da177e4SLinus Torvalds 			err = err2;
2701da177e4SLinus Torvalds 	}
2717f6b9dbdSstephen hemminger 	rcu_read_unlock();
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	if (one_sk != NULL)
2741da177e4SLinus Torvalds 		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	kfree_skb(skb2);
2771da177e4SLinus Torvalds 	kfree_skb(skb);
2781da177e4SLinus Torvalds 	return err;
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds 
28105238204STimo Teras static int pfkey_do_dump(struct pfkey_sock *pfk)
28205238204STimo Teras {
28312a169e7SHerbert Xu 	struct sadb_msg *hdr;
28405238204STimo Teras 	int rc;
28505238204STimo Teras 
28605238204STimo Teras 	rc = pfk->dump.dump(pfk);
28705238204STimo Teras 	if (rc == -ENOBUFS)
28805238204STimo Teras 		return 0;
28905238204STimo Teras 
29012a169e7SHerbert Xu 	if (pfk->dump.skb) {
29112a169e7SHerbert Xu 		if (!pfkey_can_dump(&pfk->sk))
29212a169e7SHerbert Xu 			return 0;
29312a169e7SHerbert Xu 
29412a169e7SHerbert Xu 		hdr = (struct sadb_msg *) pfk->dump.skb->data;
29512a169e7SHerbert Xu 		hdr->sadb_msg_seq = 0;
29612a169e7SHerbert Xu 		hdr->sadb_msg_errno = rc;
29712a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
29807fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
29912a169e7SHerbert Xu 		pfk->dump.skb = NULL;
30012a169e7SHerbert Xu 	}
30112a169e7SHerbert Xu 
30205238204STimo Teras 	pfkey_terminate_dump(pfk);
30305238204STimo Teras 	return rc;
30405238204STimo Teras }
30505238204STimo Teras 
306*4c93fbb0SDavid S. Miller static inline void pfkey_hdr_dup(struct sadb_msg *new,
307*4c93fbb0SDavid S. Miller 				 const struct sadb_msg *orig)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	*new = *orig;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
312*4c93fbb0SDavid S. Miller static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk)
3131da177e4SLinus Torvalds {
3141da177e4SLinus Torvalds 	struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL);
3151da177e4SLinus Torvalds 	struct sadb_msg *hdr;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (!skb)
3181da177e4SLinus Torvalds 		return -ENOBUFS;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	/* Woe be to the platform trying to support PFKEY yet
3211da177e4SLinus Torvalds 	 * having normal errnos outside the 1-255 range, inclusive.
3221da177e4SLinus Torvalds 	 */
3231da177e4SLinus Torvalds 	err = -err;
3241da177e4SLinus Torvalds 	if (err == ERESTARTSYS ||
3251da177e4SLinus Torvalds 	    err == ERESTARTNOHAND ||
3261da177e4SLinus Torvalds 	    err == ERESTARTNOINTR)
3271da177e4SLinus Torvalds 		err = EINTR;
3281da177e4SLinus Torvalds 	if (err >= 512)
3291da177e4SLinus Torvalds 		err = EINVAL;
33009a62660SKris Katterjohn 	BUG_ON(err <= 0 || err >= 256);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
3331da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
3341da177e4SLinus Torvalds 	hdr->sadb_msg_errno = (uint8_t) err;
3351da177e4SLinus Torvalds 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
3361da177e4SLinus Torvalds 			     sizeof(uint64_t));
3371da177e4SLinus Torvalds 
33807fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds static u8 sadb_ext_min_len[] = {
3441da177e4SLinus Torvalds 	[SADB_EXT_RESERVED]		= (u8) 0,
3451da177e4SLinus Torvalds 	[SADB_EXT_SA]			= (u8) sizeof(struct sadb_sa),
3461da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_CURRENT]	= (u8) sizeof(struct sadb_lifetime),
3471da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_HARD]	= (u8) sizeof(struct sadb_lifetime),
3481da177e4SLinus Torvalds 	[SADB_EXT_LIFETIME_SOFT]	= (u8) sizeof(struct sadb_lifetime),
3491da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_SRC]		= (u8) sizeof(struct sadb_address),
3501da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_DST]		= (u8) sizeof(struct sadb_address),
3511da177e4SLinus Torvalds 	[SADB_EXT_ADDRESS_PROXY]	= (u8) sizeof(struct sadb_address),
3521da177e4SLinus Torvalds 	[SADB_EXT_KEY_AUTH]		= (u8) sizeof(struct sadb_key),
3531da177e4SLinus Torvalds 	[SADB_EXT_KEY_ENCRYPT]		= (u8) sizeof(struct sadb_key),
3541da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_SRC]		= (u8) sizeof(struct sadb_ident),
3551da177e4SLinus Torvalds 	[SADB_EXT_IDENTITY_DST]		= (u8) sizeof(struct sadb_ident),
3561da177e4SLinus Torvalds 	[SADB_EXT_SENSITIVITY]		= (u8) sizeof(struct sadb_sens),
3571da177e4SLinus Torvalds 	[SADB_EXT_PROPOSAL]		= (u8) sizeof(struct sadb_prop),
3581da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_AUTH]	= (u8) sizeof(struct sadb_supported),
3591da177e4SLinus Torvalds 	[SADB_EXT_SUPPORTED_ENCRYPT]	= (u8) sizeof(struct sadb_supported),
3601da177e4SLinus Torvalds 	[SADB_EXT_SPIRANGE]		= (u8) sizeof(struct sadb_spirange),
3611da177e4SLinus Torvalds 	[SADB_X_EXT_KMPRIVATE]		= (u8) sizeof(struct sadb_x_kmprivate),
3621da177e4SLinus Torvalds 	[SADB_X_EXT_POLICY]		= (u8) sizeof(struct sadb_x_policy),
3631da177e4SLinus Torvalds 	[SADB_X_EXT_SA2]		= (u8) sizeof(struct sadb_x_sa2),
3641da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_TYPE]		= (u8) sizeof(struct sadb_x_nat_t_type),
3651da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_SPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3661da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_DPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
3671da177e4SLinus Torvalds 	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),
368df71837dSTrent Jaeger 	[SADB_X_EXT_SEC_CTX]		= (u8) sizeof(struct sadb_x_sec_ctx),
36913c1d189SArnaud Ebalard 	[SADB_X_EXT_KMADDRESS]		= (u8) sizeof(struct sadb_x_kmaddress),
3701da177e4SLinus Torvalds };
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds /* Verify sadb_address_{len,prefixlen} against sa_family.  */
373*4c93fbb0SDavid S. Miller static int verify_address_len(const void *p)
3741da177e4SLinus Torvalds {
375*4c93fbb0SDavid S. Miller 	const struct sadb_address *sp = p;
376*4c93fbb0SDavid S. Miller 	const struct sockaddr *addr = (const struct sockaddr *)(sp + 1);
377*4c93fbb0SDavid S. Miller 	const struct sockaddr_in *sin;
3781da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
379*4c93fbb0SDavid S. Miller 	const struct sockaddr_in6 *sin6;
3801da177e4SLinus Torvalds #endif
3811da177e4SLinus Torvalds 	int len;
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 	switch (addr->sa_family) {
3841da177e4SLinus Torvalds 	case AF_INET:
385356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));
3861da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
3871da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 32)
3881da177e4SLinus Torvalds 			return -EINVAL;
3891da177e4SLinus Torvalds 		break;
3901da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
3911da177e4SLinus Torvalds 	case AF_INET6:
392356f89e1SIlpo Järvinen 		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t));
3931da177e4SLinus Torvalds 		if (sp->sadb_address_len != len ||
3941da177e4SLinus Torvalds 		    sp->sadb_address_prefixlen > 128)
3951da177e4SLinus Torvalds 			return -EINVAL;
3961da177e4SLinus Torvalds 		break;
3971da177e4SLinus Torvalds #endif
3981da177e4SLinus Torvalds 	default:
3991da177e4SLinus Torvalds 		/* It is user using kernel to keep track of security
4001da177e4SLinus Torvalds 		 * associations for another protocol, such as
4011da177e4SLinus Torvalds 		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify
4021da177e4SLinus Torvalds 		 * lengths.
4031da177e4SLinus Torvalds 		 *
4041da177e4SLinus Torvalds 		 * XXX Actually, association/policy database is not yet
4051da177e4SLinus Torvalds 		 * XXX able to cope with arbitrary sockaddr families.
4061da177e4SLinus Torvalds 		 * XXX When it can, remove this -EINVAL.  -DaveM
4071da177e4SLinus Torvalds 		 */
4081da177e4SLinus Torvalds 		return -EINVAL;
4091da177e4SLinus Torvalds 		break;
4103ff50b79SStephen Hemminger 	}
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
415*4c93fbb0SDavid S. Miller static inline int pfkey_sec_ctx_len(const struct sadb_x_sec_ctx *sec_ctx)
416df71837dSTrent Jaeger {
417356f89e1SIlpo Järvinen 	return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) +
418356f89e1SIlpo Järvinen 			    sec_ctx->sadb_x_ctx_len,
419356f89e1SIlpo Järvinen 			    sizeof(uint64_t));
420df71837dSTrent Jaeger }
421df71837dSTrent Jaeger 
422*4c93fbb0SDavid S. Miller static inline int verify_sec_ctx_len(const void *p)
423df71837dSTrent Jaeger {
424*4c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx = p;
425298bb621SStephen Rothwell 	int len = sec_ctx->sadb_x_ctx_len;
426df71837dSTrent Jaeger 
427298bb621SStephen Rothwell 	if (len > PAGE_SIZE)
428df71837dSTrent Jaeger 		return -EINVAL;
429df71837dSTrent Jaeger 
430df71837dSTrent Jaeger 	len = pfkey_sec_ctx_len(sec_ctx);
431df71837dSTrent Jaeger 
432df71837dSTrent Jaeger 	if (sec_ctx->sadb_x_sec_len != len)
433df71837dSTrent Jaeger 		return -EINVAL;
434df71837dSTrent Jaeger 
435df71837dSTrent Jaeger 	return 0;
436df71837dSTrent Jaeger }
437df71837dSTrent Jaeger 
438*4c93fbb0SDavid S. Miller static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx)
439df71837dSTrent Jaeger {
440df71837dSTrent Jaeger 	struct xfrm_user_sec_ctx *uctx = NULL;
441df71837dSTrent Jaeger 	int ctx_size = sec_ctx->sadb_x_ctx_len;
442df71837dSTrent Jaeger 
443df71837dSTrent Jaeger 	uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL);
444df71837dSTrent Jaeger 
445df71837dSTrent Jaeger 	if (!uctx)
446df71837dSTrent Jaeger 		return NULL;
447df71837dSTrent Jaeger 
448df71837dSTrent Jaeger 	uctx->len = pfkey_sec_ctx_len(sec_ctx);
449df71837dSTrent Jaeger 	uctx->exttype = sec_ctx->sadb_x_sec_exttype;
450df71837dSTrent Jaeger 	uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi;
451df71837dSTrent Jaeger 	uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg;
452df71837dSTrent Jaeger 	uctx->ctx_len = sec_ctx->sadb_x_ctx_len;
453df71837dSTrent Jaeger 	memcpy(uctx + 1, sec_ctx + 1,
454df71837dSTrent Jaeger 	       uctx->ctx_len);
455df71837dSTrent Jaeger 
456df71837dSTrent Jaeger 	return uctx;
457df71837dSTrent Jaeger }
458df71837dSTrent Jaeger 
459*4c93fbb0SDavid S. Miller static int present_and_same_family(const struct sadb_address *src,
460*4c93fbb0SDavid S. Miller 				   const struct sadb_address *dst)
4611da177e4SLinus Torvalds {
462*4c93fbb0SDavid S. Miller 	const struct sockaddr *s_addr, *d_addr;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	if (!src || !dst)
4651da177e4SLinus Torvalds 		return 0;
4661da177e4SLinus Torvalds 
467*4c93fbb0SDavid S. Miller 	s_addr = (const struct sockaddr *)(src + 1);
468*4c93fbb0SDavid S. Miller 	d_addr = (const struct sockaddr *)(dst + 1);
4691da177e4SLinus Torvalds 	if (s_addr->sa_family != d_addr->sa_family)
4701da177e4SLinus Torvalds 		return 0;
4711da177e4SLinus Torvalds 	if (s_addr->sa_family != AF_INET
4721da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
4731da177e4SLinus Torvalds 	    && s_addr->sa_family != AF_INET6
4741da177e4SLinus Torvalds #endif
4751da177e4SLinus Torvalds 		)
4761da177e4SLinus Torvalds 		return 0;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	return 1;
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds 
481*4c93fbb0SDavid S. Miller static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void **ext_hdrs)
4821da177e4SLinus Torvalds {
483*4c93fbb0SDavid S. Miller 	const char *p = (char *) hdr;
4841da177e4SLinus Torvalds 	int len = skb->len;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	len -= sizeof(*hdr);
4871da177e4SLinus Torvalds 	p += sizeof(*hdr);
4881da177e4SLinus Torvalds 	while (len > 0) {
489*4c93fbb0SDavid S. Miller 		const struct sadb_ext *ehdr = (const struct sadb_ext *) p;
4901da177e4SLinus Torvalds 		uint16_t ext_type;
4911da177e4SLinus Torvalds 		int ext_len;
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 		ext_len  = ehdr->sadb_ext_len;
4941da177e4SLinus Torvalds 		ext_len *= sizeof(uint64_t);
4951da177e4SLinus Torvalds 		ext_type = ehdr->sadb_ext_type;
4961da177e4SLinus Torvalds 		if (ext_len < sizeof(uint64_t) ||
4971da177e4SLinus Torvalds 		    ext_len > len ||
4981da177e4SLinus Torvalds 		    ext_type == SADB_EXT_RESERVED)
4991da177e4SLinus Torvalds 			return -EINVAL;
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 		if (ext_type <= SADB_EXT_MAX) {
5021da177e4SLinus Torvalds 			int min = (int) sadb_ext_min_len[ext_type];
5031da177e4SLinus Torvalds 			if (ext_len < min)
5041da177e4SLinus Torvalds 				return -EINVAL;
5051da177e4SLinus Torvalds 			if (ext_hdrs[ext_type-1] != NULL)
5061da177e4SLinus Torvalds 				return -EINVAL;
5071da177e4SLinus Torvalds 			if (ext_type == SADB_EXT_ADDRESS_SRC ||
5081da177e4SLinus Torvalds 			    ext_type == SADB_EXT_ADDRESS_DST ||
5091da177e4SLinus Torvalds 			    ext_type == SADB_EXT_ADDRESS_PROXY ||
5101da177e4SLinus Torvalds 			    ext_type == SADB_X_EXT_NAT_T_OA) {
5111da177e4SLinus Torvalds 				if (verify_address_len(p))
5121da177e4SLinus Torvalds 					return -EINVAL;
5131da177e4SLinus Torvalds 			}
514df71837dSTrent Jaeger 			if (ext_type == SADB_X_EXT_SEC_CTX) {
515df71837dSTrent Jaeger 				if (verify_sec_ctx_len(p))
516df71837dSTrent Jaeger 					return -EINVAL;
517df71837dSTrent Jaeger 			}
518*4c93fbb0SDavid S. Miller 			ext_hdrs[ext_type-1] = (void *) p;
5191da177e4SLinus Torvalds 		}
5201da177e4SLinus Torvalds 		p   += ext_len;
5211da177e4SLinus Torvalds 		len -= ext_len;
5221da177e4SLinus Torvalds 	}
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	return 0;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds static uint16_t
5281da177e4SLinus Torvalds pfkey_satype2proto(uint8_t satype)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	switch (satype) {
5311da177e4SLinus Torvalds 	case SADB_SATYPE_UNSPEC:
5321da177e4SLinus Torvalds 		return IPSEC_PROTO_ANY;
5331da177e4SLinus Torvalds 	case SADB_SATYPE_AH:
5341da177e4SLinus Torvalds 		return IPPROTO_AH;
5351da177e4SLinus Torvalds 	case SADB_SATYPE_ESP:
5361da177e4SLinus Torvalds 		return IPPROTO_ESP;
5371da177e4SLinus Torvalds 	case SADB_X_SATYPE_IPCOMP:
5381da177e4SLinus Torvalds 		return IPPROTO_COMP;
5391da177e4SLinus Torvalds 		break;
5401da177e4SLinus Torvalds 	default:
5411da177e4SLinus Torvalds 		return 0;
5421da177e4SLinus Torvalds 	}
5431da177e4SLinus Torvalds 	/* NOTREACHED */
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds static uint8_t
5471da177e4SLinus Torvalds pfkey_proto2satype(uint16_t proto)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds 	switch (proto) {
5501da177e4SLinus Torvalds 	case IPPROTO_AH:
5511da177e4SLinus Torvalds 		return SADB_SATYPE_AH;
5521da177e4SLinus Torvalds 	case IPPROTO_ESP:
5531da177e4SLinus Torvalds 		return SADB_SATYPE_ESP;
5541da177e4SLinus Torvalds 	case IPPROTO_COMP:
5551da177e4SLinus Torvalds 		return SADB_X_SATYPE_IPCOMP;
5561da177e4SLinus Torvalds 		break;
5571da177e4SLinus Torvalds 	default:
5581da177e4SLinus Torvalds 		return 0;
5591da177e4SLinus Torvalds 	}
5601da177e4SLinus Torvalds 	/* NOTREACHED */
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds /* BTW, this scheme means that there is no way with PFKEY2 sockets to
5641da177e4SLinus Torvalds  * say specifically 'just raw sockets' as we encode them as 255.
5651da177e4SLinus Torvalds  */
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds static uint8_t pfkey_proto_to_xfrm(uint8_t proto)
5681da177e4SLinus Torvalds {
569a02cec21SEric Dumazet 	return proto == IPSEC_PROTO_ANY ? 0 : proto;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
5731da177e4SLinus Torvalds {
574a02cec21SEric Dumazet 	return proto ? proto : IPSEC_PROTO_ANY;
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds 
5779e8b4ed8SYOSHIFUJI Hideaki static inline int pfkey_sockaddr_len(sa_family_t family)
5789e8b4ed8SYOSHIFUJI Hideaki {
5799e8b4ed8SYOSHIFUJI Hideaki 	switch (family) {
5809e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET:
5819e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in);
5829e8b4ed8SYOSHIFUJI Hideaki #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
5839e8b4ed8SYOSHIFUJI Hideaki 	case AF_INET6:
5849e8b4ed8SYOSHIFUJI Hideaki 		return sizeof(struct sockaddr_in6);
5859e8b4ed8SYOSHIFUJI Hideaki #endif
5869e8b4ed8SYOSHIFUJI Hideaki 	}
5879e8b4ed8SYOSHIFUJI Hideaki 	return 0;
5889e8b4ed8SYOSHIFUJI Hideaki }
5899e8b4ed8SYOSHIFUJI Hideaki 
5905f95ac91SYOSHIFUJI Hideaki static
5915f95ac91SYOSHIFUJI Hideaki int pfkey_sockaddr_extract(const struct sockaddr *sa, xfrm_address_t *xaddr)
5921da177e4SLinus Torvalds {
5935f95ac91SYOSHIFUJI Hideaki 	switch (sa->sa_family) {
5941da177e4SLinus Torvalds 	case AF_INET:
5951da177e4SLinus Torvalds 		xaddr->a4 =
5965f95ac91SYOSHIFUJI Hideaki 			((struct sockaddr_in *)sa)->sin_addr.s_addr;
5971da177e4SLinus Torvalds 		return AF_INET;
5981da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
5991da177e4SLinus Torvalds 	case AF_INET6:
6001da177e4SLinus Torvalds 		memcpy(xaddr->a6,
6015f95ac91SYOSHIFUJI Hideaki 		       &((struct sockaddr_in6 *)sa)->sin6_addr,
6021da177e4SLinus Torvalds 		       sizeof(struct in6_addr));
6031da177e4SLinus Torvalds 		return AF_INET6;
6041da177e4SLinus Torvalds #endif
6055f95ac91SYOSHIFUJI Hideaki 	}
6061da177e4SLinus Torvalds 	return 0;
6071da177e4SLinus Torvalds }
6085f95ac91SYOSHIFUJI Hideaki 
6095f95ac91SYOSHIFUJI Hideaki static
610*4c93fbb0SDavid S. Miller int pfkey_sadb_addr2xfrm_addr(const struct sadb_address *addr, xfrm_address_t *xaddr)
6115f95ac91SYOSHIFUJI Hideaki {
6125f95ac91SYOSHIFUJI Hideaki 	return pfkey_sockaddr_extract((struct sockaddr *)(addr + 1),
6135f95ac91SYOSHIFUJI Hideaki 				      xaddr);
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds 
616*4c93fbb0SDavid S. Miller static struct  xfrm_state *pfkey_xfrm_state_lookup(struct net *net, const struct sadb_msg *hdr, void * const *ext_hdrs)
6171da177e4SLinus Torvalds {
618*4c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
619*4c93fbb0SDavid S. Miller 	const struct sadb_address *addr;
6201da177e4SLinus Torvalds 	uint16_t proto;
6211da177e4SLinus Torvalds 	unsigned short family;
6221da177e4SLinus Torvalds 	xfrm_address_t *xaddr;
6231da177e4SLinus Torvalds 
624*4c93fbb0SDavid S. Miller 	sa = (const struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
6251da177e4SLinus Torvalds 	if (sa == NULL)
6261da177e4SLinus Torvalds 		return NULL;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
6291da177e4SLinus Torvalds 	if (proto == 0)
6301da177e4SLinus Torvalds 		return NULL;
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 	/* sadb_address_len should be checked by caller */
633*4c93fbb0SDavid S. Miller 	addr = (const struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1];
6341da177e4SLinus Torvalds 	if (addr == NULL)
6351da177e4SLinus Torvalds 		return NULL;
6361da177e4SLinus Torvalds 
637*4c93fbb0SDavid S. Miller 	family = ((const struct sockaddr *)(addr + 1))->sa_family;
6381da177e4SLinus Torvalds 	switch (family) {
6391da177e4SLinus Torvalds 	case AF_INET:
640*4c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in *)(addr + 1))->sin_addr;
6411da177e4SLinus Torvalds 		break;
6421da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
6431da177e4SLinus Torvalds 	case AF_INET6:
644*4c93fbb0SDavid S. Miller 		xaddr = (xfrm_address_t *)&((const struct sockaddr_in6 *)(addr + 1))->sin6_addr;
6451da177e4SLinus Torvalds 		break;
6461da177e4SLinus Torvalds #endif
6471da177e4SLinus Torvalds 	default:
6481da177e4SLinus Torvalds 		xaddr = NULL;
6491da177e4SLinus Torvalds 	}
6501da177e4SLinus Torvalds 
6511da177e4SLinus Torvalds 	if (!xaddr)
6521da177e4SLinus Torvalds 		return NULL;
6531da177e4SLinus Torvalds 
654bd55775cSJamal Hadi Salim 	return xfrm_state_lookup(net, DUMMY_MARK, xaddr, sa->sadb_sa_spi, proto, family);
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
6589e8b4ed8SYOSHIFUJI Hideaki 
6591da177e4SLinus Torvalds static int
6601da177e4SLinus Torvalds pfkey_sockaddr_size(sa_family_t family)
6611da177e4SLinus Torvalds {
6629e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family));
6631da177e4SLinus Torvalds }
6641da177e4SLinus Torvalds 
66555569ce2SKazunori MIYAZAWA static inline int pfkey_mode_from_xfrm(int mode)
66655569ce2SKazunori MIYAZAWA {
66755569ce2SKazunori MIYAZAWA 	switch(mode) {
66855569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TRANSPORT:
66955569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TRANSPORT;
67055569ce2SKazunori MIYAZAWA 	case XFRM_MODE_TUNNEL:
67155569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_TUNNEL;
67255569ce2SKazunori MIYAZAWA 	case XFRM_MODE_BEET:
67355569ce2SKazunori MIYAZAWA 		return IPSEC_MODE_BEET;
67455569ce2SKazunori MIYAZAWA 	default:
67555569ce2SKazunori MIYAZAWA 		return -1;
67655569ce2SKazunori MIYAZAWA 	}
67755569ce2SKazunori MIYAZAWA }
67855569ce2SKazunori MIYAZAWA 
67955569ce2SKazunori MIYAZAWA static inline int pfkey_mode_to_xfrm(int mode)
68055569ce2SKazunori MIYAZAWA {
68155569ce2SKazunori MIYAZAWA 	switch(mode) {
68255569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_ANY:	/*XXX*/
68355569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TRANSPORT:
68455569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TRANSPORT;
68555569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_TUNNEL:
68655569ce2SKazunori MIYAZAWA 		return XFRM_MODE_TUNNEL;
68755569ce2SKazunori MIYAZAWA 	case IPSEC_MODE_BEET:
68855569ce2SKazunori MIYAZAWA 		return XFRM_MODE_BEET;
68955569ce2SKazunori MIYAZAWA 	default:
69055569ce2SKazunori MIYAZAWA 		return -1;
69155569ce2SKazunori MIYAZAWA 	}
69255569ce2SKazunori MIYAZAWA }
69355569ce2SKazunori MIYAZAWA 
694183cad12SDavid S. Miller static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port,
695e5b56652SYOSHIFUJI Hideaki 					struct sockaddr *sa,
696e5b56652SYOSHIFUJI Hideaki 					unsigned short family)
697e5b56652SYOSHIFUJI Hideaki {
698e5b56652SYOSHIFUJI Hideaki 	switch (family) {
699e5b56652SYOSHIFUJI Hideaki 	case AF_INET:
700e5b56652SYOSHIFUJI Hideaki 	    {
701e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
702e5b56652SYOSHIFUJI Hideaki 		sin->sin_family = AF_INET;
703e5b56652SYOSHIFUJI Hideaki 		sin->sin_port = port;
704e5b56652SYOSHIFUJI Hideaki 		sin->sin_addr.s_addr = xaddr->a4;
705e5b56652SYOSHIFUJI Hideaki 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
706e5b56652SYOSHIFUJI Hideaki 		return 32;
707e5b56652SYOSHIFUJI Hideaki 	    }
708e5b56652SYOSHIFUJI Hideaki #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
709e5b56652SYOSHIFUJI Hideaki 	case AF_INET6:
710e5b56652SYOSHIFUJI Hideaki 	    {
711e5b56652SYOSHIFUJI Hideaki 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
712e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_family = AF_INET6;
713e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_port = port;
714e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_flowinfo = 0;
715e5b56652SYOSHIFUJI Hideaki 		ipv6_addr_copy(&sin6->sin6_addr, (struct in6_addr *)xaddr->a6);
716e5b56652SYOSHIFUJI Hideaki 		sin6->sin6_scope_id = 0;
717e5b56652SYOSHIFUJI Hideaki 		return 128;
718e5b56652SYOSHIFUJI Hideaki 	    }
719e5b56652SYOSHIFUJI Hideaki #endif
720e5b56652SYOSHIFUJI Hideaki 	}
721e5b56652SYOSHIFUJI Hideaki 	return 0;
722e5b56652SYOSHIFUJI Hideaki }
723e5b56652SYOSHIFUJI Hideaki 
724*4c93fbb0SDavid S. Miller static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
725050f009eSHerbert Xu 					      int add_keys, int hsc)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	struct sk_buff *skb;
7281da177e4SLinus Torvalds 	struct sadb_msg *hdr;
7291da177e4SLinus Torvalds 	struct sadb_sa *sa;
7301da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
7311da177e4SLinus Torvalds 	struct sadb_address *addr;
7321da177e4SLinus Torvalds 	struct sadb_key *key;
7331da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
734df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
735df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
736df71837dSTrent Jaeger 	int ctx_size = 0;
7371da177e4SLinus Torvalds 	int size;
7381da177e4SLinus Torvalds 	int auth_key_size = 0;
7391da177e4SLinus Torvalds 	int encrypt_key_size = 0;
7401da177e4SLinus Torvalds 	int sockaddr_size;
7411da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
74255569ce2SKazunori MIYAZAWA 	int mode;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 	/* address family check */
7451da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
7461da177e4SLinus Torvalds 	if (!sockaddr_size)
7471da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
7501da177e4SLinus Torvalds 	   key(AE), (identity(SD),) (sensitivity)> */
7511da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
7521da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime) +
7531da177e4SLinus Torvalds 		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
7541da177e4SLinus Torvalds 		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
7551da177e4SLinus Torvalds 			sizeof(struct sadb_address)*2 +
7561da177e4SLinus Torvalds 				sockaddr_size*2 +
7571da177e4SLinus Torvalds 					sizeof(struct sadb_x_sa2);
758df71837dSTrent Jaeger 
759df71837dSTrent Jaeger 	if ((xfrm_ctx = x->security)) {
760df71837dSTrent Jaeger 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
761df71837dSTrent Jaeger 		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
762df71837dSTrent Jaeger 	}
763df71837dSTrent Jaeger 
7641da177e4SLinus Torvalds 	/* identity & sensitivity */
76581b302a3SYOSHIFUJI Hideaki 	if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr, x->props.family))
7661da177e4SLinus Torvalds 		size += sizeof(struct sadb_address) + sockaddr_size;
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 	if (add_keys) {
7691da177e4SLinus Torvalds 		if (x->aalg && x->aalg->alg_key_len) {
7701da177e4SLinus Torvalds 			auth_key_size =
7711da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
7721da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + auth_key_size;
7731da177e4SLinus Torvalds 		}
7741da177e4SLinus Torvalds 		if (x->ealg && x->ealg->alg_key_len) {
7751da177e4SLinus Torvalds 			encrypt_key_size =
7761da177e4SLinus Torvalds 				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
7771da177e4SLinus Torvalds 			size += sizeof(struct sadb_key) + encrypt_key_size;
7781da177e4SLinus Torvalds 		}
7791da177e4SLinus Torvalds 	}
7801da177e4SLinus Torvalds 	if (x->encap)
7811da177e4SLinus Torvalds 		natt = x->encap;
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
7841da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_type);
7851da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
7861da177e4SLinus Torvalds 		size += sizeof(struct sadb_x_nat_t_port);
7871da177e4SLinus Torvalds 	}
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
7901da177e4SLinus Torvalds 	if (skb == NULL)
7911da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	/* call should fill header later */
7941da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
7951da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
7961da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 	/* sa */
7991da177e4SLinus Torvalds 	sa = (struct sadb_sa *)  skb_put(skb, sizeof(struct sadb_sa));
8001da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
8011da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
8021da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
8031da177e4SLinus Torvalds 	sa->sadb_sa_replay = x->props.replay_window;
8044f09f0bbSHerbert Xu 	switch (x->km.state) {
8054f09f0bbSHerbert Xu 	case XFRM_STATE_VALID:
8064f09f0bbSHerbert Xu 		sa->sadb_sa_state = x->km.dying ?
8074f09f0bbSHerbert Xu 			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
8084f09f0bbSHerbert Xu 		break;
8094f09f0bbSHerbert Xu 	case XFRM_STATE_ACQ:
8101da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
8114f09f0bbSHerbert Xu 		break;
8124f09f0bbSHerbert Xu 	default:
8131da177e4SLinus Torvalds 		sa->sadb_sa_state = SADB_SASTATE_DEAD;
8144f09f0bbSHerbert Xu 		break;
8154f09f0bbSHerbert Xu 	}
8161da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
8171da177e4SLinus Torvalds 	if (x->aalg) {
8181da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
8191da177e4SLinus Torvalds 		sa->sadb_sa_auth = a ? a->desc.sadb_alg_id : 0;
8201da177e4SLinus Torvalds 	}
8211da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
8221da177e4SLinus Torvalds 	BUG_ON(x->ealg && x->calg);
8231da177e4SLinus Torvalds 	if (x->ealg) {
8241da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
8251da177e4SLinus Torvalds 		sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0;
8261da177e4SLinus Torvalds 	}
8271da177e4SLinus Torvalds 	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
8281da177e4SLinus Torvalds 	if (x->calg) {
8291da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
8301da177e4SLinus Torvalds 		sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0;
8311da177e4SLinus Torvalds 	}
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
8341da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_NOECN)
8351da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
8361da177e4SLinus Torvalds 	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
8371da177e4SLinus Torvalds 		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
838dd87147eSHerbert Xu 	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
839dd87147eSHerbert Xu 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 	/* hard time */
8421da177e4SLinus Torvalds 	if (hsc & 2) {
8431da177e4SLinus Torvalds 		lifetime = (struct sadb_lifetime *)  skb_put(skb,
8441da177e4SLinus Torvalds 							     sizeof(struct sadb_lifetime));
8451da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8461da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8471da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
8481da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
8491da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
8501da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
8511da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
8521da177e4SLinus Torvalds 	}
8531da177e4SLinus Torvalds 	/* soft time */
8541da177e4SLinus Torvalds 	if (hsc & 1) {
8551da177e4SLinus Torvalds 		lifetime = (struct sadb_lifetime *)  skb_put(skb,
8561da177e4SLinus Torvalds 							     sizeof(struct sadb_lifetime));
8571da177e4SLinus Torvalds 		lifetime->sadb_lifetime_len =
8581da177e4SLinus Torvalds 			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8591da177e4SLinus Torvalds 		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
8601da177e4SLinus Torvalds 		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
8611da177e4SLinus Torvalds 		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
8621da177e4SLinus Torvalds 		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
8631da177e4SLinus Torvalds 		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
8641da177e4SLinus Torvalds 	}
8651da177e4SLinus Torvalds 	/* current time */
8661da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
8671da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
8681da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
8691da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
8701da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
8711da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = x->curlft.packets;
8721da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
8731da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
8741da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
8751da177e4SLinus Torvalds 	/* src address */
8761da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
8771da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
8781da177e4SLinus Torvalds 	addr->sadb_address_len =
8791da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
8801da177e4SLinus Torvalds 			sizeof(uint64_t);
8811da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
8821da177e4SLinus Torvalds 	/* "if the ports are non-zero, then the sadb_address_proto field,
8831da177e4SLinus Torvalds 	   normally zero, MUST be filled in with the transport
8841da177e4SLinus Torvalds 	   protocol's number." - RFC2367 */
8851da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
8861da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
8871da177e4SLinus Torvalds 
888e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
889e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
890e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
891e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
892e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
8931da177e4SLinus Torvalds 		BUG();
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	/* dst address */
8961da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
8971da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
8981da177e4SLinus Torvalds 	addr->sadb_address_len =
8991da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
9001da177e4SLinus Torvalds 			sizeof(uint64_t);
9011da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
9021da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
9031da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
9041da177e4SLinus Torvalds 
905e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
906e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
907e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
908e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
909e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
9101da177e4SLinus Torvalds 		BUG();
9111da177e4SLinus Torvalds 
912e5b56652SYOSHIFUJI Hideaki 	if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr,
913e5b56652SYOSHIFUJI Hideaki 			  x->props.family)) {
914e5b56652SYOSHIFUJI Hideaki 		addr = (struct sadb_address*) skb_put(skb,
915e5b56652SYOSHIFUJI Hideaki 			sizeof(struct sadb_address)+sockaddr_size);
916e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_len =
917e5b56652SYOSHIFUJI Hideaki 			(sizeof(struct sadb_address)+sockaddr_size)/
918e5b56652SYOSHIFUJI Hideaki 			sizeof(uint64_t);
919e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
920e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_proto =
921e5b56652SYOSHIFUJI Hideaki 			pfkey_proto_from_xfrm(x->sel.proto);
922e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
923e5b56652SYOSHIFUJI Hideaki 		addr->sadb_address_reserved = 0;
924e5b56652SYOSHIFUJI Hideaki 
925e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
926e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
927e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
928e5b56652SYOSHIFUJI Hideaki 	}
929e5b56652SYOSHIFUJI Hideaki 
9301da177e4SLinus Torvalds 	/* auth key */
9311da177e4SLinus Torvalds 	if (add_keys && auth_key_size) {
9321da177e4SLinus Torvalds 		key = (struct sadb_key *) skb_put(skb,
9331da177e4SLinus Torvalds 						  sizeof(struct sadb_key)+auth_key_size);
9341da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
9351da177e4SLinus Torvalds 			sizeof(uint64_t);
9361da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
9371da177e4SLinus Torvalds 		key->sadb_key_bits = x->aalg->alg_key_len;
9381da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9391da177e4SLinus Torvalds 		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
9401da177e4SLinus Torvalds 	}
9411da177e4SLinus Torvalds 	/* encrypt key */
9421da177e4SLinus Torvalds 	if (add_keys && encrypt_key_size) {
9431da177e4SLinus Torvalds 		key = (struct sadb_key *) skb_put(skb,
9441da177e4SLinus Torvalds 						  sizeof(struct sadb_key)+encrypt_key_size);
9451da177e4SLinus Torvalds 		key->sadb_key_len = (sizeof(struct sadb_key) +
9461da177e4SLinus Torvalds 				     encrypt_key_size) / sizeof(uint64_t);
9471da177e4SLinus Torvalds 		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
9481da177e4SLinus Torvalds 		key->sadb_key_bits = x->ealg->alg_key_len;
9491da177e4SLinus Torvalds 		key->sadb_key_reserved = 0;
9501da177e4SLinus Torvalds 		memcpy(key + 1, x->ealg->alg_key,
9511da177e4SLinus Torvalds 		       (x->ealg->alg_key_len+7)/8);
9521da177e4SLinus Torvalds 	}
9531da177e4SLinus Torvalds 
9541da177e4SLinus Torvalds 	/* sa */
9551da177e4SLinus Torvalds 	sa2 = (struct sadb_x_sa2 *)  skb_put(skb, sizeof(struct sadb_x_sa2));
9561da177e4SLinus Torvalds 	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
9571da177e4SLinus Torvalds 	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
95855569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
95955569ce2SKazunori MIYAZAWA 		kfree_skb(skb);
96055569ce2SKazunori MIYAZAWA 		return ERR_PTR(-EINVAL);
96155569ce2SKazunori MIYAZAWA 	}
96255569ce2SKazunori MIYAZAWA 	sa2->sadb_x_sa2_mode = mode;
9631da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved1 = 0;
9641da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reserved2 = 0;
9651da177e4SLinus Torvalds 	sa2->sadb_x_sa2_sequence = 0;
9661da177e4SLinus Torvalds 	sa2->sadb_x_sa2_reqid = x->props.reqid;
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 	if (natt && natt->encap_type) {
9691da177e4SLinus Torvalds 		struct sadb_x_nat_t_type *n_type;
9701da177e4SLinus Torvalds 		struct sadb_x_nat_t_port *n_port;
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 		/* type */
9731da177e4SLinus Torvalds 		n_type = (struct sadb_x_nat_t_type*) skb_put(skb, sizeof(*n_type));
9741da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
9751da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
9761da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_type = natt->encap_type;
9771da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[0] = 0;
9781da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[1] = 0;
9791da177e4SLinus Torvalds 		n_type->sadb_x_nat_t_type_reserved[2] = 0;
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds 		/* source port */
9821da177e4SLinus Torvalds 		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
9831da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
9841da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
9851da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
9861da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds 		/* dest port */
9891da177e4SLinus Torvalds 		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
9901da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
9911da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
9921da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
9931da177e4SLinus Torvalds 		n_port->sadb_x_nat_t_port_reserved = 0;
9941da177e4SLinus Torvalds 	}
9951da177e4SLinus Torvalds 
996df71837dSTrent Jaeger 	/* security context */
997df71837dSTrent Jaeger 	if (xfrm_ctx) {
998df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
999df71837dSTrent Jaeger 				sizeof(struct sadb_x_sec_ctx) + ctx_size);
1000df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len =
1001df71837dSTrent Jaeger 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
1002df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
1003df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
1004df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
1005df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
1006df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
1007df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
1008df71837dSTrent Jaeger 	}
1009df71837dSTrent Jaeger 
10101da177e4SLinus Torvalds 	return skb;
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds 
1013050f009eSHerbert Xu 
1014*4c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg(const struct xfrm_state *x)
1015050f009eSHerbert Xu {
1016050f009eSHerbert Xu 	struct sk_buff *skb;
1017050f009eSHerbert Xu 
1018050f009eSHerbert Xu 	skb = __pfkey_xfrm_state2msg(x, 1, 3);
1019050f009eSHerbert Xu 
1020050f009eSHerbert Xu 	return skb;
1021050f009eSHerbert Xu }
1022050f009eSHerbert Xu 
1023*4c93fbb0SDavid S. Miller static inline struct sk_buff *pfkey_xfrm_state2msg_expire(const struct xfrm_state *x,
1024050f009eSHerbert Xu 							  int hsc)
1025050f009eSHerbert Xu {
1026050f009eSHerbert Xu 	return __pfkey_xfrm_state2msg(x, 0, hsc);
1027050f009eSHerbert Xu }
1028050f009eSHerbert Xu 
102907fb0f17SAlexey Dobriyan static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
1030*4c93fbb0SDavid S. Miller 						const struct sadb_msg *hdr,
1031*4c93fbb0SDavid S. Miller 						void * const *ext_hdrs)
10321da177e4SLinus Torvalds {
10331da177e4SLinus Torvalds 	struct xfrm_state *x;
1034*4c93fbb0SDavid S. Miller 	const struct sadb_lifetime *lifetime;
1035*4c93fbb0SDavid S. Miller 	const struct sadb_sa *sa;
1036*4c93fbb0SDavid S. Miller 	const struct sadb_key *key;
1037*4c93fbb0SDavid S. Miller 	const struct sadb_x_sec_ctx *sec_ctx;
10381da177e4SLinus Torvalds 	uint16_t proto;
10391da177e4SLinus Torvalds 	int err;
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 
1042*4c93fbb0SDavid S. Miller 	sa = (const struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
10431da177e4SLinus Torvalds 	if (!sa ||
10441da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
10451da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
10461da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10471da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_ESP &&
10481da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_ENCRYPT-1])
10491da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10501da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype == SADB_SATYPE_AH &&
10511da177e4SLinus Torvalds 	    !ext_hdrs[SADB_EXT_KEY_AUTH-1])
10521da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10531da177e4SLinus Torvalds 	if (!!ext_hdrs[SADB_EXT_LIFETIME_HARD-1] !=
10541da177e4SLinus Torvalds 	    !!ext_hdrs[SADB_EXT_LIFETIME_SOFT-1])
10551da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
10581da177e4SLinus Torvalds 	if (proto == 0)
10591da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 	/* default error is no buffer space */
10621da177e4SLinus Torvalds 	err = -ENOBUFS;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	/* RFC2367:
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds    Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_ADD message.
10671da177e4SLinus Torvalds    SADB_SASTATE_LARVAL SAs are created by SADB_GETSPI and it is not
10681da177e4SLinus Torvalds    sensible to add a new SA in the DYING or SADB_SASTATE_DEAD state.
10691da177e4SLinus Torvalds    Therefore, the sadb_sa_state field of all submitted SAs MUST be
10701da177e4SLinus Torvalds    SADB_SASTATE_MATURE and the kernel MUST return an error if this is
10711da177e4SLinus Torvalds    not true.
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	   However, KAME setkey always uses SADB_SASTATE_LARVAL.
10741da177e4SLinus Torvalds 	   Hence, we have to _ignore_ sadb_sa_state, which is also reasonable.
10751da177e4SLinus Torvalds 	 */
10761da177e4SLinus Torvalds 	if (sa->sadb_sa_auth > SADB_AALG_MAX ||
10771da177e4SLinus Torvalds 	    (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP &&
10781da177e4SLinus Torvalds 	     sa->sadb_sa_encrypt > SADB_X_CALG_MAX) ||
10791da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt > SADB_EALG_MAX)
10801da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
1081*4c93fbb0SDavid S. Miller 	key = (const struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
10821da177e4SLinus Torvalds 	if (key != NULL &&
10831da177e4SLinus Torvalds 	    sa->sadb_sa_auth != SADB_X_AALG_NULL &&
10841da177e4SLinus Torvalds 	    ((key->sadb_key_bits+7) / 8 == 0 ||
10851da177e4SLinus Torvalds 	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
10861da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10871da177e4SLinus Torvalds 	key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
10881da177e4SLinus Torvalds 	if (key != NULL &&
10891da177e4SLinus Torvalds 	    sa->sadb_sa_encrypt != SADB_EALG_NULL &&
10901da177e4SLinus Torvalds 	    ((key->sadb_key_bits+7) / 8 == 0 ||
10911da177e4SLinus Torvalds 	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
10921da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10931da177e4SLinus Torvalds 
109407fb0f17SAlexey Dobriyan 	x = xfrm_state_alloc(net);
10951da177e4SLinus Torvalds 	if (x == NULL)
10961da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	x->id.proto = proto;
10991da177e4SLinus Torvalds 	x->id.spi = sa->sadb_sa_spi;
11001da177e4SLinus Torvalds 	x->props.replay_window = sa->sadb_sa_replay;
11011da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
11021da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_NOECN;
11031da177e4SLinus Torvalds 	if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
11041da177e4SLinus Torvalds 		x->props.flags |= XFRM_STATE_DECAP_DSCP;
1105dd87147eSHerbert Xu 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
1106dd87147eSHerbert Xu 		x->props.flags |= XFRM_STATE_NOPMTUDISC;
11071da177e4SLinus Torvalds 
1108*4c93fbb0SDavid S. Miller 	lifetime = (const struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_HARD-1];
11091da177e4SLinus Torvalds 	if (lifetime != NULL) {
11101da177e4SLinus Torvalds 		x->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11111da177e4SLinus Torvalds 		x->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11121da177e4SLinus Torvalds 		x->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11131da177e4SLinus Torvalds 		x->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11141da177e4SLinus Torvalds 	}
1115*4c93fbb0SDavid S. Miller 	lifetime = (const struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_SOFT-1];
11161da177e4SLinus Torvalds 	if (lifetime != NULL) {
11171da177e4SLinus Torvalds 		x->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
11181da177e4SLinus Torvalds 		x->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
11191da177e4SLinus Torvalds 		x->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
11201da177e4SLinus Torvalds 		x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
11211da177e4SLinus Torvalds 	}
1122df71837dSTrent Jaeger 
1123*4c93fbb0SDavid S. Miller 	sec_ctx = (const struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
1124df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
1125df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
1126df71837dSTrent Jaeger 
1127df71837dSTrent Jaeger 		if (!uctx)
1128df71837dSTrent Jaeger 			goto out;
1129df71837dSTrent Jaeger 
1130df71837dSTrent Jaeger 		err = security_xfrm_state_alloc(x, uctx);
1131df71837dSTrent Jaeger 		kfree(uctx);
1132df71837dSTrent Jaeger 
1133df71837dSTrent Jaeger 		if (err)
1134df71837dSTrent Jaeger 			goto out;
1135df71837dSTrent Jaeger 	}
1136df71837dSTrent Jaeger 
1137*4c93fbb0SDavid S. Miller 	key = (const struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
11381da177e4SLinus Torvalds 	if (sa->sadb_sa_auth) {
11391da177e4SLinus Torvalds 		int keysize = 0;
11401da177e4SLinus Torvalds 		struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth);
11411da177e4SLinus Torvalds 		if (!a) {
11421da177e4SLinus Torvalds 			err = -ENOSYS;
11431da177e4SLinus Torvalds 			goto out;
11441da177e4SLinus Torvalds 		}
11451da177e4SLinus Torvalds 		if (key)
11461da177e4SLinus Torvalds 			keysize = (key->sadb_key_bits + 7) / 8;
11471da177e4SLinus Torvalds 		x->aalg = kmalloc(sizeof(*x->aalg) + keysize, GFP_KERNEL);
11481da177e4SLinus Torvalds 		if (!x->aalg)
11491da177e4SLinus Torvalds 			goto out;
11501da177e4SLinus Torvalds 		strcpy(x->aalg->alg_name, a->name);
11511da177e4SLinus Torvalds 		x->aalg->alg_key_len = 0;
11521da177e4SLinus Torvalds 		if (key) {
11531da177e4SLinus Torvalds 			x->aalg->alg_key_len = key->sadb_key_bits;
11541da177e4SLinus Torvalds 			memcpy(x->aalg->alg_key, key+1, keysize);
11551da177e4SLinus Torvalds 		}
1156c20a66f4SMartin Willi 		x->aalg->alg_trunc_len = a->uinfo.auth.icv_truncbits;
11571da177e4SLinus Torvalds 		x->props.aalgo = sa->sadb_sa_auth;
11581da177e4SLinus Torvalds 		/* x->algo.flags = sa->sadb_sa_flags; */
11591da177e4SLinus Torvalds 	}
11601da177e4SLinus Torvalds 	if (sa->sadb_sa_encrypt) {
11611da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) {
11621da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt);
11631da177e4SLinus Torvalds 			if (!a) {
11641da177e4SLinus Torvalds 				err = -ENOSYS;
11651da177e4SLinus Torvalds 				goto out;
11661da177e4SLinus Torvalds 			}
11671da177e4SLinus Torvalds 			x->calg = kmalloc(sizeof(*x->calg), GFP_KERNEL);
11681da177e4SLinus Torvalds 			if (!x->calg)
11691da177e4SLinus Torvalds 				goto out;
11701da177e4SLinus Torvalds 			strcpy(x->calg->alg_name, a->name);
11711da177e4SLinus Torvalds 			x->props.calgo = sa->sadb_sa_encrypt;
11721da177e4SLinus Torvalds 		} else {
11731da177e4SLinus Torvalds 			int keysize = 0;
11741da177e4SLinus Torvalds 			struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt);
11751da177e4SLinus Torvalds 			if (!a) {
11761da177e4SLinus Torvalds 				err = -ENOSYS;
11771da177e4SLinus Torvalds 				goto out;
11781da177e4SLinus Torvalds 			}
11791da177e4SLinus Torvalds 			key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
11801da177e4SLinus Torvalds 			if (key)
11811da177e4SLinus Torvalds 				keysize = (key->sadb_key_bits + 7) / 8;
11821da177e4SLinus Torvalds 			x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL);
11831da177e4SLinus Torvalds 			if (!x->ealg)
11841da177e4SLinus Torvalds 				goto out;
11851da177e4SLinus Torvalds 			strcpy(x->ealg->alg_name, a->name);
11861da177e4SLinus Torvalds 			x->ealg->alg_key_len = 0;
11871da177e4SLinus Torvalds 			if (key) {
11881da177e4SLinus Torvalds 				x->ealg->alg_key_len = key->sadb_key_bits;
11891da177e4SLinus Torvalds 				memcpy(x->ealg->alg_key, key+1, keysize);
11901da177e4SLinus Torvalds 			}
11911da177e4SLinus Torvalds 			x->props.ealgo = sa->sadb_sa_encrypt;
11921da177e4SLinus Torvalds 		}
11931da177e4SLinus Torvalds 	}
11941da177e4SLinus Torvalds 	/* x->algo.flags = sa->sadb_sa_flags; */
11951da177e4SLinus Torvalds 
11961da177e4SLinus Torvalds 	x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
11971da177e4SLinus Torvalds 						    &x->props.saddr);
11981da177e4SLinus Torvalds 	if (!x->props.family) {
11991da177e4SLinus Torvalds 		err = -EAFNOSUPPORT;
12001da177e4SLinus Torvalds 		goto out;
12011da177e4SLinus Torvalds 	}
12021da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1],
12031da177e4SLinus Torvalds 				  &x->id.daddr);
12041da177e4SLinus Torvalds 
12051da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_SA2-1]) {
1206*4c93fbb0SDavid S. Miller 		const struct sadb_x_sa2 *sa2 = ext_hdrs[SADB_X_EXT_SA2-1];
120755569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
120855569ce2SKazunori MIYAZAWA 		if (mode < 0) {
120955569ce2SKazunori MIYAZAWA 			err = -EINVAL;
121055569ce2SKazunori MIYAZAWA 			goto out;
121155569ce2SKazunori MIYAZAWA 		}
121255569ce2SKazunori MIYAZAWA 		x->props.mode = mode;
12131da177e4SLinus Torvalds 		x->props.reqid = sa2->sadb_x_sa2_reqid;
12141da177e4SLinus Torvalds 	}
12151da177e4SLinus Torvalds 
12161da177e4SLinus Torvalds 	if (ext_hdrs[SADB_EXT_ADDRESS_PROXY-1]) {
1217*4c93fbb0SDavid S. Miller 		const struct sadb_address *addr = ext_hdrs[SADB_EXT_ADDRESS_PROXY-1];
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 		/* Nobody uses this, but we try. */
12201da177e4SLinus Torvalds 		x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
12211da177e4SLinus Torvalds 		x->sel.prefixlen_s = addr->sadb_address_prefixlen;
12221da177e4SLinus Torvalds 	}
12231da177e4SLinus Torvalds 
12244da51056SKazunori MIYAZAWA 	if (!x->sel.family)
12254a4b6271SJoy Latten 		x->sel.family = x->props.family;
12264a4b6271SJoy Latten 
12271da177e4SLinus Torvalds 	if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
1228*4c93fbb0SDavid S. Miller 		const struct sadb_x_nat_t_type* n_type;
12291da177e4SLinus Torvalds 		struct xfrm_encap_tmpl *natt;
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 		x->encap = kmalloc(sizeof(*x->encap), GFP_KERNEL);
12321da177e4SLinus Torvalds 		if (!x->encap)
12331da177e4SLinus Torvalds 			goto out;
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds 		natt = x->encap;
12361da177e4SLinus Torvalds 		n_type = ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1];
12371da177e4SLinus Torvalds 		natt->encap_type = n_type->sadb_x_nat_t_type_type;
12381da177e4SLinus Torvalds 
12391da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1]) {
1240*4c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12411da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1];
12421da177e4SLinus Torvalds 			natt->encap_sport = n_port->sadb_x_nat_t_port_port;
12431da177e4SLinus Torvalds 		}
12441da177e4SLinus Torvalds 		if (ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1]) {
1245*4c93fbb0SDavid S. Miller 			const struct sadb_x_nat_t_port *n_port =
12461da177e4SLinus Torvalds 				ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1];
12471da177e4SLinus Torvalds 			natt->encap_dport = n_port->sadb_x_nat_t_port_port;
12481da177e4SLinus Torvalds 		}
1249a8d694c6STimo Teras 		memset(&natt->encap_oa, 0, sizeof(natt->encap_oa));
12501da177e4SLinus Torvalds 	}
12511da177e4SLinus Torvalds 
125272cb6962SHerbert Xu 	err = xfrm_init_state(x);
125372cb6962SHerbert Xu 	if (err)
12541da177e4SLinus Torvalds 		goto out;
125572cb6962SHerbert Xu 
12561da177e4SLinus Torvalds 	x->km.seq = hdr->sadb_msg_seq;
12571da177e4SLinus Torvalds 	return x;
12581da177e4SLinus Torvalds 
12591da177e4SLinus Torvalds out:
12601da177e4SLinus Torvalds 	x->km.state = XFRM_STATE_DEAD;
12611da177e4SLinus Torvalds 	xfrm_state_put(x);
12621da177e4SLinus Torvalds 	return ERR_PTR(err);
12631da177e4SLinus Torvalds }
12641da177e4SLinus Torvalds 
1265*4c93fbb0SDavid S. Miller static int pfkey_reserved(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
12661da177e4SLinus Torvalds {
12671da177e4SLinus Torvalds 	return -EOPNOTSUPP;
12681da177e4SLinus Torvalds }
12691da177e4SLinus Torvalds 
1270*4c93fbb0SDavid S. Miller static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
12711da177e4SLinus Torvalds {
127207fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
12731da177e4SLinus Torvalds 	struct sk_buff *resp_skb;
12741da177e4SLinus Torvalds 	struct sadb_x_sa2 *sa2;
12751da177e4SLinus Torvalds 	struct sadb_address *saddr, *daddr;
12761da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
1277658b219eSHerbert Xu 	struct sadb_spirange *range;
12781da177e4SLinus Torvalds 	struct xfrm_state *x = NULL;
127955569ce2SKazunori MIYAZAWA 	int mode;
1280658b219eSHerbert Xu 	int err;
1281658b219eSHerbert Xu 	u32 min_spi, max_spi;
12821da177e4SLinus Torvalds 	u32 reqid;
12831da177e4SLinus Torvalds 	u8 proto;
12841da177e4SLinus Torvalds 	unsigned short family;
12851da177e4SLinus Torvalds 	xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
12881da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
12891da177e4SLinus Torvalds 		return -EINVAL;
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
12921da177e4SLinus Torvalds 	if (proto == 0)
12931da177e4SLinus Torvalds 		return -EINVAL;
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds 	if ((sa2 = ext_hdrs[SADB_X_EXT_SA2-1]) != NULL) {
129655569ce2SKazunori MIYAZAWA 		mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
129755569ce2SKazunori MIYAZAWA 		if (mode < 0)
129855569ce2SKazunori MIYAZAWA 			return -EINVAL;
12991da177e4SLinus Torvalds 		reqid = sa2->sadb_x_sa2_reqid;
13001da177e4SLinus Torvalds 	} else {
13011da177e4SLinus Torvalds 		mode = 0;
13021da177e4SLinus Torvalds 		reqid = 0;
13031da177e4SLinus Torvalds 	}
13041da177e4SLinus Torvalds 
13051da177e4SLinus Torvalds 	saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
13061da177e4SLinus Torvalds 	daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds 	family = ((struct sockaddr *)(saddr + 1))->sa_family;
13091da177e4SLinus Torvalds 	switch (family) {
13101da177e4SLinus Torvalds 	case AF_INET:
13111da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
13121da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
13131da177e4SLinus Torvalds 		break;
13141da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
13151da177e4SLinus Torvalds 	case AF_INET6:
13161da177e4SLinus Torvalds 		xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
13171da177e4SLinus Torvalds 		xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
13181da177e4SLinus Torvalds 		break;
13191da177e4SLinus Torvalds #endif
13201da177e4SLinus Torvalds 	}
13211da177e4SLinus Torvalds 
13221da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq) {
1323bd55775cSJamal Hadi Salim 		x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
13241da177e4SLinus Torvalds 		if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) {
13251da177e4SLinus Torvalds 			xfrm_state_put(x);
13261da177e4SLinus Torvalds 			x = NULL;
13271da177e4SLinus Torvalds 		}
13281da177e4SLinus Torvalds 	}
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds 	if (!x)
1331bd55775cSJamal Hadi Salim 		x = xfrm_find_acq(net, &dummy_mark, mode, reqid, proto, xdaddr, xsaddr, 1, family);
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	if (x == NULL)
13341da177e4SLinus Torvalds 		return -ENOENT;
13351da177e4SLinus Torvalds 
13361da177e4SLinus Torvalds 	min_spi = 0x100;
13371da177e4SLinus Torvalds 	max_spi = 0x0fffffff;
1338658b219eSHerbert Xu 
1339658b219eSHerbert Xu 	range = ext_hdrs[SADB_EXT_SPIRANGE-1];
1340658b219eSHerbert Xu 	if (range) {
1341658b219eSHerbert Xu 		min_spi = range->sadb_spirange_min;
1342658b219eSHerbert Xu 		max_spi = range->sadb_spirange_max;
13431da177e4SLinus Torvalds 	}
1344658b219eSHerbert Xu 
1345658b219eSHerbert Xu 	err = xfrm_alloc_spi(x, min_spi, max_spi);
1346050f009eSHerbert Xu 	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x);
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds 	if (IS_ERR(resp_skb)) {
13491da177e4SLinus Torvalds 		xfrm_state_put(x);
13501da177e4SLinus Torvalds 		return  PTR_ERR(resp_skb);
13511da177e4SLinus Torvalds 	}
13521da177e4SLinus Torvalds 
13531da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) resp_skb->data;
13541da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
13551da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_GETSPI;
13561da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
13571da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
13581da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
13591da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
13601da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
13611da177e4SLinus Torvalds 
13621da177e4SLinus Torvalds 	xfrm_state_put(x);
13631da177e4SLinus Torvalds 
136407fb0f17SAlexey Dobriyan 	pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
13651da177e4SLinus Torvalds 
13661da177e4SLinus Torvalds 	return 0;
13671da177e4SLinus Torvalds }
13681da177e4SLinus Torvalds 
1369*4c93fbb0SDavid S. Miller static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
13701da177e4SLinus Torvalds {
137107fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
13721da177e4SLinus Torvalds 	struct xfrm_state *x;
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds 	if (hdr->sadb_msg_len != sizeof(struct sadb_msg)/8)
13751da177e4SLinus Torvalds 		return -EOPNOTSUPP;
13761da177e4SLinus Torvalds 
13771da177e4SLinus Torvalds 	if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
13781da177e4SLinus Torvalds 		return 0;
13791da177e4SLinus Torvalds 
1380bd55775cSJamal Hadi Salim 	x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
13811da177e4SLinus Torvalds 	if (x == NULL)
13821da177e4SLinus Torvalds 		return 0;
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 	spin_lock_bh(&x->lock);
13851da177e4SLinus Torvalds 	if (x->km.state == XFRM_STATE_ACQ) {
13861da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_ERROR;
138707fb0f17SAlexey Dobriyan 		wake_up(&net->xfrm.km_waitq);
13881da177e4SLinus Torvalds 	}
13891da177e4SLinus Torvalds 	spin_unlock_bh(&x->lock);
13901da177e4SLinus Torvalds 	xfrm_state_put(x);
13911da177e4SLinus Torvalds 	return 0;
13921da177e4SLinus Torvalds }
13931da177e4SLinus Torvalds 
139426b15dadSJamal Hadi Salim static inline int event2poltype(int event)
139526b15dadSJamal Hadi Salim {
139626b15dadSJamal Hadi Salim 	switch (event) {
1397f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
139826b15dadSJamal Hadi Salim 		return SADB_X_SPDDELETE;
1399f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
140026b15dadSJamal Hadi Salim 		return SADB_X_SPDADD;
1401f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
140226b15dadSJamal Hadi Salim 		return SADB_X_SPDUPDATE;
1403f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
140426b15dadSJamal Hadi Salim 	//	return SADB_X_SPDEXPIRE;
140526b15dadSJamal Hadi Salim 	default:
1406207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", event);
140726b15dadSJamal Hadi Salim 		break;
140826b15dadSJamal Hadi Salim 	}
140926b15dadSJamal Hadi Salim 
141026b15dadSJamal Hadi Salim 	return 0;
141126b15dadSJamal Hadi Salim }
141226b15dadSJamal Hadi Salim 
141326b15dadSJamal Hadi Salim static inline int event2keytype(int event)
141426b15dadSJamal Hadi Salim {
141526b15dadSJamal Hadi Salim 	switch (event) {
1416f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
141726b15dadSJamal Hadi Salim 		return SADB_DELETE;
1418f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
141926b15dadSJamal Hadi Salim 		return SADB_ADD;
1420f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
142126b15dadSJamal Hadi Salim 		return SADB_UPDATE;
1422f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
142326b15dadSJamal Hadi Salim 		return SADB_EXPIRE;
142426b15dadSJamal Hadi Salim 	default:
1425207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", event);
142626b15dadSJamal Hadi Salim 		break;
142726b15dadSJamal Hadi Salim 	}
142826b15dadSJamal Hadi Salim 
142926b15dadSJamal Hadi Salim 	return 0;
143026b15dadSJamal Hadi Salim }
143126b15dadSJamal Hadi Salim 
143226b15dadSJamal Hadi Salim /* ADD/UPD/DEL */
1433214e005bSDavid S. Miller static int key_notify_sa(struct xfrm_state *x, const struct km_event *c)
143426b15dadSJamal Hadi Salim {
143526b15dadSJamal Hadi Salim 	struct sk_buff *skb;
143626b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
143726b15dadSJamal Hadi Salim 
1438050f009eSHerbert Xu 	skb = pfkey_xfrm_state2msg(x);
143926b15dadSJamal Hadi Salim 
144026b15dadSJamal Hadi Salim 	if (IS_ERR(skb))
144126b15dadSJamal Hadi Salim 		return PTR_ERR(skb);
144226b15dadSJamal Hadi Salim 
144326b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb->data;
144426b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
144526b15dadSJamal Hadi Salim 	hdr->sadb_msg_type = event2keytype(c->event);
144626b15dadSJamal Hadi Salim 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
144726b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = 0;
144826b15dadSJamal Hadi Salim 	hdr->sadb_msg_reserved = 0;
144926b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
145026b15dadSJamal Hadi Salim 	hdr->sadb_msg_pid = c->pid;
145126b15dadSJamal Hadi Salim 
145207fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
145326b15dadSJamal Hadi Salim 
145426b15dadSJamal Hadi Salim 	return 0;
145526b15dadSJamal Hadi Salim }
14561da177e4SLinus Torvalds 
1457*4c93fbb0SDavid S. Miller static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
14581da177e4SLinus Torvalds {
145907fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
14601da177e4SLinus Torvalds 	struct xfrm_state *x;
14611da177e4SLinus Torvalds 	int err;
146226b15dadSJamal Hadi Salim 	struct km_event c;
14631da177e4SLinus Torvalds 
146407fb0f17SAlexey Dobriyan 	x = pfkey_msg2xfrm_state(net, hdr, ext_hdrs);
14651da177e4SLinus Torvalds 	if (IS_ERR(x))
14661da177e4SLinus Torvalds 		return PTR_ERR(x);
14671da177e4SLinus Torvalds 
146826b15dadSJamal Hadi Salim 	xfrm_state_hold(x);
14691da177e4SLinus Torvalds 	if (hdr->sadb_msg_type == SADB_ADD)
14701da177e4SLinus Torvalds 		err = xfrm_state_add(x);
14711da177e4SLinus Torvalds 	else
14721da177e4SLinus Torvalds 		err = xfrm_state_update(x);
14731da177e4SLinus Torvalds 
1474ab5f5e8bSJoy Latten 	xfrm_audit_state_add(x, err ? 0 : 1,
14752532386fSEric Paris 			     audit_get_loginuid(current),
14762532386fSEric Paris 			     audit_get_sessionid(current), 0);
1477161a09e7SJoy Latten 
14781da177e4SLinus Torvalds 	if (err < 0) {
14791da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
148021380b81SHerbert Xu 		__xfrm_state_put(x);
14817d6dfe1fSPatrick McHardy 		goto out;
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 
148426b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_ADD)
1485f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWSA;
148626b15dadSJamal Hadi Salim 	else
1487f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDSA;
148826b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
148926b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
149026b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
14917d6dfe1fSPatrick McHardy out:
149226b15dadSJamal Hadi Salim 	xfrm_state_put(x);
149326b15dadSJamal Hadi Salim 	return err;
14941da177e4SLinus Torvalds }
14951da177e4SLinus Torvalds 
1496*4c93fbb0SDavid S. Miller static int pfkey_delete(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;
150026b15dadSJamal Hadi Salim 	struct km_event c;
150126b15dadSJamal Hadi Salim 	int err;
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15041da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15051da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15061da177e4SLinus Torvalds 		return -EINVAL;
15071da177e4SLinus Torvalds 
150807fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15091da177e4SLinus Torvalds 	if (x == NULL)
15101da177e4SLinus Torvalds 		return -ESRCH;
15111da177e4SLinus Torvalds 
1512c8c05a8eSCatherine Zhang 	if ((err = security_xfrm_state_delete(x)))
1513c8c05a8eSCatherine Zhang 		goto out;
1514c8c05a8eSCatherine Zhang 
15151da177e4SLinus Torvalds 	if (xfrm_state_kern(x)) {
1516c8c05a8eSCatherine Zhang 		err = -EPERM;
1517c8c05a8eSCatherine Zhang 		goto out;
15181da177e4SLinus Torvalds 	}
15191da177e4SLinus Torvalds 
152026b15dadSJamal Hadi Salim 	err = xfrm_state_delete(x);
1521161a09e7SJoy Latten 
1522c8c05a8eSCatherine Zhang 	if (err < 0)
1523c8c05a8eSCatherine Zhang 		goto out;
152426b15dadSJamal Hadi Salim 
152526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
152626b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
1527f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELSA;
152826b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
1529c8c05a8eSCatherine Zhang out:
1530ab5f5e8bSJoy Latten 	xfrm_audit_state_delete(x, err ? 0 : 1,
15312532386fSEric Paris 				audit_get_loginuid(current),
15322532386fSEric Paris 				audit_get_sessionid(current), 0);
15331da177e4SLinus Torvalds 	xfrm_state_put(x);
15341da177e4SLinus Torvalds 
153526b15dadSJamal Hadi Salim 	return err;
15361da177e4SLinus Torvalds }
15371da177e4SLinus Torvalds 
1538*4c93fbb0SDavid S. Miller static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
15391da177e4SLinus Torvalds {
154007fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
15411da177e4SLinus Torvalds 	__u8 proto;
15421da177e4SLinus Torvalds 	struct sk_buff *out_skb;
15431da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
15441da177e4SLinus Torvalds 	struct xfrm_state *x;
15451da177e4SLinus Torvalds 
15461da177e4SLinus Torvalds 	if (!ext_hdrs[SADB_EXT_SA-1] ||
15471da177e4SLinus Torvalds 	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
15481da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
15491da177e4SLinus Torvalds 		return -EINVAL;
15501da177e4SLinus Torvalds 
155107fb0f17SAlexey Dobriyan 	x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
15521da177e4SLinus Torvalds 	if (x == NULL)
15531da177e4SLinus Torvalds 		return -ESRCH;
15541da177e4SLinus Torvalds 
1555050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
15561da177e4SLinus Torvalds 	proto = x->id.proto;
15571da177e4SLinus Torvalds 	xfrm_state_put(x);
15581da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
15591da177e4SLinus Torvalds 		return  PTR_ERR(out_skb);
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
15621da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
1563435000beSCharles Hardin 	out_hdr->sadb_msg_type = SADB_GET;
15641da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
15651da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
15661da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
15671da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
15681da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
156907fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
15701da177e4SLinus Torvalds 
15711da177e4SLinus Torvalds 	return 0;
15721da177e4SLinus Torvalds }
15731da177e4SLinus Torvalds 
1574*4c93fbb0SDavid S. Miller static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig,
1575dd0fc66fSAl Viro 					      gfp_t allocation)
15761da177e4SLinus Torvalds {
15771da177e4SLinus Torvalds 	struct sk_buff *skb;
15781da177e4SLinus Torvalds 	struct sadb_msg *hdr;
15791da177e4SLinus Torvalds 	int len, auth_len, enc_len, i;
15801da177e4SLinus Torvalds 
15811da177e4SLinus Torvalds 	auth_len = xfrm_count_auth_supported();
15821da177e4SLinus Torvalds 	if (auth_len) {
15831da177e4SLinus Torvalds 		auth_len *= sizeof(struct sadb_alg);
15841da177e4SLinus Torvalds 		auth_len += sizeof(struct sadb_supported);
15851da177e4SLinus Torvalds 	}
15861da177e4SLinus Torvalds 
15871da177e4SLinus Torvalds 	enc_len = xfrm_count_enc_supported();
15881da177e4SLinus Torvalds 	if (enc_len) {
15891da177e4SLinus Torvalds 		enc_len *= sizeof(struct sadb_alg);
15901da177e4SLinus Torvalds 		enc_len += sizeof(struct sadb_supported);
15911da177e4SLinus Torvalds 	}
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 	len = enc_len + auth_len + sizeof(struct sadb_msg);
15941da177e4SLinus Torvalds 
15951da177e4SLinus Torvalds 	skb = alloc_skb(len + 16, allocation);
15961da177e4SLinus Torvalds 	if (!skb)
15971da177e4SLinus Torvalds 		goto out_put_algs;
15981da177e4SLinus Torvalds 
15991da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(*hdr));
16001da177e4SLinus Torvalds 	pfkey_hdr_dup(hdr, orig);
16011da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
16021da177e4SLinus Torvalds 	hdr->sadb_msg_len = len / sizeof(uint64_t);
16031da177e4SLinus Torvalds 
16041da177e4SLinus Torvalds 	if (auth_len) {
16051da177e4SLinus Torvalds 		struct sadb_supported *sp;
16061da177e4SLinus Torvalds 		struct sadb_alg *ap;
16071da177e4SLinus Torvalds 
16081da177e4SLinus Torvalds 		sp = (struct sadb_supported *) skb_put(skb, auth_len);
16091da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16101da177e4SLinus Torvalds 
16111da177e4SLinus Torvalds 		sp->sadb_supported_len = auth_len / sizeof(uint64_t);
16121da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
16131da177e4SLinus Torvalds 
16141da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16151da177e4SLinus Torvalds 			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
16161da177e4SLinus Torvalds 			if (!aalg)
16171da177e4SLinus Torvalds 				break;
16181da177e4SLinus Torvalds 			if (aalg->available)
16191da177e4SLinus Torvalds 				*ap++ = aalg->desc;
16201da177e4SLinus Torvalds 		}
16211da177e4SLinus Torvalds 	}
16221da177e4SLinus Torvalds 
16231da177e4SLinus Torvalds 	if (enc_len) {
16241da177e4SLinus Torvalds 		struct sadb_supported *sp;
16251da177e4SLinus Torvalds 		struct sadb_alg *ap;
16261da177e4SLinus Torvalds 
16271da177e4SLinus Torvalds 		sp = (struct sadb_supported *) skb_put(skb, enc_len);
16281da177e4SLinus Torvalds 		ap = (struct sadb_alg *) (sp + 1);
16291da177e4SLinus Torvalds 
16301da177e4SLinus Torvalds 		sp->sadb_supported_len = enc_len / sizeof(uint64_t);
16311da177e4SLinus Torvalds 		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 		for (i = 0; ; i++) {
16341da177e4SLinus Torvalds 			struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
16351da177e4SLinus Torvalds 			if (!ealg)
16361da177e4SLinus Torvalds 				break;
16371da177e4SLinus Torvalds 			if (ealg->available)
16381da177e4SLinus Torvalds 				*ap++ = ealg->desc;
16391da177e4SLinus Torvalds 		}
16401da177e4SLinus Torvalds 	}
16411da177e4SLinus Torvalds 
16421da177e4SLinus Torvalds out_put_algs:
16431da177e4SLinus Torvalds 	return skb;
16441da177e4SLinus Torvalds }
16451da177e4SLinus Torvalds 
1646*4c93fbb0SDavid S. Miller static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
16471da177e4SLinus Torvalds {
16481da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
16491da177e4SLinus Torvalds 	struct sk_buff *supp_skb;
16501da177e4SLinus Torvalds 
16511da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype > SADB_SATYPE_MAX)
16521da177e4SLinus Torvalds 		return -EINVAL;
16531da177e4SLinus Torvalds 
16541da177e4SLinus Torvalds 	if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) {
16551da177e4SLinus Torvalds 		if (pfk->registered&(1<<hdr->sadb_msg_satype))
16561da177e4SLinus Torvalds 			return -EEXIST;
16571da177e4SLinus Torvalds 		pfk->registered |= (1<<hdr->sadb_msg_satype);
16581da177e4SLinus Torvalds 	}
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 	xfrm_probe_algs();
16611da177e4SLinus Torvalds 
16621da177e4SLinus Torvalds 	supp_skb = compose_sadb_supported(hdr, GFP_KERNEL);
16631da177e4SLinus Torvalds 	if (!supp_skb) {
16641da177e4SLinus Torvalds 		if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
16651da177e4SLinus Torvalds 			pfk->registered &= ~(1<<hdr->sadb_msg_satype);
16661da177e4SLinus Torvalds 
16671da177e4SLinus Torvalds 		return -ENOBUFS;
16681da177e4SLinus Torvalds 	}
16691da177e4SLinus Torvalds 
167007fb0f17SAlexey Dobriyan 	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk, sock_net(sk));
16711da177e4SLinus Torvalds 
16721da177e4SLinus Torvalds 	return 0;
16731da177e4SLinus Torvalds }
16741da177e4SLinus Torvalds 
1675*4c93fbb0SDavid S. Miller static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr)
16768be987d7SJamal Hadi Salim {
16778be987d7SJamal Hadi Salim 	struct sk_buff *skb;
16788be987d7SJamal Hadi Salim 	struct sadb_msg *hdr;
16798be987d7SJamal Hadi Salim 
16808be987d7SJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
16818be987d7SJamal Hadi Salim 	if (!skb)
16828be987d7SJamal Hadi Salim 		return -ENOBUFS;
16838be987d7SJamal Hadi Salim 
16848be987d7SJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
16858be987d7SJamal Hadi Salim 	memcpy(hdr, ihdr, sizeof(struct sadb_msg));
16868be987d7SJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
16878be987d7SJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
16888be987d7SJamal Hadi Salim 
16898be987d7SJamal Hadi Salim 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
16908be987d7SJamal Hadi Salim }
16918be987d7SJamal Hadi Salim 
1692214e005bSDavid S. Miller static int key_notify_sa_flush(const struct km_event *c)
169326b15dadSJamal Hadi Salim {
169426b15dadSJamal Hadi Salim 	struct sk_buff *skb;
169526b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
169626b15dadSJamal Hadi Salim 
169726b15dadSJamal Hadi Salim 	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
169826b15dadSJamal Hadi Salim 	if (!skb)
169926b15dadSJamal Hadi Salim 		return -ENOBUFS;
170026b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
1701bf08867fSHerbert Xu 	hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto);
1702151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_FLUSH;
170326b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
170426b15dadSJamal Hadi Salim 	hdr->sadb_msg_pid = c->pid;
170526b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
170626b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
170726b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
170826b15dadSJamal Hadi Salim 
170907fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
171026b15dadSJamal Hadi Salim 
171126b15dadSJamal Hadi Salim 	return 0;
171226b15dadSJamal Hadi Salim }
171326b15dadSJamal Hadi Salim 
1714*4c93fbb0SDavid S. Miller static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
17151da177e4SLinus Torvalds {
171607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
17171da177e4SLinus Torvalds 	unsigned proto;
171826b15dadSJamal Hadi Salim 	struct km_event c;
1719161a09e7SJoy Latten 	struct xfrm_audit audit_info;
17208be987d7SJamal Hadi Salim 	int err, err2;
17211da177e4SLinus Torvalds 
17221da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
17231da177e4SLinus Torvalds 	if (proto == 0)
17241da177e4SLinus Torvalds 		return -EINVAL;
17251da177e4SLinus Torvalds 
17260c11b942SAl Viro 	audit_info.loginuid = audit_get_loginuid(current);
17272532386fSEric Paris 	audit_info.sessionid = audit_get_sessionid(current);
1728161a09e7SJoy Latten 	audit_info.secid = 0;
172907fb0f17SAlexey Dobriyan 	err = xfrm_state_flush(net, proto, &audit_info);
17308be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
17319e64cc95SJamal Hadi Salim 	if (err || err2) {
17329e64cc95SJamal Hadi Salim 		if (err == -ESRCH) /* empty table - go quietly */
17339e64cc95SJamal Hadi Salim 			err = 0;
17348be987d7SJamal Hadi Salim 		return err ? err : err2;
17359e64cc95SJamal Hadi Salim 	}
17368be987d7SJamal Hadi Salim 
1737bf08867fSHerbert Xu 	c.data.proto = proto;
173826b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
173926b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
1740f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHSA;
174107fb0f17SAlexey Dobriyan 	c.net = net;
174226b15dadSJamal Hadi Salim 	km_state_notify(NULL, &c);
17431da177e4SLinus Torvalds 
17441da177e4SLinus Torvalds 	return 0;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds 
17471da177e4SLinus Torvalds static int dump_sa(struct xfrm_state *x, int count, void *ptr)
17481da177e4SLinus Torvalds {
174983321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
17501da177e4SLinus Torvalds 	struct sk_buff *out_skb;
17511da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
17521da177e4SLinus Torvalds 
175383321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
175483321d6bSTimo Teras 		return -ENOBUFS;
175583321d6bSTimo Teras 
1756050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg(x);
17571da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
17581da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
17591da177e4SLinus Torvalds 
17601da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
176183321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
17621da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_DUMP;
17631da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
17641da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
17651da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
176612a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
176783321d6bSTimo Teras 	out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
176812a169e7SHerbert Xu 
176912a169e7SHerbert Xu 	if (pfk->dump.skb)
177012a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
177107fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
177212a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
177312a169e7SHerbert Xu 
17741da177e4SLinus Torvalds 	return 0;
17751da177e4SLinus Torvalds }
17761da177e4SLinus Torvalds 
177783321d6bSTimo Teras static int pfkey_dump_sa(struct pfkey_sock *pfk)
177883321d6bSTimo Teras {
177907fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
178007fb0f17SAlexey Dobriyan 	return xfrm_state_walk(net, &pfk->dump.u.state, dump_sa, (void *) pfk);
178183321d6bSTimo Teras }
178283321d6bSTimo Teras 
178383321d6bSTimo Teras static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
178483321d6bSTimo Teras {
178583321d6bSTimo Teras 	xfrm_state_walk_done(&pfk->dump.u.state);
178683321d6bSTimo Teras }
178783321d6bSTimo Teras 
1788*4c93fbb0SDavid S. Miller static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
17891da177e4SLinus Torvalds {
17901da177e4SLinus Torvalds 	u8 proto;
179183321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
179283321d6bSTimo Teras 
179383321d6bSTimo Teras 	if (pfk->dump.dump != NULL)
179483321d6bSTimo Teras 		return -EBUSY;
17951da177e4SLinus Torvalds 
17961da177e4SLinus Torvalds 	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
17971da177e4SLinus Torvalds 	if (proto == 0)
17981da177e4SLinus Torvalds 		return -EINVAL;
17991da177e4SLinus Torvalds 
180083321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
180183321d6bSTimo Teras 	pfk->dump.msg_pid = hdr->sadb_msg_pid;
180283321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sa;
180383321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sa_done;
180483321d6bSTimo Teras 	xfrm_state_walk_init(&pfk->dump.u.state, proto);
18054c563f76STimo Teras 
180683321d6bSTimo Teras 	return pfkey_do_dump(pfk);
18071da177e4SLinus Torvalds }
18081da177e4SLinus Torvalds 
1809*4c93fbb0SDavid S. Miller static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
18101da177e4SLinus Torvalds {
18111da177e4SLinus Torvalds 	struct pfkey_sock *pfk = pfkey_sk(sk);
18121da177e4SLinus Torvalds 	int satype = hdr->sadb_msg_satype;
1813*4c93fbb0SDavid S. Miller 	bool reset_errno = false;
18141da177e4SLinus Torvalds 
18151da177e4SLinus Torvalds 	if (hdr->sadb_msg_len == (sizeof(*hdr) / sizeof(uint64_t))) {
1816*4c93fbb0SDavid S. Miller 		reset_errno = true;
18171da177e4SLinus Torvalds 		if (satype != 0 && satype != 1)
18181da177e4SLinus Torvalds 			return -EINVAL;
18191da177e4SLinus Torvalds 		pfk->promisc = satype;
18201da177e4SLinus Torvalds 	}
1821*4c93fbb0SDavid S. Miller 	if (reset_errno && skb_cloned(skb))
1822*4c93fbb0SDavid S. Miller 		skb = skb_copy(skb, GFP_KERNEL);
1823*4c93fbb0SDavid S. Miller 	else
1824*4c93fbb0SDavid S. Miller 		skb = skb_clone(skb, GFP_KERNEL);
1825*4c93fbb0SDavid S. Miller 
1826*4c93fbb0SDavid S. Miller 	if (reset_errno && skb) {
1827*4c93fbb0SDavid S. Miller 		struct sadb_msg *new_hdr = (struct sadb_msg *) skb->data;
1828*4c93fbb0SDavid S. Miller 		new_hdr->sadb_msg_errno = 0;
1829*4c93fbb0SDavid S. Miller 	}
1830*4c93fbb0SDavid S. Miller 
1831*4c93fbb0SDavid S. Miller 	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
18321da177e4SLinus Torvalds 	return 0;
18331da177e4SLinus Torvalds }
18341da177e4SLinus Torvalds 
18351da177e4SLinus Torvalds static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
18361da177e4SLinus Torvalds {
18371da177e4SLinus Torvalds 	int i;
18381da177e4SLinus Torvalds 	u32 reqid = *(u32*)ptr;
18391da177e4SLinus Torvalds 
18401da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
18411da177e4SLinus Torvalds 		if (xp->xfrm_vec[i].reqid == reqid)
18421da177e4SLinus Torvalds 			return -EEXIST;
18431da177e4SLinus Torvalds 	}
18441da177e4SLinus Torvalds 	return 0;
18451da177e4SLinus Torvalds }
18461da177e4SLinus Torvalds 
184707fb0f17SAlexey Dobriyan static u32 gen_reqid(struct net *net)
18481da177e4SLinus Torvalds {
18494c563f76STimo Teras 	struct xfrm_policy_walk walk;
18501da177e4SLinus Torvalds 	u32 start;
18514c563f76STimo Teras 	int rc;
18521da177e4SLinus Torvalds 	static u32 reqid = IPSEC_MANUAL_REQID_MAX;
18531da177e4SLinus Torvalds 
18541da177e4SLinus Torvalds 	start = reqid;
18551da177e4SLinus Torvalds 	do {
18561da177e4SLinus Torvalds 		++reqid;
18571da177e4SLinus Torvalds 		if (reqid == 0)
18581da177e4SLinus Torvalds 			reqid = IPSEC_MANUAL_REQID_MAX+1;
18594c563f76STimo Teras 		xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
186007fb0f17SAlexey Dobriyan 		rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid);
18614c563f76STimo Teras 		xfrm_policy_walk_done(&walk);
18624c563f76STimo Teras 		if (rc != -EEXIST)
18631da177e4SLinus Torvalds 			return reqid;
18641da177e4SLinus Torvalds 	} while (reqid != start);
18651da177e4SLinus Torvalds 	return 0;
18661da177e4SLinus Torvalds }
18671da177e4SLinus Torvalds 
18681da177e4SLinus Torvalds static int
18691da177e4SLinus Torvalds parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
18701da177e4SLinus Torvalds {
187107fb0f17SAlexey Dobriyan 	struct net *net = xp_net(xp);
18721da177e4SLinus Torvalds 	struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
187355569ce2SKazunori MIYAZAWA 	int mode;
18741da177e4SLinus Torvalds 
18751da177e4SLinus Torvalds 	if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
18761da177e4SLinus Torvalds 		return -ELOOP;
18771da177e4SLinus Torvalds 
18781da177e4SLinus Torvalds 	if (rq->sadb_x_ipsecrequest_mode == 0)
18791da177e4SLinus Torvalds 		return -EINVAL;
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds 	t->id.proto = rq->sadb_x_ipsecrequest_proto; /* XXX check proto */
188255569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0)
188355569ce2SKazunori MIYAZAWA 		return -EINVAL;
188455569ce2SKazunori MIYAZAWA 	t->mode = mode;
18851da177e4SLinus Torvalds 	if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE)
18861da177e4SLinus Torvalds 		t->optional = 1;
18871da177e4SLinus Torvalds 	else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) {
18881da177e4SLinus Torvalds 		t->reqid = rq->sadb_x_ipsecrequest_reqid;
18891da177e4SLinus Torvalds 		if (t->reqid > IPSEC_MANUAL_REQID_MAX)
18901da177e4SLinus Torvalds 			t->reqid = 0;
189107fb0f17SAlexey Dobriyan 		if (!t->reqid && !(t->reqid = gen_reqid(net)))
18921da177e4SLinus Torvalds 			return -ENOBUFS;
18931da177e4SLinus Torvalds 	}
18941da177e4SLinus Torvalds 
18951da177e4SLinus Torvalds 	/* addresses present only in tunnel mode */
18967e49e6deSMasahide NAKAMURA 	if (t->mode == XFRM_MODE_TUNNEL) {
18975f95ac91SYOSHIFUJI Hideaki 		u8 *sa = (u8 *) (rq + 1);
18985f95ac91SYOSHIFUJI Hideaki 		int family, socklen;
18995f95ac91SYOSHIFUJI Hideaki 
19005f95ac91SYOSHIFUJI Hideaki 		family = pfkey_sockaddr_extract((struct sockaddr *)sa,
19015f95ac91SYOSHIFUJI Hideaki 						&t->saddr);
19025f95ac91SYOSHIFUJI Hideaki 		if (!family)
19031da177e4SLinus Torvalds 			return -EINVAL;
19045f95ac91SYOSHIFUJI Hideaki 
19055f95ac91SYOSHIFUJI Hideaki 		socklen = pfkey_sockaddr_len(family);
19065f95ac91SYOSHIFUJI Hideaki 		if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen),
19075f95ac91SYOSHIFUJI Hideaki 					   &t->id.daddr) != family)
19081da177e4SLinus Torvalds 			return -EINVAL;
19095f95ac91SYOSHIFUJI Hideaki 		t->encap_family = family;
19102718aa7cSMiika Komu 	} else
19112718aa7cSMiika Komu 		t->encap_family = xp->family;
19122718aa7cSMiika Komu 
19131da177e4SLinus Torvalds 	/* No way to set this via kame pfkey */
1914c5d18e98SHerbert Xu 	t->allalgs = 1;
19151da177e4SLinus Torvalds 	xp->xfrm_nr++;
19161da177e4SLinus Torvalds 	return 0;
19171da177e4SLinus Torvalds }
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds static int
19201da177e4SLinus Torvalds parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
19211da177e4SLinus Torvalds {
19221da177e4SLinus Torvalds 	int err;
19231da177e4SLinus Torvalds 	int len = pol->sadb_x_policy_len*8 - sizeof(struct sadb_x_policy);
19241da177e4SLinus Torvalds 	struct sadb_x_ipsecrequest *rq = (void*)(pol+1);
19251da177e4SLinus Torvalds 
19261da177e4SLinus Torvalds 	while (len >= sizeof(struct sadb_x_ipsecrequest)) {
19271da177e4SLinus Torvalds 		if ((err = parse_ipsecrequest(xp, rq)) < 0)
19281da177e4SLinus Torvalds 			return err;
19291da177e4SLinus Torvalds 		len -= rq->sadb_x_ipsecrequest_len;
19301da177e4SLinus Torvalds 		rq = (void*)((u8*)rq + rq->sadb_x_ipsecrequest_len);
19311da177e4SLinus Torvalds 	}
19321da177e4SLinus Torvalds 	return 0;
19331da177e4SLinus Torvalds }
19341da177e4SLinus Torvalds 
1935*4c93fbb0SDavid S. Miller static inline int pfkey_xfrm_policy2sec_ctx_size(const struct xfrm_policy *xp)
1936df71837dSTrent Jaeger {
1937df71837dSTrent Jaeger   struct xfrm_sec_ctx *xfrm_ctx = xp->security;
1938df71837dSTrent Jaeger 
1939df71837dSTrent Jaeger 	if (xfrm_ctx) {
1940df71837dSTrent Jaeger 		int len = sizeof(struct sadb_x_sec_ctx);
1941df71837dSTrent Jaeger 		len += xfrm_ctx->ctx_len;
1942df71837dSTrent Jaeger 		return PFKEY_ALIGN8(len);
1943df71837dSTrent Jaeger 	}
1944df71837dSTrent Jaeger 	return 0;
1945df71837dSTrent Jaeger }
1946df71837dSTrent Jaeger 
1947*4c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg_size(const struct xfrm_policy *xp)
19481da177e4SLinus Torvalds {
1949*4c93fbb0SDavid S. Miller 	const struct xfrm_tmpl *t;
19501da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
19512718aa7cSMiika Komu 	int socklen = 0;
19522718aa7cSMiika Komu 	int i;
19532718aa7cSMiika Komu 
19542718aa7cSMiika Komu 	for (i=0; i<xp->xfrm_nr; i++) {
19552718aa7cSMiika Komu 		t = xp->xfrm_vec + i;
19569e8b4ed8SYOSHIFUJI Hideaki 		socklen += pfkey_sockaddr_len(t->encap_family);
19572718aa7cSMiika Komu 	}
19581da177e4SLinus Torvalds 
19591da177e4SLinus Torvalds 	return sizeof(struct sadb_msg) +
19601da177e4SLinus Torvalds 		(sizeof(struct sadb_lifetime) * 3) +
19611da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
19621da177e4SLinus Torvalds 		(sockaddr_size * 2) +
19631da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy) +
19642718aa7cSMiika Komu 		(xp->xfrm_nr * sizeof(struct sadb_x_ipsecrequest)) +
19652718aa7cSMiika Komu 		(socklen * 2) +
1966df71837dSTrent Jaeger 		pfkey_xfrm_policy2sec_ctx_size(xp);
19671da177e4SLinus Torvalds }
19681da177e4SLinus Torvalds 
1969*4c93fbb0SDavid S. Miller static struct sk_buff * pfkey_xfrm_policy2msg_prep(const struct xfrm_policy *xp)
19701da177e4SLinus Torvalds {
19711da177e4SLinus Torvalds 	struct sk_buff *skb;
19721da177e4SLinus Torvalds 	int size;
19731da177e4SLinus Torvalds 
19741da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
19771da177e4SLinus Torvalds 	if (skb == NULL)
19781da177e4SLinus Torvalds 		return ERR_PTR(-ENOBUFS);
19791da177e4SLinus Torvalds 
19801da177e4SLinus Torvalds 	return skb;
19811da177e4SLinus Torvalds }
19821da177e4SLinus Torvalds 
1983*4c93fbb0SDavid S. Miller static int pfkey_xfrm_policy2msg(struct sk_buff *skb, const struct xfrm_policy *xp, int dir)
19841da177e4SLinus Torvalds {
19851da177e4SLinus Torvalds 	struct sadb_msg *hdr;
19861da177e4SLinus Torvalds 	struct sadb_address *addr;
19871da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
19881da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
1989df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
1990df71837dSTrent Jaeger 	struct xfrm_sec_ctx *xfrm_ctx;
19911da177e4SLinus Torvalds 	int i;
19921da177e4SLinus Torvalds 	int size;
19931da177e4SLinus Torvalds 	int sockaddr_size = pfkey_sockaddr_size(xp->family);
19949e8b4ed8SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(xp->family);
19951da177e4SLinus Torvalds 
19961da177e4SLinus Torvalds 	size = pfkey_xfrm_policy2msg_size(xp);
19971da177e4SLinus Torvalds 
19981da177e4SLinus Torvalds 	/* call should fill header later */
19991da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
20001da177e4SLinus Torvalds 	memset(hdr, 0, size);	/* XXX do we need this ? */
20011da177e4SLinus Torvalds 
20021da177e4SLinus Torvalds 	/* src address */
20031da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
20041da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
20051da177e4SLinus Torvalds 	addr->sadb_address_len =
20061da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
20071da177e4SLinus Torvalds 			sizeof(uint64_t);
20081da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
20091da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
20101da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
20111da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2012e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(&xp->selector.saddr,
2013e5b56652SYOSHIFUJI Hideaki 				 xp->selector.sport,
2014e5b56652SYOSHIFUJI Hideaki 				 (struct sockaddr *) (addr + 1),
2015e5b56652SYOSHIFUJI Hideaki 				 xp->family))
20161da177e4SLinus Torvalds 		BUG();
20171da177e4SLinus Torvalds 
20181da177e4SLinus Torvalds 	/* dst address */
20191da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
20201da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
20211da177e4SLinus Torvalds 	addr->sadb_address_len =
20221da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
20231da177e4SLinus Torvalds 			sizeof(uint64_t);
20241da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
20251da177e4SLinus Torvalds 	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
20261da177e4SLinus Torvalds 	addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
20271da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
2028e5b56652SYOSHIFUJI Hideaki 
2029e5b56652SYOSHIFUJI Hideaki 	pfkey_sockaddr_fill(&xp->selector.daddr, xp->selector.dport,
2030e5b56652SYOSHIFUJI Hideaki 			    (struct sockaddr *) (addr + 1),
2031e5b56652SYOSHIFUJI Hideaki 			    xp->family);
20321da177e4SLinus Torvalds 
20331da177e4SLinus Torvalds 	/* hard time */
20341da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20351da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20361da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20371da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20381da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
20391da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.hard_packet_limit);
20401da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.hard_byte_limit);
20411da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.hard_add_expires_seconds;
20421da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.hard_use_expires_seconds;
20431da177e4SLinus Torvalds 	/* soft time */
20441da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20451da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20461da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20471da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20481da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
20491da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.soft_packet_limit);
20501da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.soft_byte_limit);
20511da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->lft.soft_add_expires_seconds;
20521da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->lft.soft_use_expires_seconds;
20531da177e4SLinus Torvalds 	/* current time */
20541da177e4SLinus Torvalds 	lifetime = (struct sadb_lifetime *)  skb_put(skb,
20551da177e4SLinus Torvalds 						     sizeof(struct sadb_lifetime));
20561da177e4SLinus Torvalds 	lifetime->sadb_lifetime_len =
20571da177e4SLinus Torvalds 		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
20581da177e4SLinus Torvalds 	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
20591da177e4SLinus Torvalds 	lifetime->sadb_lifetime_allocations = xp->curlft.packets;
20601da177e4SLinus Torvalds 	lifetime->sadb_lifetime_bytes = xp->curlft.bytes;
20611da177e4SLinus Torvalds 	lifetime->sadb_lifetime_addtime = xp->curlft.add_time;
20621da177e4SLinus Torvalds 	lifetime->sadb_lifetime_usetime = xp->curlft.use_time;
20631da177e4SLinus Torvalds 
20641da177e4SLinus Torvalds 	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
20651da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
20661da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
20671da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_DISCARD;
20681da177e4SLinus Torvalds 	if (xp->action == XFRM_POLICY_ALLOW) {
20691da177e4SLinus Torvalds 		if (xp->xfrm_nr)
20701da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
20711da177e4SLinus Torvalds 		else
20721da177e4SLinus Torvalds 			pol->sadb_x_policy_type = IPSEC_POLICY_NONE;
20731da177e4SLinus Torvalds 	}
20741da177e4SLinus Torvalds 	pol->sadb_x_policy_dir = dir+1;
20751da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
20761da177e4SLinus Torvalds 	pol->sadb_x_policy_priority = xp->priority;
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds 	for (i=0; i<xp->xfrm_nr; i++) {
2079*4c93fbb0SDavid S. Miller 		const struct xfrm_tmpl *t = xp->xfrm_vec + i;
20801da177e4SLinus Torvalds 		struct sadb_x_ipsecrequest *rq;
20811da177e4SLinus Torvalds 		int req_size;
208255569ce2SKazunori MIYAZAWA 		int mode;
20831da177e4SLinus Torvalds 
20841da177e4SLinus Torvalds 		req_size = sizeof(struct sadb_x_ipsecrequest);
2085e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2086e5b56652SYOSHIFUJI Hideaki 			socklen = pfkey_sockaddr_len(t->encap_family);
2087e5b56652SYOSHIFUJI Hideaki 			req_size += socklen * 2;
2088e5b56652SYOSHIFUJI Hideaki 		} else {
20891da177e4SLinus Torvalds 			size -= 2*socklen;
2090e5b56652SYOSHIFUJI Hideaki 		}
20911da177e4SLinus Torvalds 		rq = (void*)skb_put(skb, req_size);
20921da177e4SLinus Torvalds 		pol->sadb_x_policy_len += req_size/8;
20931da177e4SLinus Torvalds 		memset(rq, 0, sizeof(*rq));
20941da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_len = req_size;
20951da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_proto = t->id.proto;
209655569ce2SKazunori MIYAZAWA 		if ((mode = pfkey_mode_from_xfrm(t->mode)) < 0)
209755569ce2SKazunori MIYAZAWA 			return -EINVAL;
2098fefaa75eSDavid S. Miller 		rq->sadb_x_ipsecrequest_mode = mode;
20991da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
21001da177e4SLinus Torvalds 		if (t->reqid)
21011da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
21021da177e4SLinus Torvalds 		if (t->optional)
21031da177e4SLinus Torvalds 			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
21041da177e4SLinus Torvalds 		rq->sadb_x_ipsecrequest_reqid = t->reqid;
21051da177e4SLinus Torvalds 
2106e5b56652SYOSHIFUJI Hideaki 		if (t->mode == XFRM_MODE_TUNNEL) {
2107e5b56652SYOSHIFUJI Hideaki 			u8 *sa = (void *)(rq + 1);
2108e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->saddr, 0,
2109e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *)sa,
2110e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
2111e5b56652SYOSHIFUJI Hideaki 			pfkey_sockaddr_fill(&t->id.daddr, 0,
2112e5b56652SYOSHIFUJI Hideaki 					    (struct sockaddr *) (sa + socklen),
2113e5b56652SYOSHIFUJI Hideaki 					    t->encap_family);
21141da177e4SLinus Torvalds 		}
21151da177e4SLinus Torvalds 	}
2116df71837dSTrent Jaeger 
2117df71837dSTrent Jaeger 	/* security context */
2118df71837dSTrent Jaeger 	if ((xfrm_ctx = xp->security)) {
2119df71837dSTrent Jaeger 		int ctx_size = pfkey_xfrm_policy2sec_ctx_size(xp);
2120df71837dSTrent Jaeger 
2121df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb, ctx_size);
2122df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_len = ctx_size / sizeof(uint64_t);
2123df71837dSTrent Jaeger 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
2124df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
2125df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
2126df71837dSTrent Jaeger 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
2127df71837dSTrent Jaeger 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
2128df71837dSTrent Jaeger 		       xfrm_ctx->ctx_len);
2129df71837dSTrent Jaeger 	}
2130df71837dSTrent Jaeger 
21311da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
21321da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = atomic_read(&xp->refcnt);
213355569ce2SKazunori MIYAZAWA 
213455569ce2SKazunori MIYAZAWA 	return 0;
21351da177e4SLinus Torvalds }
21361da177e4SLinus Torvalds 
2137214e005bSDavid S. Miller static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
213826b15dadSJamal Hadi Salim {
213926b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
214026b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
214126b15dadSJamal Hadi Salim 	int err;
214226b15dadSJamal Hadi Salim 
214326b15dadSJamal Hadi Salim 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
21449a127aadSDan Carpenter 	if (IS_ERR(out_skb))
21459a127aadSDan Carpenter 		return PTR_ERR(out_skb);
21469a127aadSDan Carpenter 
214755569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
214855569ce2SKazunori MIYAZAWA 	if (err < 0)
214955569ce2SKazunori MIYAZAWA 		return err;
215026b15dadSJamal Hadi Salim 
215126b15dadSJamal Hadi Salim 	out_hdr = (struct sadb_msg *) out_skb->data;
215226b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_version = PF_KEY_V2;
215326b15dadSJamal Hadi Salim 
2154f60f6b8fSHerbert Xu 	if (c->data.byid && c->event == XFRM_MSG_DELPOLICY)
215526b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = SADB_X_SPDDELETE2;
215626b15dadSJamal Hadi Salim 	else
215726b15dadSJamal Hadi Salim 		out_hdr->sadb_msg_type = event2poltype(c->event);
215826b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_errno = 0;
215926b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_seq = c->seq;
216026b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_pid = c->pid;
216107fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
216226b15dadSJamal Hadi Salim 	return 0;
216326b15dadSJamal Hadi Salim 
216426b15dadSJamal Hadi Salim }
216526b15dadSJamal Hadi Salim 
2166*4c93fbb0SDavid S. Miller static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
21671da177e4SLinus Torvalds {
216807fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
2169df71837dSTrent Jaeger 	int err = 0;
21701da177e4SLinus Torvalds 	struct sadb_lifetime *lifetime;
21711da177e4SLinus Torvalds 	struct sadb_address *sa;
21721da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
21731da177e4SLinus Torvalds 	struct xfrm_policy *xp;
217426b15dadSJamal Hadi Salim 	struct km_event c;
2175df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
21781da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
21791da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
21801da177e4SLinus Torvalds 		return -EINVAL;
21811da177e4SLinus Torvalds 
21821da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
21831da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type > IPSEC_POLICY_IPSEC)
21841da177e4SLinus Torvalds 		return -EINVAL;
21851da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
21861da177e4SLinus Torvalds 		return -EINVAL;
21871da177e4SLinus Torvalds 
218807fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_KERNEL);
21891da177e4SLinus Torvalds 	if (xp == NULL)
21901da177e4SLinus Torvalds 		return -ENOBUFS;
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
21931da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
21941da177e4SLinus Torvalds 	xp->priority = pol->sadb_x_policy_priority;
21951da177e4SLinus Torvalds 
21961da177e4SLinus Torvalds 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
21971da177e4SLinus Torvalds 	xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
21981da177e4SLinus Torvalds 	if (!xp->family) {
21991da177e4SLinus Torvalds 		err = -EINVAL;
22001da177e4SLinus Torvalds 		goto out;
22011da177e4SLinus Torvalds 	}
22021da177e4SLinus Torvalds 	xp->selector.family = xp->family;
22031da177e4SLinus Torvalds 	xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
22041da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22051da177e4SLinus Torvalds 	xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
22061da177e4SLinus Torvalds 	if (xp->selector.sport)
22078f83f23eSAl Viro 		xp->selector.sport_mask = htons(0xffff);
22081da177e4SLinus Torvalds 
22091da177e4SLinus Torvalds 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
22101da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
22111da177e4SLinus Torvalds 	xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
22121da177e4SLinus Torvalds 
22131da177e4SLinus Torvalds 	/* Amusing, we set this twice.  KAME apps appear to set same value
22141da177e4SLinus Torvalds 	 * in both addresses.
22151da177e4SLinus Torvalds 	 */
22161da177e4SLinus Torvalds 	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
22171da177e4SLinus Torvalds 
22181da177e4SLinus Torvalds 	xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
22191da177e4SLinus Torvalds 	if (xp->selector.dport)
22208f83f23eSAl Viro 		xp->selector.dport_mask = htons(0xffff);
22211da177e4SLinus Torvalds 
2222df71837dSTrent Jaeger 	sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
2223df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
2224df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
2225df71837dSTrent Jaeger 
2226df71837dSTrent Jaeger 		if (!uctx) {
2227df71837dSTrent Jaeger 			err = -ENOBUFS;
2228df71837dSTrent Jaeger 			goto out;
2229df71837dSTrent Jaeger 		}
2230df71837dSTrent Jaeger 
223103e1ad7bSPaul Moore 		err = security_xfrm_policy_alloc(&xp->security, uctx);
2232df71837dSTrent Jaeger 		kfree(uctx);
2233df71837dSTrent Jaeger 
2234df71837dSTrent Jaeger 		if (err)
2235df71837dSTrent Jaeger 			goto out;
2236df71837dSTrent Jaeger 	}
2237df71837dSTrent Jaeger 
22381da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
22391da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
22401da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
22411da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
22421da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD-1]) != NULL) {
22431da177e4SLinus Torvalds 		xp->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
22441da177e4SLinus Torvalds 		xp->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
22451da177e4SLinus Torvalds 		xp->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
22461da177e4SLinus Torvalds 		xp->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
22471da177e4SLinus Torvalds 	}
22481da177e4SLinus Torvalds 	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]) != NULL) {
22491da177e4SLinus Torvalds 		xp->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
22501da177e4SLinus Torvalds 		xp->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
22511da177e4SLinus Torvalds 		xp->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
22521da177e4SLinus Torvalds 		xp->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
22531da177e4SLinus Torvalds 	}
22541da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
22551da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
22561da177e4SLinus Torvalds 	    (err = parse_ipsecrequests(xp, pol)) < 0)
22571da177e4SLinus Torvalds 		goto out;
22581da177e4SLinus Torvalds 
22591da177e4SLinus Torvalds 	err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
22601da177e4SLinus Torvalds 				 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
2261df71837dSTrent Jaeger 
2262ab5f5e8bSJoy Latten 	xfrm_audit_policy_add(xp, err ? 0 : 1,
22632532386fSEric Paris 			      audit_get_loginuid(current),
22642532386fSEric Paris 			      audit_get_sessionid(current), 0);
2265161a09e7SJoy Latten 
2266df71837dSTrent Jaeger 	if (err)
2267df71837dSTrent Jaeger 		goto out;
22681da177e4SLinus Torvalds 
226926b15dadSJamal Hadi Salim 	if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
2270f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_UPDPOLICY;
227126b15dadSJamal Hadi Salim 	else
2272f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_NEWPOLICY;
22731da177e4SLinus Torvalds 
227426b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
227526b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
227626b15dadSJamal Hadi Salim 
227726b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
22781da177e4SLinus Torvalds 	xfrm_pol_put(xp);
22791da177e4SLinus Torvalds 	return 0;
22801da177e4SLinus Torvalds 
22811da177e4SLinus Torvalds out:
228212a169e7SHerbert Xu 	xp->walk.dead = 1;
228364c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
22841da177e4SLinus Torvalds 	return err;
22851da177e4SLinus Torvalds }
22861da177e4SLinus Torvalds 
2287*4c93fbb0SDavid S. Miller static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
22881da177e4SLinus Torvalds {
228907fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
22901da177e4SLinus Torvalds 	int err;
22911da177e4SLinus Torvalds 	struct sadb_address *sa;
22921da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
229303e1ad7bSPaul Moore 	struct xfrm_policy *xp;
22941da177e4SLinus Torvalds 	struct xfrm_selector sel;
229526b15dadSJamal Hadi Salim 	struct km_event c;
2296df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
22972db3e47eSBrian Haley 	struct xfrm_sec_ctx *pol_ctx = NULL;
22981da177e4SLinus Torvalds 
22991da177e4SLinus Torvalds 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
23001da177e4SLinus Torvalds 				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
23011da177e4SLinus Torvalds 	    !ext_hdrs[SADB_X_EXT_POLICY-1])
23021da177e4SLinus Torvalds 		return -EINVAL;
23031da177e4SLinus Torvalds 
23041da177e4SLinus Torvalds 	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
23051da177e4SLinus Torvalds 	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
23061da177e4SLinus Torvalds 		return -EINVAL;
23071da177e4SLinus Torvalds 
23081da177e4SLinus Torvalds 	memset(&sel, 0, sizeof(sel));
23091da177e4SLinus Torvalds 
23101da177e4SLinus Torvalds 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
23111da177e4SLinus Torvalds 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
23121da177e4SLinus Torvalds 	sel.prefixlen_s = sa->sadb_address_prefixlen;
23131da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23141da177e4SLinus Torvalds 	sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
23151da177e4SLinus Torvalds 	if (sel.sport)
23168f83f23eSAl Viro 		sel.sport_mask = htons(0xffff);
23171da177e4SLinus Torvalds 
23181da177e4SLinus Torvalds 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
23191da177e4SLinus Torvalds 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
23201da177e4SLinus Torvalds 	sel.prefixlen_d = sa->sadb_address_prefixlen;
23211da177e4SLinus Torvalds 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
23221da177e4SLinus Torvalds 	sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
23231da177e4SLinus Torvalds 	if (sel.dport)
23248f83f23eSAl Viro 		sel.dport_mask = htons(0xffff);
23251da177e4SLinus Torvalds 
2326df71837dSTrent Jaeger 	sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
2327df71837dSTrent Jaeger 	if (sec_ctx != NULL) {
2328df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
2329df71837dSTrent Jaeger 
2330df71837dSTrent Jaeger 		if (!uctx)
2331df71837dSTrent Jaeger 			return -ENOMEM;
2332df71837dSTrent Jaeger 
233303e1ad7bSPaul Moore 		err = security_xfrm_policy_alloc(&pol_ctx, uctx);
2334df71837dSTrent Jaeger 		kfree(uctx);
2335df71837dSTrent Jaeger 		if (err)
2336df71837dSTrent Jaeger 			return err;
23372db3e47eSBrian Haley 	}
2338df71837dSTrent Jaeger 
23398ca2e93bSJamal Hadi Salim 	xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
234003e1ad7bSPaul Moore 				   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
234103e1ad7bSPaul Moore 				   1, &err);
234203e1ad7bSPaul Moore 	security_xfrm_policy_free(pol_ctx);
23431da177e4SLinus Torvalds 	if (xp == NULL)
23441da177e4SLinus Torvalds 		return -ENOENT;
23451da177e4SLinus Torvalds 
2346ab5f5e8bSJoy Latten 	xfrm_audit_policy_delete(xp, err ? 0 : 1,
23472532386fSEric Paris 				 audit_get_loginuid(current),
23482532386fSEric Paris 				 audit_get_sessionid(current), 0);
234913fcfbb0SDavid S. Miller 
235013fcfbb0SDavid S. Miller 	if (err)
2351c8c05a8eSCatherine Zhang 		goto out;
235213fcfbb0SDavid S. Miller 
235326b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
235426b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
23551839faabSTobias Brunner 	c.data.byid = 0;
2356f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_DELPOLICY;
235726b15dadSJamal Hadi Salim 	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
235826b15dadSJamal Hadi Salim 
2359c8c05a8eSCatherine Zhang out:
236026b15dadSJamal Hadi Salim 	xfrm_pol_put(xp);
236126b15dadSJamal Hadi Salim 	return err;
236226b15dadSJamal Hadi Salim }
236326b15dadSJamal Hadi Salim 
2364*4c93fbb0SDavid S. Miller static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struct sadb_msg *hdr, int dir)
236526b15dadSJamal Hadi Salim {
236626b15dadSJamal Hadi Salim 	int err;
236726b15dadSJamal Hadi Salim 	struct sk_buff *out_skb;
236826b15dadSJamal Hadi Salim 	struct sadb_msg *out_hdr;
236926b15dadSJamal Hadi Salim 	err = 0;
237026b15dadSJamal Hadi Salim 
23711da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
23721da177e4SLinus Torvalds 	if (IS_ERR(out_skb)) {
23731da177e4SLinus Torvalds 		err =  PTR_ERR(out_skb);
23741da177e4SLinus Torvalds 		goto out;
23751da177e4SLinus Torvalds 	}
237655569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
237755569ce2SKazunori MIYAZAWA 	if (err < 0)
237855569ce2SKazunori MIYAZAWA 		goto out;
23791da177e4SLinus Torvalds 
23801da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
23811da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
238226b15dadSJamal Hadi Salim 	out_hdr->sadb_msg_type = hdr->sadb_msg_type;
23831da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = 0;
23841da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
23851da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
23861da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
238707fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
23881da177e4SLinus Torvalds 	err = 0;
23891da177e4SLinus Torvalds 
23901da177e4SLinus Torvalds out:
23911da177e4SLinus Torvalds 	return err;
23921da177e4SLinus Torvalds }
23931da177e4SLinus Torvalds 
239408de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
239508de61beSShinta Sugimoto static int pfkey_sockaddr_pair_size(sa_family_t family)
239608de61beSShinta Sugimoto {
23979e8b4ed8SYOSHIFUJI Hideaki 	return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
239808de61beSShinta Sugimoto }
239908de61beSShinta Sugimoto 
240013c1d189SArnaud Ebalard static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
240108de61beSShinta Sugimoto 			       xfrm_address_t *saddr, xfrm_address_t *daddr,
240208de61beSShinta Sugimoto 			       u16 *family)
240308de61beSShinta Sugimoto {
24045f95ac91SYOSHIFUJI Hideaki 	int af, socklen;
24055f95ac91SYOSHIFUJI Hideaki 
240613c1d189SArnaud Ebalard 	if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
240708de61beSShinta Sugimoto 		return -EINVAL;
240808de61beSShinta Sugimoto 
240913c1d189SArnaud Ebalard 	af = pfkey_sockaddr_extract(sa, saddr);
24105f95ac91SYOSHIFUJI Hideaki 	if (!af)
241108de61beSShinta Sugimoto 		return -EINVAL;
241208de61beSShinta Sugimoto 
24135f95ac91SYOSHIFUJI Hideaki 	socklen = pfkey_sockaddr_len(af);
241413c1d189SArnaud Ebalard 	if (pfkey_sockaddr_extract((struct sockaddr *) (((u8 *)sa) + socklen),
24155f95ac91SYOSHIFUJI Hideaki 				   daddr) != af)
24165f95ac91SYOSHIFUJI Hideaki 		return -EINVAL;
24175f95ac91SYOSHIFUJI Hideaki 
24185f95ac91SYOSHIFUJI Hideaki 	*family = af;
241908de61beSShinta Sugimoto 	return 0;
242008de61beSShinta Sugimoto }
242108de61beSShinta Sugimoto 
242208de61beSShinta Sugimoto static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
242308de61beSShinta Sugimoto 				    struct xfrm_migrate *m)
242408de61beSShinta Sugimoto {
242508de61beSShinta Sugimoto 	int err;
242608de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq2;
242755569ce2SKazunori MIYAZAWA 	int mode;
242808de61beSShinta Sugimoto 
242908de61beSShinta Sugimoto 	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
243008de61beSShinta Sugimoto 	    len < rq1->sadb_x_ipsecrequest_len)
243108de61beSShinta Sugimoto 		return -EINVAL;
243208de61beSShinta Sugimoto 
243308de61beSShinta Sugimoto 	/* old endoints */
243413c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
243513c1d189SArnaud Ebalard 				  rq1->sadb_x_ipsecrequest_len,
243613c1d189SArnaud Ebalard 				  &m->old_saddr, &m->old_daddr,
243708de61beSShinta Sugimoto 				  &m->old_family);
243808de61beSShinta Sugimoto 	if (err)
243908de61beSShinta Sugimoto 		return err;
244008de61beSShinta Sugimoto 
244108de61beSShinta Sugimoto 	rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len);
244208de61beSShinta Sugimoto 	len -= rq1->sadb_x_ipsecrequest_len;
244308de61beSShinta Sugimoto 
244408de61beSShinta Sugimoto 	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
244508de61beSShinta Sugimoto 	    len < rq2->sadb_x_ipsecrequest_len)
244608de61beSShinta Sugimoto 		return -EINVAL;
244708de61beSShinta Sugimoto 
244808de61beSShinta Sugimoto 	/* new endpoints */
244913c1d189SArnaud Ebalard 	err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
245013c1d189SArnaud Ebalard 				  rq2->sadb_x_ipsecrequest_len,
245113c1d189SArnaud Ebalard 				  &m->new_saddr, &m->new_daddr,
245208de61beSShinta Sugimoto 				  &m->new_family);
245308de61beSShinta Sugimoto 	if (err)
245408de61beSShinta Sugimoto 		return err;
245508de61beSShinta Sugimoto 
245608de61beSShinta Sugimoto 	if (rq1->sadb_x_ipsecrequest_proto != rq2->sadb_x_ipsecrequest_proto ||
245708de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_mode != rq2->sadb_x_ipsecrequest_mode ||
245808de61beSShinta Sugimoto 	    rq1->sadb_x_ipsecrequest_reqid != rq2->sadb_x_ipsecrequest_reqid)
245908de61beSShinta Sugimoto 		return -EINVAL;
246008de61beSShinta Sugimoto 
246108de61beSShinta Sugimoto 	m->proto = rq1->sadb_x_ipsecrequest_proto;
246255569ce2SKazunori MIYAZAWA 	if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0)
246355569ce2SKazunori MIYAZAWA 		return -EINVAL;
246455569ce2SKazunori MIYAZAWA 	m->mode = mode;
246508de61beSShinta Sugimoto 	m->reqid = rq1->sadb_x_ipsecrequest_reqid;
246608de61beSShinta Sugimoto 
246708de61beSShinta Sugimoto 	return ((int)(rq1->sadb_x_ipsecrequest_len +
246808de61beSShinta Sugimoto 		      rq2->sadb_x_ipsecrequest_len));
246908de61beSShinta Sugimoto }
247008de61beSShinta Sugimoto 
247108de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
2472*4c93fbb0SDavid S. Miller 			 const struct sadb_msg *hdr, void * const *ext_hdrs)
247308de61beSShinta Sugimoto {
247408de61beSShinta Sugimoto 	int i, len, ret, err = -EINVAL;
247508de61beSShinta Sugimoto 	u8 dir;
247608de61beSShinta Sugimoto 	struct sadb_address *sa;
247713c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
247808de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
247908de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
248008de61beSShinta Sugimoto 	struct xfrm_selector sel;
248108de61beSShinta Sugimoto 	struct xfrm_migrate m[XFRM_MAX_DEPTH];
248213c1d189SArnaud Ebalard 	struct xfrm_kmaddress k;
248308de61beSShinta Sugimoto 
248408de61beSShinta Sugimoto 	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1],
248508de61beSShinta Sugimoto 				     ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) ||
248608de61beSShinta Sugimoto 	    !ext_hdrs[SADB_X_EXT_POLICY - 1]) {
248708de61beSShinta Sugimoto 		err = -EINVAL;
248808de61beSShinta Sugimoto 		goto out;
248908de61beSShinta Sugimoto 	}
249008de61beSShinta Sugimoto 
249113c1d189SArnaud Ebalard 	kma = ext_hdrs[SADB_X_EXT_KMADDRESS - 1];
249208de61beSShinta Sugimoto 	pol = ext_hdrs[SADB_X_EXT_POLICY - 1];
249308de61beSShinta Sugimoto 
249408de61beSShinta Sugimoto 	if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) {
249508de61beSShinta Sugimoto 		err = -EINVAL;
249608de61beSShinta Sugimoto 		goto out;
249708de61beSShinta Sugimoto 	}
249808de61beSShinta Sugimoto 
249913c1d189SArnaud Ebalard 	if (kma) {
250013c1d189SArnaud Ebalard 		/* convert sadb_x_kmaddress to xfrm_kmaddress */
250113c1d189SArnaud Ebalard 		k.reserved = kma->sadb_x_kmaddress_reserved;
250213c1d189SArnaud Ebalard 		ret = parse_sockaddr_pair((struct sockaddr *)(kma + 1),
250313c1d189SArnaud Ebalard 					  8*(kma->sadb_x_kmaddress_len) - sizeof(*kma),
250413c1d189SArnaud Ebalard 					  &k.local, &k.remote, &k.family);
250513c1d189SArnaud Ebalard 		if (ret < 0) {
250613c1d189SArnaud Ebalard 			err = ret;
250713c1d189SArnaud Ebalard 			goto out;
250813c1d189SArnaud Ebalard 		}
250913c1d189SArnaud Ebalard 	}
251013c1d189SArnaud Ebalard 
251108de61beSShinta Sugimoto 	dir = pol->sadb_x_policy_dir - 1;
251208de61beSShinta Sugimoto 	memset(&sel, 0, sizeof(sel));
251308de61beSShinta Sugimoto 
251408de61beSShinta Sugimoto 	/* set source address info of selector */
251508de61beSShinta Sugimoto 	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC - 1];
251608de61beSShinta Sugimoto 	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
251708de61beSShinta Sugimoto 	sel.prefixlen_s = sa->sadb_address_prefixlen;
251808de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
251908de61beSShinta Sugimoto 	sel.sport = ((struct sockaddr_in *)(sa + 1))->sin_port;
252008de61beSShinta Sugimoto 	if (sel.sport)
2521582ee43dSAl Viro 		sel.sport_mask = htons(0xffff);
252208de61beSShinta Sugimoto 
252308de61beSShinta Sugimoto 	/* set destination address info of selector */
252408de61beSShinta Sugimoto 	sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1],
252508de61beSShinta Sugimoto 	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
252608de61beSShinta Sugimoto 	sel.prefixlen_d = sa->sadb_address_prefixlen;
252708de61beSShinta Sugimoto 	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
252808de61beSShinta Sugimoto 	sel.dport = ((struct sockaddr_in *)(sa + 1))->sin_port;
252908de61beSShinta Sugimoto 	if (sel.dport)
2530582ee43dSAl Viro 		sel.dport_mask = htons(0xffff);
253108de61beSShinta Sugimoto 
253208de61beSShinta Sugimoto 	rq = (struct sadb_x_ipsecrequest *)(pol + 1);
253308de61beSShinta Sugimoto 
253408de61beSShinta Sugimoto 	/* extract ipsecrequests */
253508de61beSShinta Sugimoto 	i = 0;
253608de61beSShinta Sugimoto 	len = pol->sadb_x_policy_len * 8 - sizeof(struct sadb_x_policy);
253708de61beSShinta Sugimoto 
253808de61beSShinta Sugimoto 	while (len > 0 && i < XFRM_MAX_DEPTH) {
253908de61beSShinta Sugimoto 		ret = ipsecrequests_to_migrate(rq, len, &m[i]);
254008de61beSShinta Sugimoto 		if (ret < 0) {
254108de61beSShinta Sugimoto 			err = ret;
254208de61beSShinta Sugimoto 			goto out;
254308de61beSShinta Sugimoto 		} else {
254408de61beSShinta Sugimoto 			rq = (struct sadb_x_ipsecrequest *)((u8 *)rq + ret);
254508de61beSShinta Sugimoto 			len -= ret;
254608de61beSShinta Sugimoto 			i++;
254708de61beSShinta Sugimoto 		}
254808de61beSShinta Sugimoto 	}
254908de61beSShinta Sugimoto 
255008de61beSShinta Sugimoto 	if (!i || len > 0) {
255108de61beSShinta Sugimoto 		err = -EINVAL;
255208de61beSShinta Sugimoto 		goto out;
255308de61beSShinta Sugimoto 	}
255408de61beSShinta Sugimoto 
255513c1d189SArnaud Ebalard 	return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
255613c1d189SArnaud Ebalard 			    kma ? &k : NULL);
255708de61beSShinta Sugimoto 
255808de61beSShinta Sugimoto  out:
255908de61beSShinta Sugimoto 	return err;
256008de61beSShinta Sugimoto }
256108de61beSShinta Sugimoto #else
256208de61beSShinta Sugimoto static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
256308de61beSShinta Sugimoto 			 struct sadb_msg *hdr, void **ext_hdrs)
256408de61beSShinta Sugimoto {
256508de61beSShinta Sugimoto 	return -ENOPROTOOPT;
256608de61beSShinta Sugimoto }
256708de61beSShinta Sugimoto #endif
256808de61beSShinta Sugimoto 
256908de61beSShinta Sugimoto 
2570*4c93fbb0SDavid S. Miller static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
25711da177e4SLinus Torvalds {
257207fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
257377d8d7a6SHerbert Xu 	unsigned int dir;
2574215a2dd3SEric Paris 	int err = 0, delete;
25751da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
25761da177e4SLinus Torvalds 	struct xfrm_policy *xp;
257726b15dadSJamal Hadi Salim 	struct km_event c;
25781da177e4SLinus Torvalds 
25791da177e4SLinus Torvalds 	if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
25801da177e4SLinus Torvalds 		return -EINVAL;
25811da177e4SLinus Torvalds 
258277d8d7a6SHerbert Xu 	dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
258377d8d7a6SHerbert Xu 	if (dir >= XFRM_POLICY_MAX)
258477d8d7a6SHerbert Xu 		return -EINVAL;
258577d8d7a6SHerbert Xu 
2586215a2dd3SEric Paris 	delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
25878ca2e93bSJamal Hadi Salim 	xp = xfrm_policy_byid(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
2588bd55775cSJamal Hadi Salim 			      dir, pol->sadb_x_policy_id, delete, &err);
25891da177e4SLinus Torvalds 	if (xp == NULL)
25901da177e4SLinus Torvalds 		return -ENOENT;
25911da177e4SLinus Torvalds 
2592215a2dd3SEric Paris 	if (delete) {
2593ab5f5e8bSJoy Latten 		xfrm_audit_policy_delete(xp, err ? 0 : 1,
25942532386fSEric Paris 				audit_get_loginuid(current),
25952532386fSEric Paris 				audit_get_sessionid(current), 0);
25961da177e4SLinus Torvalds 
2597215a2dd3SEric Paris 		if (err)
2598215a2dd3SEric Paris 			goto out;
259926b15dadSJamal Hadi Salim 		c.seq = hdr->sadb_msg_seq;
260026b15dadSJamal Hadi Salim 		c.pid = hdr->sadb_msg_pid;
2601bf08867fSHerbert Xu 		c.data.byid = 1;
2602f60f6b8fSHerbert Xu 		c.event = XFRM_MSG_DELPOLICY;
260377d8d7a6SHerbert Xu 		km_policy_notify(xp, dir, &c);
260426b15dadSJamal Hadi Salim 	} else {
260577d8d7a6SHerbert Xu 		err = key_pol_get_resp(sk, xp, hdr, dir);
26061da177e4SLinus Torvalds 	}
26071da177e4SLinus Torvalds 
2608215a2dd3SEric Paris out:
26091da177e4SLinus Torvalds 	xfrm_pol_put(xp);
26101da177e4SLinus Torvalds 	return err;
26111da177e4SLinus Torvalds }
26121da177e4SLinus Torvalds 
26131da177e4SLinus Torvalds static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
26141da177e4SLinus Torvalds {
261583321d6bSTimo Teras 	struct pfkey_sock *pfk = ptr;
26161da177e4SLinus Torvalds 	struct sk_buff *out_skb;
26171da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
261855569ce2SKazunori MIYAZAWA 	int err;
26191da177e4SLinus Torvalds 
262083321d6bSTimo Teras 	if (!pfkey_can_dump(&pfk->sk))
262183321d6bSTimo Teras 		return -ENOBUFS;
262283321d6bSTimo Teras 
26231da177e4SLinus Torvalds 	out_skb = pfkey_xfrm_policy2msg_prep(xp);
26241da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
26251da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
26261da177e4SLinus Torvalds 
262755569ce2SKazunori MIYAZAWA 	err = pfkey_xfrm_policy2msg(out_skb, xp, dir);
262855569ce2SKazunori MIYAZAWA 	if (err < 0)
262955569ce2SKazunori MIYAZAWA 		return err;
26301da177e4SLinus Torvalds 
26311da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
263283321d6bSTimo Teras 	out_hdr->sadb_msg_version = pfk->dump.msg_version;
26331da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
26341da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
26351da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
263612a169e7SHerbert Xu 	out_hdr->sadb_msg_seq = count + 1;
263783321d6bSTimo Teras 	out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
263812a169e7SHerbert Xu 
263912a169e7SHerbert Xu 	if (pfk->dump.skb)
264012a169e7SHerbert Xu 		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
264107fb0f17SAlexey Dobriyan 				&pfk->sk, sock_net(&pfk->sk));
264212a169e7SHerbert Xu 	pfk->dump.skb = out_skb;
264312a169e7SHerbert Xu 
26441da177e4SLinus Torvalds 	return 0;
26451da177e4SLinus Torvalds }
26461da177e4SLinus Torvalds 
264783321d6bSTimo Teras static int pfkey_dump_sp(struct pfkey_sock *pfk)
264883321d6bSTimo Teras {
264907fb0f17SAlexey Dobriyan 	struct net *net = sock_net(&pfk->sk);
265007fb0f17SAlexey Dobriyan 	return xfrm_policy_walk(net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
265183321d6bSTimo Teras }
265283321d6bSTimo Teras 
265383321d6bSTimo Teras static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
265483321d6bSTimo Teras {
265583321d6bSTimo Teras 	xfrm_policy_walk_done(&pfk->dump.u.policy);
265683321d6bSTimo Teras }
265783321d6bSTimo Teras 
2658*4c93fbb0SDavid S. Miller static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
26591da177e4SLinus Torvalds {
266083321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
26611da177e4SLinus Torvalds 
266283321d6bSTimo Teras 	if (pfk->dump.dump != NULL)
266383321d6bSTimo Teras 		return -EBUSY;
26644c563f76STimo Teras 
266583321d6bSTimo Teras 	pfk->dump.msg_version = hdr->sadb_msg_version;
266683321d6bSTimo Teras 	pfk->dump.msg_pid = hdr->sadb_msg_pid;
266783321d6bSTimo Teras 	pfk->dump.dump = pfkey_dump_sp;
266883321d6bSTimo Teras 	pfk->dump.done = pfkey_dump_sp_done;
266983321d6bSTimo Teras 	xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
267083321d6bSTimo Teras 
267183321d6bSTimo Teras 	return pfkey_do_dump(pfk);
26721da177e4SLinus Torvalds }
26731da177e4SLinus Torvalds 
2674214e005bSDavid S. Miller static int key_notify_policy_flush(const struct km_event *c)
26751da177e4SLinus Torvalds {
26761da177e4SLinus Torvalds 	struct sk_buff *skb_out;
267726b15dadSJamal Hadi Salim 	struct sadb_msg *hdr;
26781da177e4SLinus Torvalds 
267926b15dadSJamal Hadi Salim 	skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
26801da177e4SLinus Torvalds 	if (!skb_out)
26811da177e4SLinus Torvalds 		return -ENOBUFS;
268226b15dadSJamal Hadi Salim 	hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
2683151bb0ffSJerome Borsboom 	hdr->sadb_msg_type = SADB_X_SPDFLUSH;
268426b15dadSJamal Hadi Salim 	hdr->sadb_msg_seq = c->seq;
268526b15dadSJamal Hadi Salim 	hdr->sadb_msg_pid = c->pid;
268626b15dadSJamal Hadi Salim 	hdr->sadb_msg_version = PF_KEY_V2;
268726b15dadSJamal Hadi Salim 	hdr->sadb_msg_errno = (uint8_t) 0;
268826b15dadSJamal Hadi Salim 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
268907fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
269026b15dadSJamal Hadi Salim 	return 0;
269126b15dadSJamal Hadi Salim 
269226b15dadSJamal Hadi Salim }
269326b15dadSJamal Hadi Salim 
2694*4c93fbb0SDavid S. Miller static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
269526b15dadSJamal Hadi Salim {
269607fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
269726b15dadSJamal Hadi Salim 	struct km_event c;
2698161a09e7SJoy Latten 	struct xfrm_audit audit_info;
26998be987d7SJamal Hadi Salim 	int err, err2;
27001da177e4SLinus Torvalds 
27010c11b942SAl Viro 	audit_info.loginuid = audit_get_loginuid(current);
27022532386fSEric Paris 	audit_info.sessionid = audit_get_sessionid(current);
2703161a09e7SJoy Latten 	audit_info.secid = 0;
270407fb0f17SAlexey Dobriyan 	err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
27058be987d7SJamal Hadi Salim 	err2 = unicast_flush_resp(sk, hdr);
27062f1eb65fSJamal Hadi Salim 	if (err || err2) {
27072f1eb65fSJamal Hadi Salim 		if (err == -ESRCH) /* empty table - old silent behavior */
27082f1eb65fSJamal Hadi Salim 			return 0;
27092f1eb65fSJamal Hadi Salim 		return err;
27102f1eb65fSJamal Hadi Salim 	}
27118be987d7SJamal Hadi Salim 
2712f7b6983fSMasahide NAKAMURA 	c.data.type = XFRM_POLICY_TYPE_MAIN;
2713f60f6b8fSHerbert Xu 	c.event = XFRM_MSG_FLUSHPOLICY;
271426b15dadSJamal Hadi Salim 	c.pid = hdr->sadb_msg_pid;
271526b15dadSJamal Hadi Salim 	c.seq = hdr->sadb_msg_seq;
271607fb0f17SAlexey Dobriyan 	c.net = net;
271726b15dadSJamal Hadi Salim 	km_policy_notify(NULL, 0, &c);
27181da177e4SLinus Torvalds 
27191da177e4SLinus Torvalds 	return 0;
27201da177e4SLinus Torvalds }
27211da177e4SLinus Torvalds 
27221da177e4SLinus Torvalds typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
2723*4c93fbb0SDavid S. Miller 			     const struct sadb_msg *hdr, void * const *ext_hdrs);
27241da177e4SLinus Torvalds static pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
27251da177e4SLinus Torvalds 	[SADB_RESERVED]		= pfkey_reserved,
27261da177e4SLinus Torvalds 	[SADB_GETSPI]		= pfkey_getspi,
27271da177e4SLinus Torvalds 	[SADB_UPDATE]		= pfkey_add,
27281da177e4SLinus Torvalds 	[SADB_ADD]		= pfkey_add,
27291da177e4SLinus Torvalds 	[SADB_DELETE]		= pfkey_delete,
27301da177e4SLinus Torvalds 	[SADB_GET]		= pfkey_get,
27311da177e4SLinus Torvalds 	[SADB_ACQUIRE]		= pfkey_acquire,
27321da177e4SLinus Torvalds 	[SADB_REGISTER]		= pfkey_register,
27331da177e4SLinus Torvalds 	[SADB_EXPIRE]		= NULL,
27341da177e4SLinus Torvalds 	[SADB_FLUSH]		= pfkey_flush,
27351da177e4SLinus Torvalds 	[SADB_DUMP]		= pfkey_dump,
27361da177e4SLinus Torvalds 	[SADB_X_PROMISC]	= pfkey_promisc,
27371da177e4SLinus Torvalds 	[SADB_X_PCHANGE]	= NULL,
27381da177e4SLinus Torvalds 	[SADB_X_SPDUPDATE]	= pfkey_spdadd,
27391da177e4SLinus Torvalds 	[SADB_X_SPDADD]		= pfkey_spdadd,
27401da177e4SLinus Torvalds 	[SADB_X_SPDDELETE]	= pfkey_spddelete,
27411da177e4SLinus Torvalds 	[SADB_X_SPDGET]		= pfkey_spdget,
27421da177e4SLinus Torvalds 	[SADB_X_SPDACQUIRE]	= NULL,
27431da177e4SLinus Torvalds 	[SADB_X_SPDDUMP]	= pfkey_spddump,
27441da177e4SLinus Torvalds 	[SADB_X_SPDFLUSH]	= pfkey_spdflush,
27451da177e4SLinus Torvalds 	[SADB_X_SPDSETIDX]	= pfkey_spdadd,
27461da177e4SLinus Torvalds 	[SADB_X_SPDDELETE2]	= pfkey_spdget,
274708de61beSShinta Sugimoto 	[SADB_X_MIGRATE]	= pfkey_migrate,
27481da177e4SLinus Torvalds };
27491da177e4SLinus Torvalds 
2750*4c93fbb0SDavid S. Miller static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr)
27511da177e4SLinus Torvalds {
27521da177e4SLinus Torvalds 	void *ext_hdrs[SADB_EXT_MAX];
27531da177e4SLinus Torvalds 	int err;
27541da177e4SLinus Torvalds 
27551da177e4SLinus Torvalds 	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
275607fb0f17SAlexey Dobriyan 			BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
27571da177e4SLinus Torvalds 
27581da177e4SLinus Torvalds 	memset(ext_hdrs, 0, sizeof(ext_hdrs));
27591da177e4SLinus Torvalds 	err = parse_exthdrs(skb, hdr, ext_hdrs);
27601da177e4SLinus Torvalds 	if (!err) {
27611da177e4SLinus Torvalds 		err = -EOPNOTSUPP;
27621da177e4SLinus Torvalds 		if (pfkey_funcs[hdr->sadb_msg_type])
27631da177e4SLinus Torvalds 			err = pfkey_funcs[hdr->sadb_msg_type](sk, skb, hdr, ext_hdrs);
27641da177e4SLinus Torvalds 	}
27651da177e4SLinus Torvalds 	return err;
27661da177e4SLinus Torvalds }
27671da177e4SLinus Torvalds 
27681da177e4SLinus Torvalds static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)
27691da177e4SLinus Torvalds {
27701da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
27711da177e4SLinus Torvalds 
27721da177e4SLinus Torvalds 	if (skb->len < sizeof(*hdr)) {
27731da177e4SLinus Torvalds 		*errp = -EMSGSIZE;
27741da177e4SLinus Torvalds 	} else {
27751da177e4SLinus Torvalds 		hdr = (struct sadb_msg *) skb->data;
27761da177e4SLinus Torvalds 		if (hdr->sadb_msg_version != PF_KEY_V2 ||
27771da177e4SLinus Torvalds 		    hdr->sadb_msg_reserved != 0 ||
27781da177e4SLinus Torvalds 		    (hdr->sadb_msg_type <= SADB_RESERVED ||
27791da177e4SLinus Torvalds 		     hdr->sadb_msg_type > SADB_MAX)) {
27801da177e4SLinus Torvalds 			hdr = NULL;
27811da177e4SLinus Torvalds 			*errp = -EINVAL;
27821da177e4SLinus Torvalds 		} else if (hdr->sadb_msg_len != (skb->len /
27831da177e4SLinus Torvalds 						 sizeof(uint64_t)) ||
27841da177e4SLinus Torvalds 			   hdr->sadb_msg_len < (sizeof(struct sadb_msg) /
27851da177e4SLinus Torvalds 						sizeof(uint64_t))) {
27861da177e4SLinus Torvalds 			hdr = NULL;
27871da177e4SLinus Torvalds 			*errp = -EMSGSIZE;
27881da177e4SLinus Torvalds 		} else {
27891da177e4SLinus Torvalds 			*errp = 0;
27901da177e4SLinus Torvalds 		}
27911da177e4SLinus Torvalds 	}
27921da177e4SLinus Torvalds 	return hdr;
27931da177e4SLinus Torvalds }
27941da177e4SLinus Torvalds 
2795*4c93fbb0SDavid S. Miller static inline int aalg_tmpl_set(const struct xfrm_tmpl *t,
2796*4c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
27971da177e4SLinus Torvalds {
2798f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2799f398035fSHerbert Xu 
2800f398035fSHerbert Xu 	if (id >= sizeof(t->aalgos) * 8)
2801f398035fSHerbert Xu 		return 0;
2802f398035fSHerbert Xu 
2803f398035fSHerbert Xu 	return (t->aalgos >> id) & 1;
28041da177e4SLinus Torvalds }
28051da177e4SLinus Torvalds 
2806*4c93fbb0SDavid S. Miller static inline int ealg_tmpl_set(const struct xfrm_tmpl *t,
2807*4c93fbb0SDavid S. Miller 				const struct xfrm_algo_desc *d)
28081da177e4SLinus Torvalds {
2809f398035fSHerbert Xu 	unsigned int id = d->desc.sadb_alg_id;
2810f398035fSHerbert Xu 
2811f398035fSHerbert Xu 	if (id >= sizeof(t->ealgos) * 8)
2812f398035fSHerbert Xu 		return 0;
2813f398035fSHerbert Xu 
2814f398035fSHerbert Xu 	return (t->ealgos >> id) & 1;
28151da177e4SLinus Torvalds }
28161da177e4SLinus Torvalds 
2817*4c93fbb0SDavid S. Miller static int count_ah_combs(const struct xfrm_tmpl *t)
28181da177e4SLinus Torvalds {
28191da177e4SLinus Torvalds 	int i, sz = 0;
28201da177e4SLinus Torvalds 
28211da177e4SLinus Torvalds 	for (i = 0; ; i++) {
2822*4c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
28231da177e4SLinus Torvalds 		if (!aalg)
28241da177e4SLinus Torvalds 			break;
28251da177e4SLinus Torvalds 		if (aalg_tmpl_set(t, aalg) && aalg->available)
28261da177e4SLinus Torvalds 			sz += sizeof(struct sadb_comb);
28271da177e4SLinus Torvalds 	}
28281da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
28291da177e4SLinus Torvalds }
28301da177e4SLinus Torvalds 
2831*4c93fbb0SDavid S. Miller static int count_esp_combs(const struct xfrm_tmpl *t)
28321da177e4SLinus Torvalds {
28331da177e4SLinus Torvalds 	int i, k, sz = 0;
28341da177e4SLinus Torvalds 
28351da177e4SLinus Torvalds 	for (i = 0; ; i++) {
2836*4c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
28371da177e4SLinus Torvalds 		if (!ealg)
28381da177e4SLinus Torvalds 			break;
28391da177e4SLinus Torvalds 
28401da177e4SLinus Torvalds 		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
28411da177e4SLinus Torvalds 			continue;
28421da177e4SLinus Torvalds 
28431da177e4SLinus Torvalds 		for (k = 1; ; k++) {
2844*4c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
28451da177e4SLinus Torvalds 			if (!aalg)
28461da177e4SLinus Torvalds 				break;
28471da177e4SLinus Torvalds 
28481da177e4SLinus Torvalds 			if (aalg_tmpl_set(t, aalg) && aalg->available)
28491da177e4SLinus Torvalds 				sz += sizeof(struct sadb_comb);
28501da177e4SLinus Torvalds 		}
28511da177e4SLinus Torvalds 	}
28521da177e4SLinus Torvalds 	return sz + sizeof(struct sadb_prop);
28531da177e4SLinus Torvalds }
28541da177e4SLinus Torvalds 
2855*4c93fbb0SDavid S. Miller static void dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
28561da177e4SLinus Torvalds {
28571da177e4SLinus Torvalds 	struct sadb_prop *p;
28581da177e4SLinus Torvalds 	int i;
28591da177e4SLinus Torvalds 
28601da177e4SLinus Torvalds 	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
28611da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
28621da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
28631da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
28641da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
28651da177e4SLinus Torvalds 
28661da177e4SLinus Torvalds 	for (i = 0; ; i++) {
2867*4c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
28681da177e4SLinus Torvalds 		if (!aalg)
28691da177e4SLinus Torvalds 			break;
28701da177e4SLinus Torvalds 
28711da177e4SLinus Torvalds 		if (aalg_tmpl_set(t, aalg) && aalg->available) {
28721da177e4SLinus Torvalds 			struct sadb_comb *c;
28731da177e4SLinus Torvalds 			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
28741da177e4SLinus Torvalds 			memset(c, 0, sizeof(*c));
28751da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
28761da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
28771da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
28781da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
28791da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
28801da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
28811da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
28821da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
28831da177e4SLinus Torvalds 		}
28841da177e4SLinus Torvalds 	}
28851da177e4SLinus Torvalds }
28861da177e4SLinus Torvalds 
2887*4c93fbb0SDavid S. Miller static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
28881da177e4SLinus Torvalds {
28891da177e4SLinus Torvalds 	struct sadb_prop *p;
28901da177e4SLinus Torvalds 	int i, k;
28911da177e4SLinus Torvalds 
28921da177e4SLinus Torvalds 	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
28931da177e4SLinus Torvalds 	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
28941da177e4SLinus Torvalds 	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
28951da177e4SLinus Torvalds 	p->sadb_prop_replay = 32;
28961da177e4SLinus Torvalds 	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
28971da177e4SLinus Torvalds 
28981da177e4SLinus Torvalds 	for (i=0; ; i++) {
2899*4c93fbb0SDavid S. Miller 		const struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
29001da177e4SLinus Torvalds 		if (!ealg)
29011da177e4SLinus Torvalds 			break;
29021da177e4SLinus Torvalds 
29031da177e4SLinus Torvalds 		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
29041da177e4SLinus Torvalds 			continue;
29051da177e4SLinus Torvalds 
29061da177e4SLinus Torvalds 		for (k = 1; ; k++) {
29071da177e4SLinus Torvalds 			struct sadb_comb *c;
2908*4c93fbb0SDavid S. Miller 			const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
29091da177e4SLinus Torvalds 			if (!aalg)
29101da177e4SLinus Torvalds 				break;
29111da177e4SLinus Torvalds 			if (!(aalg_tmpl_set(t, aalg) && aalg->available))
29121da177e4SLinus Torvalds 				continue;
29131da177e4SLinus Torvalds 			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
29141da177e4SLinus Torvalds 			memset(c, 0, sizeof(*c));
29151da177e4SLinus Torvalds 			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
29161da177e4SLinus Torvalds 			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
29171da177e4SLinus Torvalds 			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
29181da177e4SLinus Torvalds 			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
29191da177e4SLinus Torvalds 			c->sadb_comb_encrypt = ealg->desc.sadb_alg_id;
29201da177e4SLinus Torvalds 			c->sadb_comb_encrypt_minbits = ealg->desc.sadb_alg_minbits;
29211da177e4SLinus Torvalds 			c->sadb_comb_encrypt_maxbits = ealg->desc.sadb_alg_maxbits;
29221da177e4SLinus Torvalds 			c->sadb_comb_hard_addtime = 24*60*60;
29231da177e4SLinus Torvalds 			c->sadb_comb_soft_addtime = 20*60*60;
29241da177e4SLinus Torvalds 			c->sadb_comb_hard_usetime = 8*60*60;
29251da177e4SLinus Torvalds 			c->sadb_comb_soft_usetime = 7*60*60;
29261da177e4SLinus Torvalds 		}
29271da177e4SLinus Torvalds 	}
29281da177e4SLinus Torvalds }
29291da177e4SLinus Torvalds 
2930214e005bSDavid S. Miller static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c)
293126b15dadSJamal Hadi Salim {
293226b15dadSJamal Hadi Salim 	return 0;
293326b15dadSJamal Hadi Salim }
293426b15dadSJamal Hadi Salim 
2935214e005bSDavid S. Miller static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
29361da177e4SLinus Torvalds {
29371da177e4SLinus Torvalds 	struct sk_buff *out_skb;
29381da177e4SLinus Torvalds 	struct sadb_msg *out_hdr;
293926b15dadSJamal Hadi Salim 	int hard;
294026b15dadSJamal Hadi Salim 	int hsc;
294126b15dadSJamal Hadi Salim 
2942bf08867fSHerbert Xu 	hard = c->data.hard;
294326b15dadSJamal Hadi Salim 	if (hard)
294426b15dadSJamal Hadi Salim 		hsc = 2;
294526b15dadSJamal Hadi Salim 	else
294626b15dadSJamal Hadi Salim 		hsc = 1;
29471da177e4SLinus Torvalds 
2948050f009eSHerbert Xu 	out_skb = pfkey_xfrm_state2msg_expire(x, hsc);
29491da177e4SLinus Torvalds 	if (IS_ERR(out_skb))
29501da177e4SLinus Torvalds 		return PTR_ERR(out_skb);
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds 	out_hdr = (struct sadb_msg *) out_skb->data;
29531da177e4SLinus Torvalds 	out_hdr->sadb_msg_version = PF_KEY_V2;
29541da177e4SLinus Torvalds 	out_hdr->sadb_msg_type = SADB_EXPIRE;
29551da177e4SLinus Torvalds 	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
29561da177e4SLinus Torvalds 	out_hdr->sadb_msg_errno = 0;
29571da177e4SLinus Torvalds 	out_hdr->sadb_msg_reserved = 0;
29581da177e4SLinus Torvalds 	out_hdr->sadb_msg_seq = 0;
29591da177e4SLinus Torvalds 	out_hdr->sadb_msg_pid = 0;
29601da177e4SLinus Torvalds 
296107fb0f17SAlexey Dobriyan 	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
29621da177e4SLinus Torvalds 	return 0;
29631da177e4SLinus Torvalds }
29641da177e4SLinus Torvalds 
2965214e005bSDavid S. Miller static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
296626b15dadSJamal Hadi Salim {
296707fb0f17SAlexey Dobriyan 	struct net *net = x ? xs_net(x) : c->net;
29683fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
29693fa87a32SAlexey Dobriyan 
29703fa87a32SAlexey Dobriyan 	if (atomic_read(&net_pfkey->socks_nr) == 0)
297199c6f60eSJamal Hadi Salim 		return 0;
297299c6f60eSJamal Hadi Salim 
297326b15dadSJamal Hadi Salim 	switch (c->event) {
2974f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
297526b15dadSJamal Hadi Salim 		return key_notify_sa_expire(x, c);
2976f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
2977f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
2978f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
297926b15dadSJamal Hadi Salim 		return key_notify_sa(x, c);
2980f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHSA:
298126b15dadSJamal Hadi Salim 		return key_notify_sa_flush(c);
2982d51d081dSJamal Hadi Salim 	case XFRM_MSG_NEWAE: /* not yet supported */
2983d51d081dSJamal Hadi Salim 		break;
298426b15dadSJamal Hadi Salim 	default:
2985207024b9Sstephen hemminger 		pr_err("pfkey: Unknown SA event %d\n", c->event);
298626b15dadSJamal Hadi Salim 		break;
298726b15dadSJamal Hadi Salim 	}
298826b15dadSJamal Hadi Salim 
298926b15dadSJamal Hadi Salim 	return 0;
299026b15dadSJamal Hadi Salim }
299126b15dadSJamal Hadi Salim 
2992214e005bSDavid S. Miller static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
299326b15dadSJamal Hadi Salim {
2994f7b6983fSMasahide NAKAMURA 	if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
2995f7b6983fSMasahide NAKAMURA 		return 0;
2996f7b6983fSMasahide NAKAMURA 
299726b15dadSJamal Hadi Salim 	switch (c->event) {
2998f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
299926b15dadSJamal Hadi Salim 		return key_notify_policy_expire(xp, c);
3000f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
3001f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
3002f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
300326b15dadSJamal Hadi Salim 		return key_notify_policy(xp, dir, c);
3004f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHPOLICY:
3005f7b6983fSMasahide NAKAMURA 		if (c->data.type != XFRM_POLICY_TYPE_MAIN)
3006f7b6983fSMasahide NAKAMURA 			break;
300726b15dadSJamal Hadi Salim 		return key_notify_policy_flush(c);
300826b15dadSJamal Hadi Salim 	default:
3009207024b9Sstephen hemminger 		pr_err("pfkey: Unknown policy event %d\n", c->event);
301026b15dadSJamal Hadi Salim 		break;
301126b15dadSJamal Hadi Salim 	}
301226b15dadSJamal Hadi Salim 
301326b15dadSJamal Hadi Salim 	return 0;
301426b15dadSJamal Hadi Salim }
301526b15dadSJamal Hadi Salim 
30161da177e4SLinus Torvalds static u32 get_acqseq(void)
30171da177e4SLinus Torvalds {
30181da177e4SLinus Torvalds 	u32 res;
301928aecb9dSEric Dumazet 	static atomic_t acqseq;
30201da177e4SLinus Torvalds 
302128aecb9dSEric Dumazet 	do {
302228aecb9dSEric Dumazet 		res = atomic_inc_return(&acqseq);
302328aecb9dSEric Dumazet 	} while (!res);
30241da177e4SLinus Torvalds 	return res;
30251da177e4SLinus Torvalds }
30261da177e4SLinus Torvalds 
30271da177e4SLinus Torvalds static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir)
30281da177e4SLinus Torvalds {
30291da177e4SLinus Torvalds 	struct sk_buff *skb;
30301da177e4SLinus Torvalds 	struct sadb_msg *hdr;
30311da177e4SLinus Torvalds 	struct sadb_address *addr;
30321da177e4SLinus Torvalds 	struct sadb_x_policy *pol;
30331da177e4SLinus Torvalds 	int sockaddr_size;
30341da177e4SLinus Torvalds 	int size;
30354e2ba18eSVenkat Yekkirala 	struct sadb_x_sec_ctx *sec_ctx;
30364e2ba18eSVenkat Yekkirala 	struct xfrm_sec_ctx *xfrm_ctx;
30374e2ba18eSVenkat Yekkirala 	int ctx_size = 0;
30381da177e4SLinus Torvalds 
30391da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
30401da177e4SLinus Torvalds 	if (!sockaddr_size)
30411da177e4SLinus Torvalds 		return -EINVAL;
30421da177e4SLinus Torvalds 
30431da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
30441da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
30451da177e4SLinus Torvalds 		(sockaddr_size * 2) +
30461da177e4SLinus Torvalds 		sizeof(struct sadb_x_policy);
30471da177e4SLinus Torvalds 
30481da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
30491da177e4SLinus Torvalds 		size += count_ah_combs(t);
30501da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
30511da177e4SLinus Torvalds 		size += count_esp_combs(t);
30521da177e4SLinus Torvalds 
30534e2ba18eSVenkat Yekkirala 	if ((xfrm_ctx = x->security)) {
30544e2ba18eSVenkat Yekkirala 		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
30554e2ba18eSVenkat Yekkirala 		size +=  sizeof(struct sadb_x_sec_ctx) + ctx_size;
30564e2ba18eSVenkat Yekkirala 	}
30574e2ba18eSVenkat Yekkirala 
30581da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
30591da177e4SLinus Torvalds 	if (skb == NULL)
30601da177e4SLinus Torvalds 		return -ENOMEM;
30611da177e4SLinus Torvalds 
30621da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
30631da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
30641da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_ACQUIRE;
30651da177e4SLinus Torvalds 	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
30661da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
30671da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
30681da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
30691da177e4SLinus Torvalds 	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
30701da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
30711da177e4SLinus Torvalds 
30721da177e4SLinus Torvalds 	/* src address */
30731da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
30741da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
30751da177e4SLinus Torvalds 	addr->sadb_address_len =
30761da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
30771da177e4SLinus Torvalds 			sizeof(uint64_t);
30781da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
30791da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
30801da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3081e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3082e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3083e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3084e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3085e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
30861da177e4SLinus Torvalds 		BUG();
30871da177e4SLinus Torvalds 
30881da177e4SLinus Torvalds 	/* dst address */
30891da177e4SLinus Torvalds 	addr = (struct sadb_address*) skb_put(skb,
30901da177e4SLinus Torvalds 					      sizeof(struct sadb_address)+sockaddr_size);
30911da177e4SLinus Torvalds 	addr->sadb_address_len =
30921da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
30931da177e4SLinus Torvalds 			sizeof(uint64_t);
30941da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
30951da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
30961da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3097e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3098e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->id.daddr, 0,
3099e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3100e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3101e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
31021da177e4SLinus Torvalds 		BUG();
31031da177e4SLinus Torvalds 
31041da177e4SLinus Torvalds 	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
31051da177e4SLinus Torvalds 	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
31061da177e4SLinus Torvalds 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
31071da177e4SLinus Torvalds 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
31081da177e4SLinus Torvalds 	pol->sadb_x_policy_dir = dir+1;
31091da177e4SLinus Torvalds 	pol->sadb_x_policy_id = xp->index;
31101da177e4SLinus Torvalds 
31111da177e4SLinus Torvalds 	/* Set sadb_comb's. */
31121da177e4SLinus Torvalds 	if (x->id.proto == IPPROTO_AH)
31131da177e4SLinus Torvalds 		dump_ah_combs(skb, t);
31141da177e4SLinus Torvalds 	else if (x->id.proto == IPPROTO_ESP)
31151da177e4SLinus Torvalds 		dump_esp_combs(skb, t);
31161da177e4SLinus Torvalds 
31174e2ba18eSVenkat Yekkirala 	/* security context */
31184e2ba18eSVenkat Yekkirala 	if (xfrm_ctx) {
31194e2ba18eSVenkat Yekkirala 		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
31204e2ba18eSVenkat Yekkirala 				sizeof(struct sadb_x_sec_ctx) + ctx_size);
31214e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_len =
31224e2ba18eSVenkat Yekkirala 		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
31234e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
31244e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
31254e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
31264e2ba18eSVenkat Yekkirala 		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
31274e2ba18eSVenkat Yekkirala 		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
31284e2ba18eSVenkat Yekkirala 		       xfrm_ctx->ctx_len);
31294e2ba18eSVenkat Yekkirala 	}
31304e2ba18eSVenkat Yekkirala 
313107fb0f17SAlexey Dobriyan 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
31321da177e4SLinus Torvalds }
31331da177e4SLinus Torvalds 
3134cb969f07SVenkat Yekkirala static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
31351da177e4SLinus Torvalds 						u8 *data, int len, int *dir)
31361da177e4SLinus Torvalds {
313707fb0f17SAlexey Dobriyan 	struct net *net = sock_net(sk);
31381da177e4SLinus Torvalds 	struct xfrm_policy *xp;
31391da177e4SLinus Torvalds 	struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
3140df71837dSTrent Jaeger 	struct sadb_x_sec_ctx *sec_ctx;
31411da177e4SLinus Torvalds 
3142cb969f07SVenkat Yekkirala 	switch (sk->sk_family) {
31431da177e4SLinus Torvalds 	case AF_INET:
31441da177e4SLinus Torvalds 		if (opt != IP_IPSEC_POLICY) {
31451da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
31461da177e4SLinus Torvalds 			return NULL;
31471da177e4SLinus Torvalds 		}
31481da177e4SLinus Torvalds 		break;
31491da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
31501da177e4SLinus Torvalds 	case AF_INET6:
31511da177e4SLinus Torvalds 		if (opt != IPV6_IPSEC_POLICY) {
31521da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
31531da177e4SLinus Torvalds 			return NULL;
31541da177e4SLinus Torvalds 		}
31551da177e4SLinus Torvalds 		break;
31561da177e4SLinus Torvalds #endif
31571da177e4SLinus Torvalds 	default:
31581da177e4SLinus Torvalds 		*dir = -EINVAL;
31591da177e4SLinus Torvalds 		return NULL;
31601da177e4SLinus Torvalds 	}
31611da177e4SLinus Torvalds 
31621da177e4SLinus Torvalds 	*dir = -EINVAL;
31631da177e4SLinus Torvalds 
31641da177e4SLinus Torvalds 	if (len < sizeof(struct sadb_x_policy) ||
31651da177e4SLinus Torvalds 	    pol->sadb_x_policy_len*8 > len ||
31661da177e4SLinus Torvalds 	    pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
31671da177e4SLinus Torvalds 	    (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
31681da177e4SLinus Torvalds 		return NULL;
31691da177e4SLinus Torvalds 
317007fb0f17SAlexey Dobriyan 	xp = xfrm_policy_alloc(net, GFP_ATOMIC);
31711da177e4SLinus Torvalds 	if (xp == NULL) {
31721da177e4SLinus Torvalds 		*dir = -ENOBUFS;
31731da177e4SLinus Torvalds 		return NULL;
31741da177e4SLinus Torvalds 	}
31751da177e4SLinus Torvalds 
31761da177e4SLinus Torvalds 	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
31771da177e4SLinus Torvalds 		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
31781da177e4SLinus Torvalds 
31791da177e4SLinus Torvalds 	xp->lft.soft_byte_limit = XFRM_INF;
31801da177e4SLinus Torvalds 	xp->lft.hard_byte_limit = XFRM_INF;
31811da177e4SLinus Torvalds 	xp->lft.soft_packet_limit = XFRM_INF;
31821da177e4SLinus Torvalds 	xp->lft.hard_packet_limit = XFRM_INF;
3183cb969f07SVenkat Yekkirala 	xp->family = sk->sk_family;
31841da177e4SLinus Torvalds 
31851da177e4SLinus Torvalds 	xp->xfrm_nr = 0;
31861da177e4SLinus Torvalds 	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
31871da177e4SLinus Torvalds 	    (*dir = parse_ipsecrequests(xp, pol)) < 0)
31881da177e4SLinus Torvalds 		goto out;
31891da177e4SLinus Torvalds 
3190df71837dSTrent Jaeger 	/* security context too */
3191df71837dSTrent Jaeger 	if (len >= (pol->sadb_x_policy_len*8 +
3192df71837dSTrent Jaeger 	    sizeof(struct sadb_x_sec_ctx))) {
3193df71837dSTrent Jaeger 		char *p = (char *)pol;
3194df71837dSTrent Jaeger 		struct xfrm_user_sec_ctx *uctx;
3195df71837dSTrent Jaeger 
3196df71837dSTrent Jaeger 		p += pol->sadb_x_policy_len*8;
3197df71837dSTrent Jaeger 		sec_ctx = (struct sadb_x_sec_ctx *)p;
3198df71837dSTrent Jaeger 		if (len < pol->sadb_x_policy_len*8 +
3199cb969f07SVenkat Yekkirala 		    sec_ctx->sadb_x_sec_len) {
3200cb969f07SVenkat Yekkirala 			*dir = -EINVAL;
3201df71837dSTrent Jaeger 			goto out;
3202cb969f07SVenkat Yekkirala 		}
3203df71837dSTrent Jaeger 		if ((*dir = verify_sec_ctx_len(p)))
3204df71837dSTrent Jaeger 			goto out;
3205df71837dSTrent Jaeger 		uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
320603e1ad7bSPaul Moore 		*dir = security_xfrm_policy_alloc(&xp->security, uctx);
3207df71837dSTrent Jaeger 		kfree(uctx);
3208df71837dSTrent Jaeger 
3209df71837dSTrent Jaeger 		if (*dir)
3210df71837dSTrent Jaeger 			goto out;
3211df71837dSTrent Jaeger 	}
3212df71837dSTrent Jaeger 
32131da177e4SLinus Torvalds 	*dir = pol->sadb_x_policy_dir-1;
32141da177e4SLinus Torvalds 	return xp;
32151da177e4SLinus Torvalds 
32161da177e4SLinus Torvalds out:
321770e90679SAlexey Dobriyan 	xp->walk.dead = 1;
321864c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
32191da177e4SLinus Torvalds 	return NULL;
32201da177e4SLinus Torvalds }
32211da177e4SLinus Torvalds 
32225d36b180SAl Viro static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
32231da177e4SLinus Torvalds {
32241da177e4SLinus Torvalds 	struct sk_buff *skb;
32251da177e4SLinus Torvalds 	struct sadb_msg *hdr;
32261da177e4SLinus Torvalds 	struct sadb_sa *sa;
32271da177e4SLinus Torvalds 	struct sadb_address *addr;
32281da177e4SLinus Torvalds 	struct sadb_x_nat_t_port *n_port;
32291da177e4SLinus Torvalds 	int sockaddr_size;
32301da177e4SLinus Torvalds 	int size;
32311da177e4SLinus Torvalds 	__u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
32321da177e4SLinus Torvalds 	struct xfrm_encap_tmpl *natt = NULL;
32331da177e4SLinus Torvalds 
32341da177e4SLinus Torvalds 	sockaddr_size = pfkey_sockaddr_size(x->props.family);
32351da177e4SLinus Torvalds 	if (!sockaddr_size)
32361da177e4SLinus Torvalds 		return -EINVAL;
32371da177e4SLinus Torvalds 
32381da177e4SLinus Torvalds 	if (!satype)
32391da177e4SLinus Torvalds 		return -EINVAL;
32401da177e4SLinus Torvalds 
32411da177e4SLinus Torvalds 	if (!x->encap)
32421da177e4SLinus Torvalds 		return -EINVAL;
32431da177e4SLinus Torvalds 
32441da177e4SLinus Torvalds 	natt = x->encap;
32451da177e4SLinus Torvalds 
32461da177e4SLinus Torvalds 	/* Build an SADB_X_NAT_T_NEW_MAPPING message:
32471da177e4SLinus Torvalds 	 *
32481da177e4SLinus Torvalds 	 * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
32491da177e4SLinus Torvalds 	 * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
32501da177e4SLinus Torvalds 	 */
32511da177e4SLinus Torvalds 
32521da177e4SLinus Torvalds 	size = sizeof(struct sadb_msg) +
32531da177e4SLinus Torvalds 		sizeof(struct sadb_sa) +
32541da177e4SLinus Torvalds 		(sizeof(struct sadb_address) * 2) +
32551da177e4SLinus Torvalds 		(sockaddr_size * 2) +
32561da177e4SLinus Torvalds 		(sizeof(struct sadb_x_nat_t_port) * 2);
32571da177e4SLinus Torvalds 
32581da177e4SLinus Torvalds 	skb =  alloc_skb(size + 16, GFP_ATOMIC);
32591da177e4SLinus Torvalds 	if (skb == NULL)
32601da177e4SLinus Torvalds 		return -ENOMEM;
32611da177e4SLinus Torvalds 
32621da177e4SLinus Torvalds 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
32631da177e4SLinus Torvalds 	hdr->sadb_msg_version = PF_KEY_V2;
32641da177e4SLinus Torvalds 	hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
32651da177e4SLinus Torvalds 	hdr->sadb_msg_satype = satype;
32661da177e4SLinus Torvalds 	hdr->sadb_msg_len = size / sizeof(uint64_t);
32671da177e4SLinus Torvalds 	hdr->sadb_msg_errno = 0;
32681da177e4SLinus Torvalds 	hdr->sadb_msg_reserved = 0;
32691da177e4SLinus Torvalds 	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
32701da177e4SLinus Torvalds 	hdr->sadb_msg_pid = 0;
32711da177e4SLinus Torvalds 
32721da177e4SLinus Torvalds 	/* SA */
32731da177e4SLinus Torvalds 	sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
32741da177e4SLinus Torvalds 	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
32751da177e4SLinus Torvalds 	sa->sadb_sa_exttype = SADB_EXT_SA;
32761da177e4SLinus Torvalds 	sa->sadb_sa_spi = x->id.spi;
32771da177e4SLinus Torvalds 	sa->sadb_sa_replay = 0;
32781da177e4SLinus Torvalds 	sa->sadb_sa_state = 0;
32791da177e4SLinus Torvalds 	sa->sadb_sa_auth = 0;
32801da177e4SLinus Torvalds 	sa->sadb_sa_encrypt = 0;
32811da177e4SLinus Torvalds 	sa->sadb_sa_flags = 0;
32821da177e4SLinus Torvalds 
32831da177e4SLinus Torvalds 	/* ADDRESS_SRC (old addr) */
32841da177e4SLinus Torvalds 	addr = (struct sadb_address*)
32851da177e4SLinus Torvalds 		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
32861da177e4SLinus Torvalds 	addr->sadb_address_len =
32871da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
32881da177e4SLinus Torvalds 			sizeof(uint64_t);
32891da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
32901da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
32911da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3292e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3293e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&x->props.saddr, 0,
3294e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3295e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3296e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
32971da177e4SLinus Torvalds 		BUG();
32981da177e4SLinus Torvalds 
32991da177e4SLinus Torvalds 	/* NAT_T_SPORT (old port) */
33001da177e4SLinus Torvalds 	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
33011da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
33021da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
33031da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = natt->encap_sport;
33041da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
33051da177e4SLinus Torvalds 
33061da177e4SLinus Torvalds 	/* ADDRESS_DST (new addr) */
33071da177e4SLinus Torvalds 	addr = (struct sadb_address*)
33081da177e4SLinus Torvalds 		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
33091da177e4SLinus Torvalds 	addr->sadb_address_len =
33101da177e4SLinus Torvalds 		(sizeof(struct sadb_address)+sockaddr_size)/
33111da177e4SLinus Torvalds 			sizeof(uint64_t);
33121da177e4SLinus Torvalds 	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
33131da177e4SLinus Torvalds 	addr->sadb_address_proto = 0;
33141da177e4SLinus Torvalds 	addr->sadb_address_reserved = 0;
3315e5b56652SYOSHIFUJI Hideaki 	addr->sadb_address_prefixlen =
3316e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(ipaddr, 0,
3317e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *) (addr + 1),
3318e5b56652SYOSHIFUJI Hideaki 				    x->props.family);
3319e5b56652SYOSHIFUJI Hideaki 	if (!addr->sadb_address_prefixlen)
33201da177e4SLinus Torvalds 		BUG();
33211da177e4SLinus Torvalds 
33221da177e4SLinus Torvalds 	/* NAT_T_DPORT (new port) */
33231da177e4SLinus Torvalds 	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
33241da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
33251da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
33261da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_port = sport;
33271da177e4SLinus Torvalds 	n_port->sadb_x_nat_t_port_reserved = 0;
33281da177e4SLinus Torvalds 
332907fb0f17SAlexey Dobriyan 	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
33301da177e4SLinus Torvalds }
33311da177e4SLinus Torvalds 
333208de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
333308de61beSShinta Sugimoto static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
3334183cad12SDavid S. Miller 			    const struct xfrm_selector *sel)
333508de61beSShinta Sugimoto {
333608de61beSShinta Sugimoto 	struct sadb_address *addr;
333708de61beSShinta Sugimoto 	addr = (struct sadb_address *)skb_put(skb, sizeof(struct sadb_address) + sasize);
333808de61beSShinta Sugimoto 	addr->sadb_address_len = (sizeof(struct sadb_address) + sasize)/8;
333908de61beSShinta Sugimoto 	addr->sadb_address_exttype = type;
334008de61beSShinta Sugimoto 	addr->sadb_address_proto = sel->proto;
334108de61beSShinta Sugimoto 	addr->sadb_address_reserved = 0;
334208de61beSShinta Sugimoto 
334308de61beSShinta Sugimoto 	switch (type) {
334408de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_SRC:
334508de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_s;
3346e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->saddr, 0,
3347e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3348e5b56652SYOSHIFUJI Hideaki 				    sel->family);
334908de61beSShinta Sugimoto 		break;
335008de61beSShinta Sugimoto 	case SADB_EXT_ADDRESS_DST:
335108de61beSShinta Sugimoto 		addr->sadb_address_prefixlen = sel->prefixlen_d;
3352e5b56652SYOSHIFUJI Hideaki 		pfkey_sockaddr_fill(&sel->daddr, 0,
3353e5b56652SYOSHIFUJI Hideaki 				    (struct sockaddr *)(addr + 1),
3354e5b56652SYOSHIFUJI Hideaki 				    sel->family);
335508de61beSShinta Sugimoto 		break;
335608de61beSShinta Sugimoto 	default:
335708de61beSShinta Sugimoto 		return -EINVAL;
335808de61beSShinta Sugimoto 	}
335908de61beSShinta Sugimoto 
336008de61beSShinta Sugimoto 	return 0;
336108de61beSShinta Sugimoto }
336208de61beSShinta Sugimoto 
336313c1d189SArnaud Ebalard 
3364183cad12SDavid S. Miller static int set_sadb_kmaddress(struct sk_buff *skb, const struct xfrm_kmaddress *k)
336513c1d189SArnaud Ebalard {
336613c1d189SArnaud Ebalard 	struct sadb_x_kmaddress *kma;
336713c1d189SArnaud Ebalard 	u8 *sa;
336813c1d189SArnaud Ebalard 	int family = k->family;
336913c1d189SArnaud Ebalard 	int socklen = pfkey_sockaddr_len(family);
337013c1d189SArnaud Ebalard 	int size_req;
337113c1d189SArnaud Ebalard 
337213c1d189SArnaud Ebalard 	size_req = (sizeof(struct sadb_x_kmaddress) +
337313c1d189SArnaud Ebalard 		    pfkey_sockaddr_pair_size(family));
337413c1d189SArnaud Ebalard 
337513c1d189SArnaud Ebalard 	kma = (struct sadb_x_kmaddress *)skb_put(skb, size_req);
337613c1d189SArnaud Ebalard 	memset(kma, 0, size_req);
337713c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_len = size_req / 8;
337813c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_exttype = SADB_X_EXT_KMADDRESS;
337913c1d189SArnaud Ebalard 	kma->sadb_x_kmaddress_reserved = k->reserved;
338013c1d189SArnaud Ebalard 
338113c1d189SArnaud Ebalard 	sa = (u8 *)(kma + 1);
338213c1d189SArnaud Ebalard 	if (!pfkey_sockaddr_fill(&k->local, 0, (struct sockaddr *)sa, family) ||
338313c1d189SArnaud Ebalard 	    !pfkey_sockaddr_fill(&k->remote, 0, (struct sockaddr *)(sa+socklen), family))
338413c1d189SArnaud Ebalard 		return -EINVAL;
338513c1d189SArnaud Ebalard 
338613c1d189SArnaud Ebalard 	return 0;
338713c1d189SArnaud Ebalard }
338813c1d189SArnaud Ebalard 
338908de61beSShinta Sugimoto static int set_ipsecrequest(struct sk_buff *skb,
339008de61beSShinta Sugimoto 			    uint8_t proto, uint8_t mode, int level,
339108de61beSShinta Sugimoto 			    uint32_t reqid, uint8_t family,
3392183cad12SDavid S. Miller 			    const xfrm_address_t *src, const xfrm_address_t *dst)
339308de61beSShinta Sugimoto {
339408de61beSShinta Sugimoto 	struct sadb_x_ipsecrequest *rq;
3395e5b56652SYOSHIFUJI Hideaki 	u8 *sa;
3396e5b56652SYOSHIFUJI Hideaki 	int socklen = pfkey_sockaddr_len(family);
339708de61beSShinta Sugimoto 	int size_req;
339808de61beSShinta Sugimoto 
339908de61beSShinta Sugimoto 	size_req = sizeof(struct sadb_x_ipsecrequest) +
340008de61beSShinta Sugimoto 		   pfkey_sockaddr_pair_size(family);
340108de61beSShinta Sugimoto 
340208de61beSShinta Sugimoto 	rq = (struct sadb_x_ipsecrequest *)skb_put(skb, size_req);
340308de61beSShinta Sugimoto 	memset(rq, 0, size_req);
340408de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_len = size_req;
340508de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_proto = proto;
340608de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_mode = mode;
340708de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_level = level;
340808de61beSShinta Sugimoto 	rq->sadb_x_ipsecrequest_reqid = reqid;
340908de61beSShinta Sugimoto 
3410e5b56652SYOSHIFUJI Hideaki 	sa = (u8 *) (rq + 1);
3411e5b56652SYOSHIFUJI Hideaki 	if (!pfkey_sockaddr_fill(src, 0, (struct sockaddr *)sa, family) ||
3412e5b56652SYOSHIFUJI Hideaki 	    !pfkey_sockaddr_fill(dst, 0, (struct sockaddr *)(sa + socklen), family))
341308de61beSShinta Sugimoto 		return -EINVAL;
341408de61beSShinta Sugimoto 
341508de61beSShinta Sugimoto 	return 0;
341608de61beSShinta Sugimoto }
341708de61beSShinta Sugimoto #endif
341808de61beSShinta Sugimoto 
341908de61beSShinta Sugimoto #ifdef CONFIG_NET_KEY_MIGRATE
3420183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3421183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
3422183cad12SDavid S. Miller 			      const struct xfrm_kmaddress *k)
342308de61beSShinta Sugimoto {
342408de61beSShinta Sugimoto 	int i;
342508de61beSShinta Sugimoto 	int sasize_sel;
342608de61beSShinta Sugimoto 	int size = 0;
342708de61beSShinta Sugimoto 	int size_pol = 0;
342808de61beSShinta Sugimoto 	struct sk_buff *skb;
342908de61beSShinta Sugimoto 	struct sadb_msg *hdr;
343008de61beSShinta Sugimoto 	struct sadb_x_policy *pol;
3431183cad12SDavid S. Miller 	const struct xfrm_migrate *mp;
343208de61beSShinta Sugimoto 
343308de61beSShinta Sugimoto 	if (type != XFRM_POLICY_TYPE_MAIN)
343408de61beSShinta Sugimoto 		return 0;
343508de61beSShinta Sugimoto 
343608de61beSShinta Sugimoto 	if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH)
343708de61beSShinta Sugimoto 		return -EINVAL;
343808de61beSShinta Sugimoto 
343913c1d189SArnaud Ebalard 	if (k != NULL) {
344013c1d189SArnaud Ebalard 		/* addresses for KM */
344113c1d189SArnaud Ebalard 		size += PFKEY_ALIGN8(sizeof(struct sadb_x_kmaddress) +
344213c1d189SArnaud Ebalard 				     pfkey_sockaddr_pair_size(k->family));
344313c1d189SArnaud Ebalard 	}
344413c1d189SArnaud Ebalard 
344508de61beSShinta Sugimoto 	/* selector */
344608de61beSShinta Sugimoto 	sasize_sel = pfkey_sockaddr_size(sel->family);
344708de61beSShinta Sugimoto 	if (!sasize_sel)
344808de61beSShinta Sugimoto 		return -EINVAL;
344908de61beSShinta Sugimoto 	size += (sizeof(struct sadb_address) + sasize_sel) * 2;
345008de61beSShinta Sugimoto 
345108de61beSShinta Sugimoto 	/* policy info */
345208de61beSShinta Sugimoto 	size_pol += sizeof(struct sadb_x_policy);
345308de61beSShinta Sugimoto 
345408de61beSShinta Sugimoto 	/* ipsecrequests */
345508de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
345608de61beSShinta Sugimoto 		/* old locator pair */
345708de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
345808de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->old_family);
345908de61beSShinta Sugimoto 		/* new locator pair */
346008de61beSShinta Sugimoto 		size_pol += sizeof(struct sadb_x_ipsecrequest) +
346108de61beSShinta Sugimoto 			    pfkey_sockaddr_pair_size(mp->new_family);
346208de61beSShinta Sugimoto 	}
346308de61beSShinta Sugimoto 
346408de61beSShinta Sugimoto 	size += sizeof(struct sadb_msg) + size_pol;
346508de61beSShinta Sugimoto 
346608de61beSShinta Sugimoto 	/* alloc buffer */
346708de61beSShinta Sugimoto 	skb = alloc_skb(size, GFP_ATOMIC);
346808de61beSShinta Sugimoto 	if (skb == NULL)
346908de61beSShinta Sugimoto 		return -ENOMEM;
347008de61beSShinta Sugimoto 
347108de61beSShinta Sugimoto 	hdr = (struct sadb_msg *)skb_put(skb, sizeof(struct sadb_msg));
347208de61beSShinta Sugimoto 	hdr->sadb_msg_version = PF_KEY_V2;
347308de61beSShinta Sugimoto 	hdr->sadb_msg_type = SADB_X_MIGRATE;
347408de61beSShinta Sugimoto 	hdr->sadb_msg_satype = pfkey_proto2satype(m->proto);
347508de61beSShinta Sugimoto 	hdr->sadb_msg_len = size / 8;
347608de61beSShinta Sugimoto 	hdr->sadb_msg_errno = 0;
347708de61beSShinta Sugimoto 	hdr->sadb_msg_reserved = 0;
347808de61beSShinta Sugimoto 	hdr->sadb_msg_seq = 0;
347908de61beSShinta Sugimoto 	hdr->sadb_msg_pid = 0;
348008de61beSShinta Sugimoto 
348113c1d189SArnaud Ebalard 	/* Addresses to be used by KM for negotiation, if ext is available */
348213c1d189SArnaud Ebalard 	if (k != NULL && (set_sadb_kmaddress(skb, k) < 0))
348313c1d189SArnaud Ebalard 		return -EINVAL;
348413c1d189SArnaud Ebalard 
348508de61beSShinta Sugimoto 	/* selector src */
348608de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel);
348708de61beSShinta Sugimoto 
348808de61beSShinta Sugimoto 	/* selector dst */
348908de61beSShinta Sugimoto 	set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_DST, sel);
349008de61beSShinta Sugimoto 
349108de61beSShinta Sugimoto 	/* policy information */
349208de61beSShinta Sugimoto 	pol = (struct sadb_x_policy *)skb_put(skb, sizeof(struct sadb_x_policy));
349308de61beSShinta Sugimoto 	pol->sadb_x_policy_len = size_pol / 8;
349408de61beSShinta Sugimoto 	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
349508de61beSShinta Sugimoto 	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
349608de61beSShinta Sugimoto 	pol->sadb_x_policy_dir = dir + 1;
349708de61beSShinta Sugimoto 	pol->sadb_x_policy_id = 0;
349808de61beSShinta Sugimoto 	pol->sadb_x_policy_priority = 0;
349908de61beSShinta Sugimoto 
350008de61beSShinta Sugimoto 	for (i = 0, mp = m; i < num_bundles; i++, mp++) {
350108de61beSShinta Sugimoto 		/* old ipsecrequest */
350255569ce2SKazunori MIYAZAWA 		int mode = pfkey_mode_from_xfrm(mp->mode);
350355569ce2SKazunori MIYAZAWA 		if (mode < 0)
3504d4782c32SPatrick McHardy 			goto err;
350555569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
350608de61beSShinta Sugimoto 				     (mp->reqid ?  IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
350708de61beSShinta Sugimoto 				     mp->reqid, mp->old_family,
3508d4782c32SPatrick McHardy 				     &mp->old_saddr, &mp->old_daddr) < 0)
3509d4782c32SPatrick McHardy 			goto err;
351008de61beSShinta Sugimoto 
351108de61beSShinta Sugimoto 		/* new ipsecrequest */
351255569ce2SKazunori MIYAZAWA 		if (set_ipsecrequest(skb, mp->proto, mode,
351308de61beSShinta Sugimoto 				     (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
351408de61beSShinta Sugimoto 				     mp->reqid, mp->new_family,
3515d4782c32SPatrick McHardy 				     &mp->new_saddr, &mp->new_daddr) < 0)
3516d4782c32SPatrick McHardy 			goto err;
351708de61beSShinta Sugimoto 	}
351808de61beSShinta Sugimoto 
351908de61beSShinta Sugimoto 	/* broadcast migrate message to sockets */
352007fb0f17SAlexey Dobriyan 	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
352108de61beSShinta Sugimoto 
352208de61beSShinta Sugimoto 	return 0;
3523d4782c32SPatrick McHardy 
3524d4782c32SPatrick McHardy err:
3525d4782c32SPatrick McHardy 	kfree_skb(skb);
3526d4782c32SPatrick McHardy 	return -EINVAL;
352708de61beSShinta Sugimoto }
352808de61beSShinta Sugimoto #else
3529183cad12SDavid S. Miller static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
3530183cad12SDavid S. Miller 			      const struct xfrm_migrate *m, int num_bundles,
3531183cad12SDavid S. Miller 			      const struct xfrm_kmaddress *k)
353208de61beSShinta Sugimoto {
353308de61beSShinta Sugimoto 	return -ENOPROTOOPT;
353408de61beSShinta Sugimoto }
353508de61beSShinta Sugimoto #endif
353608de61beSShinta Sugimoto 
35371da177e4SLinus Torvalds static int pfkey_sendmsg(struct kiocb *kiocb,
35381da177e4SLinus Torvalds 			 struct socket *sock, struct msghdr *msg, size_t len)
35391da177e4SLinus Torvalds {
35401da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
35411da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
35421da177e4SLinus Torvalds 	struct sadb_msg *hdr = NULL;
35431da177e4SLinus Torvalds 	int err;
35441da177e4SLinus Torvalds 
35451da177e4SLinus Torvalds 	err = -EOPNOTSUPP;
35461da177e4SLinus Torvalds 	if (msg->msg_flags & MSG_OOB)
35471da177e4SLinus Torvalds 		goto out;
35481da177e4SLinus Torvalds 
35491da177e4SLinus Torvalds 	err = -EMSGSIZE;
35501da177e4SLinus Torvalds 	if ((unsigned)len > sk->sk_sndbuf - 32)
35511da177e4SLinus Torvalds 		goto out;
35521da177e4SLinus Torvalds 
35531da177e4SLinus Torvalds 	err = -ENOBUFS;
35541da177e4SLinus Torvalds 	skb = alloc_skb(len, GFP_KERNEL);
35551da177e4SLinus Torvalds 	if (skb == NULL)
35561da177e4SLinus Torvalds 		goto out;
35571da177e4SLinus Torvalds 
35581da177e4SLinus Torvalds 	err = -EFAULT;
35591da177e4SLinus Torvalds 	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len))
35601da177e4SLinus Torvalds 		goto out;
35611da177e4SLinus Torvalds 
35621da177e4SLinus Torvalds 	hdr = pfkey_get_base_msg(skb, &err);
35631da177e4SLinus Torvalds 	if (!hdr)
35641da177e4SLinus Torvalds 		goto out;
35651da177e4SLinus Torvalds 
35664a3e2f71SArjan van de Ven 	mutex_lock(&xfrm_cfg_mutex);
35671da177e4SLinus Torvalds 	err = pfkey_process(sk, skb, hdr);
35684a3e2f71SArjan van de Ven 	mutex_unlock(&xfrm_cfg_mutex);
35691da177e4SLinus Torvalds 
35701da177e4SLinus Torvalds out:
35711da177e4SLinus Torvalds 	if (err && hdr && pfkey_error(hdr, err, sk) == 0)
35721da177e4SLinus Torvalds 		err = 0;
35731da177e4SLinus Torvalds 	kfree_skb(skb);
35741da177e4SLinus Torvalds 
35751da177e4SLinus Torvalds 	return err ? : len;
35761da177e4SLinus Torvalds }
35771da177e4SLinus Torvalds 
35781da177e4SLinus Torvalds static int pfkey_recvmsg(struct kiocb *kiocb,
35791da177e4SLinus Torvalds 			 struct socket *sock, struct msghdr *msg, size_t len,
35801da177e4SLinus Torvalds 			 int flags)
35811da177e4SLinus Torvalds {
35821da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
358383321d6bSTimo Teras 	struct pfkey_sock *pfk = pfkey_sk(sk);
35841da177e4SLinus Torvalds 	struct sk_buff *skb;
35851da177e4SLinus Torvalds 	int copied, err;
35861da177e4SLinus Torvalds 
35871da177e4SLinus Torvalds 	err = -EINVAL;
35881da177e4SLinus Torvalds 	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
35891da177e4SLinus Torvalds 		goto out;
35901da177e4SLinus Torvalds 
35911da177e4SLinus Torvalds 	msg->msg_namelen = 0;
35921da177e4SLinus Torvalds 	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
35931da177e4SLinus Torvalds 	if (skb == NULL)
35941da177e4SLinus Torvalds 		goto out;
35951da177e4SLinus Torvalds 
35961da177e4SLinus Torvalds 	copied = skb->len;
35971da177e4SLinus Torvalds 	if (copied > len) {
35981da177e4SLinus Torvalds 		msg->msg_flags |= MSG_TRUNC;
35991da177e4SLinus Torvalds 		copied = len;
36001da177e4SLinus Torvalds 	}
36011da177e4SLinus Torvalds 
3602badff6d0SArnaldo Carvalho de Melo 	skb_reset_transport_header(skb);
36031da177e4SLinus Torvalds 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
36041da177e4SLinus Torvalds 	if (err)
36051da177e4SLinus Torvalds 		goto out_free;
36061da177e4SLinus Torvalds 
36073b885787SNeil Horman 	sock_recv_ts_and_drops(msg, sk, skb);
36081da177e4SLinus Torvalds 
36091da177e4SLinus Torvalds 	err = (flags & MSG_TRUNC) ? skb->len : copied;
36101da177e4SLinus Torvalds 
361183321d6bSTimo Teras 	if (pfk->dump.dump != NULL &&
361283321d6bSTimo Teras 	    3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
361383321d6bSTimo Teras 		pfkey_do_dump(pfk);
361483321d6bSTimo Teras 
36151da177e4SLinus Torvalds out_free:
36161da177e4SLinus Torvalds 	skb_free_datagram(sk, skb);
36171da177e4SLinus Torvalds out:
36181da177e4SLinus Torvalds 	return err;
36191da177e4SLinus Torvalds }
36201da177e4SLinus Torvalds 
362190ddc4f0SEric Dumazet static const struct proto_ops pfkey_ops = {
36221da177e4SLinus Torvalds 	.family		=	PF_KEY,
36231da177e4SLinus Torvalds 	.owner		=	THIS_MODULE,
36241da177e4SLinus Torvalds 	/* Operations that make no sense on pfkey sockets. */
36251da177e4SLinus Torvalds 	.bind		=	sock_no_bind,
36261da177e4SLinus Torvalds 	.connect	=	sock_no_connect,
36271da177e4SLinus Torvalds 	.socketpair	=	sock_no_socketpair,
36281da177e4SLinus Torvalds 	.accept		=	sock_no_accept,
36291da177e4SLinus Torvalds 	.getname	=	sock_no_getname,
36301da177e4SLinus Torvalds 	.ioctl		=	sock_no_ioctl,
36311da177e4SLinus Torvalds 	.listen		=	sock_no_listen,
36321da177e4SLinus Torvalds 	.shutdown	=	sock_no_shutdown,
36331da177e4SLinus Torvalds 	.setsockopt	=	sock_no_setsockopt,
36341da177e4SLinus Torvalds 	.getsockopt	=	sock_no_getsockopt,
36351da177e4SLinus Torvalds 	.mmap		=	sock_no_mmap,
36361da177e4SLinus Torvalds 	.sendpage	=	sock_no_sendpage,
36371da177e4SLinus Torvalds 
36381da177e4SLinus Torvalds 	/* Now the operations that really occur. */
36391da177e4SLinus Torvalds 	.release	=	pfkey_release,
36401da177e4SLinus Torvalds 	.poll		=	datagram_poll,
36411da177e4SLinus Torvalds 	.sendmsg	=	pfkey_sendmsg,
36421da177e4SLinus Torvalds 	.recvmsg	=	pfkey_recvmsg,
36431da177e4SLinus Torvalds };
36441da177e4SLinus Torvalds 
3645ec1b4cf7SStephen Hemminger static const struct net_proto_family pfkey_family_ops = {
36461da177e4SLinus Torvalds 	.family	=	PF_KEY,
36471da177e4SLinus Torvalds 	.create	=	pfkey_create,
36481da177e4SLinus Torvalds 	.owner	=	THIS_MODULE,
36491da177e4SLinus Torvalds };
36501da177e4SLinus Torvalds 
36511da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
3652bd2f7476SPavel Emelyanov static int pfkey_seq_show(struct seq_file *f, void *v)
36531da177e4SLinus Torvalds {
365427b5b865SLi Zefan 	struct sock *s = sk_entry(v);
36551da177e4SLinus Torvalds 
3656bd2f7476SPavel Emelyanov 	if (v == SEQ_START_TOKEN)
3657bd2f7476SPavel Emelyanov 		seq_printf(f ,"sk       RefCnt Rmem   Wmem   User   Inode\n");
3658bd2f7476SPavel Emelyanov 	else
3659bd2f7476SPavel Emelyanov 		seq_printf(f ,"%p %-6d %-6u %-6u %-6u %-6lu\n",
36601da177e4SLinus Torvalds 			       s,
36611da177e4SLinus Torvalds 			       atomic_read(&s->sk_refcnt),
366231e6d363SEric Dumazet 			       sk_rmem_alloc_get(s),
366331e6d363SEric Dumazet 			       sk_wmem_alloc_get(s),
36641da177e4SLinus Torvalds 			       sock_i_uid(s),
36651da177e4SLinus Torvalds 			       sock_i_ino(s)
36661da177e4SLinus Torvalds 			       );
3667bd2f7476SPavel Emelyanov 	return 0;
36681da177e4SLinus Torvalds }
36691da177e4SLinus Torvalds 
3670bd2f7476SPavel Emelyanov static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
3671ada440e3Sstephen hemminger 	__acquires(rcu)
3672bd2f7476SPavel Emelyanov {
36737013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
36743fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
3675bd2f7476SPavel Emelyanov 
36767f6b9dbdSstephen hemminger 	rcu_read_lock();
36777f6b9dbdSstephen hemminger 	return seq_hlist_start_head_rcu(&net_pfkey->table, *ppos);
3678bd2f7476SPavel Emelyanov }
3679bd2f7476SPavel Emelyanov 
3680bd2f7476SPavel Emelyanov static void *pfkey_seq_next(struct seq_file *f, void *v, loff_t *ppos)
3681bd2f7476SPavel Emelyanov {
36827013ec30SAlexey Dobriyan 	struct net *net = seq_file_net(f);
36833fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
36843fa87a32SAlexey Dobriyan 
36857f6b9dbdSstephen hemminger 	return seq_hlist_next_rcu(v, &net_pfkey->table, ppos);
3686bd2f7476SPavel Emelyanov }
3687bd2f7476SPavel Emelyanov 
3688bd2f7476SPavel Emelyanov static void pfkey_seq_stop(struct seq_file *f, void *v)
3689ada440e3Sstephen hemminger 	__releases(rcu)
3690bd2f7476SPavel Emelyanov {
36917f6b9dbdSstephen hemminger 	rcu_read_unlock();
36921da177e4SLinus Torvalds }
369361145aa1SPavel Emelyanov 
369498147d52SStephen Hemminger static const struct seq_operations pfkey_seq_ops = {
3695bd2f7476SPavel Emelyanov 	.start	= pfkey_seq_start,
3696bd2f7476SPavel Emelyanov 	.next	= pfkey_seq_next,
3697bd2f7476SPavel Emelyanov 	.stop	= pfkey_seq_stop,
3698bd2f7476SPavel Emelyanov 	.show	= pfkey_seq_show,
3699bd2f7476SPavel Emelyanov };
3700bd2f7476SPavel Emelyanov 
3701bd2f7476SPavel Emelyanov static int pfkey_seq_open(struct inode *inode, struct file *file)
3702bd2f7476SPavel Emelyanov {
37037013ec30SAlexey Dobriyan 	return seq_open_net(inode, file, &pfkey_seq_ops,
37047013ec30SAlexey Dobriyan 			    sizeof(struct seq_net_private));
3705bd2f7476SPavel Emelyanov }
3706bd2f7476SPavel Emelyanov 
37075ca1b998SStephen Hemminger static const struct file_operations pfkey_proc_ops = {
3708bd2f7476SPavel Emelyanov 	.open	 = pfkey_seq_open,
3709bd2f7476SPavel Emelyanov 	.read	 = seq_read,
3710bd2f7476SPavel Emelyanov 	.llseek	 = seq_lseek,
37117013ec30SAlexey Dobriyan 	.release = seq_release_net,
3712bd2f7476SPavel Emelyanov };
3713bd2f7476SPavel Emelyanov 
37147013ec30SAlexey Dobriyan static int __net_init pfkey_init_proc(struct net *net)
371561145aa1SPavel Emelyanov {
3716bd2f7476SPavel Emelyanov 	struct proc_dir_entry *e;
3717bd2f7476SPavel Emelyanov 
37187013ec30SAlexey Dobriyan 	e = proc_net_fops_create(net, "pfkey", 0, &pfkey_proc_ops);
3719bd2f7476SPavel Emelyanov 	if (e == NULL)
372061145aa1SPavel Emelyanov 		return -ENOMEM;
3721bd2f7476SPavel Emelyanov 
372261145aa1SPavel Emelyanov 	return 0;
372361145aa1SPavel Emelyanov }
372461145aa1SPavel Emelyanov 
37252c8c1e72SAlexey Dobriyan static void __net_exit pfkey_exit_proc(struct net *net)
372661145aa1SPavel Emelyanov {
37277013ec30SAlexey Dobriyan 	proc_net_remove(net, "pfkey");
372861145aa1SPavel Emelyanov }
372961145aa1SPavel Emelyanov #else
37302c8c1e72SAlexey Dobriyan static inline int pfkey_init_proc(struct net *net)
373161145aa1SPavel Emelyanov {
373261145aa1SPavel Emelyanov 	return 0;
373361145aa1SPavel Emelyanov }
373461145aa1SPavel Emelyanov 
37352c8c1e72SAlexey Dobriyan static inline void pfkey_exit_proc(struct net *net)
373661145aa1SPavel Emelyanov {
373761145aa1SPavel Emelyanov }
37381da177e4SLinus Torvalds #endif
37391da177e4SLinus Torvalds 
37401da177e4SLinus Torvalds static struct xfrm_mgr pfkeyv2_mgr =
37411da177e4SLinus Torvalds {
37421da177e4SLinus Torvalds 	.id		= "pfkeyv2",
37431da177e4SLinus Torvalds 	.notify		= pfkey_send_notify,
37441da177e4SLinus Torvalds 	.acquire	= pfkey_send_acquire,
37451da177e4SLinus Torvalds 	.compile_policy	= pfkey_compile_policy,
37461da177e4SLinus Torvalds 	.new_mapping	= pfkey_send_new_mapping,
374726b15dadSJamal Hadi Salim 	.notify_policy	= pfkey_send_policy_notify,
374808de61beSShinta Sugimoto 	.migrate	= pfkey_send_migrate,
37491da177e4SLinus Torvalds };
37501da177e4SLinus Torvalds 
37513fa87a32SAlexey Dobriyan static int __net_init pfkey_net_init(struct net *net)
37523fa87a32SAlexey Dobriyan {
375323c049caSEric W. Biederman 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
37543fa87a32SAlexey Dobriyan 	int rv;
37553fa87a32SAlexey Dobriyan 
37563fa87a32SAlexey Dobriyan 	INIT_HLIST_HEAD(&net_pfkey->table);
37573fa87a32SAlexey Dobriyan 	atomic_set(&net_pfkey->socks_nr, 0);
37583fa87a32SAlexey Dobriyan 
375923c049caSEric W. Biederman 	rv = pfkey_init_proc(net);
376023c049caSEric W. Biederman 
37613fa87a32SAlexey Dobriyan 	return rv;
37623fa87a32SAlexey Dobriyan }
37633fa87a32SAlexey Dobriyan 
37643fa87a32SAlexey Dobriyan static void __net_exit pfkey_net_exit(struct net *net)
37653fa87a32SAlexey Dobriyan {
37663fa87a32SAlexey Dobriyan 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
37673fa87a32SAlexey Dobriyan 
37687013ec30SAlexey Dobriyan 	pfkey_exit_proc(net);
37693fa87a32SAlexey Dobriyan 	BUG_ON(!hlist_empty(&net_pfkey->table));
37703fa87a32SAlexey Dobriyan }
37713fa87a32SAlexey Dobriyan 
37723fa87a32SAlexey Dobriyan static struct pernet_operations pfkey_net_ops = {
37733fa87a32SAlexey Dobriyan 	.init = pfkey_net_init,
37743fa87a32SAlexey Dobriyan 	.exit = pfkey_net_exit,
377523c049caSEric W. Biederman 	.id   = &pfkey_net_id,
377623c049caSEric W. Biederman 	.size = sizeof(struct netns_pfkey),
37773fa87a32SAlexey Dobriyan };
37783fa87a32SAlexey Dobriyan 
37791da177e4SLinus Torvalds static void __exit ipsec_pfkey_exit(void)
37801da177e4SLinus Torvalds {
37811da177e4SLinus Torvalds 	xfrm_unregister_km(&pfkeyv2_mgr);
37821da177e4SLinus Torvalds 	sock_unregister(PF_KEY);
3783180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
37841da177e4SLinus Torvalds 	proto_unregister(&key_proto);
37851da177e4SLinus Torvalds }
37861da177e4SLinus Torvalds 
37871da177e4SLinus Torvalds static int __init ipsec_pfkey_init(void)
37881da177e4SLinus Torvalds {
37891da177e4SLinus Torvalds 	int err = proto_register(&key_proto, 0);
37901da177e4SLinus Torvalds 
37911da177e4SLinus Torvalds 	if (err != 0)
37921da177e4SLinus Torvalds 		goto out;
37931da177e4SLinus Torvalds 
3794180211b8SAlexey Dobriyan 	err = register_pernet_subsys(&pfkey_net_ops);
37951da177e4SLinus Torvalds 	if (err != 0)
37961da177e4SLinus Torvalds 		goto out_unregister_key_proto;
3797180211b8SAlexey Dobriyan 	err = sock_register(&pfkey_family_ops);
3798180211b8SAlexey Dobriyan 	if (err != 0)
3799180211b8SAlexey Dobriyan 		goto out_unregister_pernet;
38001da177e4SLinus Torvalds 	err = xfrm_register_km(&pfkeyv2_mgr);
38011da177e4SLinus Torvalds 	if (err != 0)
38027013ec30SAlexey Dobriyan 		goto out_sock_unregister;
38031da177e4SLinus Torvalds out:
38041da177e4SLinus Torvalds 	return err;
3805180211b8SAlexey Dobriyan 
38061da177e4SLinus Torvalds out_sock_unregister:
38071da177e4SLinus Torvalds 	sock_unregister(PF_KEY);
3808180211b8SAlexey Dobriyan out_unregister_pernet:
3809180211b8SAlexey Dobriyan 	unregister_pernet_subsys(&pfkey_net_ops);
38101da177e4SLinus Torvalds out_unregister_key_proto:
38111da177e4SLinus Torvalds 	proto_unregister(&key_proto);
38121da177e4SLinus Torvalds 	goto out;
38131da177e4SLinus Torvalds }
38141da177e4SLinus Torvalds 
38151da177e4SLinus Torvalds module_init(ipsec_pfkey_init);
38161da177e4SLinus Torvalds module_exit(ipsec_pfkey_exit);
38171da177e4SLinus Torvalds MODULE_LICENSE("GPL");
38181da177e4SLinus Torvalds MODULE_ALIAS_NETPROTO(PF_KEY);
3819