diff mbox

[4/4] xfrm_user: Add new 32/64-agnostic netlink messages

Message ID 20170121000507.34381-5-cernekee@chromium.org
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Kevin Cernekee Jan. 21, 2017, 12:05 a.m. UTC
Add several new message types to address longstanding 32-bit/64-bit
compatibility issues.  Use xfrm_user_legacy to handle the existing
message types, which will retain the old IDs for compatibility with
existing binaries.

For user->kernel messages, the nlmsg_type will determine whether to use
the old format or the new format (for both requests and replies).  For
kernel->user multicasts, both types will be sent.

setsockopt() will deduce the format from the length.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
---
 include/uapi/linux/xfrm.h   | 152 ++++++++++++++++++++++++++++++---------
 net/xfrm/xfrm_user.c        | 136 ++++++++++++++++++++++++++++++++---
 net/xfrm/xfrm_user.h        |  75 ++++++++++++++++++++
 net/xfrm/xfrm_user_legacy.c | 169 ++++++++++++++++++++++++++++----------------
 security/selinux/nlmsgtab.c |  61 +++++++++-------
 5 files changed, 466 insertions(+), 127 deletions(-)

Comments

kernel test robot Jan. 21, 2017, 8:21 a.m. UTC | #1
Hi Kevin,

[auto build test ERROR on ipsec-next/master]
[also build test ERROR on v4.10-rc4 next-20170120]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Kevin-Cernekee/Make-xfrm-usable-by-32-bit-programs/20170121-150712
base:   https://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next.git master
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 6.2.0
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=ia64 

All errors (new ones prefixed by >>):

>> net/xfrm/xfrm_user_legacy.c:845:5: error: redefinition of 'xfrm_exp_state_notify_legacy'
    int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from net/xfrm/xfrm_user_legacy.c:30:0:
   net/xfrm/xfrm_user.h:131:19: note: previous definition of 'xfrm_exp_state_notify_legacy' was here
    static inline int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> net/xfrm/xfrm_user_legacy.c:863:5: error: redefinition of 'xfrm_notify_sa_legacy'
    int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c)
        ^~~~~~~~~~~~~~~~~~~~~
   In file included from net/xfrm/xfrm_user_legacy.c:30:0:
   net/xfrm/xfrm_user.h:137:19: note: previous definition of 'xfrm_notify_sa_legacy' was here
    static inline int xfrm_notify_sa_legacy(const struct xfrm_state *x,
                      ^~~~~~~~~~~~~~~~~~~~~
>> net/xfrm/xfrm_user_legacy.c:983:5: error: redefinition of 'xfrm_send_acquire_legacy'
    int xfrm_send_acquire_legacy(struct xfrm_state *x,
        ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from net/xfrm/xfrm_user_legacy.c:30:0:
   net/xfrm/xfrm_user.h:143:19: note: previous definition of 'xfrm_send_acquire_legacy' was here
    static inline int xfrm_send_acquire_legacy(struct xfrm_state *x,
                      ^~~~~~~~~~~~~~~~~~~~~~~~
>> net/xfrm/xfrm_user_legacy.c:1043:5: error: redefinition of 'xfrm_exp_policy_notify_legacy'
    int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from net/xfrm/xfrm_user_legacy.c:30:0:
   net/xfrm/xfrm_user.h:150:19: note: previous definition of 'xfrm_exp_policy_notify_legacy' was here
    static inline int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> net/xfrm/xfrm_user_legacy.c:1060:5: error: redefinition of 'xfrm_notify_policy_legacy'
    int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
        ^~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from net/xfrm/xfrm_user_legacy.c:30:0:
   net/xfrm/xfrm_user.h:157:19: note: previous definition of 'xfrm_notify_policy_legacy' was here
    static inline int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
                      ^~~~~~~~~~~~~~~~~~~~~~~~~

vim +/xfrm_exp_state_notify_legacy +845 net/xfrm/xfrm_user_legacy.c

   839			return err;
   840	
   841		nlmsg_end(skb, nlh);
   842		return 0;
   843	}
   844	
 > 845	int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
   846					 const struct km_event *c)
   847	{
   848		struct net *net = xs_net(x);
   849		struct sk_buff *skb;
   850	
   851		skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
   852		if (skb == NULL)
   853			return -ENOMEM;
   854	
   855		if (build_expire(skb, x, c) < 0) {
   856			kfree_skb(skb);
   857			return -EMSGSIZE;
   858		}
   859	
   860		return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
   861	}
   862	
 > 863	int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c)
   864	{
   865		struct net *net = xs_net(x);
   866		struct xfrm_usersa_info_legacy *p;
   867		struct xfrm_usersa_id *id;
   868		struct nlmsghdr *nlh;
   869		struct sk_buff *skb;
   870		int len = xfrm_sa_len(x);
   871		int headlen, err;
   872		u32 event = 0;
   873	
   874		headlen = sizeof(*p);
   875		if (c->event == XFRM_MSG_DELSA) {
   876			len += nla_total_size(headlen);
   877			headlen = sizeof(*id);
   878			len += nla_total_size(sizeof(struct xfrm_mark));
   879		}
   880		len += NLMSG_ALIGN(headlen);
   881	
   882		skb = nlmsg_new(len, GFP_ATOMIC);
   883		if (skb == NULL)
   884			return -ENOMEM;
   885	
   886		switch (c->event) {
   887		case XFRM_MSG_NEWSA:
   888			event = XFRM_MSG_NEWSA_LEGACY;
   889			break;
   890		case XFRM_MSG_UPDSA:
   891			event = XFRM_MSG_UPDSA_LEGACY;
   892			break;
   893		case XFRM_MSG_DELSA:
   894			event = XFRM_MSG_DELSA_LEGACY;
   895			break;
   896		}
   897	
   898		nlh = nlmsg_put(skb, c->portid, c->seq, event, headlen, 0);
   899		err = -EMSGSIZE;
   900		if (nlh == NULL)
   901			goto out_free_skb;
   902	
   903		p = nlmsg_data(nlh);
   904		if (c->event == XFRM_MSG_DELSA) {
   905			struct nlattr *attr;
   906	
   907			id = nlmsg_data(nlh);
   908			memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr));
   909			id->spi = x->id.spi;
   910			id->family = x->props.family;
   911			id->proto = x->id.proto;
   912	
   913			attr = nla_reserve(skb, XFRMA_SA, sizeof(*p));
   914			err = -EMSGSIZE;
   915			if (attr == NULL)
   916				goto out_free_skb;
   917	
   918			p = nla_data(attr);
   919		}
   920		err = copy_to_user_state_extra(x, p, skb);
   921		if (err)
   922			goto out_free_skb;
   923	
   924		nlmsg_end(skb, nlh);
   925	
   926		return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
   927	
   928	out_free_skb:
   929		kfree_skb(skb);
   930		return err;
   931	}
   932	
   933	static inline size_t xfrm_acquire_msgsize(const struct xfrm_state *x,
   934						  const struct xfrm_policy *xp)
   935	{
   936		return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire_legacy))
   937		       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
   938		       + nla_total_size(sizeof(struct xfrm_mark))
   939		       + nla_total_size(xfrm_user_sec_ctx_size(x->security))
   940		       + userpolicy_type_attrsize();
   941	}
   942	
   943	static int build_acquire(struct sk_buff *skb,
   944				 struct xfrm_state *x,
   945				 const struct xfrm_tmpl *xt,
   946				 const struct xfrm_policy *xp)
   947	{
   948		__u32 seq = xfrm_get_acqseq();
   949		struct xfrm_user_acquire_legacy *ua;
   950		struct nlmsghdr *nlh;
   951		int err;
   952	
   953		nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE_LEGACY, sizeof(*ua), 0);
   954		if (nlh == NULL)
   955			return -EMSGSIZE;
   956	
   957		ua = nlmsg_data(nlh);
   958		memcpy(&ua->id, &x->id, sizeof(ua->id));
   959		memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr));
   960		memcpy(&ua->sel, &x->sel, sizeof(ua->sel));
   961		copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT);
   962		ua->aalgos = xt->aalgos;
   963		ua->ealgos = xt->ealgos;
   964		ua->calgos = xt->calgos;
   965		ua->seq = x->km.seq = seq;
   966	
   967		err = xfrm_copy_to_user_tmpl(xp, skb);
   968		if (!err)
   969			err = copy_to_user_state_sec_ctx(x, skb);
   970		if (!err)
   971			err = copy_to_user_policy_type(xp->type, skb);
   972		if (!err)
   973			err = xfrm_mark_put(skb, &xp->mark);
   974		if (err) {
   975			nlmsg_cancel(skb, nlh);
   976			return err;
   977		}
   978	
   979		nlmsg_end(skb, nlh);
   980		return 0;
   981	}
   982	
 > 983	int xfrm_send_acquire_legacy(struct xfrm_state *x,
   984				     const struct xfrm_tmpl *xt,
   985				     const struct xfrm_policy *xp)
   986	{
   987		struct net *net = xs_net(x);
   988		struct sk_buff *skb;
   989	
   990		skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
   991		if (skb == NULL)
   992			return -ENOMEM;
   993	
   994		if (build_acquire(skb, x, xt, xp) < 0)
   995			BUG();
   996	
   997		return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
   998	}
   999	
  1000	static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
  1001	{
  1002		return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire_legacy))
  1003		       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
  1004		       + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
  1005		       + nla_total_size(sizeof(struct xfrm_mark))
  1006		       + userpolicy_type_attrsize();
  1007	}
  1008	
  1009	static int build_polexpire(struct sk_buff *skb,
  1010				   const struct xfrm_policy *xp,
  1011				   int dir,
  1012				   const struct km_event *c)
  1013	{
  1014		struct xfrm_user_polexpire_legacy *upe;
  1015		int hard = c->data.hard;
  1016		struct nlmsghdr *nlh;
  1017		int err;
  1018	
  1019		nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE_LEGACY,
  1020				sizeof(*upe), 0);
  1021		if (nlh == NULL)
  1022			return -EMSGSIZE;
  1023	
  1024		upe = nlmsg_data(nlh);
  1025		copy_to_user_policy(xp, &upe->pol, dir);
  1026		err = xfrm_copy_to_user_tmpl(xp, skb);
  1027		if (!err)
  1028			err = copy_to_user_sec_ctx(xp, skb);
  1029		if (!err)
  1030			err = copy_to_user_policy_type(xp->type, skb);
  1031		if (!err)
  1032			err = xfrm_mark_put(skb, &xp->mark);
  1033		if (err) {
  1034			nlmsg_cancel(skb, nlh);
  1035			return err;
  1036		}
  1037		upe->hard = !!hard;
  1038	
  1039		nlmsg_end(skb, nlh);
  1040		return 0;
  1041	}
  1042	
> 1043	int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
  1044					  int dir,
  1045					  const struct km_event *c)
  1046	{

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index 1fc62b239f1b..ae5f97681989 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -1,6 +1,7 @@ 
 #ifndef _LINUX_XFRM_H
 #define _LINUX_XFRM_H
 
+#include <linux/compiler.h>
 #include <linux/in6.h>
 #include <linux/types.h>
 
@@ -157,34 +158,34 @@  enum {
 enum {
 	XFRM_MSG_BASE = 0x10,
 
-	XFRM_MSG_NEWSA = 0x10,
-#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA
-	XFRM_MSG_DELSA,
-#define XFRM_MSG_DELSA XFRM_MSG_DELSA
-	XFRM_MSG_GETSA,
-#define XFRM_MSG_GETSA XFRM_MSG_GETSA
-
-	XFRM_MSG_NEWPOLICY,
-#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY
-	XFRM_MSG_DELPOLICY,
-#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY
-	XFRM_MSG_GETPOLICY,
-#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY
-
-	XFRM_MSG_ALLOCSPI,
-#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI
-	XFRM_MSG_ACQUIRE,
-#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE
-	XFRM_MSG_EXPIRE,
-#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE
-
-	XFRM_MSG_UPDPOLICY,
-#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY
-	XFRM_MSG_UPDSA,
-#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA
-
-	XFRM_MSG_POLEXPIRE,
-#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE
+	XFRM_MSG_NEWSA_LEGACY = 0x10,
+#define XFRM_MSG_NEWSA_LEGACY XFRM_MSG_NEWSA_LEGACY
+	XFRM_MSG_DELSA_LEGACY,
+#define XFRM_MSG_DELSA_LEGACY XFRM_MSG_DELSA_LEGACY
+	XFRM_MSG_GETSA_LEGACY,
+#define XFRM_MSG_GETSA_LEGACY XFRM_MSG_GETSA_LEGACY
+
+	XFRM_MSG_NEWPOLICY_LEGACY,
+#define XFRM_MSG_NEWPOLICY_LEGACY XFRM_MSG_NEWPOLICY_LEGACY
+	XFRM_MSG_DELPOLICY_LEGACY,
+#define XFRM_MSG_DELPOLICY_LEGACY XFRM_MSG_DELPOLICY_LEGACY
+	XFRM_MSG_GETPOLICY_LEGACY,
+#define XFRM_MSG_GETPOLICY_LEGACY XFRM_MSG_GETPOLICY_LEGACY
+
+	XFRM_MSG_ALLOCSPI_LEGACY,
+#define XFRM_MSG_ALLOCSPI_LEGACY XFRM_MSG_ALLOCSPI_LEGACY
+	XFRM_MSG_ACQUIRE_LEGACY,
+#define XFRM_MSG_ACQUIRE_LEGACY XFRM_MSG_ACQUIRE_LEGACY
+	XFRM_MSG_EXPIRE_LEGACY,
+#define XFRM_MSG_EXPIRE_LEGACY XFRM_MSG_EXPIRE_LEGACY
+
+	XFRM_MSG_UPDPOLICY_LEGACY,
+#define XFRM_MSG_UPDPOLICY_LEGACY XFRM_MSG_UPDPOLICY_LEGACY
+	XFRM_MSG_UPDSA_LEGACY,
+#define XFRM_MSG_UPDSA_LEGACY XFRM_MSG_UPDSA_LEGACY
+
+	XFRM_MSG_POLEXPIRE_LEGACY,
+#define XFRM_MSG_POLEXPIRE_LEGACY XFRM_MSG_POLEXPIRE_LEGACY
 
 	XFRM_MSG_FLUSHSA,
 #define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA
@@ -214,6 +215,34 @@  enum {
 
 	XFRM_MSG_MAPPING,
 #define XFRM_MSG_MAPPING XFRM_MSG_MAPPING
+
+	XFRM_MSG_ALLOCSPI,
+#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI
+	XFRM_MSG_ACQUIRE,
+#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE
+	XFRM_MSG_EXPIRE,
+#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE
+	XFRM_MSG_POLEXPIRE,
+#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE
+
+	XFRM_MSG_NEWSA,
+#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA
+	XFRM_MSG_UPDSA,
+#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA
+	XFRM_MSG_DELSA,
+#define XFRM_MSG_DELSA XFRM_MSG_DELSA
+	XFRM_MSG_GETSA,
+#define XFRM_MSG_GETSA XFRM_MSG_GETSA
+
+	XFRM_MSG_NEWPOLICY,
+#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY
+	XFRM_MSG_UPDPOLICY,
+#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY
+	XFRM_MSG_DELPOLICY,
+#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY
+	XFRM_MSG_GETPOLICY,
+#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY
+
 	__XFRM_MSG_MAX
 };
 #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -221,7 +250,7 @@  enum {
 #define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)
 
 /*
- * Generic LSM security context for comunicating to user space
+ * Generic LSM security context for communicating to user space
  * NOTE: Same format as sadb_x_sec_ctx
  */
 struct xfrm_user_sec_ctx {
@@ -357,6 +386,22 @@  struct xfrmu_spdhthresh {
 	__u8 rbits;
 };
 
+/* Legacy structs are incompatible between 32-bit and 64-bit. */
+struct xfrm_usersa_info_legacy {
+	struct xfrm_selector		sel;
+	struct xfrm_id			id;
+	xfrm_address_t			saddr;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	struct xfrm_stats		stats;
+	__u32				seq;
+	__u32				reqid;
+	__u16				family;
+	__u8				mode;		/* XFRM_MODE_xxx */
+	__u8				replay_window;
+	__u8				flags;
+};
+
 struct xfrm_usersa_info {
 	struct xfrm_selector		sel;
 	struct xfrm_id			id;
@@ -378,7 +423,8 @@  struct xfrm_usersa_info {
 #define XFRM_STATE_AF_UNSPEC	32
 #define XFRM_STATE_ALIGN4	64
 #define XFRM_STATE_ESN		128
-};
+	__u8				reserved[7];
+} __packed;
 
 #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP	1
 
@@ -396,10 +442,28 @@  struct xfrm_aevent_id {
 	__u32				reqid;
 };
 
+struct xfrm_userspi_info_legacy {
+	struct xfrm_usersa_info_legacy	info;
+	__u32				min;
+	__u32				max;
+};
+
 struct xfrm_userspi_info {
 	struct xfrm_usersa_info		info;
 	__u32				min;
 	__u32				max;
+} __packed;
+
+struct xfrm_userpolicy_info_legacy {
+	struct xfrm_selector		sel;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	__u32				priority;
+	__u32				index;
+	__u8				dir;
+	__u8				action;
+	__u8				flags;
+	__u8				share;
 };
 
 struct xfrm_userpolicy_info {
@@ -417,7 +481,8 @@  struct xfrm_userpolicy_info {
 	/* Automatically expand selector to include matching ICMP payloads. */
 #define XFRM_POLICY_ICMP	2
 	__u8				share;
-};
+	__u8				reserved[4];
+} __packed;
 
 struct xfrm_userpolicy_id {
 	struct xfrm_selector		sel;
@@ -425,6 +490,17 @@  struct xfrm_userpolicy_id {
 	__u8				dir;
 };
 
+struct xfrm_user_acquire_legacy {
+	struct xfrm_id				id;
+	xfrm_address_t				saddr;
+	struct xfrm_selector			sel;
+	struct xfrm_userpolicy_info_legacy	policy;
+	__u32					aalgos;
+	__u32					ealgos;
+	__u32					calgos;
+	__u32					seq;
+};
+
 struct xfrm_user_acquire {
 	struct xfrm_id			id;
 	xfrm_address_t			saddr;
@@ -434,17 +510,29 @@  struct xfrm_user_acquire {
 	__u32				ealgos;
 	__u32				calgos;
 	__u32				seq;
+} __packed;
+
+struct xfrm_user_expire_legacy {
+	struct xfrm_usersa_info_legacy	state;
+	__u8				hard;
 };
 
 struct xfrm_user_expire {
 	struct xfrm_usersa_info		state;
 	__u8				hard;
+	__u8				reserved[7];
+} __packed;
+
+struct xfrm_user_polexpire_legacy {
+	struct xfrm_userpolicy_info_legacy	pol;
+	__u8					hard;
 };
 
 struct xfrm_user_polexpire {
 	struct xfrm_userpolicy_info	pol;
 	__u8				hard;
-};
+	__u8				reserved[7];
+} __packed;
 
 struct xfrm_usersa_flush {
 	__u8				proto;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 4d733f02c3a1..5456dde974bc 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2350,6 +2350,32 @@  static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
 	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
 	[XFRM_MSG_NEWSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
+
+	[XFRM_MSG_ALLOCSPI_LEGACY  - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userspi_info_legacy),
+	[XFRM_MSG_ACQUIRE_LEGACY   - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_acquire_legacy),
+	[XFRM_MSG_EXPIRE_LEGACY    - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_expire_legacy),
+	[XFRM_MSG_POLEXPIRE_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_polexpire_legacy),
+
+	[XFRM_MSG_NEWSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_info_legacy),
+	[XFRM_MSG_UPDSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_info_legacy),
+	[XFRM_MSG_DELSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_id),
+	[XFRM_MSG_GETSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_id),
+	[XFRM_MSG_NEWPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_info_legacy),
+	[XFRM_MSG_UPDPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_info_legacy),
+	[XFRM_MSG_DELPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_id),
+	[XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_id),
 };
 
 #undef XMSGSIZE
@@ -2396,6 +2422,7 @@  static const struct xfrm_link {
 	int (*done)(struct netlink_callback *);
 	const struct nla_policy *nla_pol;
 	int nla_max;
+	bool legacy;
 } xfrm_dispatch[XFRM_NR_MSGTYPES] = {
 	[XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = { .doit = xfrm_add_sa        },
 	[XFRM_MSG_DELSA       - XFRM_MSG_BASE] = { .doit = xfrm_del_sa        },
@@ -2423,6 +2450,62 @@  static const struct xfrm_link {
 						   .nla_pol = xfrma_spd_policy,
 						   .nla_max = XFRMA_SPD_MAX },
 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo   },
+
+#ifdef CONFIG_XFRM_USER_LEGACY
+	[XFRM_MSG_ALLOCSPI_LEGACY  - XFRM_MSG_BASE] = {
+		.doit = xfrm_alloc_userspi_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_ACQUIRE_LEGACY   - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_acquire_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_EXPIRE_LEGACY    - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_expire_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_POLEXPIRE_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_pol_expire_legacy,
+		.legacy = true,
+	},
+
+	[XFRM_MSG_NEWSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_UPDSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_DELSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_del_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_GETSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_sa_legacy,
+		.dump = xfrm_dump_sa_legacy,
+		.done = xfrm_dump_sa_done_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_NEWPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_UPDPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_DELPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_policy_legacy,
+		.dump = xfrm_dump_policy_legacy,
+		.done = xfrm_dump_policy_done_legacy,
+		.legacy = true,
+	},
+#endif /* CONFIG_XFRM_USER_LEGACY */
 };
 
 static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -2432,11 +2515,6 @@  static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 	const struct xfrm_link *link;
 	int type, err;
 
-#ifdef CONFIG_COMPAT
-	if (in_compat_syscall())
-		return -EOPNOTSUPP;
-#endif
-
 	type = nlh->nlmsg_type;
 	if (type > XFRM_MSG_MAX)
 		return -EINVAL;
@@ -2444,12 +2522,19 @@  static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 	type -= XFRM_MSG_BASE;
 	link = &xfrm_dispatch[type];
 
+#ifdef CONFIG_COMPAT
+	if (link->legacy && in_compat_syscall())
+		return -EOPNOTSUPP;
+#endif
+
 	/* All operations require privileges, even GET */
 	if (!netlink_net_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
-	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) &&
+	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE) ||
+	     type == (XFRM_MSG_GETSA_LEGACY - XFRM_MSG_BASE) ||
+	     type == (XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE)) &&
 	    (nlh->nlmsg_flags & NLM_F_DUMP)) {
 		if (link->dump == NULL)
 			return -EINVAL;
@@ -2670,16 +2755,23 @@  static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 static int xfrm_send_state_notify(const struct xfrm_state *x,
 				  const struct km_event *c)
 {
+	int err;
 
 	switch (c->event) {
 	case XFRM_MSG_EXPIRE:
-		return xfrm_exp_state_notify(x, c);
+		err = xfrm_exp_state_notify(x, c);
+		if (err)
+			return err;
+		return xfrm_exp_state_notify_legacy(x, c);
 	case XFRM_MSG_NEWAE:
 		return xfrm_aevent_state_notify(x, c);
 	case XFRM_MSG_DELSA:
 	case XFRM_MSG_UPDSA:
 	case XFRM_MSG_NEWSA:
-		return xfrm_notify_sa(x, c);
+		err = xfrm_notify_sa(x, c);
+		if (err)
+			return err;
+		return xfrm_notify_sa_legacy(x, c);
 	case XFRM_MSG_FLUSHSA:
 		return xfrm_notify_sa_flush(c);
 	default:
@@ -2748,6 +2840,7 @@  static int xfrm_send_acquire(struct xfrm_state *x,
 {
 	struct net *net = xs_net(x);
 	struct sk_buff *skb;
+	int err;
 
 	skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
 	if (skb == NULL)
@@ -2756,7 +2849,11 @@  static int xfrm_send_acquire(struct xfrm_state *x,
 	if (build_acquire(skb, x, xt, xp) < 0)
 		BUG();
 
-	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+	err = xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+	if (err)
+		return err;
+
+	return xfrm_send_acquire_legacy(x, xt, xp);
 }
 
 /* User gives us xfrm_user_policy_info followed by an array of 0
@@ -2799,6 +2896,16 @@  static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
 		return NULL;
 
 	nr = ((len - sizeof(*p)) / sizeof(*ut));
+	if (len == (nr + 1) * sizeof(*ut) + sizeof(*p) - sizeof(u32)) {
+		/* The user passed a legacy xfrm_userpolicy_info struct whose
+		 * length is padded to 32 bits instead of 64, so the above
+		 * division had a remainder.  Adjust the start address and
+		 * count accordingly.
+		 */
+		ut = (void *)data + sizeof(*p) - sizeof(u32);
+		nr++;
+	}
+
 	if (validate_tmpl(nr, ut, p->sel.family))
 		return NULL;
 
@@ -2979,16 +3086,23 @@  static int xfrm_send_policy_notify(const struct xfrm_policy *xp,
 				   int dir,
 				   const struct km_event *c)
 {
+	int err;
 
 	switch (c->event) {
 	case XFRM_MSG_NEWPOLICY:
 	case XFRM_MSG_UPDPOLICY:
 	case XFRM_MSG_DELPOLICY:
-		return xfrm_notify_policy(xp, dir, c);
+		err = xfrm_notify_policy(xp, dir, c);
+		if (err)
+			return err;
+		return xfrm_notify_policy_legacy(xp, dir, c);
 	case XFRM_MSG_FLUSHPOLICY:
 		return xfrm_notify_policy_flush(c);
 	case XFRM_MSG_POLEXPIRE:
-		return xfrm_exp_policy_notify(xp, dir, c);
+		err = xfrm_exp_policy_notify(xp, dir, c);
+		if (err)
+			return err;
+		return xfrm_exp_policy_notify_legacy(xp, dir, c);
 	default:
 		printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n",
 		       c->event);
diff --git a/net/xfrm/xfrm_user.h b/net/xfrm/xfrm_user.h
index 29bab2ebee83..78627d1c1cec 100644
--- a/net/xfrm/xfrm_user.h
+++ b/net/xfrm/xfrm_user.h
@@ -1,6 +1,7 @@ 
 #ifndef _XFRM_USER_H
 #define _XFRM_USER_H
 
+#include <linux/errno.h>
 #include <linux/netlink.h>
 #include <linux/skbuff.h>
 #include <linux/types.h>
@@ -87,4 +88,78 @@  static inline int copy_to_user_state_sec_ctx(const struct xfrm_state *x,
 	return 0;
 }
 
+/* Legacy functions */
+
+#ifdef CONFIG_XFRM_USER_LEGACY
+int xfrm_alloc_userspi_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs);
+int xfrm_add_pol_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			       struct nlattr **attrs);
+int xfrm_add_sa_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs);
+int xfrm_add_acquire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs);
+
+int xfrm_add_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_del_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_dump_sa_done_legacy(struct netlink_callback *cb);
+int xfrm_dump_sa_legacy(struct sk_buff *skb, struct netlink_callback *cb);
+int xfrm_get_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_add_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs);
+int xfrm_dump_policy_done_legacy(struct netlink_callback *cb);
+int xfrm_dump_policy_legacy(struct sk_buff *skb, struct netlink_callback *cb);
+int xfrm_get_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs);
+
+int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
+				 const struct km_event *c);
+int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c);
+int xfrm_send_acquire_legacy(struct xfrm_state *x,
+			     const struct xfrm_tmpl *xt,
+			     const struct xfrm_policy *xp);
+int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
+				  int dir,
+				  const struct km_event *c);
+int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
+			      int dir,
+			      const struct km_event *c);
+#else /* CONFIG_XFRM_USER_LEGACY */
+static inline int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
+					       const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_notify_sa_legacy(const struct xfrm_state *x,
+					const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_send_acquire_legacy(struct xfrm_state *x,
+					   const struct xfrm_tmpl *xt,
+					   const struct xfrm_policy *xp)
+{
+	return 0;
+}
+
+static inline int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
+						int dir,
+						const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
+					    int dir,
+					    const struct km_event *c)
+{
+	return 0;
+}
+#endif /* CONFIG_XFRM_USER_LEGACY */
+
 #endif /* _XFRM_USER_H */
diff --git a/net/xfrm/xfrm_user_legacy.c b/net/xfrm/xfrm_user_legacy.c
index 058accfefc83..aa48845b47fa 100644
--- a/net/xfrm/xfrm_user_legacy.c
+++ b/net/xfrm/xfrm_user_legacy.c
@@ -29,28 +29,33 @@ 
 #include <asm/unaligned.h>
 #include "xfrm_user.h"
 
-static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_sa_legacy(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);
+	const struct xfrm_usersa_info_legacy *p = nlmsg_data(nlh);
 	struct xfrm_state *x;
 	int err;
 	struct km_event c;
 
-	err = xfrm_verify_newsa_info(p, attrs);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newsa_info((const struct xfrm_usersa_info *)p, attrs);
 	if (err)
 		return err;
 
-	x = xfrm_state_construct(net, p, attrs, &err);
+	x = xfrm_state_construct(net, (const struct xfrm_usersa_info *)p,
+				 attrs, &err);
 	if (!x)
 		return err;
 
 	xfrm_state_hold(x);
-	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
+	if (nlh->nlmsg_type == XFRM_MSG_NEWSA_LEGACY) {
 		err = xfrm_state_add(x);
-	else
+		c.event = XFRM_MSG_NEWSA;
+	} else {
 		err = xfrm_state_update(x);
+		c.event = XFRM_MSG_UPDSA;
+	}
 
 	xfrm_audit_state_add(x, err ? 0 : 1, true);
 
@@ -62,7 +67,6 @@  static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
-	c.event = nlh->nlmsg_type;
 
 	km_state_notify(x, &c);
 out:
@@ -70,7 +74,7 @@  static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_del_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -98,7 +102,7 @@  static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
-	c.event = nlh->nlmsg_type;
+	c.event = XFRM_MSG_DELSA;
 	km_state_notify(x, &c);
 
 out:
@@ -108,7 +112,7 @@  static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 }
 
 static void copy_to_user_state(const struct xfrm_state *x,
-			       struct xfrm_usersa_info *p)
+			       struct xfrm_usersa_info_legacy *p)
 {
 	memset(p, 0, sizeof(*p));
 	memcpy(&p->id, &x->id, sizeof(p->id));
@@ -128,7 +132,7 @@  static void copy_to_user_state(const struct xfrm_state *x,
 }
 
 static int copy_to_user_state_extra(const struct xfrm_state *x,
-				    struct xfrm_usersa_info *p,
+				    struct xfrm_usersa_info_legacy *p,
 				    struct sk_buff *skb)
 {
 	int ret = 0;
@@ -209,12 +213,12 @@  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 xfrm_usersa_info_legacy *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);
+			XFRM_MSG_NEWSA_LEGACY, sizeof(*p), sp->nlmsg_flags);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -229,7 +233,7 @@  static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
 	return 0;
 }
 
-static int xfrm_dump_sa_done(struct netlink_callback *cb)
+int xfrm_dump_sa_done_legacy(struct netlink_callback *cb)
 {
 	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
 	struct sock *sk = cb->skb->sk;
@@ -241,7 +245,7 @@  static int xfrm_dump_sa_done(struct netlink_callback *cb)
 }
 
 static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
-static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
+int xfrm_dump_sa_legacy(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];
@@ -311,7 +315,7 @@  static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
 	return skb;
 }
 
-static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_get_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -335,12 +339,12 @@  static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_alloc_userspi_legacy(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;
+	const struct xfrm_userspi_info_legacy *p;
 	struct sk_buff *resp_skb;
 	const xfrm_address_t *daddr;
 	int family;
@@ -395,7 +399,7 @@  static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
 }
 
 static void copy_to_user_policy(const struct xfrm_policy *xp,
-				struct xfrm_userpolicy_info *p,
+				struct xfrm_userpolicy_info_legacy *p,
 				int dir)
 {
 	memset(p, 0, sizeof(*p));
@@ -411,24 +415,27 @@  static void copy_to_user_policy(const struct xfrm_policy *xp,
 	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
 }
 
-static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_policy_legacy(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);
+	const struct xfrm_userpolicy_info_legacy *p = nlmsg_data(nlh);
 	struct xfrm_policy *xp;
 	struct km_event c;
 	int err;
 	int excl;
 
-	err = xfrm_verify_newpolicy_info(p);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newpolicy_info(
+		(const struct xfrm_userpolicy_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);
+	xp = xfrm_policy_construct(net, (const struct xfrm_userpolicy_info *)p,
+				   attrs, &err);
 	if (!xp)
 		return err;
 
@@ -436,7 +443,13 @@  static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	 * 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;
+	if (nlh->nlmsg_type == XFRM_MSG_NEWPOLICY_LEGACY) {
+		excl = 1;
+		c.event = XFRM_MSG_NEWPOLICY;
+	} else {
+		excl = 0;
+		c.event = XFRM_MSG_UPDPOLICY;
+	}
 	err = xfrm_policy_insert(p->dir, xp, excl);
 	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
@@ -446,7 +459,6 @@  static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		return err;
 	}
 
-	c.event = nlh->nlmsg_type;
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
 	km_policy_notify(xp, p->dir, &c);
@@ -471,14 +483,14 @@  static int dump_one_policy(const struct xfrm_policy *xp,
 			   void *ptr)
 {
 	struct xfrm_dump_info *sp = ptr;
-	struct xfrm_userpolicy_info *p;
+	struct xfrm_userpolicy_info_legacy *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);
+			XFRM_MSG_NEWPOLICY_LEGACY, sizeof(*p), sp->nlmsg_flags);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -499,7 +511,7 @@  static int dump_one_policy(const struct xfrm_policy *xp,
 	return 0;
 }
 
-static int xfrm_dump_policy_done(struct netlink_callback *cb)
+int xfrm_dump_policy_done_legacy(struct netlink_callback *cb)
 {
 	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
 	struct net *net = sock_net(cb->skb->sk);
@@ -508,7 +520,7 @@  static int xfrm_dump_policy_done(struct netlink_callback *cb)
 	return 0;
 }
 
-static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
+int xfrm_dump_policy_legacy(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];
@@ -559,7 +571,7 @@  static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
 	return skb;
 }
 
-static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_get_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			   struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -573,7 +585,13 @@  static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	u32 mark = xfrm_mark_get(attrs, &m);
 
 	p = nlmsg_data(nlh);
-	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
+	if (nlh->nlmsg_type == XFRM_MSG_DELPOLICY_LEGACY) {
+		delete = 1;
+		c.event = XFRM_MSG_DELPOLICY;
+	} else {
+		delete = 0;
+		c.event = XFRM_MSG_GETPOLICY;
+	}
 
 	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
@@ -625,7 +643,6 @@  static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			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);
@@ -638,13 +655,13 @@  static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_pol_expire_legacy(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;
+	const struct xfrm_user_polexpire_legacy *up = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info_legacy *p = &up->pol;
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err = -ENOENT;
 	struct xfrm_mark m;
@@ -698,14 +715,14 @@  static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_sa_expire_legacy(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;
+	const struct xfrm_user_expire_legacy *ue = nlmsg_data(nlh);
+	const struct xfrm_usersa_info_legacy *p = &ue->state;
 	struct xfrm_mark m;
 	u32 mark = xfrm_mark_get(attrs, &m);
 
@@ -732,7 +749,7 @@  static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_acquire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -742,7 +759,7 @@  static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	struct nlattr *rt = attrs[XFRMA_TMPL];
 	struct xfrm_mark mark;
 
-	const struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+	const struct xfrm_user_acquire_legacy *ua = nlmsg_data(nlh);
 	struct xfrm_state *x = xfrm_state_alloc(net);
 	int err = -ENOMEM;
 
@@ -751,12 +768,15 @@  static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	xfrm_mark_get(attrs, &mark);
 
-	err = xfrm_verify_newpolicy_info(&ua->policy);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newpolicy_info(
+		(const struct xfrm_userpolicy_info *)&ua->policy);
 	if (err)
 		goto free_state;
 
 	/*   build an XP */
-	xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+	xp = xfrm_policy_construct(net, (const struct xfrm_userpolicy_info *)
+				   &ua->policy, attrs, &err);
 	if (!xp)
 		goto free_state;
 
@@ -793,7 +813,7 @@  static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 static inline size_t xfrm_expire_msgsize(void)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire_legacy))
 	       + nla_total_size(sizeof(struct xfrm_mark));
 }
 
@@ -801,11 +821,12 @@  static int build_expire(struct sk_buff *skb,
 			const struct xfrm_state *x,
 			const struct km_event *c)
 {
-	struct xfrm_user_expire *ue;
+	struct xfrm_user_expire_legacy *ue;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE_LEGACY,
+			sizeof(*ue), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -821,7 +842,7 @@  static int build_expire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_exp_state_notify(const struct xfrm_state *x,
+int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
 				 const struct km_event *c)
 {
 	struct net *net = xs_net(x);
@@ -839,15 +860,16 @@  static int xfrm_exp_state_notify(const struct xfrm_state *x,
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
+int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c)
 {
 	struct net *net = xs_net(x);
-	struct xfrm_usersa_info *p;
+	struct xfrm_usersa_info_legacy *p;
 	struct xfrm_usersa_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
 	int len = xfrm_sa_len(x);
 	int headlen, err;
+	u32 event = 0;
 
 	headlen = sizeof(*p);
 	if (c->event == XFRM_MSG_DELSA) {
@@ -861,7 +883,19 @@  static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 	if (skb == NULL)
 		return -ENOMEM;
 
-	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	switch (c->event) {
+	case XFRM_MSG_NEWSA:
+		event = XFRM_MSG_NEWSA_LEGACY;
+		break;
+	case XFRM_MSG_UPDSA:
+		event = XFRM_MSG_UPDSA_LEGACY;
+		break;
+	case XFRM_MSG_DELSA:
+		event = XFRM_MSG_DELSA_LEGACY;
+		break;
+	}
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, event, headlen, 0);
 	err = -EMSGSIZE;
 	if (nlh == NULL)
 		goto out_free_skb;
@@ -899,7 +933,7 @@  static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 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))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire_legacy))
 	       + 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))
@@ -912,11 +946,11 @@  static int build_acquire(struct sk_buff *skb,
 			 const struct xfrm_policy *xp)
 {
 	__u32 seq = xfrm_get_acqseq();
-	struct xfrm_user_acquire *ua;
+	struct xfrm_user_acquire_legacy *ua;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
+	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE_LEGACY, sizeof(*ua), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -946,7 +980,7 @@  static int build_acquire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_send_acquire(struct xfrm_state *x,
+int xfrm_send_acquire_legacy(struct xfrm_state *x,
 			     const struct xfrm_tmpl *xt,
 			     const struct xfrm_policy *xp)
 {
@@ -965,7 +999,7 @@  static int xfrm_send_acquire(struct xfrm_state *x,
 
 static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire_legacy))
 	       + 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))
@@ -977,12 +1011,13 @@  static int build_polexpire(struct sk_buff *skb,
 			   int dir,
 			   const struct km_event *c)
 {
-	struct xfrm_user_polexpire *upe;
+	struct xfrm_user_polexpire_legacy *upe;
 	int hard = c->data.hard;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE_LEGACY,
+			sizeof(*upe), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -1005,7 +1040,7 @@  static int build_polexpire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
+int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
 				  int dir,
 				  const struct km_event *c)
 {
@@ -1022,19 +1057,21 @@  static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_notify_policy(const struct xfrm_policy *xp,
+int xfrm_notify_policy_legacy(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_info_legacy *p;
 	struct xfrm_userpolicy_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
 	int headlen, err;
+	u32 event = 0;
 
 	headlen = sizeof(*p);
+
 	if (c->event == XFRM_MSG_DELPOLICY) {
 		len += nla_total_size(headlen);
 		headlen = sizeof(*id);
@@ -1047,7 +1084,19 @@  static int xfrm_notify_policy(const struct xfrm_policy *xp,
 	if (skb == NULL)
 		return -ENOMEM;
 
-	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	switch (c->event) {
+	case XFRM_MSG_NEWPOLICY:
+		event = XFRM_MSG_NEWPOLICY_LEGACY;
+		break;
+	case XFRM_MSG_UPDPOLICY:
+		event = XFRM_MSG_UPDPOLICY_LEGACY;
+		break;
+	case XFRM_MSG_DELPOLICY:
+		event = XFRM_MSG_DELPOLICY_LEGACY;
+		break;
+	}
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, event, headlen, 0);
 	err = -EMSGSIZE;
 	if (nlh == NULL)
 		goto out_free_skb;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2ca9cde939d4..15e8b1381c13 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -90,29 +90,42 @@  static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
 
 static struct nlmsg_perm nlmsg_xfrm_perms[] =
 {
-	{ XFRM_MSG_NEWSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_DELSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETSA,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_NEWPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_DELPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_ALLOCSPI,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_ACQUIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_EXPIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_UPDPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_UPDSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_POLEXPIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_FLUSHSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_FLUSHPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_NEWAE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETAE,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_REPORT,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_MIGRATE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_NEWSADINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_GETSADINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_NEWSPDINFO,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETSPDINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_MAPPING,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_ALLOCSPI_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_ACQUIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_EXPIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_POLEXPIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_FLUSHSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_FLUSHPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWAE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETAE,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_REPORT,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_MIGRATE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSADINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_GETSADINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWSPDINFO,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSPDINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_MAPPING,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_ALLOCSPI,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_ACQUIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_EXPIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_POLEXPIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSA,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
 };
 
 static struct nlmsg_perm nlmsg_audit_perms[] =
@@ -168,7 +181,7 @@  int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 		break;
 
 	case SECCLASS_NETLINK_XFRM_SOCKET:
-		BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MAPPING);
+		BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETPOLICY);
 		err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
 				 sizeof(nlmsg_xfrm_perms));
 		break;