Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ae4da45 authored by Dmitry Safonov's avatar Dmitry Safonov Committed by Alistair Delva
Browse files

BACKPORT: xfrm: Provide API to register translator module



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: default avatarDmitry Safonov <dima@arista.com>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
(cherry picked from commit c9e7c76d70fa50582ca96759829c93d0dd024662)
[adelva: Edited around some context changes]
Bug: 163141236
Signed-off-by: default avatarAlistair Delva <adelva@google.com>
Change-Id: Ic825c6a0367fa192cc3f7af6b7d2682ef8f9d58b
parent 8506fc47
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -2093,4 +2093,24 @@ 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

#endif	/* _NET_XFRM_H */
+10 −0
Original line number Diff line number Diff line
@@ -27,6 +27,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
+1 −0
Original line number Diff line number Diff line
@@ -9,5 +9,6 @@ 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

net/xfrm/xfrm_compat.c

0 → 100644
+29 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * XFRM compat layer
 * Author: Dmitry Safonov <dima@arista.com>
 * Based on code and translator idea by: Florian Westphal <fw@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");
+60 −0
Original line number Diff line number Diff line
@@ -2118,6 +2118,66 @@ bool km_is_alive(const struct km_event *c)
}
EXPORT_SYMBOL(km_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, u8 __user *optval, int optlen)
{
	int err;