diff options
Diffstat (limited to 'net/bpfilter/bpfilter_kern.c')
-rw-r--r-- | net/bpfilter/bpfilter_kern.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c new file mode 100644 index 0000000000..97e129e3f3 --- /dev/null +++ b/net/bpfilter/bpfilter_kern.c @@ -0,0 +1,136 @@ +// 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)); + umd_cleanup_helper(info); + } +} + +static void __stop_umh(void) +{ + if (IS_ENABLED(CONFIG_INET)) + shutdown_umh(); +} + +static int bpfilter_send_req(struct mbox_request *req) +{ + struct mbox_reply reply; + loff_t pos = 0; + ssize_t n; + + if (!bpfilter_ops.info.tgid) + return -EFAULT; + pos = 0; + n = kernel_write(bpfilter_ops.info.pipe_to_umh, req, sizeof(*req), + &pos); + if (n != sizeof(*req)) { + pr_err("write fail %zd\n", n); + goto stop; + } + pos = 0; + n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply), + &pos); + if (n != sizeof(reply)) { + pr_err("read fail %zd\n", n); + goto stop; + } + return reply.status; +stop: + __stop_umh(); + return -EFAULT; +} + +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, + }; + if (sockptr_is_kernel(optval)) { + pr_err("kernel access not supported\n"); + return -EFAULT; + } + return bpfilter_send_req(&req); +} + +static int start_umh(void) +{ + struct mbox_request req = { .pid = current->pid }; + 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 (bpfilter_send_req(&req) != 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; +} + +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); +} +module_init(load_umh); +module_exit(fini_umh); +MODULE_LICENSE("GPL"); |