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