Merge tag 'compiler-attributes-for-linus-4.20-rc1' of https://github.com/ojeda/linux
[linux-2.6-block.git] / net / bpfilter / bpfilter_kern.c
CommitLineData
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
13extern char bpfilter_umh_start;
14extern char bpfilter_umh_end;
d2ba09c1
AS
15
16static struct umh_info info;
17/* since ip_getsockopt() can run in parallel, serialize access to umh */
18static DEFINE_MUTEX(bpfilter_lock);
19
20static void shutdown_umh(struct umh_info *info)
21{
22 struct task_struct *tsk;
23
66e58e0e
AS
24 if (!info->pid)
25 return;
84258438
TY
26 tsk = get_pid_task(find_vpid(info->pid), PIDTYPE_PID);
27 if (tsk) {
d2ba09c1 28 force_sig(SIGKILL, tsk);
84258438
TY
29 put_task_struct(tsk);
30 }
d2ba09c1
AS
31 fput(info->pipe_to_umh);
32 fput(info->pipe_from_umh);
66e58e0e 33 info->pid = 0;
d2ba09c1
AS
34}
35
36static void __stop_umh(void)
37{
66e58e0e 38 if (IS_ENABLED(CONFIG_INET)) {
d2ba09c1
AS
39 bpfilter_process_sockopt = NULL;
40 shutdown_umh(&info);
41 }
42}
43
44static void stop_umh(void)
45{
46 mutex_lock(&bpfilter_lock);
47 __stop_umh();
48 mutex_unlock(&bpfilter_lock);
49}
50
51static int __bpfilter_process_sockopt(struct sock *sk, int optname,
52 char __user *optval,
53 unsigned int optlen, bool is_set)
54{
55 struct mbox_request req;
56 struct mbox_reply reply;
57 loff_t pos;
58 ssize_t n;
66e58e0e 59 int ret = -EFAULT;
d2ba09c1
AS
60
61 req.is_set = is_set;
62 req.pid = current->pid;
63 req.cmd = optname;
33aa8da1 64 req.addr = (long __force __user)optval;
d2ba09c1
AS
65 req.len = optlen;
66 mutex_lock(&bpfilter_lock);
66e58e0e
AS
67 if (!info.pid)
68 goto out;
d2ba09c1
AS
69 n = __kernel_write(info.pipe_to_umh, &req, sizeof(req), &pos);
70 if (n != sizeof(req)) {
71 pr_err("write fail %zd\n", n);
72 __stop_umh();
73 ret = -EFAULT;
74 goto out;
75 }
76 pos = 0;
77 n = kernel_read(info.pipe_from_umh, &reply, sizeof(reply), &pos);
78 if (n != sizeof(reply)) {
79 pr_err("read fail %zd\n", n);
80 __stop_umh();
81 ret = -EFAULT;
82 goto out;
83 }
84 ret = reply.status;
85out:
86 mutex_unlock(&bpfilter_lock);
87 return ret;
88}
89
90static int __init load_umh(void)
91{
92 int err;
93
94 /* fork usermode process */
4b78030b 95 info.cmdline = "bpfilter_umh";
8e75887d
MY
96 err = fork_usermode_blob(&bpfilter_umh_start,
97 &bpfilter_umh_end - &bpfilter_umh_start,
98 &info);
d2ba09c1
AS
99 if (err)
100 return err;
101 pr_info("Loaded bpfilter_umh pid %d\n", info.pid);
102
103 /* health check that usermode process started correctly */
33aa8da1 104 if (__bpfilter_process_sockopt(NULL, 0, NULL, 0, 0) != 0) {
d2ba09c1
AS
105 stop_umh();
106 return -EFAULT;
107 }
d71dbdaa
AB
108 if (IS_ENABLED(CONFIG_INET))
109 bpfilter_process_sockopt = &__bpfilter_process_sockopt;
110
d2ba09c1
AS
111 return 0;
112}
113
114static void __exit fini_umh(void)
115{
116 stop_umh();
117}
118module_init(load_umh);
119module_exit(fini_umh);
120MODULE_LICENSE("GPL");