The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/5099

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Closes #4874.

Signed-off-by: Christian Brauner <[email protected]>
From d1990de61e82f6b02d7a3324b0a08221f39177e0 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Wed, 3 Oct 2018 23:28:26 +0200
Subject: [PATCH] lxd: support uevent injection for USB devices

Closes #4874.

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/daemon.go            |   8 ++
 lxd/devices.go           |  58 +++++++----
 lxd/main.go              |   4 +
 lxd/main_checkfeature.go |  23 ++++-
 lxd/main_forkuevent.go   | 214 +++++++++++++++++++++++++++++++++++++++
 lxd/main_nsexec.go       |   3 +
 lxd/sys/os.go            |   1 +
 7 files changed, 292 insertions(+), 19 deletions(-)
 create mode 100644 lxd/main_forkuevent.go

diff --git a/lxd/daemon.go b/lxd/daemon.go
index 2a94c65cf9..2219ee0054 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -439,6 +439,14 @@ func (d *Daemon) init() error {
                logger.Debugf("Running kernel does not support netnsid-based 
network retrieval")
        }
 
+       /* Check if uevent injection is supported. */
+       d.os.UeventInjection = CanUseUeventInjection()
+       if d.os.UeventInjection {
+               logger.Debugf("Running kernel supports uevent injection")
+       } else {
+               logger.Debugf("Running kernel does not support uevent 
injection")
+       }
+
        /* Initialize the database */
        dump, err := initializeDbObject(d)
        if err != nil {
diff --git a/lxd/devices.go b/lxd/devices.go
index 82540e3303..4296934d47 100644
--- a/lxd/devices.go
+++ b/lxd/devices.go
@@ -53,9 +53,11 @@ type usbDevice struct {
        vendor  string
        product string
 
-       path  string
-       major int
-       minor int
+       path        string
+       major       int
+       minor       int
+       ueventParts []string
+       ueventLen   int
 }
 
 // /dev/nvidia[0-9]+
@@ -378,7 +380,7 @@ func deviceLoadGpu(all bool) ([]gpuDevice, 
[]nvidiaGpuDevices, error) {
        return gpus, nvidiaDevices, nil
 }
 
-func createUSBDevice(action string, vendor string, product string, major 
string, minor string, busnum string, devnum string, devname string) (usbDevice, 
error) {
+func createUSBDevice(action string, vendor string, product string, major 
string, minor string, busnum string, devnum string, devname string, ueventParts 
[]string, ueventLen int) (usbDevice, error) {
        majorInt, err := strconv.Atoi(major)
        if err != nil {
                return usbDevice{}, err
@@ -414,6 +416,8 @@ func createUSBDevice(action string, vendor string, product 
string, major string,
                path,
                majorInt,
                minorInt,
+               ueventParts,
+               ueventLen,
        }, nil
 }
 
@@ -448,28 +452,29 @@ func deviceNetlinkListener() (chan []string, chan 
[]string, chan usbDevice, erro
        go func(chCPU chan []string, chNetwork chan []string, chUSB chan 
usbDevice) {
                b := make([]byte, UEVENT_BUFFER_SIZE*2)
                for {
-                       _, err := syscall.Read(fd, b)
+                       r, err := syscall.Read(fd, b)
                        if err != nil {
                                continue
                        }
 
+                       ueventBuf := make([]byte, r)
+                       copy(ueventBuf, b)
+                       ueventLen := 0
+                       ueventParts := strings.Split(string(ueventBuf), "\x00")
                        props := map[string]string{}
-                       last := 0
-                       for i, e := range b {
-                               if i == len(b) || e == 0 {
-                                       msg := string(b[last+1 : i])
-                                       last = i
-                                       if len(msg) == 0 || msg == "\x00" {
-                                               continue
-                                       }
+                       for _, part := range ueventParts {
+                               if strings.HasPrefix(part, "SEQNUM=") {
+                                       continue
+                               }
 
-                                       fields := strings.SplitN(msg, "=", 2)
-                                       if len(fields) != 2 {
-                                               continue
-                                       }
+                               ueventLen += len(part) + 1
 
-                                       props[fields[0]] = fields[1]
+                               fields := strings.SplitN(part, "=", 2)
+                               if len(fields) != 2 {
+                                       continue
                                }
+
+                               props[fields[0]] = fields[1]
                        }
 
                        if props["SUBSYSTEM"] == "cpu" {
@@ -554,6 +559,8 @@ func deviceNetlinkListener() (chan []string, chan []string, 
chan usbDevice, erro
                                        busnum,
                                        devnum,
                                        devname,
+                                       ueventParts[:len(ueventParts)-1],
+                                       ueventLen,
                                )
                                if err != nil {
                                        logger.Error("Error reading usb 
device", log.Ctx{"err": err, "path": props["PHYSDEVPATH"]})
@@ -851,18 +858,31 @@ func deviceUSBEvent(s *state.State, usb usbDevice) {
                                continue
                        }
 
+                       ueventArray := make([]string, 4)
+                       ueventArray[0] = "forkuevent"
+                       ueventArray[1] = "inject"
+                       ueventArray[2] = fmt.Sprintf("%d", c.InitPID())
+                       ueventArray[3] = fmt.Sprintf("%d", usb.ueventLen)
+                       ueventArray = append(ueventArray, usb.ueventParts...)
+
                        if usb.action == "add" {
                                err := 
c.insertUnixDeviceNum(fmt.Sprintf("unix.%s", name), m, usb.major, usb.minor, 
usb.path, false)
                                if err != nil {
                                        logger.Error("Failed to create usb 
device", log.Ctx{"err": err, "usb": usb, "container": c.Name()})
                                        return
                                }
+                               shared.RunCommand(s.OS.ExecPath, ueventArray...)
                        } else if usb.action == "remove" {
                                err := 
c.removeUnixDeviceNum(fmt.Sprintf("unix.%s", name), m, usb.major, usb.minor, 
usb.path)
                                if err != nil {
                                        logger.Error("Failed to remove usb 
device", log.Ctx{"err": err, "usb": usb, "container": c.Name()})
                                        return
                                }
+                               shared.RunCommand(s.OS.ExecPath, ueventArray...)
+                       } else if usb.action == "bind" {
+                               shared.RunCommand(s.OS.ExecPath, ueventArray...)
+                       } else if usb.action == "change" {
+                               shared.RunCommand(s.OS.ExecPath, ueventArray...)
                        } else {
                                logger.Error("Unknown action for usb device", 
log.Ctx{"usb": usb})
                                continue
@@ -1392,6 +1412,8 @@ func deviceLoadUsb() ([]usbDevice, error) {
                        values["busnum"],
                        values["devnum"],
                        values["devname"],
+                       []string{},
+                       0,
                )
                if err != nil {
                        if os.IsNotExist(err) {
diff --git a/lxd/main.go b/lxd/main.go
index e4ccec708c..49b0f6520b 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -124,6 +124,10 @@ func main() {
        forkstartCmd := cmdForkstart{global: &globalCmd}
        app.AddCommand(forkstartCmd.Command())
 
+       // forkuevent sub-command
+       forkueventCmd := cmdForkuevent{global: &globalCmd}
+       app.AddCommand(forkueventCmd.Command())
+
        // import sub-command
        importCmd := cmdImport{global: &globalCmd}
        app.AddCommand(importCmd.Command())
diff --git a/lxd/main_checkfeature.go b/lxd/main_checkfeature.go
index 9b7b3d443b..ea4aa8e0c2 100644
--- a/lxd/main_checkfeature.go
+++ b/lxd/main_checkfeature.go
@@ -22,8 +22,11 @@ import (
 #include "../shared/netns_getifaddrs.c"
 
 bool netnsid_aware = false;
+bool uevent_aware = false;
 char errbuf[4096];
 
+extern int inject_uevent(const char *uevent, size_t len);
+
 static int netns_set_nsid(int fd)
 {
        int sockfd, ret;
@@ -64,7 +67,8 @@ static int netns_set_nsid(int fd)
        return 0;
 }
 
-void checkfeature() {
+void is_netnsid_aware()
+{
        int netnsid, ret;
        struct netns_ifaddrs *ifaddrs;
        int hostnetns_fd = -1, newnetns_fd = -1;
@@ -118,6 +122,19 @@ on_error:
                close(newnetns_fd);
 }
 
+void is_uevent_aware()
+{
+       if (inject_uevent("dummy", 6) < 0)
+               _exit(1);
+
+       uevent_aware = true;
+}
+
+void checkfeature() {
+       is_netnsid_aware();
+       is_uevent_aware();
+}
+
 static bool is_empty_string(char *s)
 {
        return (errbuf[0] == '\0');
@@ -133,3 +150,7 @@ func CanUseNetnsGetifaddrs() bool {
 
        return bool(C.netnsid_aware)
 }
+
+func CanUseUeventInjection() bool {
+       return bool(C.uevent_aware)
+}
diff --git a/lxd/main_forkuevent.go b/lxd/main_forkuevent.go
new file mode 100644
index 0000000000..0710264013
--- /dev/null
+++ b/lxd/main_forkuevent.go
@@ -0,0 +1,214 @@
+package main
+
+import (
+       "github.com/spf13/cobra"
+)
+
+/*
+
+#define _GNU_SOURCE
+#include <asm/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "../shared/network.c"
+
+#ifndef UEVENT_SEND
+#define UEVENT_SEND 16
+#endif
+
+extern char *advance_arg(bool required);
+extern void attach_userns(int pid);
+extern int dosetns(int pid, char *nstype);
+
+struct nlmsg {
+       struct nlmsghdr *nlmsghdr;
+       ssize_t cap;
+};
+
+static struct nlmsg *nlmsg_alloc(size_t size)
+{
+       struct nlmsg *nlmsg;
+       size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size);
+
+       nlmsg = (struct nlmsg *)malloc(sizeof(struct nlmsg));
+       if (!nlmsg)
+               return NULL;
+
+       nlmsg->nlmsghdr = (struct nlmsghdr *)malloc(len);
+       if (!nlmsg->nlmsghdr)
+               goto errout;
+
+       memset(nlmsg->nlmsghdr, 0, len);
+       nlmsg->cap = len;
+       nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN;
+
+       return nlmsg;
+errout:
+       free(nlmsg);
+       return NULL;
+}
+
+static void *nlmsg_reserve_unaligned(struct nlmsg *nlmsg, size_t len)
+{
+       char *buf;
+       size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len;
+       size_t tlen = len;
+
+       if ((ssize_t)(nlmsg_len + tlen) > nlmsg->cap)
+               return NULL;
+
+       buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len;
+       nlmsg->nlmsghdr->nlmsg_len += tlen;
+
+       if (tlen > len)
+               memset(buf + len, 0, tlen - len);
+
+       return buf;
+}
+
+int inject_uevent(const char *uevent, size_t len)
+{
+       int ret, sock_fd;
+       char *umsg = NULL;
+       struct nlmsg *nlmsg = NULL;
+
+       sock_fd = netlink_open(NETLINK_KOBJECT_UEVENT);
+       if (sock_fd < 0) {
+               return -1;
+       }
+
+       nlmsg = nlmsg_alloc(len);
+       if (!nlmsg) {
+               ret = -1;
+               goto on_error;
+       }
+
+       nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST;
+       nlmsg->nlmsghdr->nlmsg_type = UEVENT_SEND;
+       nlmsg->nlmsghdr->nlmsg_pid = 0;
+
+       umsg = nlmsg_reserve_unaligned(nlmsg, len);
+       if (!umsg) {
+               ret = -1;
+               goto on_error;
+       }
+
+       memcpy(umsg, uevent, len);
+
+       ret = netlink_transaction(sock_fd, nlmsg->nlmsghdr, nlmsg->nlmsghdr);
+       if (ret < 0) {
+               ret = -1;
+               goto on_error;
+       }
+
+       ret = 0;
+
+on_error:
+       close(sock_fd);
+       free(nlmsg);
+       return ret;
+}
+
+void forkuevent() {
+       char *uevent = NULL;
+       char *cur = NULL;
+       pid_t pid = 0;
+       size_t len = 0;
+
+       cur = advance_arg(false);
+       if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, 
"--version") == 0 || strcmp(cur, "-h") == 0)) {
+               fprintf(stderr, "Error: Missing PID\n");
+               _exit(1);
+       }
+
+       // Get the pid
+       cur = advance_arg(false);
+       if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, 
"--version") == 0 || strcmp(cur, "-h") == 0)) {
+               fprintf(stderr, "Error: Missing PID\n");
+               _exit(1);
+       }
+       pid = atoi(cur);
+
+       // Get the size
+       cur = advance_arg(false);
+       if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, 
"--version") == 0 || strcmp(cur, "-h") == 0)) {
+               fprintf(stderr, "Error: Missing uevent length\n");
+               _exit(1);
+       }
+       len = atoi(cur);
+
+       // Get the uevent
+       cur = advance_arg(false);
+       if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, 
"--version") == 0 || strcmp(cur, "-h") == 0)) {
+               fprintf(stderr, "Error: Missing uevent\n");
+               _exit(1);
+       }
+       uevent = cur;
+
+       // Check that we're root
+       if (geteuid() != 0) {
+               fprintf(stderr, "Error: forkuevent requires root privileges\n");
+               _exit(1);
+       }
+
+       attach_userns(pid);
+
+       if (dosetns(pid, "net") < 0) {
+               fprintf(stderr, "Failed to setns to container network 
namespace: %s\n", strerror(errno));
+               _exit(1);
+       }
+
+       if (inject_uevent(uevent, len) < 0) {
+               fprintf(stderr, "Failed to inject uevent\n");
+               _exit(1);
+       }
+}
+*/
+// #cgo CFLAGS: -std=gnu11 -Wvla
+import "C"
+
+type cmdForkuevent struct {
+       global *cmdGlobal
+}
+
+func (c *cmdForkuevent) Command() *cobra.Command {
+       // Main subcommand
+       cmd := &cobra.Command{}
+       cmd.Use = "forkuevent"
+       cmd.Short = "Inject uevents into container's network namespace"
+       cmd.Long = `Description:
+  Inject uevent into a container's network namespace
+
+  This internal command is used to inject uevents into unprivileged container's
+  network namespaces.
+`
+       cmd.Hidden = true
+
+       // pull
+       cmdInject := &cobra.Command{}
+       cmdInject.Use = "inject <PID> <len> <uevent>"
+       cmdInject.Args = cobra.ExactArgs(3)
+       cmdInject.RunE = c.Run
+       cmd.AddCommand(cmdInject)
+
+       return cmd
+}
+
+func (c *cmdForkuevent) Run(cmd *cobra.Command, args []string) error {
+       return nil
+}
diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go
index 56fbf2fd46..c03a5d24f0 100644
--- a/lxd/main_nsexec.go
+++ b/lxd/main_nsexec.go
@@ -39,6 +39,7 @@ extern void forkfile();
 extern void forkmount();
 extern void forknet();
 extern void forkproxy();
+extern void forkuevent();
 
 // Command line parsing and tracking
 #define CMDLINE_SIZE (8 * PATH_MAX)
@@ -246,6 +247,8 @@ __attribute__((constructor)) void init(void) {
                forknet();
        else if (strcmp(cmdline_cur, "forkproxy") == 0)
                forkproxy();
+       else if (strcmp(cmdline_cur, "forkuevent") == 0)
+               forkuevent();
        else if (strncmp(cmdline_cur, "-", 1) == 0 || strcmp(cmdline_cur, 
"daemon") == 0)
                checkfeature();
 }
diff --git a/lxd/sys/os.go b/lxd/sys/os.go
index 9e93da327a..3d28f0e7a9 100644
--- a/lxd/sys/os.go
+++ b/lxd/sys/os.go
@@ -59,6 +59,7 @@ type OS struct {
        CGroupSwapAccounting    bool
        InotifyWatch            InotifyInfo
        NetnsGetifaddrs         bool
+       UeventInjection         bool
 
        MockMode bool // If true some APIs will be mocked (for testing)
 }
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to