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