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

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) ===
Signed-off-by: Christian Brauner <[email protected]>
From 6adb2a803ef2a9575ee25bee5d96bff173608f8a Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:04:09 +0200
Subject: [PATCH 01/13] memory: fix format string

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/debug/memory.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/debug/memory.go b/lxd/debug/memory.go
index c7d6d3908..9055495d2 100644
--- a/lxd/debug/memory.go
+++ b/lxd/debug/memory.go
@@ -50,7 +50,7 @@ func memoryWatcher(ctx context.Context, signals <-chan 
os.Signal, filename strin
 func memoryDump(filename string) {
        f, err := os.Create(filename)
        if err != nil {
-               logger.Debugf("Error opening memory profile file '%s': %s", err)
+               logger.Debugf("Error opening memory profile file '%s': %s", 
filename, err)
                return
        }
        pprof.WriteHeapProfile(f)

From e99cd9dfec5d6c609e4847ac53bef7045e17d52f Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:04:50 +0200
Subject: [PATCH 02/13] exec: fix format string

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/container_exec.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 0807c3cb7..6b1ec1917 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -218,10 +218,10 @@ func (s *execWs) Do(op *operation) error {
                                        }
                                } else if command.Command == "signal" {
                                        if err := 
syscall.Kill(attachedChildPid, syscall.Signal(command.Signal)); err != nil {
-                                               logger.Debugf("Failed 
forwarding signal '%s' to PID %d.", command.Signal, attachedChildPid)
+                                               logger.Debugf("Failed 
forwarding signal '%d' to PID %d", command.Signal, attachedChildPid)
                                                continue
                                        }
-                                       logger.Debugf("Forwarded signal '%d' to 
PID %d.", command.Signal, attachedChildPid)
+                                       logger.Debugf("Forwarded signal '%d' to 
PID %d", command.Signal, attachedChildPid)
                                }
                        }
                }()

From b86d7d5db158429f4539f563c10c4f47045ae926 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:05:45 +0200
Subject: [PATCH 03/13] images: fix format string

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/images.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/images.go b/lxd/images.go
index adf3a28b3..ac4128d58 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -1040,7 +1040,7 @@ func pruneExpiredImages(ctx context.Context, d *Daemon) {
                for _, pool := range poolNames {
                        err := doDeleteImageFromPool(d.State(), fp, pool)
                        if err != nil {
-                               logger.Debugf("Error deleting image %s from 
storage pool %: %s", fp, pool, err)
+                               logger.Debugf("Error deleting image %s from 
storage pool %s: %s", fp, pool, err)
                                continue
                        }
                }

From 23fab32473c338a166b8bdf95c6be1460a39b8df Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:06:54 +0200
Subject: [PATCH 04/13] migrate: remove debug residuals

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/migrate_container.go | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 2d583fc28..9129ce1fb 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -150,7 +150,6 @@ func (s *migrationSourceWs) checkForPreDumpSupport() (bool, 
int) {
        if tmp != "" {
                use_pre_dumps = shared.IsTrue(tmp)
        }
-       logger.Debugf("migration.incremental.memory %d", use_pre_dumps)
 
        var max_iterations int
 
@@ -910,7 +909,6 @@ func (c *migrationSink) Do(migrateOp *operation) error {
                        }
 
                        if resp.GetPredump() {
-                               logger.Debugf("Before the receive loop %s", 
sync.GetFinalPreDump())
                                for !sync.GetFinalPreDump() {
                                        logger.Debugf("About to receive rsync")
                                        // Transfer a CRIU pre-dump
@@ -940,7 +938,6 @@ func (c *migrationSink) Do(migrateOp *operation) error {
                                                restore <- err
                                                return
                                        }
-                                       logger.Debugf("At the end of the 
receive loop %s", sync.GetFinalPreDump())
                                }
                        }
 

From 70fbb7406dd847304fc0aca0ceb3ae9d4af3cee9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:07:41 +0200
Subject: [PATCH 05/13] lvm: fix format string

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/storage_lvm.go | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index c4662a466..72e2b9b5a 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -770,10 +770,7 @@ func (s *storageLvm) StoragePoolUpdate(writable 
*api.StoragePoolPut, changedConf
 
                        err := lvmVGRename(newName, oldPoolName)
                        if err != nil {
-                               logger.Warnf(`Failed to rename LVM volume `+
-                                       `group from "%s" to "%s": %s. Manual `+
-                                       `intervention needed`, newName,
-                                       oldPoolName)
+                               logger.Warnf(`Failed to rename LVM volume group 
from "%s" to "%s": %s. Manual intervention needed`, newName, oldPoolName, err)
                        }
                        s.setOnDiskPoolName(oldPoolName)
                }()

From e6d4d6bbeeffe2f7bfaee94eb973ff04b77da55a Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 13:08:15 +0200
Subject: [PATCH 06/13] db: fix format string

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/db/query/dump.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/db/query/dump.go b/lxd/db/query/dump.go
index 33ce6105f..3aa12933c 100644
--- a/lxd/db/query/dump.go
+++ b/lxd/db/query/dump.go
@@ -102,7 +102,7 @@ func dumpTable(tx *sql.Tx, table, schema string) (string, 
error) {
                }
                err := rows.Scan(row...)
                if err != nil {
-                       return "", errors.Wrapf(err, "failed to scan row %d")
+                       return "", errors.Wrapf(err, "failed to scan row %d", i)
                }
                values := make([]string, len(columns))
                for j, v := range raw {

From f73a963bd12a0c6e525c4ff6ef33bc4c8a3b5858 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 20:03:38 +0200
Subject: [PATCH 07/13] nsexec: prevent fd leak

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/main_nsexec.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go
index a04658c56..4c6837cbf 100644
--- a/lxd/main_nsexec.go
+++ b/lxd/main_nsexec.go
@@ -183,6 +183,7 @@ void attach_userns(int pid) {
        }
 
        ret = setns(userns_fd, CLONE_NEWUSER);
+       close(userns_fd);
        if (ret < 0) {
                fprintf(stderr, "Failed setns to container user namespace: 
%s\n", strerror(errno));
                _exit(EXIT_FAILURE);

From 55dfa3dd89ee7ae45be6df1730fa7c099b70397b Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Fri, 25 May 2018 19:53:23 +0200
Subject: [PATCH 08/13] proxy: unix

Closes #4167.

Signed-off-by: Christian Brauner <[email protected]>
Signed-off-by: Thomas Hipp <[email protected]>
---
 lxd/container_lxc.go      |  45 ++--
 lxd/main_forkproxy.go     | 519 ++++++++++++++++++++++++++++++++++++----------
 lxd/main_nsexec.go        |   2 +-
 lxd/proxy_device_utils.go |   4 +-
 shared/util_linux.go      | 111 ++++++++++
 5 files changed, 535 insertions(+), 146 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index aadde3e56..f9ea10ed1 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -2488,7 +2488,13 @@ func (c *containerLXC) Start(stateful bool) error {
                return err
        }
 
-       c.restartProxyDevices()
+       // Start proxy devices
+       err = c.restartProxyDevices()
+       if err != nil {
+               // Attempt to stop the container
+               c.Stop(false)
+               return err
+       }
 
        logger.Info("Started container", ctxMap)
        eventSendLifecycle("container-started",
@@ -6798,8 +6804,6 @@ func (c *containerLXC) insertProxyDevice(devName string, 
m types.Device) error {
                proxyValues.listenAddr,
                proxyValues.connectPid,
                proxyValues.connectAddr,
-               "0",
-               "0",
                logPath,
                pidPath)
        if err != nil {
@@ -6858,49 +6862,28 @@ func (c *containerLXC) updateProxyDevice(devName 
string, m types.Device) error {
                return fmt.Errorf("Can't update proxy device in stopped 
container")
        }
 
-       proxyValues, err := setupProxyProcInfo(c, m)
-       if err != nil {
-               return err
-       }
-
        devFileName := fmt.Sprintf("proxy.%s", devName)
        pidPath := filepath.Join(c.DevicesPath(), devFileName)
-       logFileName := fmt.Sprintf("proxy.%s.log", devName)
-       logPath := filepath.Join(c.LogPath(), logFileName)
-
-       err = killProxyProc(pidPath)
-       if err != nil {
-               return fmt.Errorf("Error occurred when removing old proxy 
device")
-       }
-
-       _, err = shared.RunCommand(
-               c.state.OS.ExecPath,
-               "forkproxy",
-               proxyValues.listenPid,
-               proxyValues.listenAddr,
-               proxyValues.connectPid,
-               proxyValues.connectAddr,
-               "0",
-               "0",
-               logPath,
-               pidPath)
+       err := killProxyProc(pidPath)
        if err != nil {
-               return fmt.Errorf("Error occurred when starting new proxy 
device")
+               return fmt.Errorf("Error occurred when removing old proxy 
device: %v", err)
        }
 
-       return nil
+       return c.insertProxyDevice(devName, m)
 }
 
-func (c *containerLXC) restartProxyDevices() {
+func (c *containerLXC) restartProxyDevices() error {
        for _, name := range c.expandedDevices.DeviceNames() {
                m := c.expandedDevices[name]
                if m["type"] == "proxy" {
                        err := c.insertProxyDevice(name, m)
                        if err != nil {
-                               fmt.Printf("Error when starting proxy device 
'%s' for container %s: %s\n", name, c.name, err)
+                               return fmt.Errorf("Error when starting proxy 
device '%s' for container %s: %v\n", name, c.name, err)
                        }
                }
        }
+
+       return nil
 }
 
 // Network device handling
diff --git a/lxd/main_forkproxy.go b/lxd/main_forkproxy.go
index 0c02199f9..167db12ef 100644
--- a/lxd/main_forkproxy.go
+++ b/lxd/main_forkproxy.go
@@ -5,13 +5,13 @@ import (
        "io"
        "net"
        "os"
-       "strconv"
+       "os/signal"
        "strings"
        "syscall"
+       "time"
 
        "github.com/spf13/cobra"
 
-       "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/eagain"
 )
@@ -19,115 +19,251 @@ import (
 /*
 #define _GNU_SOURCE
 #include <errno.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 extern char* advance_arg(bool required);
+extern void attach_userns(int pid);
 extern int dosetns(int pid, char *nstype);
 
-void forkproxy() {
-       char *cur = NULL;
+int whoami = -ESRCH;
 
-       int cmdline, listen_pid, connect_pid, fdnum, forked, childPid, ret;
-       char *logPath = NULL, *pidPath = NULL;
-       FILE *logFile = NULL, *pidFile = NULL;
+#define FORKPROXY_CHILD 1
+#define FORKPROXY_PARENT 0
+#define FORKPROXY_UDS_SOCK_FD_NUM 200
 
-       // /proc/self/fd/<num> (14 (path) + 21 (int64) + 1 (null))
-       char fdpath[36];
+int wait_for_pid(pid_t pid)
+{
+       int status, ret;
+
+again:
+       ret = waitpid(pid, &status, 0);
+       if (ret == -1) {
+               if (errno == EINTR)
+                       goto again;
+               return -1;
+       }
+       if (ret != pid)
+               goto again;
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               return -1;
+       return 0;
+}
+
+ssize_t lxc_read_nointr(int fd, void* buf, size_t count)
+{
+       ssize_t ret;
+again:
+       ret = read(fd, buf, count);
+       if (ret < 0 && errno == EINTR)
+               goto again;
+       return ret;
+}
+
+void forkproxy()
+{
+       int connect_pid, listen_pid, log_fd;
+       ssize_t ret;
+       pid_t pid;
+       char *connect_addr, *cur, *listen_addr, *log_path, *pid_path;
+       int sk_fds[2] = {-EBADF, -EBADF};
+       FILE *pid_file;
 
        // Get the pid
        cur = advance_arg(false);
-       if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, 
"--version") == 0 || strcmp(cur, "-h") == 0)) {
-               return;
-       }
-       listen_pid = atoi(cur);
+       if (cur == NULL ||
+           (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 ||
+            strcmp(cur, "-h") == 0))
+               _exit(EXIT_FAILURE);
 
-       // Get the arguments
-       advance_arg(true);
+       listen_pid = atoi(cur);
+       listen_addr = advance_arg(true);
        connect_pid = atoi(advance_arg(true));
-       advance_arg(true);
-       fdnum = atoi(advance_arg(true));
-       forked = atoi(advance_arg(true));
-       logPath = advance_arg(true);
-       pidPath = advance_arg(true);
-
-       // Check if proxy daemon already forked
-       if (forked == 0) {
-               logFile = fopen(logPath, "w+");
-               if (logFile == NULL) {
-                       _exit(1);
-               }
+       connect_addr = advance_arg(true);
+       log_path = advance_arg(true);
+       pid_path = advance_arg(true);
+
+       close(STDIN_FILENO);
+       log_fd = open(log_path, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0600);
+       if (log_fd < 0)
+               _exit(EXIT_FAILURE);
+
+       ret = dup3(log_fd, STDOUT_FILENO, O_CLOEXEC);
+       if (ret < 0)
+               _exit(EXIT_FAILURE);
+
+       ret = dup3(log_fd, STDERR_FILENO, O_CLOEXEC);
+       if (ret < 0)
+               _exit(EXIT_FAILURE);
+
+       pid_file = fopen(pid_path, "w+");
+       if (!pid_file) {
+               fprintf(stderr,
+                       "%s - Failed to create pid file for proxy daemon\n",
+                       strerror(errno));
+               _exit(EXIT_FAILURE);
+       }
 
-               if (dup2(fileno(logFile), STDOUT_FILENO) < 0) {
-                       fprintf(logFile, "Failed to redirect STDOUT to logfile: 
%s\n", strerror(errno));
-                       _exit(1);
+       ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sk_fds);
+       if (ret < 0) {
+               fprintf(stderr,
+                       "%s - Failed to create anonymous unix socket pair\n",
+                       strerror(errno));
+               _exit(EXIT_FAILURE);
+       }
+       fprintf(stderr, "Created anonymous pair {%d,%d} of unix sockets\n",
+               sk_fds[0], sk_fds[1]);
+
+       pid = fork();
+       if (pid < 0) {
+               fprintf(stderr, "%s - Failed to create new process\n",
+                       strerror(errno));
+               _exit(EXIT_FAILURE);
+       }
+
+       if (pid == 0) {
+               whoami = FORKPROXY_CHILD;
+
+               fclose(pid_file);
+               close(sk_fds[0]);
+
+               // Attach to the user namespace of the listener
+               attach_userns(listen_pid);
+
+               // Attach to the network namespace of the listener
+               ret = dosetns(listen_pid, "net");
+               if (ret < 0) {
+                       fprintf(stderr, "Failed setns to listener network 
namespace: %s\n",
+                               strerror(errno));
+                       _exit(EXIT_FAILURE);
                }
-               if (dup2(fileno(logFile), STDERR_FILENO) < 0) {
-                       fprintf(logFile, "Failed to redirect STDERR to logfile: 
%s\n", strerror(errno));
-                       _exit(1);
+
+               // Attach to the mount namespace of the listener
+               ret = dosetns(listen_pid, "mnt");
+               if (ret < 0) {
+                       fprintf(stderr, "Failed setns to listener mount 
namespace: %s\n",
+                               strerror(errno));
+                       _exit(EXIT_FAILURE);
                }
-               fclose(logFile);
 
-               pidFile = fopen(pidPath, "w+");
-               if (pidFile == NULL) {
-                       fprintf(stderr, "Failed to create pid file for proxy 
daemon: %s\n", strerror(errno));
+               ret = dup3(sk_fds[1], FORKPROXY_UDS_SOCK_FD_NUM, O_CLOEXEC);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "%s - Failed to duplicate fd %d to fd 200\n",
+                               strerror(errno), sk_fds[1]);
                        _exit(1);
                }
 
-               childPid = fork();
-               if (childPid < 0) {
-                       fprintf(stderr, "Failed to fork proxy daemon: %s\n", 
strerror(errno));
+               ret = close(sk_fds[1]);
+               if (ret < 0) {
+                       fprintf(stderr, "%s - Failed to close socket fd %d\n",
+                               strerror(errno), sk_fds[1]);
                        _exit(1);
-               } else if (childPid != 0) {
-                       fprintf(pidFile, "%d", childPid);
-                       fclose(pidFile);
-                       fclose(stdin);
-                       fclose(stdout);
-                       fclose(stderr);
-                       _exit(0);
-               } else {
-                       ret = setsid();
-                       if (ret < 0) {
-                               fprintf(stderr, "Failed to setsid in proxy 
daemon: %s\n", strerror(errno));
-                               _exit(1);
-                       }
                }
-       }
+       } else {
+               whoami = FORKPROXY_PARENT;
 
-       // Cannot pass through -1 to runCommand since it is interpreted as a 
flag
-       fdnum = fdnum == 0 ? -1 : fdnum;
+               close(sk_fds[1]);
 
-       ret = snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fdnum);
-       if (ret < 0 || (size_t)ret >= sizeof(fdpath)) {
-               fprintf(stderr, "Failed to format file descriptor path\n");
-               _exit(1);
-       }
+               // Attach to the user namespace of the listener
+               attach_userns(connect_pid);
 
-       // Join the listener ns if not already setup
-       if (access(fdpath, F_OK) < 0) {
                // Attach to the network namespace of the listener
-               if (dosetns(listen_pid, "net") < 0) {
-                       fprintf(stderr, "Failed setns to listener network 
namespace: %s\n", strerror(errno));
+               ret = dosetns(connect_pid, "net");
+               if (ret < 0) {
+                       fprintf(stderr, "Failed setns to listener network 
namespace: %s\n",
+                               strerror(errno));
+                       _exit(EXIT_FAILURE);
+               }
+
+               // Attach to the mount namespace of the listener
+               ret = dosetns(connect_pid, "mnt");
+               if (ret < 0) {
+                       fprintf(stderr, "Failed setns to listener mount 
namespace: %s\n",
+                               strerror(errno));
+                       _exit(EXIT_FAILURE);
+               }
+
+               ret = dup3(sk_fds[0], FORKPROXY_UDS_SOCK_FD_NUM, O_CLOEXEC);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "%s - Failed to duplicate fd %d to fd 200\n",
+                               strerror(errno), sk_fds[1]);
                        _exit(1);
                }
-       } else {
-               // Join the connector ns now
-               if (dosetns(connect_pid, "net") < 0) {
-                       fprintf(stderr, "Failed setns to connector network 
namespace: %s\n", strerror(errno));
+
+               ret = close(sk_fds[0]);
+               if (ret < 0) {
+                       fprintf(stderr, "%s - Failed to close socket fd %d\n",
+                               strerror(errno), sk_fds[1]);
                        _exit(1);
                }
+
+               ret = wait_for_pid(pid);
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to start listener\n");
+                       _exit(EXIT_FAILURE);
+               }
+
+               // daemonize
+               pid = fork();
+               if (pid < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (pid != 0) {
+                       ret = wait_for_pid(pid);
+                       if (ret < 0)
+                               _exit(EXIT_FAILURE);
+
+                       _exit(EXIT_SUCCESS);
+               }
+
+               pid = fork();
+               if (pid < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (pid != 0) {
+                       ret = fprintf(pid_file, "%d", pid);
+                       fclose(pid_file);
+                       if (ret < 0) {
+                               fprintf(stderr, "Failed to write proxy daemon 
pid %d to \"%s\"\n",
+                                       pid, pid_path);
+                               ret = EXIT_FAILURE;
+                       }
+                       close(STDOUT_FILENO);
+                       close(STDERR_FILENO);
+                       _exit(EXIT_SUCCESS);
+               }
+
+               ret = setsid();
+               if (ret < 0)
+                       fprintf(stderr, "%s - Failed to setsid in proxy 
daemon\n",
+                               strerror(errno));
        }
 }
 */
 import "C"
 
+const forkproxyUDSSockFDNum int = C.FORKPROXY_UDS_SOCK_FD_NUM
+
 type cmdForkproxy struct {
        global *cmdGlobal
 }
 
+type proxyAddress struct {
+       connType string
+       addr     string
+       abstract bool
+}
+
 func (c *cmdForkproxy) Command() *cobra.Command {
        // Main subcommand
        cmd := &cobra.Command{}
@@ -147,8 +283,13 @@ func (c *cmdForkproxy) Command() *cobra.Command {
 }
 
 func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
+       // Only root should run this
+       if os.Geteuid() != 0 {
+               return fmt.Errorf("This must be run as root")
+       }
+
        // Sanity checks
-       if len(args) != 8 {
+       if len(args) != 6 {
                cmd.Help()
 
                if len(args) == 0 {
@@ -158,64 +299,75 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
                return fmt.Errorf("Missing required arguments")
        }
 
-       // Only root should run this
-       if os.Geteuid() != 0 {
-               return fmt.Errorf("This must be run as root")
-       }
-
        // Get all our arguments
-       listenPid := args[0]
        listenAddr := args[1]
-       connectPid := args[2]
-       connectAddr := args[3]
 
-       fd := -1
-       if args[4] != "0" {
-               fd, _ = strconv.Atoi(args[4])
+       // Check where we are in initialization
+       if C.whoami != C.FORKPROXY_PARENT && C.whoami != C.FORKPROXY_CHILD {
+               return fmt.Errorf("Failed to call forkproxy constructor")
        }
 
-       // At this point we have already forked and should set this flag to 1
-       args[5] = "1"
+       lAddr := parseAddr(listenAddr)
 
-       // Check where we are in initialization
-       if !shared.PathExists(fmt.Sprintf("/proc/self/fd/%d", fd)) {
-               fmt.Printf("Listening on %s in %s, forwarding to %s from %s\n", 
listenAddr, listenPid, connectAddr, connectPid)
+       if C.whoami == C.FORKPROXY_CHILD {
+               err := os.Remove(lAddr.addr)
+               if err != nil && !os.IsNotExist(err) {
+                       return err
+               }
 
                file, err := getListenerFile(listenAddr)
                if err != nil {
                        return err
                }
-               defer file.Close()
 
-               listenerFd := file.Fd()
-               if err != nil {
-                       return fmt.Errorf("Failed to duplicate the listener fd: 
%v", err)
-               }
+               err = shared.AbstractUnixSendFd(forkproxyUDSSockFDNum, 
int(file.Fd()))
+               syscall.Close(forkproxyUDSSockFDNum)
+               file.Close()
+               return err
+       }
 
-               newFd, err := syscall.Dup(int(listenerFd))
-               if err != nil {
-                       return fmt.Errorf("Failed to dup fd: %v", err)
-               }
+       file, err := shared.AbstractUnixReceiveFd(forkproxyUDSSockFDNum)
+       syscall.Close(forkproxyUDSSockFDNum)
+       if err != nil {
+               fmt.Printf("Failed to receive fd from listener process: %v\n", 
err)
+               return err
+       }
 
-               fmt.Printf("Re-executing proxy process\n")
+       listener, err := net.FileListener(file)
+       if err != nil {
+               fmt.Printf("Failed to re-assemble listener: %v", err)
+               return err
+       }
+
+       // Handle SIGTERM which is sent when the proxy is to be removed
+       terminate := false
+       sigs := make(chan os.Signal, 1)
+       signal.Notify(sigs, syscall.SIGTERM)
 
-               args[4] = strconv.Itoa(int(newFd))
-               execArgs := append([]string{"lxd", "forkproxy"}, args...)
+       // Wait for SIGTERM and close the listener in order to exit the loop 
below
+       go func() {
+               <-sigs
+               terminate = true
+               listener.Close()
+       }()
+
+       connectAddr := args[3]
+       cAddr := parseAddr(connectAddr)
 
-               err = syscall.Exec(util.GetExecPath(), execArgs, os.Environ())
+       if cAddr.connType == "unix" && !cAddr.abstract {
+               // Create socket
+               file, err := getListenerFile(fmt.Sprintf("unix:%s", cAddr.addr))
                if err != nil {
-                       return fmt.Errorf("Failed to re-exec: %v", err)
+                       return err
                }
-       }
+               file.Close()
 
-       // Re-create listener from fd
-       listenFile := os.NewFile(uintptr(fd), "listener")
-       listener, err := net.FileListener(listenFile)
-       if err != nil {
-               return fmt.Errorf("Failed to re-assemble listener: %v", err)
+               defer os.Remove(cAddr.addr)
        }
 
-       defer listener.Close()
+       if lAddr.connType == "unix" && !lAddr.abstract {
+               defer os.Remove(lAddr.addr)
+       }
 
        fmt.Printf("Starting to proxy\n")
 
@@ -224,6 +376,10 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
                // Accept a new client
                srcConn, err := listener.Accept()
                if err != nil {
+                       if terminate {
+                               break
+                       }
+
                        fmt.Printf("error: Failed to accept new connection: 
%v\n", err)
                        continue
                }
@@ -237,18 +393,149 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
                        continue
                }
 
-               go io.Copy(eagain.Writer{Writer: srcConn}, 
eagain.Reader{Reader: dstConn})
-               go io.Copy(eagain.Writer{Writer: dstConn}, 
eagain.Reader{Reader: srcConn})
+               if cAddr.connType == "unix" && lAddr.connType == "unix" {
+                       // Handle OOB if both src and dst are using unix sockets
+                       go unixRelay(srcConn, dstConn)
+               } else {
+                       go genericRelay(srcConn, dstConn)
+               }
+       }
+
+       fmt.Printf("Stopping proxy\n")
+
+       return nil
+}
+
+func genericRelay(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
+       relayer := func(dst io.Writer, src io.Reader, ch chan bool) {
+               io.Copy(eagain.Writer{Writer: dst}, eagain.Reader{Reader: src})
+               ch <- true
        }
+
+       chSend := make(chan bool)
+       go relayer(dst, src, chSend)
+
+       chRecv := make(chan bool)
+       go relayer(src, dst, chRecv)
+
+       <-chSend
+       <-chRecv
+
+       src.Close()
+       dst.Close()
+}
+
+func unixRelayer(src *net.UnixConn, dst *net.UnixConn, ch chan bool) {
+       dataBuf := make([]byte, 4096)
+       oobBuf := make([]byte, 4096)
+
+       for {
+               // Read from the source
+       readAgain:
+               sData, sOob, _, _, err := src.ReadMsgUnix(dataBuf, oobBuf)
+               if err != nil {
+                       errno, ok := shared.GetErrno(err)
+                       if ok && errno == syscall.EAGAIN {
+                               goto readAgain
+                       }
+                       fmt.Printf("Disconnected during read: %v\n", err)
+                       ch <- true
+                       return
+               }
+
+               var fds []int
+               if sOob > 0 {
+                       entries, err := 
syscall.ParseSocketControlMessage(oobBuf[:sOob])
+                       if err != nil {
+                               fmt.Printf("Failed to parse control message: 
%v\n", err)
+                               ch <- true
+                               return
+                       }
+
+                       for _, msg := range entries {
+                               fds, err = syscall.ParseUnixRights(&msg)
+                               if err != nil {
+                                       fmt.Printf("Failed to get fd list for 
control message: %v\n", err)
+                                       ch <- true
+                                       return
+                               }
+                       }
+               }
+
+               // Send to the destination
+       writeAgain:
+               tData, tOob, err := dst.WriteMsgUnix(dataBuf[:sData], 
oobBuf[:sOob], nil)
+               if err != nil {
+                       errno, ok := shared.GetErrno(err)
+                       if ok && errno == syscall.EAGAIN {
+                               goto writeAgain
+                       }
+                       fmt.Printf("Disconnected during write: %v\n", err)
+                       ch <- true
+                       return
+               }
+
+               if sData != tData || sOob != tOob {
+                       fmt.Printf("Some data got lost during transfer, 
disconnecting.")
+                       ch <- true
+                       return
+               }
+
+               // Close those fds we received
+               if fds != nil {
+                       for _, fd := range fds {
+                               err := syscall.Close(fd)
+                               if err != nil {
+                                       fmt.Printf("Failed to close fd %d: 
%v\n", fd, err)
+                                       ch <- true
+                                       return
+                               }
+                       }
+               }
+       }
+}
+
+func unixRelay(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
+       chSend := make(chan bool)
+       go unixRelayer(dst.(*net.UnixConn), src.(*net.UnixConn), chSend)
+
+       chRecv := make(chan bool)
+       go unixRelayer(src.(*net.UnixConn), dst.(*net.UnixConn), chRecv)
+
+       <-chSend
+       <-chRecv
+
+       src.Close()
+       dst.Close()
+}
+
+func tryListen(protocol string, addr string) (net.Listener, error) {
+       var listener net.Listener
+       var err error
+
+       for i := 0; i < 10; i++ {
+               listener, err = net.Listen(protocol, addr)
+               if err == nil {
+                       break
+               }
+
+               time.Sleep(500 * time.Millisecond)
+       }
+
+       if err != nil {
+               return nil, err
+       }
+
+       return listener, nil
 }
 
 func getListenerFile(listenAddr string) (os.File, error) {
        fields := strings.SplitN(listenAddr, ":", 2)
        addr := strings.Join(fields[1:], "")
 
-       listener, err := net.Listen(fields[0], addr)
+       listener, err := tryListen(fields[0], addr)
        if err != nil {
-               return os.File{}, err
+               return os.File{}, fmt.Errorf("Failed to listen on %s: %v", 
addr, err)
        }
 
        file := &os.File{}
@@ -260,7 +547,6 @@ func getListenerFile(listenAddr string) (os.File, error) {
                unixListener := listener.(*net.UnixListener)
                file, err = unixListener.File()
        }
-
        if err != nil {
                return os.File{}, fmt.Errorf("Failed to get file from listener: 
%v", err)
        }
@@ -273,3 +559,12 @@ func getDestConn(connectAddr string) (net.Conn, error) {
        addr := strings.Join(fields[1:], "")
        return net.Dial(fields[0], addr)
 }
+
+func parseAddr(addr string) *proxyAddress {
+       fields := strings.SplitN(addr, ":", 2)
+       return &proxyAddress{
+               connType: fields[0],
+               addr:     fields[1],
+               abstract: strings.HasPrefix(fields[1], "@"),
+       }
+}
diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go
index 4c6837cbf..881a47109 100644
--- a/lxd/main_nsexec.go
+++ b/lxd/main_nsexec.go
@@ -177,7 +177,7 @@ void attach_userns(int pid) {
        userns_fd = in_same_namespace(getpid(), pid, "user");
        if (userns_fd < 0) {
                if (userns_fd == -EINVAL)
-                       _exit(EXIT_SUCCESS);
+                       return;
 
                _exit(EXIT_FAILURE);
        }
diff --git a/lxd/proxy_device_utils.go b/lxd/proxy_device_utils.go
index 1984062b5..63d815dfd 100644
--- a/lxd/proxy_device_utils.go
+++ b/lxd/proxy_device_utils.go
@@ -31,10 +31,10 @@ func setupProxyProcInfo(c container, device 
map[string]string) (*proxyProcInfo,
        connectionType := strings.SplitN(connectAddr, ":", 2)[0]
        listenerType := strings.SplitN(listenAddr, ":", 2)[0]
 
-       if connectionType != "tcp" {
+       if !shared.StringInSlice(connectionType, []string{"tcp", "unix"}) {
                return nil, fmt.Errorf("Proxy device doesn't support the 
connection type: %s", connectionType)
        }
-       if listenerType != "tcp" {
+       if !shared.StringInSlice(listenerType, []string{"tcp", "unix"}) {
                return nil, fmt.Errorf("Proxy device doesn't support the 
listener type: %s", listenerType)
        }
 
diff --git a/shared/util_linux.go b/shared/util_linux.go
index 88613479a..a6582ba87 100644
--- a/shared/util_linux.go
+++ b/shared/util_linux.go
@@ -35,6 +35,7 @@ import (
 #include <string.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 
@@ -149,6 +150,93 @@ again:
 
        return ret;
 }
+
+int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds,
+                              void *data, size_t size)
+{
+       int ret;
+       struct msghdr msg;
+       struct iovec iov;
+       struct cmsghdr *cmsg = NULL;
+       char buf[1] = {0};
+       char *cmsgbuf;
+       size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int));
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&iov, 0, sizeof(iov));
+
+       cmsgbuf = malloc(cmsgbufsize);
+       if (!cmsgbuf)
+               return -1;
+
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = cmsgbufsize;
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+       cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int));
+
+       msg.msg_controllen = cmsg->cmsg_len;
+
+       memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int));
+
+       iov.iov_base = data ? data : buf;
+       iov.iov_len = data ? size : sizeof(buf);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+       if (ret < 0)
+               fprintf(stderr, "%s - Failed to send file descriptor\n", 
strerror(errno));
+       free(cmsgbuf);
+       return ret;
+}
+
+int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds,
+                              void *data, size_t size)
+{
+       int ret;
+       struct msghdr msg;
+       struct iovec iov;
+       struct cmsghdr *cmsg = NULL;
+       char buf[1] = {0};
+       char *cmsgbuf;
+       size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int));
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&iov, 0, sizeof(iov));
+
+       cmsgbuf = malloc(cmsgbufsize);
+       if (!cmsgbuf)
+               return -1;
+
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = cmsgbufsize;
+
+       iov.iov_base = data ? data : buf;
+       iov.iov_len = data ? size : sizeof(buf);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       ret = recvmsg(fd, &msg, 0);
+       if (ret <= 0) {
+               fprintf(stderr, "%s - Failed to receive file descriptor\n", 
strerror(errno));
+               goto out;
+       }
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+
+       memset(recvfds, -1, num_recvfds * sizeof(int));
+       if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) &&
+           cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+               memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int));
+       }
+
+out:
+       free(cmsgbuf);
+       return ret;
+}
 */
 import "C"
 
@@ -174,6 +262,29 @@ func GetPollRevents(fd int, timeout int, flags int) (int, 
int, error) {
        return int(ret), int(revents), err
 }
 
+func AbstractUnixSendFd(sockFD int, sendFD int) error {
+       fd := C.int(sendFD)
+       sk_fd := C.int(sockFD)
+       ret := C.lxc_abstract_unix_send_fds(sk_fd, &fd, C.int(1), nil, 
C.size_t(0))
+       if ret < 0 {
+               return fmt.Errorf("Failed to send file descriptor via abstract 
unix socket")
+       }
+
+       return nil
+}
+
+func AbstractUnixReceiveFd(sockFD int) (*os.File, error) {
+       fd := C.int(-1)
+       sk_fd := C.int(sockFD)
+       ret := C.lxc_abstract_unix_recv_fds(sk_fd, &fd, C.int(1), nil, 
C.size_t(0))
+       if ret < 0 {
+               return nil, fmt.Errorf("Failed to receive file descriptor via 
abstract unix socket")
+       }
+
+       file := os.NewFile(uintptr(fd), "")
+       return file, nil
+}
+
 func OpenPty(uid, gid int64) (master *os.File, slave *os.File, err error) {
        fd_master := C.int(-1)
        fd_slave := C.int(-1)

From b811673d0aa2c3537c1eac6eaa4dbbfec771a9fd Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Tue, 29 May 2018 20:04:25 +0200
Subject: [PATCH 09/13] api: proxy_unix

Closes #4167.

Signed-off-by: Thomas Hipp <[email protected]>
---
 doc/api-extensions.md | 12 ++++++++++++
 doc/containers.md     |  5 ++++-
 shared/version/api.go |  1 +
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 868838750..fe2e3f41e 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -486,3 +486,15 @@ images from the host.
 ## container\_local\_cross\_pool\_handling
 This enables copying or moving containers between storage pools on the same LXD
 instance.
+
+## proxy_unix
+Add support for both unix sockets and abstract unix sockets in proxy devices.
+They can be used by specifying the address as `unix:/path/to/unix.sock` (normal
+socket) or `unix:@/tmp/unix.sock` (abstract socket).
+
+Supported connections are now:
+
+* `TCP <-> TCP`
+* `UNIX <-> UNIX`
+* `TCP <-> UNIX`
+* `UNIX <-> TCP`
diff --git a/doc/containers.md b/doc/containers.md
index 7d059f606..2f716eaa3 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -421,7 +421,10 @@ addresses to an address inside the container or to do the 
reverse and
 have an address in the container connect through the host.
 
 The supported connection types are:
- - `TCP - TCP`
+* `TCP <-> TCP`
+* `UNIX <-> UNIX`
+* `TCP <-> UNIX`
+* `UNIX <-> TCP`
 
 Key         | Type      | Default           | Required  | Description
 :--         | :--       | :--               | :--       | :--
diff --git a/shared/version/api.go b/shared/version/api.go
index 2a3391393..e0208a277 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -106,6 +106,7 @@ var APIExtensions = []string{
        "container_backup",
        "devlxd_images",
        "container_local_cross_pool_handling",
+       "proxy_unix",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From a32e680e2c2ffe92d101f4a00beff6289fc699cf Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Wed, 30 May 2018 11:38:14 +0200
Subject: [PATCH 10/13] tests: add tcp proxy tests

Closes #4167.

Signed-off-by: Thomas Hipp <[email protected]>
Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/container_lxc.go |   2 +-
 test/suites/proxy.sh | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 227 insertions(+), 5 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index f9ea10ed1..2f9b3bdb5 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -6850,7 +6850,7 @@ func (c *containerLXC) removeProxyDevices() error {
                devicePath := filepath.Join(c.DevicesPath(), f.Name())
                err = killProxyProc(devicePath)
                if err != nil {
-                       logger.Error("failed removing proxy device", 
log.Ctx{"err": err, "path": devicePath})
+                       logger.Error("Failed removing proxy device", 
log.Ctx{"err": err, "path": devicePath})
                }
        }
 
diff --git a/test/suites/proxy.sh b/test/suites/proxy.sh
index a7bb0d949..9685b2601 100755
--- a/test/suites/proxy.sh
+++ b/test/suites/proxy.sh
@@ -1,49 +1,271 @@
 test_proxy_device() {
+  test_proxy_device_tcp
+  test_proxy_device_unix
+  test_proxy_device_tcp_unix
+  test_proxy_device_unix_tcp
+}
+
+test_proxy_device_tcp() {
   ensure_import_testimage
   ensure_has_localhost_remote "${LXD_ADDR}"
 
+  # Setup
   MESSAGE="Proxy device test string"
   HOST_TCP_PORT=$(local_tcp_port)
-
   lxc launch testimage proxyTester
+
+  # Initial test
   lxc config device add proxyTester proxyDev proxy 
"listen=tcp:127.0.0.1:$HOST_TCP_PORT" connect=tcp:127.0.0.1:4321 bind=host
-  nsenter -n -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" -- 
nc -6 -l 4321 > proxyTest.out &
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > proxyTest.out &
   sleep 2
 
   echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
 
   if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
     echo "Proxy device did not properly send data from host to container"
     false
   fi
 
   rm -f proxyTest.out
 
+  # Restart the container
   lxc restart -f proxyTester
-  nsenter -n -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" -- 
nc -6 -l 4321 > proxyTest.out &
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > proxyTest.out &
   sleep 2
 
   echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
 
   if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
     echo "Proxy device did not properly restart on container restart"
     false
   fi
 
   rm -f proxyTest.out
 
+  # Change the port
   lxc config device set proxyTester proxyDev connect tcp:127.0.0.1:1337
-  nsenter -n -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" -- 
nc -6 -l 1337 > proxyTest.out &
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 1337 > proxyTest.out &
   sleep 2
 
   echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
 
   if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
     echo "Proxy device did not properly restart when config was updated"
     false
   fi
 
   rm -f proxyTest.out
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_unix() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  OUTFILE="${TEST_DIR}/proxyTest.out"
+  HOST_SOCK="${TEST_DIR}/lxdtest-$(basename "${LXD_DIR}")-host.sock"
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy "listen=unix:${HOST_SOCK}" 
connect=unix:/tmp/"lxdtest-$(basename "${LXD_DIR}").sock" bind=host
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Restart the container
+  lxc restart -f proxyTester
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Change the socket
+  lxc config device set proxyTester proxyDev connect 
unix:/tmp/"lxdtest-$(basename "${LXD_DIR}")-2.sock"
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}")-2.sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}")-2.sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Cleanup
   lxc delete -f proxyTester
 }
 
+test_proxy_device_tcp_unix() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  HOST_TCP_PORT=$(local_tcp_port)
+  OUTFILE="${TEST_DIR}/proxyTest.out"
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy 
"listen=tcp:127.0.0.1:${HOST_TCP_PORT}" connect=unix:/tmp/"lxdtest-$(basename 
"${LXD_DIR}").sock" bind=host
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Restart the container
+  lxc restart -f proxyTester
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Change the socket
+  lxc config device set proxyTester proxyDev connect 
unix:/tmp/"lxdtest-$(basename "${LXD_DIR}")-2.sock"
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}")-2.sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}")-2.sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_unix_tcp() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  OUTFILE="${TEST_DIR}/proxyTest.out"
+  HOST_SOCK="${TEST_DIR}/lxdtest-$(basename "${LXD_DIR}")-host.sock"
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy "listen=unix:${HOST_SOCK}" 
connect=tcp:127.0.0.1:4321 bind=host
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Restart the container
+  lxc restart -f proxyTester
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Change the port
+  lxc config device set proxyTester proxyDev connect tcp:127.0.0.1:1337
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 1337 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Cleanup
+  lxc delete -f proxyTester
+}

From bd54d4c7f9a6e603f5af6d9350a75f3bcf41be12 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 15:28:49 +0200
Subject: [PATCH 11/13] proxy: udp

Closes #4566.

Signed-off-by: Christian Brauner <[email protected]>
---
 lxd/main_forkproxy.go     | 166 +++++++++++++++++++++++++++++++++++-----------
 lxd/proxy_device_utils.go |   4 +-
 2 files changed, 130 insertions(+), 40 deletions(-)

diff --git a/lxd/main_forkproxy.go b/lxd/main_forkproxy.go
index 167db12ef..50ff0508b 100644
--- a/lxd/main_forkproxy.go
+++ b/lxd/main_forkproxy.go
@@ -333,7 +333,16 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
                return err
        }
 
-       listener, err := net.FileListener(file)
+       var srcConn net.Conn
+       var listener net.Listener
+
+       udpFD := -1
+       if lAddr.connType == "udp" {
+               udpFD = int(file.Fd())
+               srcConn, err = net.FileConn(file)
+       } else {
+               listener, err = net.FileListener(file)
+       }
        if err != nil {
                fmt.Printf("Failed to re-assemble listener: %v", err)
                return err
@@ -348,7 +357,12 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
        go func() {
                <-sigs
                terminate = true
-               listener.Close()
+               if lAddr.connType == "udp" {
+                       srcConn.Close()
+               } else {
+                       listener.Close()
+               }
+               file.Close()
        }()
 
        connectAddr := args[3]
@@ -369,35 +383,65 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
                defer os.Remove(lAddr.addr)
        }
 
-       fmt.Printf("Starting to proxy\n")
+       fmt.Printf("Starting %s <-> %s proxy\n", lAddr.connType, cAddr.connType)
+       if lAddr.connType == "udp" {
+               for {
+                       ret, revents, err := shared.GetPollRevents(udpFD, -1, 
(shared.POLLIN | shared.POLLPRI | shared.POLLERR | shared.POLLHUP | 
shared.POLLRDHUP | shared.POLLNVAL))
+                       if ret < 0 {
+                               fmt.Printf("Failed to poll on file descriptor: 
%s\n", err)
+                               srcConn.Close()
+                               return err
+                       }
 
-       // begin proxying
-       for {
-               // Accept a new client
-               srcConn, err := listener.Accept()
-               if err != nil {
-                       if terminate {
-                               break
+                       if (revents & (shared.POLLERR | shared.POLLHUP | 
shared.POLLRDHUP | shared.POLLNVAL)) > 0 {
+                               err := fmt.Errorf("Invalid UDP socket file 
descriptor")
+                               fmt.Printf("%s\n", err)
+                               srcConn.Close()
+                               return err
                        }
 
-                       fmt.Printf("error: Failed to accept new connection: 
%v\n", err)
-                       continue
-               }
-               fmt.Printf("Accepted a new connection\n")
+                       // Connect to the target
+                       dstConn, err := getDestConn(connectAddr)
+                       if err != nil {
+                               fmt.Printf("error: Failed to connect to target: 
%v\n", err)
+                               srcConn.Close()
+                               return err
+                       }
 
-               // Connect to the target
-               dstConn, err := getDestConn(connectAddr)
-               if err != nil {
-                       fmt.Printf("error: Failed to connect to target: %v\n", 
err)
-                       srcConn.Close()
-                       continue
+                       genericRelay(srcConn, dstConn, false)
                }
+       } else {
+               // begin proxying
+               for {
+                       // Accept a new client
+                       srcConn, err = listener.Accept()
+                       if err != nil {
+                               if terminate {
+                                       break
+                               }
 
-               if cAddr.connType == "unix" && lAddr.connType == "unix" {
-                       // Handle OOB if both src and dst are using unix sockets
-                       go unixRelay(srcConn, dstConn)
-               } else {
-                       go genericRelay(srcConn, dstConn)
+                               fmt.Printf("error: Failed to accept new 
connection: %v\n", err)
+                               continue
+                       }
+                       fmt.Printf("Accepted a new connection\n")
+
+                       // Connect to the target
+                       dstConn, err := getDestConn(connectAddr)
+                       if err != nil {
+                               fmt.Printf("error: Failed to connect to target: 
%v\n", err)
+                               if lAddr.connType != "udp" {
+                                       srcConn.Close()
+                               }
+
+                               continue
+                       }
+
+                       if cAddr.connType == "unix" && lAddr.connType == "unix" 
{
+                               // Handle OOB if both src and dst are using 
unix sockets
+                               go unixRelay(srcConn, dstConn)
+                       } else {
+                               go genericRelay(srcConn, dstConn, true)
+                       }
                }
        }
 
@@ -406,23 +450,33 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args 
[]string) error {
        return nil
 }
 
-func genericRelay(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
-       relayer := func(dst io.Writer, src io.Reader, ch chan bool) {
-               io.Copy(eagain.Writer{Writer: dst}, eagain.Reader{Reader: src})
-               ch <- true
+func genericRelay(dst io.ReadWriteCloser, src io.ReadWriteCloser, closeDst 
bool) {
+       relayer := func(dst io.Writer, src io.Reader, ch chan error) {
+               _, err := io.Copy(eagain.Writer{Writer: dst}, 
eagain.Reader{Reader: src})
+               ch <- err
        }
 
-       chSend := make(chan bool)
+       chSend := make(chan error)
        go relayer(dst, src, chSend)
 
-       chRecv := make(chan bool)
+       chRecv := make(chan error)
        go relayer(src, dst, chRecv)
 
-       <-chSend
-       <-chRecv
+       errSnd := <-chSend
+       errRcv := <-chRecv
 
        src.Close()
-       dst.Close()
+       if closeDst {
+               dst.Close()
+       }
+
+       if errSnd != nil {
+               fmt.Printf("Error while sending data %s\n", errSnd)
+       }
+
+       if errRcv != nil {
+               fmt.Printf("Error while reading data %s\n", errRcv)
+       }
 }
 
 func unixRelayer(src *net.UnixConn, dst *net.UnixConn, ch chan bool) {
@@ -529,13 +583,49 @@ func tryListen(protocol string, addr string) 
(net.Listener, error) {
        return listener, nil
 }
 
-func getListenerFile(listenAddr string) (os.File, error) {
+func tryListenUDP(protocol string, addr string) (*os.File, error) {
+       var UDPConn *net.UDPConn
+       var err error
+
+       udpAddr, err := net.ResolveUDPAddr(protocol, addr)
+       if err != nil {
+               return nil, err
+       }
+
+       for i := 0; i < 10; i++ {
+               UDPConn, err = net.ListenUDP(protocol, udpAddr)
+               if err == nil {
+                       file, err := UDPConn.File()
+                       UDPConn.Close()
+                       return file, err
+               }
+
+               time.Sleep(500 * time.Millisecond)
+       }
+       if err != nil {
+               return nil, err
+       }
+
+       if UDPConn == nil {
+               return nil, fmt.Errorf("Failed to setup UDP listener")
+       }
+
+       file, err := UDPConn.File()
+       UDPConn.Close()
+       return file, err
+}
+
+func getListenerFile(listenAddr string) (*os.File, error) {
        fields := strings.SplitN(listenAddr, ":", 2)
        addr := strings.Join(fields[1:], "")
 
+       if fields[0] == "udp" {
+               return tryListenUDP(fields[0], addr)
+       }
+
        listener, err := tryListen(fields[0], addr)
        if err != nil {
-               return os.File{}, fmt.Errorf("Failed to listen on %s: %v", 
addr, err)
+               return nil, fmt.Errorf("Failed to listen on %s: %v", addr, err)
        }
 
        file := &os.File{}
@@ -548,10 +638,10 @@ func getListenerFile(listenAddr string) (os.File, error) {
                file, err = unixListener.File()
        }
        if err != nil {
-               return os.File{}, fmt.Errorf("Failed to get file from listener: 
%v", err)
+               return nil, fmt.Errorf("Failed to get file from listener: %v", 
err)
        }
 
-       return *file, nil
+       return file, nil
 }
 
 func getDestConn(connectAddr string) (net.Conn, error) {
diff --git a/lxd/proxy_device_utils.go b/lxd/proxy_device_utils.go
index 63d815dfd..995d0b42a 100644
--- a/lxd/proxy_device_utils.go
+++ b/lxd/proxy_device_utils.go
@@ -31,10 +31,10 @@ func setupProxyProcInfo(c container, device 
map[string]string) (*proxyProcInfo,
        connectionType := strings.SplitN(connectAddr, ":", 2)[0]
        listenerType := strings.SplitN(listenAddr, ":", 2)[0]
 
-       if !shared.StringInSlice(connectionType, []string{"tcp", "unix"}) {
+       if !shared.StringInSlice(connectionType, []string{"tcp", "udp", 
"unix"}) {
                return nil, fmt.Errorf("Proxy device doesn't support the 
connection type: %s", connectionType)
        }
-       if !shared.StringInSlice(listenerType, []string{"tcp", "unix"}) {
+       if !shared.StringInSlice(listenerType, []string{"tcp", "udp", "unix"}) {
                return nil, fmt.Errorf("Proxy device doesn't support the 
listener type: %s", listenerType)
        }
 

From ff8ee52dc0ebffb633655c17368fd067f47100d8 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 15:31:33 +0200
Subject: [PATCH 12/13] api: proxy_udp

Closes #4566.

Signed-off-by: Christian Brauner <[email protected]>
---
 doc/api-extensions.md | 15 +++++++++++++++
 doc/containers.md     |  5 +++++
 shared/version/api.go |  1 +
 3 files changed, 21 insertions(+)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index fe2e3f41e..f767b0186 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -498,3 +498,18 @@ Supported connections are now:
 * `UNIX <-> UNIX`
 * `TCP <-> UNIX`
 * `UNIX <-> TCP`
+
+## proxy_udp
+Add support for udp in proxy devices.
+
+Supported connections are now:
+
+* `TCP <-> TCP`
+* `UNIX <-> UNIX`
+* `TCP <-> UNIX`
+* `UNIX <-> TCP`
+* `UDP <-> UDP`
+* `UDP <-> TCP`
+* `TCP <-> UDP`
+* `UDP <-> UNIX`
+* `UNIX <-> UDP`
diff --git a/doc/containers.md b/doc/containers.md
index 2f716eaa3..26107e113 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -422,9 +422,14 @@ have an address in the container connect through the host.
 
 The supported connection types are:
 * `TCP <-> TCP`
+* `UDP <-> UDP`
 * `UNIX <-> UNIX`
 * `TCP <-> UNIX`
 * `UNIX <-> TCP`
+* `UDP <-> TCP`
+* `TCP <-> UDP`
+* `UDP <-> UNIX`
+* `UNIX <-> UDP`
 
 Key         | Type      | Default           | Required  | Description
 :--         | :--       | :--               | :--       | :--
diff --git a/shared/version/api.go b/shared/version/api.go
index e0208a277..b4a3e0e93 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -107,6 +107,7 @@ var APIExtensions = []string{
        "devlxd_images",
        "container_local_cross_pool_handling",
        "proxy_unix",
+       "proxy_udp",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From 0d198c6e076cd0dcf80ced1f5985372357f03907 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Tue, 12 Jun 2018 15:54:37 +0200
Subject: [PATCH 13/13] tests: add udp proxy tests

Closes #4566.

Signed-off-by: Christian Brauner <[email protected]>
---
 test/suites/proxy.sh | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 312 insertions(+)

diff --git a/test/suites/proxy.sh b/test/suites/proxy.sh
index 9685b2601..57f2684c5 100755
--- a/test/suites/proxy.sh
+++ b/test/suites/proxy.sh
@@ -1,8 +1,13 @@
 test_proxy_device() {
   test_proxy_device_tcp
+  test_proxy_device_udp
+  test_proxy_device_udp_unix
+  test_proxy_device_unix_udp
   test_proxy_device_unix
   test_proxy_device_tcp_unix
   test_proxy_device_unix_tcp
+  test_proxy_device_udp_tcp
+  test_proxy_device_tcp_udp
 }
 
 test_proxy_device_tcp() {
@@ -269,3 +274,310 @@ test_proxy_device_unix_tcp() {
   # Cleanup
   lxc delete -f proxyTester
 }
+
+test_proxy_device_udp() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  HOST_UDP_PORT=$(local_tcp_port)
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy 
"listen=udp:127.0.0.1:$HOST_UDP_PORT" connect=udp:127.0.0.1:4321 bind=host
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Restart the container
+  lxc restart -f proxyTester
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Change the port
+  lxc config device set proxyTester proxyDev connect udp:127.0.0.1:1337
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 1337 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_udp_unix() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  HOST_UDP_PORT=$(local_tcp_port)
+  OUTFILE="${TEST_DIR}/proxyTest.out"
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy 
"listen=udp:127.0.0.1:${HOST_UDP_PORT}" connect=unix:/tmp/"lxdtest-$(basename 
"${LXD_DIR}").sock" bind=host
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Restart the container
+  lxc restart -f proxyTester
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}").sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}").sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Change the socket
+  lxc config device set proxyTester proxyDev connect 
unix:/tmp/"lxdtest-$(basename "${LXD_DIR}")-2.sock"
+  (
+    cd "${LXD_DIR}/containers/proxyTester/rootfs/tmp/" || exit
+    umask 0000
+    rm -f "lxdtest-$(basename "${LXD_DIR}")-2.sock"
+    exec nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq 
.pid)" -- nc -l -U "lxdtest-$(basename "${LXD_DIR}")-2.sock" > "${OUTFILE}"
+  ) &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f "${OUTFILE}"
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_unix_udp() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  OUTFILE="${TEST_DIR}/proxyTest.out"
+  HOST_SOCK="${TEST_DIR}/lxdtest-$(basename "${LXD_DIR}")-host.sock"
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy "listen=unix:${HOST_SOCK}" 
connect=udp:127.0.0.1:4321 bind=host
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Restart the container
+  lxc restart -f proxyTester
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Change the port
+  lxc config device set proxyTester proxyDev connect udp:127.0.0.1:1337
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 1337 > "${OUTFILE}" &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -U "${HOST_SOCK#$(pwd)/}"
+
+  if [ "$(cat "${OUTFILE}")" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f "${OUTFILE}" "${HOST_SOCK}"
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_udp_tcp() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  HOST_UDP_PORT=$(local_tcp_port)
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy 
"listen=udp:127.0.0.1:$HOST_UDP_PORT" connect=tcp:127.0.0.1:4321 bind=host
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Restart the container
+  lxc restart -f proxyTester
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Change the port
+  lxc config device set proxyTester proxyDev connect tcp:127.0.0.1:1337
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -6 -l 1337 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 -u 127.0.0.1 "${HOST_UDP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
+
+test_proxy_device_tcp_udp() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  # Setup
+  MESSAGE="Proxy device test string"
+  HOST_TCP_PORT=$(local_tcp_port)
+  lxc launch testimage proxyTester
+
+  # Initial test
+  lxc config device add proxyTester proxyDev proxy 
"listen=tcp:127.0.0.1:$HOST_TCP_PORT" connect=udp:127.0.0.1:4321 bind=host
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly send data from host to container"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Restart the container
+  lxc restart -f proxyTester
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 4321 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart on container restart"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Change the port
+  lxc config device set proxyTester proxyDev connect udp:127.0.0.1:1337
+  nsenter -n -U -t "$(lxc query /1.0/containers/proxyTester/state | jq .pid)" 
-- nc -u -l 1337 > proxyTest.out &
+  sleep 2
+
+  echo "${MESSAGE}" | nc -w1 127.0.0.1 "${HOST_TCP_PORT}"
+
+  if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+    cat "${LXD_DIR}/logs/proxyTester/proxy.proxyDev.log"
+    echo "Proxy device did not properly restart when config was updated"
+    false
+  fi
+
+  rm -f proxyTest.out
+
+  # Cleanup
+  lxc delete -f proxyTester
+}
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to