1// SPDX-License-Identifier: GPL-2.0
2#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3#include <linux/init.h>
4#include <linux/module.h>
5#include <linux/umh.h>
6#include <linux/bpfilter.h>
7#include <linux/sched.h>
8#include <linux/sched/signal.h>
9#include <linux/fs.h>
10#include <linux/file.h>
11#include "msgfmt.h"
12
13extern char bpfilter_umh_start;
14extern char bpfilter_umh_end;
15
16static void shutdown_umh(void)
17{
18 struct umd_info *info = &bpfilter_ops.info;
19 struct pid *tgid = info->tgid;
20
21 if (tgid) {
22 kill_pid(pid: tgid, SIGKILL, priv: 1);
23 wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
24 umd_cleanup_helper(info);
25 }
26}
27
28static void __stop_umh(void)
29{
30 if (IS_ENABLED(CONFIG_INET))
31 shutdown_umh();
32}
33
34static int bpfilter_send_req(struct mbox_request *req)
35{
36 struct mbox_reply reply;
37 loff_t pos = 0;
38 ssize_t n;
39
40 if (!bpfilter_ops.info.tgid)
41 return -EFAULT;
42 pos = 0;
43 n = kernel_write(bpfilter_ops.info.pipe_to_umh, req, sizeof(*req),
44 &pos);
45 if (n != sizeof(*req)) {
46 pr_err("write fail %zd\n", n);
47 goto stop;
48 }
49 pos = 0;
50 n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply),
51 &pos);
52 if (n != sizeof(reply)) {
53 pr_err("read fail %zd\n", n);
54 goto stop;
55 }
56 return reply.status;
57stop:
58 __stop_umh();
59 return -EFAULT;
60}
61
62static int bpfilter_process_sockopt(struct sock *sk, int optname,
63 sockptr_t optval, unsigned int optlen,
64 bool is_set)
65{
66 struct mbox_request req = {
67 .is_set = is_set,
68 .pid = current->pid,
69 .cmd = optname,
70 .addr = (uintptr_t)optval.user,
71 .len = optlen,
72 };
73 if (sockptr_is_kernel(sockptr: optval)) {
74 pr_err("kernel access not supported\n");
75 return -EFAULT;
76 }
77 return bpfilter_send_req(req: &req);
78}
79
80static int start_umh(void)
81{
82 struct mbox_request req = { .pid = current->pid };
83 int err;
84
85 /* fork usermode process */
86 err = fork_usermode_driver(info: &bpfilter_ops.info);
87 if (err)
88 return err;
89 pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid));
90
91 /* health check that usermode process started correctly */
92 if (bpfilter_send_req(req: &req) != 0) {
93 shutdown_umh();
94 return -EFAULT;
95 }
96
97 return 0;
98}
99
100static int __init load_umh(void)
101{
102 int err;
103
104 err = umd_load_blob(info: &bpfilter_ops.info,
105 data: &bpfilter_umh_start,
106 len: &bpfilter_umh_end - &bpfilter_umh_start);
107 if (err)
108 return err;
109
110 mutex_lock(&bpfilter_ops.lock);
111 err = start_umh();
112 if (!err && IS_ENABLED(CONFIG_INET)) {
113 bpfilter_ops.sockopt = &bpfilter_process_sockopt;
114 bpfilter_ops.start = &start_umh;
115 }
116 mutex_unlock(lock: &bpfilter_ops.lock);
117 if (err)
118 umd_unload_blob(info: &bpfilter_ops.info);
119 return err;
120}
121
122static void __exit fini_umh(void)
123{
124 mutex_lock(&bpfilter_ops.lock);
125 if (IS_ENABLED(CONFIG_INET)) {
126 shutdown_umh();
127 bpfilter_ops.start = NULL;
128 bpfilter_ops.sockopt = NULL;
129 }
130 mutex_unlock(lock: &bpfilter_ops.lock);
131
132 umd_unload_blob(info: &bpfilter_ops.info);
133}
134module_init(load_umh);
135module_exit(fini_umh);
136MODULE_LICENSE("GPL");
137

source code of linux/net/bpfilter/bpfilter_kern.c