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