[lxc-devel] [lxd/master] [TESTING] proxy: udp support
brauner on Github
lxc-bot at linuxcontainers.org
Wed Jun 13 00:07:58 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180613/e007a64c/attachment.bin>
-------------- next part --------------
From 6adb2a803ef2a9575ee25bee5d96bff173608f8a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:04:09 +0200
Subject: [PATCH 01/13] memory: fix format string
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:04:50 +0200
Subject: [PATCH 02/13] exec: fix format string
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:05:45 +0200
Subject: [PATCH 03/13] images: fix format string
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:06:54 +0200
Subject: [PATCH 04/13] migrate: remove debug residuals
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:07:41 +0200
Subject: [PATCH 05/13] lvm: fix format string
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 13:08:15 +0200
Subject: [PATCH 06/13] db: fix format string
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 20:03:38 +0200
Subject: [PATCH 07/13] nsexec: prevent fd leak
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Fri, 25 May 2018 19:53:23 +0200
Subject: [PATCH 08/13] proxy: unix
Closes #4167.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
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 <thomas.hipp at canonical.com>
Date: Tue, 29 May 2018 20:04:25 +0200
Subject: [PATCH 09/13] api: proxy_unix
Closes #4167.
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
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 <thomas.hipp at canonical.com>
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 <thomas.hipp at canonical.com>
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 15:28:49 +0200
Subject: [PATCH 11/13] proxy: udp
Closes #4566.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
Date: Tue, 12 Jun 2018 15:31:33 +0200
Subject: [PATCH 12/13] api: proxy_udp
Closes #4566.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
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 <christian.brauner at ubuntu.com>
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 <christian.brauner at ubuntu.com>
---
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
+}
More information about the lxc-devel
mailing list