summaryrefslogtreecommitdiffstats
path: root/net/bpfilter/bpfilter_kern.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /net/bpfilter/bpfilter_kern.c
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'net/bpfilter/bpfilter_kern.c')
-rw-r--r--net/bpfilter/bpfilter_kern.c136
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 000000000..422ec6e7c
--- /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));
+ bpfilter_umh_cleanup(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");