Add a skeleton for xfrm_compat module and provide API to register it in
xfrm_state.ko. struct xfrm_translator will have function pointers to
translate messages received from 32-bit userspace or to be sent to it
from 64-bit kernel.
module_get()/module_put() are used instead of rcu_read_lock() as the
module will vmalloc() memory for translation.
The new API is registered with xfrm_state module, not with xfrm_user as
the former needs translator for user_policy set by setsockopt() and
xfrm_user already uses functions from xfrm_state.

Signed-off-by: Dmitry Safonov <d...@arista.com>
---
 include/net/xfrm.h     | 19 +++++++++++++
 net/xfrm/Kconfig       | 10 +++++++
 net/xfrm/Makefile      |  1 +
 net/xfrm/xfrm_compat.c | 29 ++++++++++++++++++++
 net/xfrm/xfrm_state.c  | 60 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 119 insertions(+)
 create mode 100644 net/xfrm/xfrm_compat.c

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 2737d24ec244..fe2e3717da14 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -2000,6 +2000,25 @@ static inline int xfrm_tunnel_check(struct sk_buff *skb, 
struct xfrm_state *x,
        return 0;
 }
 
+struct xfrm_translator {
+       struct module *owner;
+};
+
+#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT)
+extern int xfrm_register_translator(struct xfrm_translator *xtr);
+extern int xfrm_unregister_translator(struct xfrm_translator *xtr);
+extern struct xfrm_translator *xfrm_get_translator(void);
+extern void xfrm_put_translator(struct xfrm_translator *xtr);
+#else
+static inline struct xfrm_translator *xfrm_get_translator(void)
+{
+       return NULL;
+}
+static inline void xfrm_put_translator(struct xfrm_translator *xtr)
+{
+}
+#endif
+
 #if IS_ENABLED(CONFIG_IPV6)
 static inline bool xfrm6_local_dontfrag(const struct sock *sk)
 {
diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
index 5b9a5ab48111..e79b48dab61b 100644
--- a/net/xfrm/Kconfig
+++ b/net/xfrm/Kconfig
@@ -28,6 +28,16 @@ config XFRM_USER
 
          If unsure, say Y.
 
+config XFRM_USER_COMPAT
+       tristate "Compatible ABI support"
+       depends on XFRM_USER && COMPAT_FOR_U64_ALIGNMENT
+       select WANT_COMPAT_NETLINK_MESSAGES
+       help
+         Transformation(XFRM) user configuration interface like IPsec
+         used by compatible Linux applications.
+
+         If unsure, say N.
+
 config XFRM_INTERFACE
        tristate "Transformation virtual interface"
        depends on XFRM && IPV6
diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile
index 2d4bb4b9f75e..494aa744bfb9 100644
--- a/net/xfrm/Makefile
+++ b/net/xfrm/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
 obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
 obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
 obj-$(CONFIG_XFRM_USER) += xfrm_user.o
+obj-$(CONFIG_XFRM_USER_COMPAT) += xfrm_compat.o
 obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
 obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o
 obj-$(CONFIG_XFRM_ESPINTCP) += espintcp.o
diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c
new file mode 100644
index 000000000000..f01d9af41c55
--- /dev/null
+++ b/net/xfrm/xfrm_compat.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * XFRM compat layer
+ * Author: Dmitry Safonov <d...@arista.com>
+ * Based on code and translator idea by: Florian Westphal <f...@strlen.de>
+ */
+#include <linux/compat.h>
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+
+static struct xfrm_translator xfrm_translator = {
+       .owner                          = THIS_MODULE,
+};
+
+static int __init xfrm_compat_init(void)
+{
+       return xfrm_register_translator(&xfrm_translator);
+}
+
+static void __exit xfrm_compat_exit(void)
+{
+       xfrm_unregister_translator(&xfrm_translator);
+}
+
+module_init(xfrm_compat_init);
+module_exit(xfrm_compat_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dmitry Safonov");
+MODULE_DESCRIPTION("XFRM 32-bit compatibility layer");
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 69520ad3d83b..cc206ca3df78 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2264,6 +2264,66 @@ static bool km_is_alive(const struct km_event *c)
        return is_alive;
 }
 
+#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT)
+static DEFINE_SPINLOCK(xfrm_translator_lock);
+static struct xfrm_translator __rcu *xfrm_translator;
+
+struct xfrm_translator *xfrm_get_translator(void)
+{
+       struct xfrm_translator *xtr;
+
+       rcu_read_lock();
+       xtr = rcu_dereference(xfrm_translator);
+       if (unlikely(!xtr))
+               goto out;
+       if (!try_module_get(xtr->owner))
+               xtr = NULL;
+out:
+       rcu_read_unlock();
+       return xtr;
+}
+EXPORT_SYMBOL_GPL(xfrm_get_translator);
+
+void xfrm_put_translator(struct xfrm_translator *xtr)
+{
+       module_put(xtr->owner);
+}
+EXPORT_SYMBOL_GPL(xfrm_put_translator);
+
+int xfrm_register_translator(struct xfrm_translator *xtr)
+{
+       int err = 0;
+
+       spin_lock_bh(&xfrm_translator_lock);
+       if (unlikely(xfrm_translator != NULL))
+               err = -EEXIST;
+       else
+               rcu_assign_pointer(xfrm_translator, xtr);
+       spin_unlock_bh(&xfrm_translator_lock);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_register_translator);
+
+int xfrm_unregister_translator(struct xfrm_translator *xtr)
+{
+       int err = 0;
+
+       spin_lock_bh(&xfrm_translator_lock);
+       if (likely(xfrm_translator != NULL)) {
+               if (rcu_access_pointer(xfrm_translator) != xtr)
+                       err = -EINVAL;
+               else
+                       RCU_INIT_POINTER(xfrm_translator, NULL);
+       }
+       spin_unlock_bh(&xfrm_translator_lock);
+       synchronize_rcu();
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_unregister_translator);
+#endif
+
 int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int 
optlen)
 {
        int err;
-- 
2.28.0

Reply via email to