[PATCH 3/5] bpfilter: Port to user mode driver management API

From: Roberto Sassu
Date: Fri Mar 17 2023 - 10:54:45 EST


From: Roberto Sassu <roberto.sassu@xxxxxxxxxx>

Move bpfilter_process_sockopt() to sockopt.c, and call umd_mgmt_send_recv()
instead of umd_send_recv(), so that it has the original capability of
bpfilter_mbox_request() to request the loading of the kernel module
containing the user mode driver.

Move also the connection test originally in start_umh() in bpfilter.c, to
sockopt.c. Create the new function bpfilter_post_start_umh() with the moved
code.

Directly call bpfilter_process_sockopt() from bpfilter_ip_set_sockopt() and
bpfilter_ip_get_sockopt(), which now it is equivalent to
bpfilter_mbox_request().

Remove the struct bpfilter_umh_ops definition, and declare the global
variable bpfilter_ops as a umd_mgmt structure.

Set kmod to 'bpfilter', kmod_loaded to false and the new function
bpfilter_post_start_umh() as the post_start method for bpfilter_ops.

Replace load_umh() and fini_umh() in bpfilter.c respectively with
umd_mgmt_load() and umd_mgmt_unload().

Finally, remove the remaining functions, as their job is done by the user
mode driver management.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
include/linux/bpfilter.h | 12 +----
net/bpfilter/bpfilter_kern.c | 98 ++----------------------------------
net/ipv4/bpfilter/sockopt.c | 67 ++++++++++++++----------
3 files changed, 45 insertions(+), 132 deletions(-)

diff --git a/include/linux/bpfilter.h b/include/linux/bpfilter.h
index 2ae3c8e1d83..655e6ec6e9d 100644
--- a/include/linux/bpfilter.h
+++ b/include/linux/bpfilter.h
@@ -3,7 +3,7 @@
#define _LINUX_BPFILTER_H

#include <uapi/linux/bpfilter.h>
-#include <linux/usermode_driver.h>
+#include <linux/usermode_driver_mgmt.h>
#include <linux/sockptr.h>

struct sock;
@@ -13,13 +13,5 @@ int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
int __user *optlen);
void bpfilter_umh_cleanup(struct umd_info *info);

-struct bpfilter_umh_ops {
- struct umd_info info;
- /* since ip_getsockopt() can run in parallel, serialize access to umh */
- struct mutex lock;
- int (*sockopt)(struct sock *sk, int optname, sockptr_t optval,
- unsigned int optlen, bool is_set);
- int (*start)(void);
-};
-extern struct bpfilter_umh_ops bpfilter_ops;
+extern struct umd_mgmt bpfilter_ops;
#endif
diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c
index 17d4df5f8fe..f2137d889c9 100644
--- a/net/bpfilter/bpfilter_kern.c
+++ b/net/bpfilter/bpfilter_kern.c
@@ -1,113 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
#include <linux/module.h>
-#include <linux/umh.h>
#include <linux/bpfilter.h>
-#include <linux/sched.h>
-#include <linux/sched/signal.h>
-#include <linux/fs.h>
-#include <linux/file.h>
#include "msgfmt.h"

extern char bpfilter_umh_start;
extern char bpfilter_umh_end;

-static void shutdown_umh(void)
-{
- struct umd_info *info = &bpfilter_ops.info;
- struct pid *tgid = info->tgid;
-
- if (tgid) {
- kill_pid(tgid, SIGKILL, 1);
- wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
- bpfilter_umh_cleanup(info);
- }
-}
-
-static int bpfilter_process_sockopt(struct sock *sk, int optname,
- sockptr_t optval, unsigned int optlen,
- bool is_set)
-{
- struct mbox_request req = {
- .is_set = is_set,
- .pid = current->pid,
- .cmd = optname,
- .addr = (uintptr_t)optval.user,
- .len = optlen,
- };
- struct mbox_reply reply;
- int err;
-
- if (sockptr_is_kernel(optval)) {
- pr_err("kernel access not supported\n");
- return -EFAULT;
- }
- err = umd_send_recv(&bpfilter_ops.info, &req, sizeof(req), &reply,
- sizeof(reply));
- if (err) {
- shutdown_umh();
- return err;
- }
-
- return reply.status;
-}
-
-static int start_umh(void)
-{
- struct mbox_request req = { .pid = current->pid };
- struct mbox_reply reply;
- int err;
-
- /* fork usermode process */
- err = fork_usermode_driver(&bpfilter_ops.info);
- if (err)
- return err;
- pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid));
-
- /* health check that usermode process started correctly */
- if (umd_send_recv(&bpfilter_ops.info, &req, sizeof(req), &reply,
- sizeof(reply)) != 0 || reply.status != 0) {
- shutdown_umh();
- return -EFAULT;
- }
-
- return 0;
-}
-
static int __init load_umh(void)
{
- int err;
-
- err = umd_load_blob(&bpfilter_ops.info,
- &bpfilter_umh_start,
- &bpfilter_umh_end - &bpfilter_umh_start);
- if (err)
- return err;
-
- mutex_lock(&bpfilter_ops.lock);
- err = start_umh();
- if (!err && IS_ENABLED(CONFIG_INET)) {
- bpfilter_ops.sockopt = &bpfilter_process_sockopt;
- bpfilter_ops.start = &start_umh;
- }
- mutex_unlock(&bpfilter_ops.lock);
- if (err)
- umd_unload_blob(&bpfilter_ops.info);
- return err;
+ return umd_mgmt_load(&bpfilter_ops, &bpfilter_umh_start,
+ &bpfilter_umh_end);
}

static void __exit fini_umh(void)
{
- mutex_lock(&bpfilter_ops.lock);
- if (IS_ENABLED(CONFIG_INET)) {
- shutdown_umh();
- bpfilter_ops.start = NULL;
- bpfilter_ops.sockopt = NULL;
- }
- mutex_unlock(&bpfilter_ops.lock);
-
- umd_unload_blob(&bpfilter_ops.info);
+ umd_mgmt_unload(&bpfilter_ops);
}
module_init(load_umh);
module_exit(fini_umh);
diff --git a/net/ipv4/bpfilter/sockopt.c b/net/ipv4/bpfilter/sockopt.c
index 1b34cb9a770..491f5042612 100644
--- a/net/ipv4/bpfilter/sockopt.c
+++ b/net/ipv4/bpfilter/sockopt.c
@@ -8,8 +8,9 @@
#include <linux/kmod.h>
#include <linux/fs.h>
#include <linux/file.h>
+#include "../../bpfilter/msgfmt.h"

-struct bpfilter_umh_ops bpfilter_ops;
+struct umd_mgmt bpfilter_ops;
EXPORT_SYMBOL_GPL(bpfilter_ops);

void bpfilter_umh_cleanup(struct umd_info *info)
@@ -21,40 +22,49 @@ void bpfilter_umh_cleanup(struct umd_info *info)
}
EXPORT_SYMBOL_GPL(bpfilter_umh_cleanup);

-static int bpfilter_mbox_request(struct sock *sk, int optname, sockptr_t optval,
- unsigned int optlen, bool is_set)
+static int bpfilter_process_sockopt(struct sock *sk, int optname,
+ sockptr_t optval, unsigned int optlen,
+ bool is_set)
{
+ struct mbox_request req = {
+ .is_set = is_set,
+ .pid = current->pid,
+ .cmd = optname,
+ .addr = (uintptr_t)optval.user,
+ .len = optlen,
+ };
+ struct mbox_reply reply;
int err;
- mutex_lock(&bpfilter_ops.lock);
- if (!bpfilter_ops.sockopt) {
- mutex_unlock(&bpfilter_ops.lock);
- request_module("bpfilter");
- mutex_lock(&bpfilter_ops.lock);

- if (!bpfilter_ops.sockopt) {
- err = -ENOPROTOOPT;
- goto out;
- }
+ if (sockptr_is_kernel(optval)) {
+ pr_err("kernel access not supported\n");
+ return -EFAULT;
}
- if (bpfilter_ops.info.tgid &&
- thread_group_exited(bpfilter_ops.info.tgid))
- bpfilter_umh_cleanup(&bpfilter_ops.info);
+ err = umd_mgmt_send_recv(&bpfilter_ops, &req, sizeof(req), &reply,
+ sizeof(reply));
+ if (err)
+ return err;

- if (!bpfilter_ops.info.tgid) {
- err = bpfilter_ops.start();
- if (err)
- goto out;
- }
- err = bpfilter_ops.sockopt(sk, optname, optval, optlen, is_set);
-out:
- mutex_unlock(&bpfilter_ops.lock);
- return err;
+ return reply.status;
+}
+
+static int bpfilter_post_start_umh(struct umd_mgmt *mgmt)
+{
+ struct mbox_request req = { .pid = current->pid };
+ struct mbox_reply reply;
+
+ /* health check that usermode process started correctly */
+ if (umd_send_recv(&bpfilter_ops.info, &req, sizeof(req), &reply,
+ sizeof(reply)) != 0 || reply.status != 0)
+ return -EFAULT;
+
+ return 0;
}

int bpfilter_ip_set_sockopt(struct sock *sk, int optname, sockptr_t optval,
unsigned int optlen)
{
- return bpfilter_mbox_request(sk, optname, optval, optlen, true);
+ return bpfilter_process_sockopt(sk, optname, optval, optlen, true);
}

int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
@@ -65,8 +75,8 @@ int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
if (get_user(len, optlen))
return -EFAULT;

- return bpfilter_mbox_request(sk, optname, USER_SOCKPTR(optval), len,
- false);
+ return bpfilter_process_sockopt(sk, optname, USER_SOCKPTR(optval), len,
+ false);
}

static int __init bpfilter_sockopt_init(void)
@@ -74,6 +84,9 @@ static int __init bpfilter_sockopt_init(void)
mutex_init(&bpfilter_ops.lock);
bpfilter_ops.info.tgid = NULL;
bpfilter_ops.info.driver_name = "bpfilter_umh";
+ bpfilter_ops.post_start = bpfilter_post_start_umh;
+ bpfilter_ops.kmod = "bpfilter";
+ bpfilter_ops.kmod_loaded = false;

return 0;
}
--
2.25.1