linux/net/bpfilter/bpfilter_kern.c
<<
>>
Prefs
   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
  13extern char bpfilter_umh_start;
  14extern char bpfilter_umh_end;
  15
  16static void shutdown_umh(void)
  17{
  18        struct task_struct *tsk;
  19
  20        if (bpfilter_ops.stop)
  21                return;
  22
  23        tsk = get_pid_task(find_vpid(bpfilter_ops.info.pid), PIDTYPE_PID);
  24        if (tsk) {
  25                send_sig(SIGKILL, tsk, 1);
  26                put_task_struct(tsk);
  27        }
  28}
  29
  30static void __stop_umh(void)
  31{
  32        if (IS_ENABLED(CONFIG_INET))
  33                shutdown_umh();
  34}
  35
  36static 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;
  44        int ret = -EFAULT;
  45
  46        req.is_set = is_set;
  47        req.pid = current->pid;
  48        req.cmd = optname;
  49        req.addr = (long __force __user)optval;
  50        req.len = optlen;
  51        if (!bpfilter_ops.info.pid)
  52                goto out;
  53        n = __kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
  54                           &pos);
  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;
  62        n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply),
  63                        &pos);
  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;
  71out:
  72        return ret;
  73}
  74
  75static int start_umh(void)
  76{
  77        int err;
  78
  79        /* fork usermode process */
  80        err = fork_usermode_blob(&bpfilter_umh_start,
  81                                 &bpfilter_umh_end - &bpfilter_umh_start,
  82                                 &bpfilter_ops.info);
  83        if (err)
  84                return err;
  85        bpfilter_ops.stop = false;
  86        pr_info("Loaded bpfilter_umh pid %d\n", bpfilter_ops.info.pid);
  87
  88        /* health check that usermode process started correctly */
  89        if (__bpfilter_process_sockopt(NULL, 0, NULL, 0, 0) != 0) {
  90                shutdown_umh();
  91                return -EFAULT;
  92        }
  93
  94        return 0;
  95}
  96
  97static int __init load_umh(void)
  98{
  99        int err;
 100
 101        mutex_lock(&bpfilter_ops.lock);
 102        if (!bpfilter_ops.stop) {
 103                err = -EFAULT;
 104                goto out;
 105        }
 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        }
 111out:
 112        mutex_unlock(&bpfilter_ops.lock);
 113        return err;
 114}
 115
 116static void __exit fini_umh(void)
 117{
 118        mutex_lock(&bpfilter_ops.lock);
 119        if (IS_ENABLED(CONFIG_INET)) {
 120                shutdown_umh();
 121                bpfilter_ops.start = NULL;
 122                bpfilter_ops.sockopt = NULL;
 123        }
 124        mutex_unlock(&bpfilter_ops.lock);
 125}
 126module_init(load_umh);
 127module_exit(fini_umh);
 128MODULE_LICENSE("GPL");
 129