Several xfrm_* structs are incompatible between 32bit and 64bit builds:

    xfrm_usersa_info 220 bytes on i386 -> 224 bytes on amd64
    xfrm_userpolicy_info 164 -> 168
    xfrm_userspi_info 228 -> 232, offset mismatch on min
    xfrm_user_acquire 276 -> 280, offset mismatch on aalgos
    xfrm_user_expire 224 -> 232, offset mismatch on hard
    xfrm_user_polexpire 168 -> 176, offset mismatch on hard

Fork all of the functions that handle these structs into a new file so
that it is possible to support both legacy + new layouts.

This commit contains an exact copy of the necessary functions from
xfrm_user.c, for ease of reviewing.  The next commit will contain all
of the changes needed to make these functions handle legacy messages
correctly.

Signed-off-by: Kevin Cernekee <cerne...@chromium.org>
---
 net/xfrm/Kconfig            |   14 +
 net/xfrm/Makefile           |    8 +-
 net/xfrm/xfrm_user_legacy.c | 1091 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1112 insertions(+), 1 deletion(-)
 create mode 100644 net/xfrm/xfrm_user_legacy.c

diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
index bda1a13628a8..317dcc411345 100644
--- a/net/xfrm/Kconfig
+++ b/net/xfrm/Kconfig
@@ -20,6 +20,20 @@ config XFRM_USER
 
          If unsure, say Y.
 
+config XFRM_USER_LEGACY
+       tristate "Legacy transformation user configuration interface"
+       depends on XFRM_USER
+       default y
+       ---help---
+         The original Transformation(XFRM) netlink messages were not
+         compatible between 32-bit programs and 64-bit kernels, so they
+         have been deprecated.  Enable this option if you have existing
+         binaries that rely on the old format messages.  Disable this
+         option if you know that all users of the interface have been
+         built against recent kernel headers.
+
+         If unsure, say Y.
+
 config XFRM_SUB_POLICY
        bool "Transformation sub policy support"
        depends on XFRM
diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile
index c0e961983f17..6cf6f8da3dc8 100644
--- a/net/xfrm/Makefile
+++ b/net/xfrm/Makefile
@@ -7,5 +7,11 @@ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
                      xfrm_sysctl.o xfrm_replay.o
 obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
 obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
-obj-$(CONFIG_XFRM_USER) += xfrm_user.o
+
+xfrm-user-objs := xfrm_user.o
+ifneq ($(CONFIG_XFRM_USER_LEGACY),)
+xfrm-user-objs += xfrm_user_legacy.o
+endif
+obj-$(CONFIG_XFRM_USER) += xfrm-user.o
+
 obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
diff --git a/net/xfrm/xfrm_user_legacy.c b/net/xfrm/xfrm_user_legacy.c
new file mode 100644
index 000000000000..058accfefc83
--- /dev/null
+++ b/net/xfrm/xfrm_user_legacy.c
@@ -0,0 +1,1091 @@
+/* xfrm_user.c: User interface to configure xfrm engine.
+ *
+ * Copyright (C) 2002 David S. Miller (da...@redhat.com)
+ *
+ * Changes:
+ *     Mitsuru KANDA @USAGI
+ *     Kazunori MIYAZAWA @USAGI
+ *     Kunihiro Ishiguro <kunih...@ipinfusion.com>
+ *             IPv6 support
+ *
+ */
+
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/pfkeyv2.h>
+#include <linux/ipsec.h>
+#include <linux/security.h>
+#include <net/sock.h>
+#include <net/xfrm.h>
+#include <net/netlink.h>
+#include <net/ah.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include "xfrm_user.h"
+
+static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                      struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       const struct xfrm_usersa_info *p = nlmsg_data(nlh);
+       struct xfrm_state *x;
+       int err;
+       struct km_event c;
+
+       err = xfrm_verify_newsa_info(p, attrs);
+       if (err)
+               return err;
+
+       x = xfrm_state_construct(net, p, attrs, &err);
+       if (!x)
+               return err;
+
+       xfrm_state_hold(x);
+       if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
+               err = xfrm_state_add(x);
+       else
+               err = xfrm_state_update(x);
+
+       xfrm_audit_state_add(x, err ? 0 : 1, true);
+
+       if (err < 0) {
+               x->km.state = XFRM_STATE_DEAD;
+               __xfrm_state_put(x);
+               goto out;
+       }
+
+       c.seq = nlh->nlmsg_seq;
+       c.portid = nlh->nlmsg_pid;
+       c.event = nlh->nlmsg_type;
+
+       km_state_notify(x, &c);
+out:
+       xfrm_state_put(x);
+       return err;
+}
+
+static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                      struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_state *x;
+       int err = -ESRCH;
+       struct km_event c;
+       const struct xfrm_usersa_id *p = nlmsg_data(nlh);
+
+       x = xfrm_user_state_lookup(net, p, attrs, &err);
+       if (x == NULL)
+               return err;
+
+       if ((err = security_xfrm_state_delete(x)) != 0)
+               goto out;
+
+       if (xfrm_state_kern(x)) {
+               err = -EPERM;
+               goto out;
+       }
+
+       err = xfrm_state_delete(x);
+
+       if (err < 0)
+               goto out;
+
+       c.seq = nlh->nlmsg_seq;
+       c.portid = nlh->nlmsg_pid;
+       c.event = nlh->nlmsg_type;
+       km_state_notify(x, &c);
+
+out:
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
+       xfrm_state_put(x);
+       return err;
+}
+
+static void copy_to_user_state(const struct xfrm_state *x,
+                              struct xfrm_usersa_info *p)
+{
+       memset(p, 0, sizeof(*p));
+       memcpy(&p->id, &x->id, sizeof(p->id));
+       memcpy(&p->sel, &x->sel, sizeof(p->sel));
+       memcpy(&p->lft, &x->lft, sizeof(p->lft));
+       memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
+       put_unaligned(x->stats.replay_window, &p->stats.replay_window);
+       put_unaligned(x->stats.replay, &p->stats.replay);
+       put_unaligned(x->stats.integrity_failed, &p->stats.integrity_failed);
+       memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));
+       p->mode = x->props.mode;
+       p->replay_window = x->props.replay_window;
+       p->reqid = x->props.reqid;
+       p->family = x->props.family;
+       p->flags = x->props.flags;
+       p->seq = x->km.seq;
+}
+
+static int copy_to_user_state_extra(const struct xfrm_state *x,
+                                   struct xfrm_usersa_info *p,
+                                   struct sk_buff *skb)
+{
+       int ret = 0;
+
+       copy_to_user_state(x, p);
+
+       if (x->props.extra_flags) {
+               ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS,
+                                 x->props.extra_flags);
+               if (ret)
+                       goto out;
+       }
+
+       if (x->coaddr) {
+               ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
+               if (ret)
+                       goto out;
+       }
+       if (x->lastused) {
+               ret = nla_put_u64_64bit(skb, XFRMA_LASTUSED, x->lastused,
+                                       XFRMA_PAD);
+               if (ret)
+                       goto out;
+       }
+       if (x->aead) {
+               ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
+               if (ret)
+                       goto out;
+       }
+       if (x->aalg) {
+               ret = xfrm_copy_to_user_auth(x->aalg, skb);
+               if (!ret)
+                       ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC,
+                                     xfrm_alg_auth_len(x->aalg), x->aalg);
+               if (ret)
+                       goto out;
+       }
+       if (x->ealg) {
+               ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), 
x->ealg);
+               if (ret)
+                       goto out;
+       }
+       if (x->calg) {
+               ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg);
+               if (ret)
+                       goto out;
+       }
+       if (x->encap) {
+               ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
+               if (ret)
+                       goto out;
+       }
+       if (x->tfcpad) {
+               ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad);
+               if (ret)
+                       goto out;
+       }
+       ret = xfrm_mark_put(skb, &x->mark);
+       if (ret)
+               goto out;
+       if (x->replay_esn)
+               ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
+                             xfrm_replay_state_esn_len(x->replay_esn),
+                             x->replay_esn);
+       else
+               ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
+                             &x->replay);
+       if (ret)
+               goto out;
+       if (x->security)
+               ret = xfrm_copy_sec_ctx(x->security, skb);
+out:
+       return ret;
+}
+
+static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
+{
+       struct xfrm_dump_info *sp = ptr;
+       struct sk_buff *in_skb = sp->in_skb;
+       struct sk_buff *skb = sp->out_skb;
+       struct xfrm_usersa_info *p;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
+                       XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       p = nlmsg_data(nlh);
+
+       err = copy_to_user_state_extra(x, p, skb);
+       if (err) {
+               nlmsg_cancel(skb, nlh);
+               return err;
+       }
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
+static int xfrm_dump_sa_done(struct netlink_callback *cb)
+{
+       struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
+       struct sock *sk = cb->skb->sk;
+       struct net *net = sock_net(sk);
+
+       if (cb->args[0])
+               xfrm_state_walk_done(walk, net);
+       return 0;
+}
+
+static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
+static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
+       struct xfrm_dump_info info;
+
+       BUILD_BUG_ON(sizeof(struct xfrm_state_walk) >
+                    sizeof(cb->args) - sizeof(cb->args[0]));
+
+       info.in_skb = cb->skb;
+       info.out_skb = skb;
+       info.nlmsg_seq = cb->nlh->nlmsg_seq;
+       info.nlmsg_flags = NLM_F_MULTI;
+
+       if (!cb->args[0]) {
+               struct nlattr *attrs[XFRMA_MAX+1];
+               struct xfrm_address_filter *filter = NULL;
+               u8 proto = 0;
+               int err;
+
+               err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX,
+                                 xfrma_policy);
+               if (err < 0)
+                       return err;
+
+               if (attrs[XFRMA_ADDRESS_FILTER]) {
+                       filter = kmemdup(nla_data(attrs[XFRMA_ADDRESS_FILTER]),
+                                        sizeof(*filter), GFP_KERNEL);
+                       if (filter == NULL)
+                               return -ENOMEM;
+               }
+
+               if (attrs[XFRMA_PROTO])
+                       proto = nla_get_u8(attrs[XFRMA_PROTO]);
+
+               xfrm_state_walk_init(walk, proto, filter);
+               cb->args[0] = 1;
+       }
+
+       (void) xfrm_state_walk(net, walk, dump_one_state, &info);
+
+       return skb->len;
+}
+
+static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
+                                         const struct xfrm_state *x,
+                                         u32 seq)
+{
+       struct xfrm_dump_info info;
+       struct sk_buff *skb;
+       int err;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       info.in_skb = in_skb;
+       info.out_skb = skb;
+       info.nlmsg_seq = seq;
+       info.nlmsg_flags = 0;
+
+       err = dump_one_state(x, 0, &info);
+       if (err) {
+               kfree_skb(skb);
+               return ERR_PTR(err);
+       }
+
+       return skb;
+}
+
+static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                      struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       const struct xfrm_usersa_id *p = nlmsg_data(nlh);
+       struct xfrm_state *x;
+       struct sk_buff *resp_skb;
+       int err = -ESRCH;
+
+       x = xfrm_user_state_lookup(net, p, attrs, &err);
+       if (x == NULL)
+               goto out_noput;
+
+       resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
+       if (IS_ERR(resp_skb)) {
+               err = PTR_ERR(resp_skb);
+       } else {
+               err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 
NETLINK_CB(skb).portid);
+       }
+       xfrm_state_put(x);
+out_noput:
+       return err;
+}
+
+static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                             struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_state *x;
+       const struct xfrm_userspi_info *p;
+       struct sk_buff *resp_skb;
+       const xfrm_address_t *daddr;
+       int family;
+       int err;
+       u32 mark;
+       struct xfrm_mark m;
+
+       p = nlmsg_data(nlh);
+       err = verify_spi_info(p->info.id.proto, p->min, p->max);
+       if (err)
+               goto out_noput;
+
+       family = p->info.family;
+       daddr = &p->info.id.daddr;
+
+       x = NULL;
+
+       mark = xfrm_mark_get(attrs, &m);
+       if (p->info.seq) {
+               x = xfrm_find_acq_byseq(net, mark, p->info.seq);
+               if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
+                       xfrm_state_put(x);
+                       x = NULL;
+               }
+       }
+
+       if (!x)
+               x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
+                                 p->info.id.proto, daddr,
+                                 &p->info.saddr, 1,
+                                 family);
+       err = -ENOENT;
+       if (x == NULL)
+               goto out_noput;
+
+       err = xfrm_alloc_spi(x, p->min, p->max);
+       if (err)
+               goto out;
+
+       resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
+       if (IS_ERR(resp_skb)) {
+               err = PTR_ERR(resp_skb);
+               goto out;
+       }
+
+       err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid);
+
+out:
+       xfrm_state_put(x);
+out_noput:
+       return err;
+}
+
+static void copy_to_user_policy(const struct xfrm_policy *xp,
+                               struct xfrm_userpolicy_info *p,
+                               int dir)
+{
+       memset(p, 0, sizeof(*p));
+       memcpy(&p->sel, &xp->selector, sizeof(p->sel));
+       memcpy(&p->lft, &xp->lft, sizeof(p->lft));
+       memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
+       p->priority = xp->priority;
+       p->index = xp->index;
+       p->sel.family = xp->family;
+       p->dir = dir;
+       p->action = xp->action;
+       p->flags = xp->flags;
+       p->share = XFRM_SHARE_ANY; /* XXX xp->share */
+}
+
+static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                          struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       const struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
+       struct xfrm_policy *xp;
+       struct km_event c;
+       int err;
+       int excl;
+
+       err = xfrm_verify_newpolicy_info(p);
+       if (err)
+               return err;
+       err = xfrm_verify_sec_ctx_len(attrs);
+       if (err)
+               return err;
+
+       xp = xfrm_policy_construct(net, p, attrs, &err);
+       if (!xp)
+               return err;
+
+       /* shouldn't excl be based on nlh flags??
+        * Aha! this is anti-netlink really i.e  more pfkey derived
+        * in netlink excl is a flag and you wouldnt need
+        * a type XFRM_MSG_UPDPOLICY - JHS */
+       excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
+       err = xfrm_policy_insert(p->dir, xp, excl);
+       xfrm_audit_policy_add(xp, err ? 0 : 1, true);
+
+       if (err) {
+               security_xfrm_policy_free(xp->security);
+               kfree(xp);
+               return err;
+       }
+
+       c.event = nlh->nlmsg_type;
+       c.seq = nlh->nlmsg_seq;
+       c.portid = nlh->nlmsg_pid;
+       km_policy_notify(xp, p->dir, &c);
+
+       xfrm_pol_put(xp);
+
+       return 0;
+}
+
+static inline size_t userpolicy_type_attrsize(void)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+       return nla_total_size(sizeof(struct xfrm_userpolicy_type));
+#else
+       return 0;
+#endif
+}
+
+static int dump_one_policy(const struct xfrm_policy *xp,
+                          int dir,
+                          int count,
+                          void *ptr)
+{
+       struct xfrm_dump_info *sp = ptr;
+       struct xfrm_userpolicy_info *p;
+       struct sk_buff *in_skb = sp->in_skb;
+       struct sk_buff *skb = sp->out_skb;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
+                       XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       p = nlmsg_data(nlh);
+       copy_to_user_policy(xp, p, dir);
+       err = xfrm_copy_to_user_tmpl(xp, skb);
+       if (!err)
+               err = copy_to_user_sec_ctx(xp, skb);
+       if (!err)
+               err = copy_to_user_policy_type(xp->type, skb);
+       if (!err)
+               err = xfrm_mark_put(skb, &xp->mark);
+       if (err) {
+               nlmsg_cancel(skb, nlh);
+               return err;
+       }
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
+static int xfrm_dump_policy_done(struct netlink_callback *cb)
+{
+       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) 
&cb->args[1];
+       struct net *net = sock_net(cb->skb->sk);
+
+       xfrm_policy_walk_done(walk, net);
+       return 0;
+}
+
+static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) 
&cb->args[1];
+       struct xfrm_dump_info info;
+
+       BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) >
+                    sizeof(cb->args) - sizeof(cb->args[0]));
+
+       info.in_skb = cb->skb;
+       info.out_skb = skb;
+       info.nlmsg_seq = cb->nlh->nlmsg_seq;
+       info.nlmsg_flags = NLM_F_MULTI;
+
+       if (!cb->args[0]) {
+               cb->args[0] = 1;
+               xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
+       }
+
+       (void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
+
+       return skb->len;
+}
+
+static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
+                                          const struct xfrm_policy *xp,
+                                          int dir,
+                                          u32 seq)
+{
+       struct xfrm_dump_info info;
+       struct sk_buff *skb;
+       int err;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       info.in_skb = in_skb;
+       info.out_skb = skb;
+       info.nlmsg_seq = seq;
+       info.nlmsg_flags = 0;
+
+       err = dump_one_policy(xp, dir, 0, &info);
+       if (err) {
+               kfree_skb(skb);
+               return ERR_PTR(err);
+       }
+
+       return skb;
+}
+
+static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                          struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_policy *xp;
+       const struct xfrm_userpolicy_id *p;
+       u8 type = XFRM_POLICY_TYPE_MAIN;
+       int err;
+       struct km_event c;
+       int delete;
+       struct xfrm_mark m;
+       u32 mark = xfrm_mark_get(attrs, &m);
+
+       p = nlmsg_data(nlh);
+       delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
+
+       err = xfrm_copy_from_user_policy_type(&type, attrs);
+       if (err)
+               return err;
+
+       err = xfrm_verify_policy_dir(p->dir);
+       if (err)
+               return err;
+
+       if (p->index)
+               xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 
delete, &err);
+       else {
+               struct nlattr *rt = attrs[XFRMA_SEC_CTX];
+               struct xfrm_sec_ctx *ctx;
+
+               err = xfrm_verify_sec_ctx_len(attrs);
+               if (err)
+                       return err;
+
+               ctx = NULL;
+               if (rt) {
+                       struct xfrm_user_sec_ctx *uctx = nla_data(rt);
+
+                       err = security_xfrm_policy_alloc(&ctx, uctx, 
GFP_KERNEL);
+                       if (err)
+                               return err;
+               }
+               xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel,
+                                          ctx, delete, &err);
+               security_xfrm_policy_free(ctx);
+       }
+       if (xp == NULL)
+               return -ENOENT;
+
+       if (!delete) {
+               struct sk_buff *resp_skb;
+
+               resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq);
+               if (IS_ERR(resp_skb)) {
+                       err = PTR_ERR(resp_skb);
+               } else {
+                       err = nlmsg_unicast(net->xfrm.nlsk, resp_skb,
+                                           NETLINK_CB(skb).portid);
+               }
+       } else {
+               xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
+
+               if (err != 0)
+                       goto out;
+
+               c.data.byid = p->index;
+               c.event = nlh->nlmsg_type;
+               c.seq = nlh->nlmsg_seq;
+               c.portid = nlh->nlmsg_pid;
+               km_policy_notify(xp, p->dir, &c);
+       }
+
+out:
+       xfrm_pol_put(xp);
+       if (delete && err == 0)
+               xfrm_garbage_collect(net);
+       return err;
+}
+
+static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                              struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_policy *xp;
+       const struct xfrm_user_polexpire *up = nlmsg_data(nlh);
+       const struct xfrm_userpolicy_info *p = &up->pol;
+       u8 type = XFRM_POLICY_TYPE_MAIN;
+       int err = -ENOENT;
+       struct xfrm_mark m;
+       u32 mark = xfrm_mark_get(attrs, &m);
+
+       err = xfrm_copy_from_user_policy_type(&type, attrs);
+       if (err)
+               return err;
+
+       err = xfrm_verify_policy_dir(p->dir);
+       if (err)
+               return err;
+
+       if (p->index)
+               xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, 
&err);
+       else {
+               struct nlattr *rt = attrs[XFRMA_SEC_CTX];
+               struct xfrm_sec_ctx *ctx;
+
+               err = xfrm_verify_sec_ctx_len(attrs);
+               if (err)
+                       return err;
+
+               ctx = NULL;
+               if (rt) {
+                       struct xfrm_user_sec_ctx *uctx = nla_data(rt);
+
+                       err = security_xfrm_policy_alloc(&ctx, uctx, 
GFP_KERNEL);
+                       if (err)
+                               return err;
+               }
+               xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir,
+                                          &p->sel, ctx, 0, &err);
+               security_xfrm_policy_free(ctx);
+       }
+       if (xp == NULL)
+               return -ENOENT;
+
+       if (unlikely(xp->walk.dead))
+               goto out;
+
+       err = 0;
+       if (up->hard) {
+               xfrm_policy_delete(xp, p->dir);
+               xfrm_audit_policy_delete(xp, 1, true);
+       }
+       km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid);
+
+out:
+       xfrm_pol_put(xp);
+       return err;
+}
+
+static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                             struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_state *x;
+       int err;
+       const struct xfrm_user_expire *ue = nlmsg_data(nlh);
+       const struct xfrm_usersa_info *p = &ue->state;
+       struct xfrm_mark m;
+       u32 mark = xfrm_mark_get(attrs, &m);
+
+       x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, 
p->family);
+
+       err = -ENOENT;
+       if (x == NULL)
+               return err;
+
+       spin_lock_bh(&x->lock);
+       err = -EINVAL;
+       if (x->km.state != XFRM_STATE_VALID)
+               goto out;
+       km_state_expired(x, ue->hard, nlh->nlmsg_pid);
+
+       if (ue->hard) {
+               __xfrm_state_delete(x);
+               xfrm_audit_state_delete(x, 1, true);
+       }
+       err = 0;
+out:
+       spin_unlock_bh(&x->lock);
+       xfrm_state_put(x);
+       return err;
+}
+
+static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+                           struct nlattr **attrs)
+{
+       struct net *net = sock_net(skb->sk);
+       struct xfrm_policy *xp;
+       struct xfrm_user_tmpl *ut;
+       int i;
+       struct nlattr *rt = attrs[XFRMA_TMPL];
+       struct xfrm_mark mark;
+
+       const struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+       struct xfrm_state *x = xfrm_state_alloc(net);
+       int err = -ENOMEM;
+
+       if (!x)
+               goto nomem;
+
+       xfrm_mark_get(attrs, &mark);
+
+       err = xfrm_verify_newpolicy_info(&ua->policy);
+       if (err)
+               goto free_state;
+
+       /*   build an XP */
+       xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+       if (!xp)
+               goto free_state;
+
+       memcpy(&x->id, &ua->id, sizeof(ua->id));
+       memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
+       memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
+       xp->mark.m = x->mark.m = mark.m;
+       xp->mark.v = x->mark.v = mark.v;
+       ut = nla_data(rt);
+       /* extract the templates and for each call km_key */
+       for (i = 0; i < xp->xfrm_nr; i++, ut++) {
+               struct xfrm_tmpl *t = &xp->xfrm_vec[i];
+               memcpy(&x->id, &t->id, sizeof(x->id));
+               x->props.mode = t->mode;
+               x->props.reqid = t->reqid;
+               x->props.family = ut->family;
+               t->aalgos = ua->aalgos;
+               t->ealgos = ua->ealgos;
+               t->calgos = ua->calgos;
+               err = km_query(x, t, xp);
+
+       }
+
+       kfree(x);
+       kfree(xp);
+
+       return 0;
+
+free_state:
+       kfree(x);
+nomem:
+       return err;
+}
+
+static inline size_t xfrm_expire_msgsize(void)
+{
+       return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+              + nla_total_size(sizeof(struct xfrm_mark));
+}
+
+static int build_expire(struct sk_buff *skb,
+                       const struct xfrm_state *x,
+                       const struct km_event *c)
+{
+       struct xfrm_user_expire *ue;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       ue = nlmsg_data(nlh);
+       copy_to_user_state(x, &ue->state);
+       ue->hard = (c->data.hard != 0) ? 1 : 0;
+
+       err = xfrm_mark_put(skb, &x->mark);
+       if (err)
+               return err;
+
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
+static int xfrm_exp_state_notify(const struct xfrm_state *x,
+                                const struct km_event *c)
+{
+       struct net *net = xs_net(x);
+       struct sk_buff *skb;
+
+       skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       if (build_expire(skb, x, c) < 0) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
+}
+
+static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
+{
+       struct net *net = xs_net(x);
+       struct xfrm_usersa_info *p;
+       struct xfrm_usersa_id *id;
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       int len = xfrm_sa_len(x);
+       int headlen, err;
+
+       headlen = sizeof(*p);
+       if (c->event == XFRM_MSG_DELSA) {
+               len += nla_total_size(headlen);
+               headlen = sizeof(*id);
+               len += nla_total_size(sizeof(struct xfrm_mark));
+       }
+       len += NLMSG_ALIGN(headlen);
+
+       skb = nlmsg_new(len, GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+       err = -EMSGSIZE;
+       if (nlh == NULL)
+               goto out_free_skb;
+
+       p = nlmsg_data(nlh);
+       if (c->event == XFRM_MSG_DELSA) {
+               struct nlattr *attr;
+
+               id = nlmsg_data(nlh);
+               memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr));
+               id->spi = x->id.spi;
+               id->family = x->props.family;
+               id->proto = x->id.proto;
+
+               attr = nla_reserve(skb, XFRMA_SA, sizeof(*p));
+               err = -EMSGSIZE;
+               if (attr == NULL)
+                       goto out_free_skb;
+
+               p = nla_data(attr);
+       }
+       err = copy_to_user_state_extra(x, p, skb);
+       if (err)
+               goto out_free_skb;
+
+       nlmsg_end(skb, nlh);
+
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
+
+out_free_skb:
+       kfree_skb(skb);
+       return err;
+}
+
+static inline size_t xfrm_acquire_msgsize(const struct xfrm_state *x,
+                                         const struct xfrm_policy *xp)
+{
+       return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
+              + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
+              + nla_total_size(sizeof(struct xfrm_mark))
+              + nla_total_size(xfrm_user_sec_ctx_size(x->security))
+              + userpolicy_type_attrsize();
+}
+
+static int build_acquire(struct sk_buff *skb,
+                        struct xfrm_state *x,
+                        const struct xfrm_tmpl *xt,
+                        const struct xfrm_policy *xp)
+{
+       __u32 seq = xfrm_get_acqseq();
+       struct xfrm_user_acquire *ua;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       ua = nlmsg_data(nlh);
+       memcpy(&ua->id, &x->id, sizeof(ua->id));
+       memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr));
+       memcpy(&ua->sel, &x->sel, sizeof(ua->sel));
+       copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT);
+       ua->aalgos = xt->aalgos;
+       ua->ealgos = xt->ealgos;
+       ua->calgos = xt->calgos;
+       ua->seq = x->km.seq = seq;
+
+       err = xfrm_copy_to_user_tmpl(xp, skb);
+       if (!err)
+               err = copy_to_user_state_sec_ctx(x, skb);
+       if (!err)
+               err = copy_to_user_policy_type(xp->type, skb);
+       if (!err)
+               err = xfrm_mark_put(skb, &xp->mark);
+       if (err) {
+               nlmsg_cancel(skb, nlh);
+               return err;
+       }
+
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
+static int xfrm_send_acquire(struct xfrm_state *x,
+                            const struct xfrm_tmpl *xt,
+                            const struct xfrm_policy *xp)
+{
+       struct net *net = xs_net(x);
+       struct sk_buff *skb;
+
+       skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       if (build_acquire(skb, x, xt, xp) < 0)
+               BUG();
+
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+}
+
+static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
+{
+       return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
+              + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
+              + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
+              + nla_total_size(sizeof(struct xfrm_mark))
+              + userpolicy_type_attrsize();
+}
+
+static int build_polexpire(struct sk_buff *skb,
+                          const struct xfrm_policy *xp,
+                          int dir,
+                          const struct km_event *c)
+{
+       struct xfrm_user_polexpire *upe;
+       int hard = c->data.hard;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       upe = nlmsg_data(nlh);
+       copy_to_user_policy(xp, &upe->pol, dir);
+       err = xfrm_copy_to_user_tmpl(xp, skb);
+       if (!err)
+               err = copy_to_user_sec_ctx(xp, skb);
+       if (!err)
+               err = copy_to_user_policy_type(xp->type, skb);
+       if (!err)
+               err = xfrm_mark_put(skb, &xp->mark);
+       if (err) {
+               nlmsg_cancel(skb, nlh);
+               return err;
+       }
+       upe->hard = !!hard;
+
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
+static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
+                                 int dir,
+                                 const struct km_event *c)
+{
+       struct net *net = xp_net(xp);
+       struct sk_buff *skb;
+
+       skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       if (build_polexpire(skb, xp, dir, c) < 0)
+               BUG();
+
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
+}
+
+static int xfrm_notify_policy(const struct xfrm_policy *xp,
+                             int dir,
+                             const struct km_event *c)
+{
+       int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
+       struct net *net = xp_net(xp);
+       struct xfrm_userpolicy_info *p;
+       struct xfrm_userpolicy_id *id;
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       int headlen, err;
+
+       headlen = sizeof(*p);
+       if (c->event == XFRM_MSG_DELPOLICY) {
+               len += nla_total_size(headlen);
+               headlen = sizeof(*id);
+       }
+       len += userpolicy_type_attrsize();
+       len += nla_total_size(sizeof(struct xfrm_mark));
+       len += NLMSG_ALIGN(headlen);
+
+       skb = nlmsg_new(len, GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+       err = -EMSGSIZE;
+       if (nlh == NULL)
+               goto out_free_skb;
+
+       p = nlmsg_data(nlh);
+       if (c->event == XFRM_MSG_DELPOLICY) {
+               struct nlattr *attr;
+
+               id = nlmsg_data(nlh);
+               memset(id, 0, sizeof(*id));
+               id->dir = dir;
+               if (c->data.byid)
+                       id->index = xp->index;
+               else
+                       memcpy(&id->sel, &xp->selector, sizeof(id->sel));
+
+               attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p));
+               err = -EMSGSIZE;
+               if (attr == NULL)
+                       goto out_free_skb;
+
+               p = nla_data(attr);
+       }
+
+       copy_to_user_policy(xp, p, dir);
+       err = xfrm_copy_to_user_tmpl(xp, skb);
+       if (!err)
+               err = copy_to_user_policy_type(xp->type, skb);
+       if (!err)
+               err = xfrm_mark_put(skb, &xp->mark);
+       if (err)
+               goto out_free_skb;
+
+       nlmsg_end(skb, nlh);
+
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
+
+out_free_skb:
+       kfree_skb(skb);
+       return err;
+}
-- 
2.11.0.483.g087da7b7c-goog

Reply via email to