[lxc-devel] [lxd/master] proxy: Support unix sockets

monstermunchkin on Github lxc-bot at linuxcontainers.org
Mon May 28 14:10:25 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 316 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180528/b31d88d5/attachment.bin>
-------------- next part --------------
From c849cb7d9e0d279359a09fe880ac567fa8755613 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 25 May 2018 19:53:23 +0200
Subject: [PATCH 1/2] proxy: Support unix sockets

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/main_forkproxy.go     | 39 ++++++++++++++++++++++++++++++++++-----
 lxd/proxy_device_utils.go | 15 +++++++++++++--
 2 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/lxd/main_forkproxy.go b/lxd/main_forkproxy.go
index 0c02199f9..89f62ce71 100644
--- a/lxd/main_forkproxy.go
+++ b/lxd/main_forkproxy.go
@@ -5,6 +5,7 @@ import (
 	"io"
 	"net"
 	"os"
+	"os/signal"
 	"strconv"
 	"strings"
 	"syscall"
@@ -188,9 +189,6 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 		defer file.Close()
 
 		listenerFd := file.Fd()
-		if err != nil {
-			return fmt.Errorf("Failed to duplicate the listener fd: %v", err)
-		}
 
 		newFd, err := syscall.Dup(int(listenerFd))
 		if err != nil {
@@ -215,7 +213,30 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("Failed to re-assemble listener: %v", err)
 	}
 
-	defer listener.Close()
+	// Handl SIGTERM which is sent when the proxy is to be removed
+	terminate := false
+	sigs := make(chan os.Signal, 1)
+	signal.Notify(sigs, syscall.SIGTERM)
+
+	// Wait for SIGTERM and close the listener in order to exit the loop below
+	go func() {
+		<-sigs
+		terminate = true
+		listener.Close()
+	}()
+
+	if strings.HasPrefix(connectAddr, "unix:") {
+		file, err := getListenerFile(connectAddr)
+		if err != nil {
+			return err
+		}
+
+		defer func() {
+			file.Close()
+			os.Remove(strings.TrimPrefix(listenAddr, "unix:"))
+			os.Remove(strings.TrimPrefix(connectAddr, "unix:"))
+		}()
+	}
 
 	fmt.Printf("Starting to proxy\n")
 
@@ -224,6 +245,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
 		}
@@ -240,6 +265,10 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 		go io.Copy(eagain.Writer{Writer: srcConn}, eagain.Reader{Reader: dstConn})
 		go io.Copy(eagain.Writer{Writer: dstConn}, eagain.Reader{Reader: srcConn})
 	}
+
+	fmt.Println("Stopping proxy")
+
+	return nil
 }
 
 func getListenerFile(listenAddr string) (os.File, error) {
@@ -248,7 +277,7 @@ func getListenerFile(listenAddr string) (os.File, error) {
 
 	listener, err := net.Listen(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{}
diff --git a/lxd/proxy_device_utils.go b/lxd/proxy_device_utils.go
index 1984062b5..bea9dabce 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)
 	}
 
@@ -53,6 +53,17 @@ func setupProxyProcInfo(c container, device map[string]string) (*proxyProcInfo,
 		return nil, fmt.Errorf("Invalid binding side given. Must be \"host\" or \"container\".")
 	}
 
+	if connectionType == "unix" {
+		fields := strings.SplitN(connectAddr, ":", 2)
+		if len(fields) == 2 {
+			// Prefix provided connectAddr with the container rootfs path
+			addr := filepath.Join(c.Path(), "rootfs", fields[1])
+			connectAddr = fmt.Sprintf("%s:%s", connectionType, addr)
+		} else {
+			return nil, fmt.Errorf("Missing connection address")
+		}
+	}
+
 	p := &proxyProcInfo{
 		listenPid:   listenPid,
 		connectPid:  connectPid,

From 9d6b866d5dcbe2114defc271c449f906f3c6a490 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Mon, 28 May 2018 16:05:47 +0200
Subject: [PATCH 2/2] proxy: Handle abstract sockets and OOB data

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/main_forkproxy.go     | 104 +++++++++++++++++++++++++++++++++++++++++++---
 lxd/proxy_device_utils.go |   9 ++--
 2 files changed, 105 insertions(+), 8 deletions(-)

diff --git a/lxd/main_forkproxy.go b/lxd/main_forkproxy.go
index 89f62ce71..1dbbb8ccc 100644
--- a/lxd/main_forkproxy.go
+++ b/lxd/main_forkproxy.go
@@ -129,6 +129,12 @@ 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{}
@@ -225,7 +231,11 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 		listener.Close()
 	}()
 
-	if strings.HasPrefix(connectAddr, "unix:") {
+	cAddr := parseAddr(connectAddr)
+	lAddr := parseAddr(listenAddr)
+
+	if cAddr.connType == "unix" && !cAddr.abstract {
+		// Create socket
 		file, err := getListenerFile(connectAddr)
 		if err != nil {
 			return err
@@ -233,11 +243,14 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 
 		defer func() {
 			file.Close()
-			os.Remove(strings.TrimPrefix(listenAddr, "unix:"))
-			os.Remove(strings.TrimPrefix(connectAddr, "unix:"))
+			os.Remove(cAddr.addr)
 		}()
 	}
 
+	if lAddr.connType == "unix" && !lAddr.abstract {
+		defer os.Remove(lAddr.addr)
+	}
+
 	fmt.Printf("Starting to proxy\n")
 
 	// begin proxying
@@ -262,8 +275,14 @@ 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 relay(srcConn.(*net.UnixConn), dstConn.(*net.UnixConn))
+			go relay(dstConn.(*net.UnixConn), srcConn.(*net.UnixConn))
+		} else {
+			go io.Copy(eagain.Writer{Writer: srcConn}, eagain.Reader{Reader: dstConn})
+			go io.Copy(eagain.Writer{Writer: dstConn}, eagain.Reader{Reader: srcConn})
+		}
 	}
 
 	fmt.Println("Stopping proxy")
@@ -302,3 +321,78 @@ func getDestConn(connectAddr string) (net.Conn, error) {
 	addr := strings.Join(fields[1:], "")
 	return net.Dial(fields[0], addr)
 }
+
+func relay(src *net.UnixConn, dst *net.UnixConn) {
+	for {
+		dataBuf := make([]byte, 4096)
+		oobBuf := make([]byte, 4096)
+
+		// Read from the source
+		sData, sOob, _, _, err := src.ReadMsgUnix(dataBuf, oobBuf)
+		if err != nil {
+			fmt.Printf("Disconnected during read: %v\n", err)
+			src.Close()
+			dst.Close()
+			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)
+				src.Close()
+				dst.Close()
+				return
+			}
+
+			for _, msg := range entries {
+				fds, err = syscall.ParseUnixRights(&msg)
+				if err != nil {
+					fmt.Printf("Failed to get fds list for control message: %v\n", err)
+					src.Close()
+					dst.Close()
+					return
+				}
+			}
+		}
+
+		// Send to the destination
+		tData, tOob, err := dst.WriteMsgUnix(dataBuf[:sData], oobBuf[:sOob], nil)
+		if err != nil {
+			fmt.Printf("Disconnected during write: %v\n", err)
+			src.Close()
+			dst.Close()
+			return
+		}
+
+		if sData != tData || sOob != tOob {
+			fmt.Printf("Some data got lost during transfer, disconnecting.")
+			src.Close()
+			dst.Close()
+			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)
+					src.Close()
+					dst.Close()
+					return
+				}
+			}
+		}
+	}
+}
+
+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/proxy_device_utils.go b/lxd/proxy_device_utils.go
index bea9dabce..c85f0b137 100644
--- a/lxd/proxy_device_utils.go
+++ b/lxd/proxy_device_utils.go
@@ -56,9 +56,12 @@ func setupProxyProcInfo(c container, device map[string]string) (*proxyProcInfo,
 	if connectionType == "unix" {
 		fields := strings.SplitN(connectAddr, ":", 2)
 		if len(fields) == 2 {
-			// Prefix provided connectAddr with the container rootfs path
-			addr := filepath.Join(c.Path(), "rootfs", fields[1])
-			connectAddr = fmt.Sprintf("%s:%s", connectionType, addr)
+			// Prefix provided connectAddr with the container rootfs path if
+			// it's not an abstract socket
+			if !strings.HasPrefix(fields[1], "@") {
+				addr := filepath.Join(c.Path(), "rootfs", fields[1])
+				connectAddr = fmt.Sprintf("%s:%s", connectionType, addr)
+			}
 		} else {
 			return nil, fmt.Errorf("Missing connection address")
 		}


More information about the lxc-devel mailing list