]>
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 AS |
17 | { |
18 | struct task_struct *tsk; | |
19 | ||
61fbf593 | 20 | if (bpfilter_ops.stop) |
66e58e0e | 21 | return; |
61fbf593 TY |
22 | |
23 | tsk = get_pid_task(find_vpid(bpfilter_ops.info.pid), PIDTYPE_PID); | |
84258438 | 24 | if (tsk) { |
d2ba09c1 | 25 | force_sig(SIGKILL, tsk); |
84258438 TY |
26 | put_task_struct(tsk); |
27 | } | |
d2ba09c1 AS |
28 | } |
29 | ||
30 | static void __stop_umh(void) | |
31 | { | |
61fbf593 TY |
32 | if (IS_ENABLED(CONFIG_INET)) |
33 | shutdown_umh(); | |
d2ba09c1 AS |
34 | } |
35 | ||
d2ba09c1 AS |
36 | static int __bpfilter_process_sockopt(struct sock *sk, int optname, |
37 | char __user *optval, | |
38 | unsigned int optlen, bool is_set) | |
39 | { | |
40 | struct mbox_request req; | |
41 | struct mbox_reply reply; | |
42 | loff_t pos; | |
43 | ssize_t n; | |
66e58e0e | 44 | int ret = -EFAULT; |
d2ba09c1 AS |
45 | |
46 | req.is_set = is_set; | |
47 | req.pid = current->pid; | |
48 | req.cmd = optname; | |
33aa8da1 | 49 | req.addr = (long __force __user)optval; |
d2ba09c1 | 50 | req.len = optlen; |
5b4cb650 | 51 | if (!bpfilter_ops.info.pid) |
66e58e0e | 52 | goto out; |
5b4cb650 TY |
53 | n = __kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req), |
54 | &pos); | |
d2ba09c1 AS |
55 | if (n != sizeof(req)) { |
56 | pr_err("write fail %zd\n", n); | |
57 | __stop_umh(); | |
58 | ret = -EFAULT; | |
59 | goto out; | |
60 | } | |
61 | pos = 0; | |
5b4cb650 TY |
62 | n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply), |
63 | &pos); | |
d2ba09c1 AS |
64 | if (n != sizeof(reply)) { |
65 | pr_err("read fail %zd\n", n); | |
66 | __stop_umh(); | |
67 | ret = -EFAULT; | |
68 | goto out; | |
69 | } | |
70 | ret = reply.status; | |
71 | out: | |
d2ba09c1 AS |
72 | return ret; |
73 | } | |
74 | ||
61fbf593 | 75 | static int start_umh(void) |
d2ba09c1 AS |
76 | { |
77 | int err; | |
78 | ||
79 | /* fork usermode process */ | |
8e75887d MY |
80 | err = fork_usermode_blob(&bpfilter_umh_start, |
81 | &bpfilter_umh_end - &bpfilter_umh_start, | |
5b4cb650 | 82 | &bpfilter_ops.info); |
d2ba09c1 AS |
83 | if (err) |
84 | return err; | |
61fbf593 | 85 | bpfilter_ops.stop = false; |
5b4cb650 | 86 | pr_info("Loaded bpfilter_umh pid %d\n", bpfilter_ops.info.pid); |
d2ba09c1 AS |
87 | |
88 | /* health check that usermode process started correctly */ | |
33aa8da1 | 89 | if (__bpfilter_process_sockopt(NULL, 0, NULL, 0, 0) != 0) { |
71a85084 | 90 | shutdown_umh(); |
d2ba09c1 AS |
91 | return -EFAULT; |
92 | } | |
d71dbdaa | 93 | |
d2ba09c1 AS |
94 | return 0; |
95 | } | |
96 | ||
61fbf593 TY |
97 | static int __init load_umh(void) |
98 | { | |
99 | int err; | |
100 | ||
71a85084 TY |
101 | mutex_lock(&bpfilter_ops.lock); |
102 | if (!bpfilter_ops.stop) { | |
103 | err = -EFAULT; | |
104 | goto out; | |
105 | } | |
61fbf593 TY |
106 | err = start_umh(); |
107 | if (!err && IS_ENABLED(CONFIG_INET)) { | |
108 | bpfilter_ops.sockopt = &__bpfilter_process_sockopt; | |
109 | bpfilter_ops.start = &start_umh; | |
110 | } | |
71a85084 TY |
111 | out: |
112 | mutex_unlock(&bpfilter_ops.lock); | |
61fbf593 TY |
113 | return err; |
114 | } | |
115 | ||
d2ba09c1 AS |
116 | static void __exit fini_umh(void) |
117 | { | |
71a85084 | 118 | mutex_lock(&bpfilter_ops.lock); |
61fbf593 | 119 | if (IS_ENABLED(CONFIG_INET)) { |
71a85084 | 120 | shutdown_umh(); |
61fbf593 TY |
121 | bpfilter_ops.start = NULL; |
122 | bpfilter_ops.sockopt = NULL; | |
123 | } | |
71a85084 | 124 | mutex_unlock(&bpfilter_ops.lock); |
d2ba09c1 AS |
125 | } |
126 | module_init(load_umh); | |
127 | module_exit(fini_umh); | |
128 | MODULE_LICENSE("GPL"); |