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 <ian.campbell@docker.com>
This commit is contained in:
Ian Campbell 2016-07-01 11:04:17 +01:00
parent f70ff0aeac
commit b61451047d
2 changed files with 92 additions and 44 deletions

View File

@ -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"
}

View File

@ -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