From 0324ad2da291c296de047321986178cdb5ea3029 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 6 Oct 2018 10:17:51 +0100 Subject: [PATCH 1/5] vpnkit: remove the old port forwarding shutdown protocol Before we multiplexed all port forwards over one connection, we had to layer our own custom shutdown protocol on top to work around Hyper-V socket bugs. This is not needed any more because the same work is done within the multiplexing protocol. Note this requires an updated vpnkit-forwarder. Signed-off-by: David Scott --- src/bin/connect.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/connect.ml b/src/bin/connect.ml index 320af7260..6d7cf729e 100644 --- a/src/bin/connect.ml +++ b/src/bin/connect.ml @@ -45,7 +45,7 @@ module Hvsock = struct (* Avoid using `detach` because we don't want to exhaust the thread pool since this will block the main TCP/IP stack. *) module F = - Hvsock_lwt.Flow_shutdown.Make(Host.Time) + Hvsock_lwt.Flow.Make(Host.Time) (Hvsock_lwt.In_main_thread.Make(Host.Main)) (Hvsock.Af_hyperv) From 630d36291b31106a0d05c6bd2e1372ec7be0573e Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 10 Mar 2019 10:17:49 +0000 Subject: [PATCH 2/5] go: revendor virtsock to latest master This removes the legacy shutdown protocol. Signed-off-by: David Scott --- go/Gopkg.lock | 5 +- go/Gopkg.toml | 2 +- .../linuxkit/virtsock/pkg/hvsock/hvsock.go | 436 +++--------------- .../virtsock/pkg/hvsock/hvsock_fallback.go | 73 +-- .../virtsock/pkg/hvsock/hvsock_linux.go | 308 ++++++------- .../virtsock/pkg/hvsock/hvsock_windows.go | 297 ++++++++---- .../virtsock/pkg/hvsock/zsyscall_windows.go | 29 +- .../linuxkit/virtsock/pkg/vsock/vsock.go | 12 +- .../virtsock/pkg/vsock/vsock_linux.go | 47 +- 9 files changed, 477 insertions(+), 732 deletions(-) diff --git a/go/Gopkg.lock b/go/Gopkg.lock index 5c611c4e4..2e2f076fb 100644 --- a/go/Gopkg.lock +++ b/go/Gopkg.lock @@ -214,15 +214,14 @@ version = "v1.0.1" [[projects]] - branch = "10586_fixes" - digest = "1:afcc93a5ffcbf5a73618d5f832fbf65a7d62f63763a1b31dccddf2390a7f11e4" + digest = "1:71f099e06b92187612832766c80a4bc4c8641b1a5cd262778c8b5716cb63fc4d" name = "github.com/linuxkit/virtsock" packages = [ "pkg/hvsock", "pkg/vsock", ] pruneopts = "NUT" - revision = "76c5cbe7912fc569ca7d3c02ddf9974ce670f7dd" + revision = "8e79449dea0735c1c056d814934dd035734cc97c" [[projects]] branch = "master" diff --git a/go/Gopkg.toml b/go/Gopkg.toml index 1471aa4c4..dcf142222 100644 --- a/go/Gopkg.toml +++ b/go/Gopkg.toml @@ -21,7 +21,7 @@ [[constraint]] name = "github.com/linuxkit/virtsock" - branch = "10586_fixes" + revision = "8e79449dea0735c1c056d814934dd035734cc97c" [[constraint]] name = "github.com/docker/go-p9p" diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go index dde48a8b5..f54ddda5f 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go @@ -1,43 +1,45 @@ +// Package hvsock provides a Go interface to Hyper-V sockets both on +// Windows and on Linux. The Linux bindings require patches for the +// 4.9.x kernel. If you are using a Linux kernel 4.14.x or newer you +// should use the vsock package instead as the Hyper-V socket support +// in these kernels have been merged with the virtio sockets +// implementation. package hvsock import ( - "errors" + "encoding/binary" "fmt" - "io" - "log" "net" - "sync" - "syscall" - - "encoding/binary" + "reflect" ) -// This package provides a Go interface to Hyper-V sockets both on -// Windows and on Linux (assuming the appropriate Linux kernel patches -// have been applied). -// -// Unfortunately, it is not easy/possible to extend the existing Go -// socket implementations with new Address Families, so this module -// wraps directly around system calls (and handles Windows' -// asynchronous system calls). -// -// There is an additional wrinkle. Hyper-V sockets in currently -// shipping versions of Windows don't support graceful and/or -// unidirectional shutdown(). So we turn a stream based protocol into -// message based protocol which allows to send in-line "messages" to -// the other end. We then provide a stream based interface on top of -// that. Yuk. -// -// The message interface is pretty simple. We first send a 32bit -// message containing the size of the data in the following -// message. Messages are limited to 'maxmsgsize'. Special message -// (without data), `shutdownrd` and 'shutdownwr' are used to used to -// signal a shutdown to the other end. +var ( + // GUIDZero used by listeners to accept connections from all partitions + GUIDZero, _ = GUIDFromString("00000000-0000-0000-0000-000000000000") + // GUIDWildcard used by listeners to accept connections from all partitions + GUIDWildcard, _ = GUIDFromString("00000000-0000-0000-0000-000000000000") + // GUIDBroadcast undocumented + GUIDBroadcast, _ = GUIDFromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") + // GUIDChildren used by listeners to accept connections from children + GUIDChildren, _ = GUIDFromString("90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd") + // GUIDLoopback use to connect in loopback mode + GUIDLoopback, _ = GUIDFromString("e0e16197-dd56-4a10-9195-5ee7a155a838") + // GUIDParent use to connect to the parent partition + GUIDParent, _ = GUIDFromString("a42e7cda-d03f-480c-9cc2-a4de20abb878") + + // GUIDs for LinuxVMs with the new Hyper-V socket implementation need to match this template + guidTemplate, _ = GUIDFromString("00000000-facb-11e6-bd58-64006a7986d3") +) -// On Windows 10 build 10586 larger maxMsgSize values work, but on -// newer builds it fails. It is unclear what the cause is... const ( - maxMsgSize = 4 * 1024 // Maximum message size + // The Hyper-V socket implementation used in the 4.9.x kernels + // seems to fail silently if messages are above 8k. The newer + // implementation in the 4.14.x (and newer) kernels seems to + // work fine with larger messages. This is constant is used as + // a temporary workaround to limit the amount of data sent and + // should be removed once support for 4.9.x kernels is + // deprecated. + maxMsgSize = 8 * 1024 ) // GUID is used by Hypper-V sockets for "addresses" and "ports" @@ -54,6 +56,18 @@ func (g *GUID) String() string { g[10], g[11], g[12], g[13], g[14], g[15]) } +// Port converts a Service GUID to a "port" usable by the vsock package. +// It can be used to convert hvsock code to vsock code. On 4.14.x +// kernels Service GUIDs for talking to Linux should have the form of +// xxxxxxxx-facb-11e6-bd58-64006a7986d3, where xxxxxxxx is the vsock port. +func (g *GUID) Port() (uint32, error) { + // Check that the GUID is as expected + if !reflect.DeepEqual(g[4:], guidTemplate[4:]) { + return 0, fmt.Errorf("%s does not conform with the template", g) + } + return binary.LittleEndian.Uint32(g[0:4]), nil +} + // GUIDFromString parses a string and returns a GUID func GUIDFromString(s string) (GUID, error) { var g GUID @@ -67,87 +81,24 @@ func GUIDFromString(s string) (GUID, error) { return g, err } -// HypervAddr combined "address" and "port" structure -type HypervAddr struct { +// Addr represents a Hyper-V socket address +type Addr struct { VMID GUID ServiceID GUID } // Network returns the type of network for Hyper-V sockets -func (a HypervAddr) Network() string { return "hvsock" } +func (a Addr) Network() string { + return "hvsock" +} -func (a HypervAddr) String() string { +func (a Addr) String() string { vmid := a.VMID.String() svc := a.ServiceID.String() return vmid + ":" + svc } -var ( - // Debug enables additional debug output - Debug = false - - // GUIDZero used by listeners to accept connections from all partitions - GUIDZero, _ = GUIDFromString("00000000-0000-0000-0000-000000000000") - // GUIDWildcard used by listeners to accept connections from all partitions - GUIDWildcard, _ = GUIDFromString("00000000-0000-0000-0000-000000000000") - // GUIDBroadcast undocumented - GUIDBroadcast, _ = GUIDFromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") - // GUIDChildren used by listeners to accept connections from children - GUIDChildren, _ = GUIDFromString("90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd") - // GUIDLoopback use to connect in loopback mode - GUIDLoopback, _ = GUIDFromString("e0e16197-dd56-4a10-9195-5ee7a155a838") - // GUIDParent use to connect to the parent partition - GUIDParent, _ = GUIDFromString("a42e7cda-d03f-480c-9cc2-a4de20abb878") -) - -// Dial a Hyper-V socket address -func Dial(raddr HypervAddr) (Conn, error) { - fd, err := hvsocket(syscall.SOCK_STREAM, sysSHV_PROTO_RAW) - if err != nil { - return nil, err - } - - err = connect(fd, &raddr) - if err != nil { - return nil, err - } - - v, err := newHVsockConn(fd, HypervAddr{VMID: GUIDZero, ServiceID: GUIDZero}, raddr) - if err != nil { - return nil, err - } - v.wrlock = &sync.Mutex{} - return v, nil -} - -// Listen on a Hyper-V socket address -func Listen(addr HypervAddr) (net.Listener, error) { - - acceptFD, err := hvsocket(syscall.SOCK_STREAM, sysSHV_PROTO_RAW) - if err != nil { - return nil, err - } - - err = bind(acceptFD, addr) - if err != nil { - return nil, err - } - - err = syscall.Listen(acceptFD, syscall.SOMAXCONN) - if err != nil { - return nil, err - } - - return &hvsockListener{acceptFD, addr}, nil -} - -const ( - shutdownrd = 0xdeadbeef // Message for CloseRead() - shutdownwr = 0xbeefdead // Message for CloseWrite() - closemsg = 0xdeaddead // Message for Close() -) - // Conn is a hvsock connection which supports half-close. type Conn interface { net.Conn @@ -155,285 +106,10 @@ type Conn interface { CloseWrite() error } -func (v *hvsockListener) Accept() (net.Conn, error) { - var raddr HypervAddr - fd, err := accept(v.acceptFD, &raddr) - if err != nil { - return nil, err - } - - a, err := newHVsockConn(fd, v.laddr, raddr) - if err != nil { - return nil, err - } - a.wrlock = &sync.Mutex{} - return a, nil -} - -func (v *hvsockListener) Close() error { - // Note this won't cause the Accept to unblock. - return syscall.Close(v.acceptFD) -} - -func (v *hvsockListener) Addr() net.Addr { - return HypervAddr{VMID: v.laddr.VMID, ServiceID: v.laddr.ServiceID} -} - -/* - * A wrapper around FileConn which supports CloseRead and CloseWrite - */ - -var ( - // ErrSocketClosed is returned when an operation is attempted on a socket which has been closed - ErrSocketClosed = errors.New("HvSocket has already been closed") - // ErrSocketWriteClosed is returned on a write when the socket has been closed for write - ErrSocketWriteClosed = errors.New("HvSocket has been closed for write") - // ErrSocketReadClosed is returned on a write when the socket has been closed for read - ErrSocketReadClosed = errors.New("HvSocket has been closed for read") - // ErrSocketMsgSize is returned a message has the wrong size - ErrSocketMsgSize = errors.New("HvSocket message was of wrong size") - // ErrSocketMsgWrite is returned when a message write failed - ErrSocketMsgWrite = errors.New("HvSocket writing message") - // ErrSocketNotEnoughData is returned when not all data could be written - ErrSocketNotEnoughData = errors.New("HvSocket not enough data written") - // ErrSocketUnImplemented is returned a function is not implemented - ErrSocketUnImplemented = errors.New("Function not implemented") -) - -// HVsockConn maintains the state of a Hyper-V socket connection -type HVsockConn struct { - hvsockConn - - wrlock *sync.Mutex - - writeClosed bool - readClosed bool - - bytesToRead int -} - -// LocalAddr returns the local address of the Hyper-V socket connection -func (v *HVsockConn) LocalAddr() net.Addr { - return v.local -} - -// RemoteAddr returns the remote address of the Hyper-V socket connection -func (v *HVsockConn) RemoteAddr() net.Addr { - return v.remote -} - -// Close closes a Hyper-V connection -func (v *HVsockConn) Close() error { - prDebug("Close\n") - - v.readClosed = true - v.writeClosed = true - - prDebug("TX: Close\n") - v.wrlock.Lock() - err := v.sendMsg(closemsg) - v.wrlock.Unlock() - if err != nil { - // chances are that the other end beat us to the close - prDebug("Mmmm. %s\n", err) - return v.close() - } - - // wait for reply/ignore errors - // we may get a EOF because the other end closed, - b := make([]byte, 4) - _, _ = v.read(b) - prDebug("close\n") - return v.close() -} - -// CloseRead closes a Hyper-V connection for reading -func (v *HVsockConn) CloseRead() error { - if v.readClosed { - return ErrSocketReadClosed - } - - prDebug("TX: Shutdown Read\n") - v.wrlock.Lock() - err := v.sendMsg(shutdownrd) - v.wrlock.Unlock() - if err != nil { - return err - } - - v.readClosed = true - return nil -} - -// CloseWrite closes a Hyper-V connection for writing -func (v *HVsockConn) CloseWrite() error { - if v.writeClosed { - return ErrSocketWriteClosed - } - - prDebug("TX: Shutdown Write\n") - v.wrlock.Lock() - err := v.sendMsg(shutdownwr) - v.wrlock.Unlock() - if err != nil { - return err - } - - v.writeClosed = true - return nil -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -// Read into buffer -// Also handles the inband control messages. -func (v *HVsockConn) Read(buf []byte) (int, error) { - if v.readClosed { - return 0, io.EOF - } - - if v.bytesToRead == 0 { - for { - // wait for next message - b := make([]byte, 4) - - n, err := v.read(b) - if err != nil { - return 0, err - } - - if n != 4 { - return n, ErrSocketMsgSize - } - - msg := int(binary.LittleEndian.Uint32(b)) - if msg == shutdownwr { - // The other end shutdown write. No point reading more - v.readClosed = true - prDebug("RX: ShutdownWrite\n") - return 0, io.EOF - } else if msg == shutdownrd { - // The other end shutdown read. No point writing more - v.writeClosed = true - prDebug("RX: ShutdownRead\n") - } else if msg == closemsg { - // Setting write close here forces a proper close - v.writeClosed = true - prDebug("RX: Close\n") - v.Close() - } else { - v.bytesToRead = msg - if v.bytesToRead == 0 { - // XXX Something is odd. If I don't have this here, this - // case is hit. However, with this code in place this - // case never get's hit. Suspect overly eager GC... - log.Printf("RX: Zero length %02x", b) - continue - } - break - } - } - } - - // If we get here, we know there is v.bytesToRead worth of - // data coming our way. Read it directly into to buffer passed - // in by the caller making sure we do not read mode than we - // should read by splicing the buffer. - toRead := min(len(buf), v.bytesToRead) - prDebug("READ: %d len=0x%x\n", int(v.fd), toRead) - n, err := v.read(buf[:toRead]) - if err != nil || n == 0 { - v.readClosed = true - return n, err - } - v.bytesToRead -= n - return n, nil -} - -// Write a buffer -func (v *HVsockConn) Write(buf []byte) (int, error) { - if v.writeClosed { - return 0, ErrSocketWriteClosed - } - - var err error - toWrite := len(buf) - written := 0 - - prDebug("WRITE: %d Total len=%x\n", int(v.fd), len(buf)) - - for toWrite > 0 { - if v.writeClosed { - return 0, ErrSocketWriteClosed - } - - // We write batches of MSG + data which need to be - // "atomic". We don't want to hold the lock for the - // entire Write() in case some other threads wants to - // send OOB data, e.g. for closing. - - v.wrlock.Lock() - - thisBatch := min(toWrite, maxMsgSize) - prDebug("WRITE: %d len=%x\n", int(v.fd), thisBatch) - // Write message header - err = v.sendMsg(uint32(thisBatch)) - if err != nil { - prDebug("Write MSG Error: %s\n", err) - goto ErrOut - } - - // Write data - n, err := v.write(buf[written : written+thisBatch]) - if err != nil { - prDebug("Write Error 3\n") - goto ErrOut - } - if n != thisBatch { - prDebug("Write Error 4\n") - err = ErrSocketNotEnoughData - goto ErrOut - } - toWrite -= n - written += n - v.wrlock.Unlock() - } - - return written, nil - -ErrOut: - v.wrlock.Unlock() - v.writeClosed = true - return 0, err -} - -// hvsockConn, SetDeadline(), SetReadDeadline(), and -// SetWriteDeadline() are OS specific. - -// Send a message to the other end -// The Lock must be held to call this functions -func (v *HVsockConn) sendMsg(msg uint32) error { - b := make([]byte, 4) - - binary.LittleEndian.PutUint32(b, msg) - n, err := v.write(b) - if err != nil { - prDebug("Write Error 1\n") - return err - } - if n != len(b) { - return ErrSocketMsgWrite - } - return nil -} - -func prDebug(format string, args ...interface{}) { - if Debug { - log.Printf(format, args...) +// Since there doesn't seem to be a standard min function +func min(x, y int) int { + if x < y { + return x } + return y } diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go index 0c06204dd..050419bd3 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go @@ -3,73 +3,20 @@ package hvsock import ( - "errors" - "time" + "fmt" + "net" + "runtime" ) -const ( - sysAF_HYPERV = 42 - sysSHV_PROTO_RAW = 1 -) - -type hvsockListener struct { - acceptFD int - laddr HypervAddr -} - -// -// System call wrapper -// -func hvsocket(typ, proto int) (int, error) { - return 0, errors.New("hvsocket() not implemented") -} - -func connect(s int, a *HypervAddr) (err error) { - return errors.New("connect() not implemented") -} - -func bind(s int, a HypervAddr) error { - return errors.New("bind() not implemented") -} - -func accept(s int, a *HypervAddr) (int, error) { - return 0, errors.New("accept() not implemented") -} - -type hvsockConn struct { - fd int - local HypervAddr - remote HypervAddr -} - -func newHVsockConn(fd int, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { - v := &hvsockConn{local: local, remote: remote} - return &HVsockConn{hvsockConn: *v}, errors.New("newHVsockConn() not implemented") -} - -func (v *HVsockConn) close() error { - return errors.New("close() not implemented") -} - -func (v *HVsockConn) read(buf []byte) (int, error) { - return 0, errors.New("read() not implemented") -} - -func (v *HVsockConn) write(buf []byte) (int, error) { - return 0, errors.New("write() not implemented") -} - -// SetReadDeadline dummy doc to silence lint -func (v *HVsockConn) SetReadDeadline(t time.Time) error { - return nil // FIXME +// Supported returns if hvsocks are supported on your platform +func Supported() bool { + return false } -// SetWriteDeadline dummy doc to silence lint -func (v *HVsockConn) SetWriteDeadline(t time.Time) error { - return nil // FIXME +func Dial(raddr Addr) (Conn, error) { + return nil, fmt.Errorf("Dial() not implemented on %s", runtime.GOOS) } -// SetDeadline dummy doc to silence lint -func (v *HVsockConn) SetDeadline(t time.Time) error { - return nil // FIXME +func Listen(addr Addr) (net.Listener, error) { + return nil, fmt.Errorf("Listen() not implemented on %s", runtime.GOOS) } diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go index bc8295e85..8a9983eac 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go @@ -32,229 +32,227 @@ int connect_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) { int accept_hv(int fd, struct sockaddr_hv *sa_hv, socklen_t *sa_hv_len) { return accept4(fd, (struct sockaddr *)sa_hv, sa_hv_len, 0); } - -struct sockaddr_vsock { - sa_family_t svm_family; - unsigned short svm_reserved1; - unsigned int svm_port; - unsigned int svm_cid; - unsigned char svm_zero[sizeof(struct sockaddr) - - sizeof(sa_family_t) - sizeof(unsigned short) - - sizeof(unsigned int) - sizeof(unsigned int)]; -}; -int bind_sockaddr_vsock(int fd, const struct sockaddr_vsock *sa_vsock) { - return bind(fd, (const struct sockaddr*)sa_vsock, sizeof(*sa_vsock)); -} -int connect_sockaddr_vsock(int fd, const struct sockaddr_vsock *sa_vsock) { - return connect(fd, (const struct sockaddr*)sa_vsock, sizeof(*sa_vsock)); -} -int accept_vsock(int fd, struct sockaddr_vsock *sa_vsock, socklen_t *sa_vsock_len) { - return accept4(fd, (struct sockaddr *)sa_vsock, sa_vsock_len, 0); -} */ import "C" import ( - "encoding/binary" - "errors" "fmt" + "net" "os" "syscall" "time" - "github.com/linuxkit/virtsock/pkg/vsock" -) - -var ( - legacyMode bool + "github.com/pkg/errors" + "golang.org/x/sys/unix" ) const ( - sysAF_HYPERV = 43 - sysAF_VSOCK = 40 - sysSHV_PROTO_RAW = 1 - - guidTmpl = "00000000-facb-11e6-bd58-64006a7986d3" + hvsockAF = 43 //SHV_PROTO_RAW + hvsockRaw = 1 // SHV_PROTO_RAW ) -type hvsockListener struct { - acceptFD int - laddr HypervAddr -} - -// Try to determine if we need to run in legacy mode. -// 4.11 defines AF_SMC as 43 but it doesn't support protocol 1 so the -// socket() call should fail. -func init() { - fd, err := syscall.Socket(sysAF_HYPERV, syscall.SOCK_STREAM, sysSHV_PROTO_RAW) +// Supported returns if hvsocks are supported on your platform +func Supported() bool { + // Try opening a hvsockAF socket. If it works we are on older, i.e. 4.9.x kernels. + // 4.11 defines AF_SMC as 43 but it doesn't support protocol 1 so the + // socket() call should fail. + fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw) if err != nil { - legacyMode = false - } else { - legacyMode = true - syscall.Close(fd) + return false } + syscall.Close(fd) + return true } -// -// System call wrapper -// -func hvsocket(typ, proto int) (int, error) { - if legacyMode { - return syscall.Socket(sysAF_HYPERV, typ, proto) +// Dial a Hyper-V socket address +func Dial(raddr Addr) (Conn, error) { + fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw) + if err != nil { + return nil, err } - return syscall.Socket(sysAF_VSOCK, typ, 0) -} -func connect(s int, a *HypervAddr) (err error) { - if legacyMode { - sa := C.struct_sockaddr_hv{} - sa.shv_family = sysAF_HYPERV - sa.reserved = 0 + sa := C.struct_sockaddr_hv{} + sa.shv_family = hvsockAF + sa.reserved = 0 - for i := 0; i < 16; i++ { - sa.shv_vm_id[i] = C.uchar(a.VMID[i]) - } - for i := 0; i < 16; i++ { - sa.shv_service_id[i] = C.uchar(a.ServiceID[i]) - } + for i := 0; i < 16; i++ { + sa.shv_vm_id[i] = C.uchar(raddr.VMID[i]) + } + for i := 0; i < 16; i++ { + sa.shv_service_id[i] = C.uchar(raddr.ServiceID[i]) + } - if ret, errno := C.connect_sockaddr_hv(C.int(s), &sa); ret != 0 { - return errors.New(fmt.Sprintf( - "connect(%s:%s) returned %d, errno %d: %s", - a.VMID, a.ServiceID, ret, errno, errno)) + // Retry connect in a loop if EINTR is encountered. + for { + if ret, errno := C.connect_sockaddr_hv(C.int(fd), &sa); ret != 0 { + if errno == syscall.EINTR { + continue + } + return nil, fmt.Errorf("connect(%s) failed with %d, errno=%d", raddr, ret, errno) } - return nil + break } - sa := C.struct_sockaddr_vsock{} - sa.svm_family = sysAF_VSOCK - sa.svm_port = C.uint(binary.LittleEndian.Uint32(a.ServiceID[0:4])) - // Ignore what's passed in. Use CIDAny as this is an accepted value - sa.svm_cid = C.uint(vsock.CIDAny) - - if ret, errno := C.connect_sockaddr_vsock(C.int(s), &sa); ret != 0 { - return errors.New(fmt.Sprintf( - "connect(%08x.%08x) returned %d, errno %d: %s", - sa.svm_cid, sa.svm_port, ret, errno, errno)) - } - return nil + return newHVsockConn(uintptr(fd), &Addr{VMID: GUIDZero, ServiceID: GUIDZero}, &raddr), nil } -func bind(s int, a HypervAddr) error { - if legacyMode { - sa := C.struct_sockaddr_hv{} - sa.shv_family = sysAF_HYPERV - sa.reserved = 0 +// Listen returns a net.Listener which can accept connections on the given port +func Listen(addr Addr) (net.Listener, error) { + fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw) + if err != nil { + return nil, err + } - for i := 0; i < 16; i++ { - sa.shv_vm_id[i] = C.uchar(GUIDZero[i]) - } - for i := 0; i < 16; i++ { - sa.shv_service_id[i] = C.uchar(a.ServiceID[i]) - } + sa := C.struct_sockaddr_hv{} + sa.shv_family = hvsockAF + sa.reserved = 0 - if ret, errno := C.bind_sockaddr_hv(C.int(s), &sa); ret != 0 { - return errors.New(fmt.Sprintf( - "bind(%s:%s) returned %d, errno %d: %s", - GUIDZero, a.ServiceID, ret, errno, errno)) - } - return nil + for i := 0; i < 16; i++ { + sa.shv_vm_id[i] = C.uchar(addr.VMID[i]) + } + for i := 0; i < 16; i++ { + sa.shv_service_id[i] = C.uchar(addr.ServiceID[i]) } - sa := C.struct_sockaddr_vsock{} - sa.svm_family = sysAF_VSOCK - sa.svm_port = C.uint(binary.LittleEndian.Uint32(a.ServiceID[0:4])) - // Ignore what's passed in. Use CIDAny as this is the only accepted value - sa.svm_cid = C.uint(vsock.CIDAny) + if ret, errno := C.bind_sockaddr_hv(C.int(fd), &sa); ret != 0 { + return nil, fmt.Errorf("listen(%s) failed with %d, errno=%d", addr, ret, errno) + } - if ret, errno := C.bind_sockaddr_vsock(C.int(s), &sa); ret != 0 { - return errors.New(fmt.Sprintf( - "connect(%08x.%08x) returned %d, errno %d: %s", - sa.svm_cid, sa.svm_port, ret, errno, errno)) + err = syscall.Listen(fd, syscall.SOMAXCONN) + if err != nil { + return nil, errors.Wrapf(err, "listen(%s) failed", addr) } - return nil + return &hvsockListener{fd, addr}, nil } -func accept(s int, a *HypervAddr) (int, error) { - if legacyMode { - var acceptSA C.struct_sockaddr_hv - var acceptSALen C.socklen_t - - acceptSALen = C.sizeof_struct_sockaddr_hv - fd, err := C.accept_hv(C.int(s), &acceptSA, &acceptSALen) - if err != nil { - return -1, err - } - - a.VMID = guidFromC(acceptSA.shv_vm_id) - a.ServiceID = guidFromC(acceptSA.shv_service_id) +// +// Hyper-v sockets Listener implementation +// - return int(fd), nil - } +type hvsockListener struct { + fd int + local Addr +} - var acceptSA C.struct_sockaddr_vsock +// Accept accepts an incoming call and returns the new connection. +func (v *hvsockListener) Accept() (net.Conn, error) { + var acceptSA C.struct_sockaddr_hv var acceptSALen C.socklen_t - acceptSALen = C.sizeof_struct_sockaddr_vsock - fd, err := C.accept_vsock(C.int(s), &acceptSA, &acceptSALen) + acceptSALen = C.sizeof_struct_sockaddr_hv + fd, err := C.accept_hv(C.int(v.fd), &acceptSA, &acceptSALen) if err != nil { - return -1, err + return nil, errors.Wrapf(err, "accept(%s) failed", v.local) } - a.VMID = GUIDParent - a.ServiceID, _ = GUIDFromString(guidTmpl) - tmp := make([]byte, 4) - binary.LittleEndian.PutUint32(tmp, uint32(acceptSA.svm_port)) - a.ServiceID[0] = tmp[0] - a.ServiceID[1] = tmp[1] - a.ServiceID[2] = tmp[2] - a.ServiceID[3] = tmp[3] - return int(fd), nil + remote := &Addr{VMID: guidFromC(acceptSA.shv_vm_id), ServiceID: guidFromC(acceptSA.shv_service_id)} + return newHVsockConn(uintptr(fd), &v.local, remote), nil +} + +// Close closes the listening connection +func (v *hvsockListener) Close() error { + // Note this won't cause the Accept to unblock. + return unix.Close(v.fd) } -// Internal representation. Complex mostly due to asynch send()/recv() syscalls. +// Addr returns the address the Listener is listening on +func (v *hvsockListener) Addr() net.Addr { + return v.local +} + +// +// Hyper-V socket connection implementation +// + +// hvsockConn represents a connection over a Hyper-V socket type hvsockConn struct { - fd int hvsock *os.File - local HypervAddr - remote HypervAddr + fd uintptr + local *Addr + remote *Addr +} + +func newHVsockConn(fd uintptr, local, remote *Addr) *hvsockConn { + hvsock := os.NewFile(fd, fmt.Sprintf("hvsock:%d", fd)) + return &hvsockConn{hvsock: hvsock, fd: fd, local: local, remote: remote} } -// Main constructor -func newHVsockConn(fd int, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { - hvsock := os.NewFile(uintptr(fd), fmt.Sprintf("hvsock:%d", fd)) - v := &hvsockConn{fd: fd, hvsock: hvsock, local: local, remote: remote} +// LocalAddr returns the local address of a connection +func (v *hvsockConn) LocalAddr() net.Addr { + return v.local +} - return &HVsockConn{hvsockConn: *v}, nil +// RemoteAddr returns the remote address of a connection +func (v *hvsockConn) RemoteAddr() net.Addr { + return v.remote } -func (v *HVsockConn) close() error { +// Close closes the connection +func (v *hvsockConn) Close() error { return v.hvsock.Close() } -func (v *HVsockConn) read(buf []byte) (int, error) { +// CloseRead shuts down the reading side of a hvsock connection +func (v *hvsockConn) CloseRead() error { + return syscall.Shutdown(int(v.fd), syscall.SHUT_RD) +} + +// CloseWrite shuts down the writing side of a hvsock connection +func (v *hvsockConn) CloseWrite() error { + return syscall.Shutdown(int(v.fd), syscall.SHUT_WR) +} + +// Read reads data from the connection +func (v *hvsockConn) Read(buf []byte) (int, error) { return v.hvsock.Read(buf) } -func (v *HVsockConn) write(buf []byte) (int, error) { - return v.hvsock.Write(buf) +// Write writes data over the connection +// TODO(rn): replace with a straight call to v.hvsock.Write() once 4.9.x support is deprecated +func (v *hvsockConn) Write(buf []byte) (int, error) { + written := 0 + toWrite := len(buf) + for toWrite > 0 { + thisBatch := min(toWrite, maxMsgSize) + n, err := v.hvsock.Write(buf[written : written+thisBatch]) + if err != nil { + return written, err + } + if n != thisBatch { + return written, fmt.Errorf("short write %d != %d", n, thisBatch) + } + toWrite -= n + written += n + } + + return written, nil } -// SetReadDeadline is un-implemented -func (v *HVsockConn) SetReadDeadline(t time.Time) error { +// SetDeadline sets the read and write deadlines associated with the connection +func (v *hvsockConn) SetDeadline(t time.Time) error { return nil // FIXME } -// SetWriteDeadline is un-implemented -func (v *HVsockConn) SetWriteDeadline(t time.Time) error { +// SetReadDeadline sets the deadline for future Read calls. +func (v *hvsockConn) SetReadDeadline(t time.Time) error { return nil // FIXME } -// SetDeadline is un-implemented -func (v *HVsockConn) SetDeadline(t time.Time) error { +// SetWriteDeadline sets the deadline for future Write calls +func (v *hvsockConn) SetWriteDeadline(t time.Time) error { return nil // FIXME } +// File duplicates the underlying socket descriptor and returns it. +func (v *hvsockConn) File() (*os.File, error) { + // This is equivalent to dup(2) but creates the new fd with CLOEXEC already set. + r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(v.hvsock.Fd()), syscall.F_DUPFD_CLOEXEC, 0) + if e1 != 0 { + return nil, os.NewSyscallError("fcntl", e1) + } + return os.NewFile(r0, v.hvsock.Name()), nil +} + func guidFromC(cg [16]C.uchar) GUID { var g GUID for i := 0; i < 16; i++ { diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go index 09a71b590..f21e9a315 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go @@ -1,15 +1,18 @@ package hvsock import ( - "errors" + "fmt" "io" "log" + "net" "runtime" "sync" "sync/atomic" "syscall" "time" "unsafe" + + "github.com/pkg/errors" ) // Make sure Winsock2 is initialised @@ -21,53 +24,124 @@ func init() { } const ( - sysAF_HYPERV = 34 - sysSHV_PROTO_RAW = 1 - - socket_error = uintptr(^uint32(0)) + hvsockAF = 34 // AF_HYPERV + hvsockRaw = 1 // SHV_PROTO_RAW ) var ( + // ErrTimeout is an error returned on timeout ErrTimeout = &timeoutError{} wsaData syscall.WSAData ) -// struck sockaddr equivalent -type rawSockaddrHyperv struct { - Family uint16 - Reserved uint16 - VMID GUID - ServiceID GUID +// Supported returns if hvsocks are supported on your platform +func Supported() bool { + return true +} + +// Dial a Hyper-V socket address +func Dial(raddr Addr) (Conn, error) { + fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw) + if err != nil { + return nil, err + } + + var sa rawSockaddrHyperv + ptr, n, err := raddr.sockaddr(&sa) + if err != nil { + return nil, err + } + + if err := sys_connect(fd, ptr, n); err != nil { + return nil, errors.Wrapf(err, "connect(%s) failed", raddr) + } + + return newHVsockConn(fd, Addr{VMID: GUIDZero, ServiceID: GUIDZero}, raddr) +} + +// Listen returns a net.Listener which can accept connections on the given port +func Listen(addr Addr) (net.Listener, error) { + fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw) + if err != nil { + return nil, err + } + + var sa rawSockaddrHyperv + ptr, n, err := addr.sockaddr(&sa) + if err != nil { + return nil, err + } + if err := sys_bind(fd, ptr, n); err != nil { + return nil, fmt.Errorf("bind(%s) failed with %v", addr, err) + } + + err = syscall.Listen(fd, syscall.SOMAXCONN) + if err != nil { + return nil, errors.Wrapf(err, "listen(%s) failed", addr) + } + + return &hvsockListener{fd, addr}, nil } +// +// Hyper-v sockets Listener implementation +// + type hvsockListener struct { - acceptFD syscall.Handle - laddr HypervAddr + fd syscall.Handle + local Addr +} + +// Accept accepts an incoming call and returns the new connection +func (v *hvsockListener) Accept() (net.Conn, error) { + var sa rawSockaddrHyperv + var n = int32(unsafe.Sizeof(sa)) + fd, err := sys_accept(v.fd, &sa, &n) + if err != nil { + return nil, err + } + + // Extract an Addr from sa + raddr := Addr{} + for i := 0; i < len(raddr.VMID); i++ { + raddr.VMID[i] = sa.VMID[i] + } + for i := 0; i < len(raddr.ServiceID); i++ { + raddr.ServiceID[i] = sa.ServiceID[i] + } + return newHVsockConn(fd, v.local, raddr) +} + +// Close closes the listening connection +func (v *hvsockListener) Close() error { + return syscall.Close(v.fd) +} + +// Addr returns the address the Listener is listening on +func (v *hvsockListener) Addr() net.Addr { + return v.local } -// Internal representation. Complex mostly due to asynch send()/recv() syscalls. +// +// Hyper-V socket connection implementation +// + +// hvsockConn represent a Hyper-V connection. Complex mostly due to asynch send()/recv() syscalls. type hvsockConn struct { fd syscall.Handle - local HypervAddr - remote HypervAddr + local Addr + remote Addr + + wg sync.WaitGroup + wgLock sync.RWMutex + closing atomicBool - wg sync.WaitGroup - closing bool readDeadline deadlineHandler writeDeadline deadlineHandler } -type deadlineHandler struct { - setLock sync.Mutex - channel timeoutChan - channelLock sync.RWMutex - timer *time.Timer - timedout atomicBool -} - -// Main constructor -func newHVsockConn(h syscall.Handle, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { +func newHVsockConn(h syscall.Handle, local Addr, remote Addr) (*hvsockConn, error) { ioInitOnce.Do(initIo) v := &hvsockConn{fd: h, local: local, remote: remote} @@ -83,62 +157,37 @@ func newHVsockConn(h syscall.Handle, local HypervAddr, remote HypervAddr) (*HVso v.readDeadline.channel = make(timeoutChan) v.writeDeadline.channel = make(timeoutChan) - return &HVsockConn{hvsockConn: *v}, nil + return v, nil } -// Utility function to build a struct sockaddr for syscalls. -func (a HypervAddr) sockaddr(sa *rawSockaddrHyperv) (unsafe.Pointer, int32, error) { - sa.Family = sysAF_HYPERV - sa.Reserved = 0 - for i := 0; i < len(sa.VMID); i++ { - sa.VMID[i] = a.VMID[i] - } - for i := 0; i < len(sa.ServiceID); i++ { - sa.ServiceID[i] = a.ServiceID[i] - } - - return unsafe.Pointer(sa), int32(unsafe.Sizeof(*sa)), nil +// LocalAddr returns the local address of a connection +func (v *hvsockConn) LocalAddr() net.Addr { + return v.local } -func hvsocket(typ, proto int) (syscall.Handle, error) { - return syscall.Socket(sysAF_HYPERV, typ, proto) +// RemoteAddr returns the remote address of a connection +func (v *hvsockConn) RemoteAddr() net.Addr { + return v.remote } -func connect(s syscall.Handle, a *HypervAddr) (err error) { - var sa rawSockaddrHyperv - ptr, n, err := a.sockaddr(&sa) - if err != nil { - return err - } - - return sys_connect(s, ptr, n) -} - -func bind(s syscall.Handle, a HypervAddr) error { - var sa rawSockaddrHyperv - ptr, n, err := a.sockaddr(&sa) - if err != nil { - return err - } - - return sys_bind(s, ptr, n) +// Close closes the connection +func (v *hvsockConn) Close() error { + v.close() + return nil } -func accept(s syscall.Handle, a *HypervAddr) (syscall.Handle, error) { - return 0, errors.New("accept(): Unimplemented") +// CloseRead shuts down the reading side of a hvsock connection +func (v *hvsockConn) CloseRead() error { + return syscall.Shutdown(v.fd, syscall.SHUT_RD) } -// -// File IO/Socket interface -// -func (v *HVsockConn) close() error { - v.closeHandle() - - return nil +// CloseWrite shuts down the writing side of a hvsock connection +func (v *hvsockConn) CloseWrite() error { + return syscall.Shutdown(v.fd, syscall.SHUT_WR) } -// Underlying raw read() function. -func (v *HVsockConn) read(buf []byte) (int, error) { +// Read reads data from the connection +func (v *hvsockConn) Read(buf []byte) (int, error) { var b syscall.WSABuf var f uint32 @@ -149,6 +198,7 @@ func (v *HVsockConn) read(buf []byte) (int, error) { if err != nil { return 0, err } + defer v.wg.Done() if v.readDeadline.timedout.isSet() { return 0, ErrTimeout @@ -169,8 +219,28 @@ func (v *HVsockConn) read(buf []byte) (int, error) { } } -// Underlying raw write() function. -func (v *HVsockConn) write(buf []byte) (int, error) { +// Write writes data over the connection +// TODO(rn): Remove once 4.9.x support is deprecated +func (v *hvsockConn) Write(buf []byte) (int, error) { + written := 0 + toWrite := len(buf) + for toWrite > 0 { + thisBatch := min(toWrite, maxMsgSize) + n, err := v.write(buf[written : written+thisBatch]) + if err != nil { + return written, err + } + if n != thisBatch { + return written, fmt.Errorf("short write %d != %d", n, thisBatch) + } + toWrite -= n + written += n + } + + return written, nil +} + +func (v *hvsockConn) write(buf []byte) (int, error) { var b syscall.WSABuf var f uint32 @@ -186,6 +256,8 @@ func (v *HVsockConn) write(buf []byte) (int, error) { if err != nil { return 0, err } + defer v.wg.Done() + if v.writeDeadline.timedout.isSet() { return 0, ErrTimeout } @@ -198,23 +270,56 @@ func (v *HVsockConn) write(buf []byte) (int, error) { } // SetReadDeadline implementation for Hyper-V sockets -func (v *HVsockConn) SetReadDeadline(deadline time.Time) error { +func (v *hvsockConn) SetReadDeadline(deadline time.Time) error { return v.readDeadline.set(deadline) } // SetWriteDeadline implementation for Hyper-V sockets -func (v *HVsockConn) SetWriteDeadline(deadline time.Time) error { +func (v *hvsockConn) SetWriteDeadline(deadline time.Time) error { return v.writeDeadline.set(deadline) } // SetDeadline implementation for Hyper-V sockets -func (v *HVsockConn) SetDeadline(deadline time.Time) error { +func (v *hvsockConn) SetDeadline(deadline time.Time) error { if err := v.SetReadDeadline(deadline); err != nil { return err } return v.SetWriteDeadline(deadline) } +// Helper functions for conversion to sockaddr + +// struck sockaddr equivalent +type rawSockaddrHyperv struct { + Family uint16 + Reserved uint16 + VMID GUID + ServiceID GUID +} + +// Utility function to build a struct sockaddr for syscalls. +func (a Addr) sockaddr(sa *rawSockaddrHyperv) (unsafe.Pointer, int32, error) { + sa.Family = hvsockAF + sa.Reserved = 0 + for i := 0; i < len(sa.VMID); i++ { + sa.VMID[i] = a.VMID[i] + } + for i := 0; i < len(sa.ServiceID); i++ { + sa.ServiceID[i] = a.ServiceID[i] + } + + return unsafe.Pointer(sa), int32(unsafe.Sizeof(*sa)), nil +} + +// Help for read/write timeouts +type deadlineHandler struct { + setLock sync.Mutex + channel timeoutChan + channelLock sync.RWMutex + timer *time.Timer + timedout atomicBool +} + // The code below here is adjusted from: // https://github.com/Microsoft/go-winio/blob/master/file.go type atomicBool int32 @@ -222,11 +327,13 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } - -const ( - cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 - cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 -) +func (b *atomicBool) swap(new bool) bool { + var newInt int32 + if new { + newInt = 1 + } + return atomic.SwapInt32((*int32)(b), newInt) == 1 +} type timeoutError struct{} @@ -259,24 +366,30 @@ func initIo() { go ioCompletionProcessor(h) } -func (v *hvsockConn) closeHandle() { - if !v.closing { +func (v *hvsockConn) close() { + v.wgLock.Lock() + if !v.closing.swap(true) { + v.wgLock.Unlock() // cancel all IO and wait for it to complete - v.closing = true cancelIoEx(v.fd, nil) v.wg.Wait() // at this point, no new IO can start syscall.Close(v.fd) v.fd = 0 + } else { + v.wgLock.Unlock() } } // prepareIo prepares for a new IO operation func (v *hvsockConn) prepareIo() (*ioOperation, error) { - v.wg.Add(1) - if v.closing { - return nil, ErrSocketClosed + v.wgLock.RLock() + if v.closing.isSet() { + v.wgLock.RUnlock() + return nil, fmt.Errorf("HvSocket has already been closed") } + v.wg.Add(1) + v.wgLock.RUnlock() c := &ioOperation{} c.ch = make(chan ioResult) return c, nil @@ -302,11 +415,10 @@ func ioCompletionProcessor(h syscall.Handle) { // the operation has actually completed. func (v *hvsockConn) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { if err != syscall.ERROR_IO_PENDING { - v.wg.Done() return int(bytes), err } - if v.closing { + if v.closing.isSet() { cancelIoEx(v.fd, &c.o) } @@ -322,8 +434,8 @@ func (v *hvsockConn) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, e case r = <-c.ch: err = r.err if err == syscall.ERROR_OPERATION_ABORTED { - if v.closing { - err = ErrSocketClosed + if v.closing.isSet() { + err = fmt.Errorf("HvSocket has already been closed") } } case <-timeout: @@ -339,7 +451,6 @@ func (v *hvsockConn) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, e // code to ioCompletionProcessor, c must remain alive // until the channel read is complete. runtime.KeepAlive(c) - v.wg.Done() return int(r.bytes), err } diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go index d90ec985b..bd4ca8a4f 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go @@ -37,8 +37,10 @@ var ( modwinmm = syscall.NewLazyDLL("winmm.dll") modkernel32 = syscall.NewLazyDLL("kernel32.dll") - procConnect = modws2_32.NewProc("connect") - procbind = modws2_32.NewProc("bind") + procConnect = modws2_32.NewProc("connect") + procBind = modws2_32.NewProc("bind") + procAccept = modws2_32.NewProc("accept") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") @@ -50,6 +52,10 @@ var ( // Errno values. const ( errnoERROR_IO_PENDING = 997 + socketError = uintptr(^uint32(0)) + + cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 + cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 ) var ( @@ -73,7 +79,7 @@ func errnoErr(e syscall.Errno) error { func sys_connect(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { r1, _, e1 := syscall.Syscall(procConnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if r1 == socket_error { + if r1 == socketError { if e1 != 0 { err = errnoErr(e1) } else { @@ -85,8 +91,21 @@ func sys_connect(s syscall.Handle, name unsafe.Pointer, namelen int32) (err erro } func sys_bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { - r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if r1 == socket_error { + r1, _, e1 := syscall.Syscall(procBind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socketError { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func sys_accept(s syscall.Handle, rsa *rawSockaddrHyperv, addrlen *int32) (handle syscall.Handle, err error) { + r1, _, e1 := syscall.Syscall(procAccept.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + handle = syscall.Handle(r1) + if r1 == socketError { if e1 != 0 { err = errnoErr(e1) } else { diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock.go b/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock.go index d2a0f34b2..819be1d36 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock.go @@ -26,19 +26,19 @@ const ( CIDHost = 2 ) -// VsockAddr represents the address of a vsock end point. -type VsockAddr struct { +// Addr represents the address of a vsock end point. +type Addr struct { CID uint32 Port uint32 } -// Network returns the network type for a VsockAddr -func (a VsockAddr) Network() string { +// Network returns the network type for a Addr +func (a Addr) Network() string { return "vsock" } -// String returns a string representation of a VsockAddr -func (a VsockAddr) String() string { +// String returns a string representation of a Addr +func (a Addr) String() string { return fmt.Sprintf("%08x.%08x", a.CID, a.Port) } diff --git a/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock_linux.go b/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock_linux.go index 221181a57..f8838eb5d 100644 --- a/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock_linux.go +++ b/go/vendor/github.com/linuxkit/virtsock/pkg/vsock/vsock_linux.go @@ -17,11 +17,11 @@ import ( func SocketMode(m string) { } -// Convert a generic unix.Sockaddr to a VsockAddr -func sockaddrToVsock(sa unix.Sockaddr) *VsockAddr { +// Convert a generic unix.Sockaddr to a Addr +func sockaddrToVsock(sa unix.Sockaddr) *Addr { switch sa := sa.(type) { case *unix.SockaddrVM: - return &VsockAddr{CID: sa.CID, Port: sa.Port} + return &Addr{CID: sa.CID, Port: sa.Port} } return nil } @@ -43,7 +43,7 @@ func Dial(cid, port uint32) (Conn, error) { } break } - return newVsockConn(uintptr(fd), nil, &VsockAddr{cid, port}), nil + return newVsockConn(uintptr(fd), nil, &Addr{cid, port}), nil } // Listen returns a net.Listener which can accept connections on the given port @@ -62,12 +62,12 @@ func Listen(cid, port uint32) (net.Listener, error) { if err != nil { return nil, errors.Wrapf(err, "listen() on %08x.%08x failed", cid, port) } - return &vsockListener{fd, VsockAddr{cid, port}}, nil + return &vsockListener{fd, Addr{cid, port}}, nil } type vsockListener struct { fd int - local VsockAddr + local Addr } // Accept accepts an incoming call and returns the new connection. @@ -94,72 +94,67 @@ func (v *vsockListener) Addr() net.Addr { type vsockConn struct { vsock *os.File fd uintptr - local *VsockAddr - remote *VsockAddr + local *Addr + remote *Addr } -// VsockConn represents a connection over a vsock -type VsockConn struct { - vsockConn -} - -func newVsockConn(fd uintptr, local, remote *VsockAddr) *VsockConn { +func newVsockConn(fd uintptr, local, remote *Addr) *vsockConn { vsock := os.NewFile(fd, fmt.Sprintf("vsock:%d", fd)) - return &VsockConn{vsockConn{vsock: vsock, fd: fd, local: local, remote: remote}} + return &vsockConn{vsock: vsock, fd: fd, local: local, remote: remote} } // LocalAddr returns the local address of a connection -func (v *VsockConn) LocalAddr() net.Addr { +func (v *vsockConn) LocalAddr() net.Addr { return v.local } // RemoteAddr returns the remote address of a connection -func (v *VsockConn) RemoteAddr() net.Addr { +func (v *vsockConn) RemoteAddr() net.Addr { return v.remote } // Close closes the connection -func (v *VsockConn) Close() error { +func (v *vsockConn) Close() error { return v.vsock.Close() } // CloseRead shuts down the reading side of a vsock connection -func (v *VsockConn) CloseRead() error { +func (v *vsockConn) CloseRead() error { return syscall.Shutdown(int(v.fd), syscall.SHUT_RD) } // CloseWrite shuts down the writing side of a vsock connection -func (v *VsockConn) CloseWrite() error { +func (v *vsockConn) CloseWrite() error { return syscall.Shutdown(int(v.fd), syscall.SHUT_WR) } // Read reads data from the connection -func (v *VsockConn) Read(buf []byte) (int, error) { +func (v *vsockConn) Read(buf []byte) (int, error) { return v.vsock.Read(buf) } // Write writes data over the connection -func (v *VsockConn) Write(buf []byte) (int, error) { +func (v *vsockConn) Write(buf []byte) (int, error) { return v.vsock.Write(buf) } // SetDeadline sets the read and write deadlines associated with the connection -func (v *VsockConn) SetDeadline(t time.Time) error { +func (v *vsockConn) SetDeadline(t time.Time) error { return nil // FIXME } // SetReadDeadline sets the deadline for future Read calls. -func (v *VsockConn) SetReadDeadline(t time.Time) error { +func (v *vsockConn) SetReadDeadline(t time.Time) error { return nil // FIXME } // SetWriteDeadline sets the deadline for future Write calls -func (v *VsockConn) SetWriteDeadline(t time.Time) error { +func (v *vsockConn) SetWriteDeadline(t time.Time) error { return nil // FIXME } // File duplicates the underlying socket descriptor and returns it. -func (v *VsockConn) File() (*os.File, error) { +func (v *vsockConn) File() (*os.File, error) { // This is equivalent to dup(2) but creates the new fd with CLOEXEC already set. r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(v.vsock.Fd()), syscall.F_DUPFD_CLOEXEC, 0) if e1 != 0 { From 995168d83daa7c4437e564ced3842964e51c8712 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 10 Mar 2019 10:41:26 +0000 Subject: [PATCH 3/5] vpnkit-forwarder: update to new hvsock API Signed-off-by: David Scott --- go/cmd/vpnkit-forwarder/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/cmd/vpnkit-forwarder/main.go b/go/cmd/vpnkit-forwarder/main.go index 54c7ced12..7e67cbd05 100644 --- a/go/cmd/vpnkit-forwarder/main.go +++ b/go/cmd/vpnkit-forwarder/main.go @@ -43,7 +43,7 @@ func main() { func hyperVListener(port int) net.Listener { serviceID := fmt.Sprintf("%08x-FACB-11E6-BD58-64006A7986D3", port) svcid, _ := hvsock.GUIDFromString(serviceID) - l, err := hvsock.Listen(hvsock.HypervAddr{VMID: hvsock.GUIDWildcard, ServiceID: svcid}) + l, err := hvsock.Listen(hvsock.Addr{VMID: hvsock.GUIDWildcard, ServiceID: svcid}) if err != nil { log.Fatalf("Failed to bind AF_HVSOCK guid: %s: %v", serviceID, err) } From a038627b8762aa53d6260c8373b5812c4c1d33d8 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 10 Mar 2019 10:41:45 +0000 Subject: [PATCH 4/5] vpnkit-forwarder: remove dead code Signed-off-by: David Scott --- go/cmd/vpnkit-forwarder/proxy.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/go/cmd/vpnkit-forwarder/proxy.go b/go/cmd/vpnkit-forwarder/proxy.go index 5f1083a6d..0c87a5853 100644 --- a/go/cmd/vpnkit-forwarder/proxy.go +++ b/go/cmd/vpnkit-forwarder/proxy.go @@ -6,8 +6,6 @@ import ( "log" "net" "os" - "os/signal" - "syscall" ) var interactiveMode bool @@ -74,12 +72,3 @@ func parseHostContainerAddrs() (host net.Addr, port int, container net.Addr, loc localIP = !*noLocalIP return host, port, container, localIP } - -func handleStopSignals() { - s := make(chan os.Signal, 10) - signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP) - - for range s { - os.Exit(0) - } -} From 4a0abb574ca488348da5341c8eb87482851e9ad7 Mon Sep 17 00:00:00 2001 From: David Scott Date: Tue, 12 Mar 2019 08:13:24 +0000 Subject: [PATCH 5/5] Start adding CHANGES.md for 0.4.0 Signed-off-by: David Scott --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0844ef3c8..cded439e3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +### v0.4.0 (unreleased) +* allow longer paths when forwarding Unix domain sockets + ### v0.3.0 (2019-02-06) * support multiplexing forwarded connections along one Hyper-V socket connection