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 | |
13 | extern char bpfilter_umh_start; |
14 | extern char bpfilter_umh_end; |
15 | |
16 | static 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 | |
28 | static void __stop_umh(void) |
29 | { |
30 | if (IS_ENABLED(CONFIG_INET)) |
31 | shutdown_umh(); |
32 | } |
33 | |
34 | static 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; |
57 | stop: |
58 | __stop_umh(); |
59 | return -EFAULT; |
60 | } |
61 | |
62 | static 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 | |
80 | static 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 | |
100 | static 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 | |
122 | static 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 | } |
134 | module_init(load_umh); |
135 | module_exit(fini_umh); |
136 | MODULE_LICENSE("GPL" ); |
137 | |