From b61451047d55994311706bba5036c7994e0f151b Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 1 Jul 2016 11:04:17 +0100 Subject: [PATCH] vsudd: Make incoming socket forwarding more generic Rather than hardcoding a single vsock<->docker.sock mapping allow arbitrary incoming connection forwarding between vsocks and unix domain sockets. The intention was to subsequently extend this further to support arbitrary forwarding of outgoing connections too and to use that to forward the syslog socket out to a vsock. This turned out not to be a good plan, partly since the syslog socket needs to be SOCK_DATAGRAM but vsocks only does SOCK_STREAM today (meaning we need some additional framing here) and partly because handling syslog forwarding in common code makes error logging in the common code somewhat trickier (logging syslog errors over syslog). So instead syslog will be handled as a special case in a following patch. However some vestiges of the original plan remain, e.g. the inForwards name and the net field in the forwards which could be unixgram but currently is only supporting unix(stream). In principal this patch could be dropped, but it adds some flexibility which might be useful in the future. Signed-off-by: Ian Campbell --- alpine/packages/vsudd/etc/init.d/vsudd | 8 +- alpine/packages/vsudd/main.go | 128 +++++++++++++++++-------- 2 files changed, 92 insertions(+), 44 deletions(-) diff --git a/alpine/packages/vsudd/etc/init.d/vsudd b/alpine/packages/vsudd/etc/init.d/vsudd index 686fc030c..c6439a9f2 100755 --- a/alpine/packages/vsudd/etc/init.d/vsudd +++ b/alpine/packages/vsudd/etc/init.d/vsudd @@ -9,12 +9,12 @@ depend() start() { - ebegin "Starting docker socket vsock passthrough" + ebegin "Starting vsock proxy" if [ -d /sys/bus/vmbus ]; then - PORT=23a432c2-537a-4291-bcb5-d62504644739 + DOCKER_PORT=23a432c2-537a-4291-bcb5-d62504644739 else - PORT=2376 + DOCKER_PORT=2376 fi [ -n "${PIDFILE}" ] || PIDFILE=/var/run/vsudd.pid @@ -25,7 +25,7 @@ start() --exec /sbin/vsudd \ --make-pidfile --pidfile ${PIDFILE} \ --stderr "${LOGFILE}" --stdout "${LOGFILE}" \ - -- -port "${PORT}" -sock /var/run/docker.sock + -- -inport "${DOCKER_PORT}:unix:/var/run/docker.sock" eend $? "Failed to start vsudd" } diff --git a/alpine/packages/vsudd/main.go b/alpine/packages/vsudd/main.go index 1565e2700..7d35eb211 100644 --- a/alpine/packages/vsudd/main.go +++ b/alpine/packages/vsudd/main.go @@ -2,12 +2,14 @@ package main import ( "flag" + "fmt" "io" "log" "net" "os" "strconv" "strings" + "sync" "syscall" "time" @@ -15,11 +17,18 @@ import ( "github.com/rneugeba/virtsock/go/vsock" ) +type forward struct { + vsock string + net string // "unix" or "unixgram" + usock string +} + +type forwards []forward + var ( - portstr string - sock string - detach bool - useHVsock bool + inForwards forwards + detach bool + useHVsock bool ) type vConn interface { @@ -28,9 +37,25 @@ type vConn interface { CloseWrite() error } +func (f *forwards) String() string { + return "Forwards" +} + +func (f *forwards) Set(value string) error { + s := strings.SplitN(value, ":", 3) + if len(s) != 3 { + return fmt.Errorf("Failed to parse: %s", value) + } + var newF forward + newF.vsock = s[0] + newF.net = s[1] + newF.usock = s[2] + *f = append(*f, newF) + return nil +} + func init() { - flag.StringVar(&portstr, "port", "2376", "vsock port to forward") - flag.StringVar(&sock, "sock", "/var/run/docker.sock", "path of the local Unix domain socket to forward to") + flag.Var(&inForwards, "inport", "incoming port to forward") flag.BoolVar(&detach, "detach", false, "detach from terminal") } @@ -54,46 +79,69 @@ func main() { syscall.Dup2(int(fd), int(os.Stderr.Fd())) } - var l net.Listener - if strings.Contains(portstr, "-") { - svcid, err := hvsock.GuidFromString(portstr) - if err != nil { - log.Fatalln("Failed to parse GUID", portstr, err) - } - l, err = hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid}) - if err != nil { - log.Fatalf("Failed to bind to hvsock port: %s", err) - } - log.Printf("Listening on ServiceId %s", svcid) - useHVsock = true - } else { - port, err := strconv.ParseUint(portstr, 10, 32) - if err != nil { - log.Fatalln("Can't convert %s to a uint.", portstr, err) - } - l, err = vsock.Listen(uint(port)) - if err != nil { - log.Fatalf("Failed to bind to vsock port %u: %s", port, err) - } - log.Printf("Listening on port %u", port) - useHVsock = false - } + var wg sync.WaitGroup connid := 0 - for { - connid++ - conn, err := l.Accept() - if err != nil { - log.Printf("Error accepting connection: %s", err) - return // no more listening - } - log.Printf("Connection %d from: %s\n", connid, conn.RemoteAddr()) - go handleOne(connid, conn.(vConn)) + for _, inF := range inForwards { + var portstr = inF.vsock + var network = inF.net + var usock = inF.usock + + var l net.Listener + + if network != "unix" { + log.Fatalf("cannot forward incoming port to %s:%s", network, usock) + } + + log.Printf("incoming port forward from %s to %s", portstr, usock) + + if strings.Contains(portstr, "-") { + svcid, err := hvsock.GuidFromString(portstr) + if err != nil { + log.Fatalln("Failed to parse GUID", portstr, err) + } + l, err = hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid}) + if err != nil { + log.Fatalf("Failed to bind to hvsock port: %s", err) + } + log.Printf("Listening on ServiceId %s", svcid) + useHVsock = true + } else { + port, err := strconv.ParseUint(portstr, 10, 32) + if err != nil { + log.Fatalln("Can't convert %s to a uint.", portstr, err) + } + l, err = vsock.Listen(uint(port)) + if err != nil { + log.Fatalf("Failed to bind to vsock port %d: %s", port, err) + } + log.Printf("Listening on port %s", portstr) + useHVsock = false + } + + wg.Add(1) + + go func() { + defer wg.Done() + for { + connid++ + conn, err := l.Accept() + if err != nil { + log.Printf("Error accepting connection: %s", err) + return // no more listening + } + log.Printf("Connection %d to: %s from: %s\n", connid, portstr, conn.RemoteAddr()) + + go handleOneIn(connid, conn.(vConn), usock) + } + }() } + + wg.Wait() } -func handleOne(connid int, conn vConn) { +func handleOneIn(connid int, conn vConn, sock string) { defer func() { if err := conn.Close(); err != nil { // On windows we get an EINVAL when the other end already closed