]>
Commit | Line | Data |
---|---|---|
d2ba09c1 AS |
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 | ||
8e75887d MY |
13 | extern char bpfilter_umh_start; |
14 | extern char bpfilter_umh_end; | |
d2ba09c1 | 15 | |
61fbf593 | 16 | static void shutdown_umh(void) |
d2ba09c1 | 17 | { |
1c340ead EB |
18 | struct umd_info *info = &bpfilter_ops.info; |
19 | struct pid *tgid = info->tgid; | |
d2ba09c1 | 20 | |
e80eb1dc EB |
21 | if (tgid) { |
22 | kill_pid(tgid, SIGKILL, 1); | |
23 | wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); | |
24 | bpfilter_umh_cleanup(info); | |
84258438 | 25 | } |
d2ba09c1 AS |
26 | } |
27 | ||
28 | static void __stop_umh(void) | |
29 | { | |
61fbf593 TY |
30 | if (IS_ENABLED(CONFIG_INET)) |
31 | shutdown_umh(); | |
d2ba09c1 AS |
32 | } |
33 | ||
c9ffebdd | 34 | static int bpfilter_send_req(struct mbox_request *req) |
d2ba09c1 | 35 | { |
d2ba09c1 | 36 | struct mbox_reply reply; |
4f010246 | 37 | loff_t pos = 0; |
d2ba09c1 | 38 | ssize_t n; |
d2ba09c1 | 39 | |
1c340ead | 40 | if (!bpfilter_ops.info.tgid) |
c9ffebdd | 41 | return -EFAULT; |
a4fa4589 | 42 | pos = 0; |
c9ffebdd | 43 | n = kernel_write(bpfilter_ops.info.pipe_to_umh, req, sizeof(*req), |
5b4cb650 | 44 | &pos); |
c9ffebdd | 45 | if (n != sizeof(*req)) { |
d2ba09c1 | 46 | pr_err("write fail %zd\n", n); |
c9ffebdd | 47 | goto stop; |
d2ba09c1 AS |
48 | } |
49 | pos = 0; | |
5b4cb650 TY |
50 | n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply), |
51 | &pos); | |
d2ba09c1 AS |
52 | if (n != sizeof(reply)) { |
53 | pr_err("read fail %zd\n", n); | |
c9ffebdd | 54 | goto stop; |
d2ba09c1 | 55 | } |
c9ffebdd CH |
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, | |
b03afaa8 | 63 | sockptr_t optval, unsigned int optlen, |
c9ffebdd CH |
64 | bool is_set) |
65 | { | |
66 | struct mbox_request req = { | |
67 | .is_set = is_set, | |
68 | .pid = current->pid, | |
69 | .cmd = optname, | |
b03afaa8 | 70 | .addr = (uintptr_t)optval.user, |
c9ffebdd CH |
71 | .len = optlen, |
72 | }; | |
b03afaa8 | 73 | if (uaccess_kernel() || sockptr_is_kernel(optval)) { |
d200cf62 CH |
74 | pr_err("kernel access not supported\n"); |
75 | return -EFAULT; | |
76 | } | |
c9ffebdd | 77 | return bpfilter_send_req(&req); |
d2ba09c1 AS |
78 | } |
79 | ||
61fbf593 | 80 | static int start_umh(void) |
d2ba09c1 | 81 | { |
c9ffebdd | 82 | struct mbox_request req = { .pid = current->pid }; |
d2ba09c1 AS |
83 | int err; |
84 | ||
85 | /* fork usermode process */ | |
e2dc9bf3 | 86 | err = fork_usermode_driver(&bpfilter_ops.info); |
d2ba09c1 AS |
87 | if (err) |
88 | return err; | |
1c340ead | 89 | pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid)); |
d2ba09c1 AS |
90 | |
91 | /* health check that usermode process started correctly */ | |
c9ffebdd | 92 | if (bpfilter_send_req(&req) != 0) { |
71a85084 | 93 | shutdown_umh(); |
d2ba09c1 AS |
94 | return -EFAULT; |
95 | } | |
d71dbdaa | 96 | |
d2ba09c1 AS |
97 | return 0; |
98 | } | |
99 | ||
61fbf593 TY |
100 | static int __init load_umh(void) |
101 | { | |
102 | int err; | |
103 | ||
e2dc9bf3 EB |
104 | err = umd_load_blob(&bpfilter_ops.info, |
105 | &bpfilter_umh_start, | |
106 | &bpfilter_umh_end - &bpfilter_umh_start); | |
107 | if (err) | |
108 | return err; | |
109 | ||
71a85084 | 110 | mutex_lock(&bpfilter_ops.lock); |
61fbf593 TY |
111 | err = start_umh(); |
112 | if (!err && IS_ENABLED(CONFIG_INET)) { | |
c9ffebdd | 113 | bpfilter_ops.sockopt = &bpfilter_process_sockopt; |
61fbf593 TY |
114 | bpfilter_ops.start = &start_umh; |
115 | } | |
71a85084 | 116 | mutex_unlock(&bpfilter_ops.lock); |
e2dc9bf3 EB |
117 | if (err) |
118 | umd_unload_blob(&bpfilter_ops.info); | |
61fbf593 TY |
119 | return err; |
120 | } | |
121 | ||
d2ba09c1 AS |
122 | static void __exit fini_umh(void) |
123 | { | |
71a85084 | 124 | mutex_lock(&bpfilter_ops.lock); |
61fbf593 | 125 | if (IS_ENABLED(CONFIG_INET)) { |
71a85084 | 126 | shutdown_umh(); |
61fbf593 TY |
127 | bpfilter_ops.start = NULL; |
128 | bpfilter_ops.sockopt = NULL; | |
129 | } | |
71a85084 | 130 | mutex_unlock(&bpfilter_ops.lock); |
e2dc9bf3 EB |
131 | |
132 | umd_unload_blob(&bpfilter_ops.info); | |
d2ba09c1 AS |
133 | } |
134 | module_init(load_umh); | |
135 | module_exit(fini_umh); | |
136 | MODULE_LICENSE("GPL"); |