1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 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");
|