go: vendor shared hvsock/vsock packages

These packages will be shared by a number of utilities
so vendoring them in a shared place.

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
Rolf Neugebauer 2016-05-19 10:49:07 +01:00
parent aeacb7b283
commit 124eb81ca2
11 changed files with 1716 additions and 0 deletions

View File

@ -0,0 +1,71 @@
Copyright 2016 Rolf Neugebauer <rolf.neugebauer@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Some of the code in ./go/hvsock.go, ./go/hvsock_windows.go, and
zsyscall_windows.go is covered by the following licenses:
==============================================================================
The MIT License (MIT)
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
and
===
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,49 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"../"
)
var (
vmstr string
portstr string
)
func init() {
flag.StringVar(&vmstr, "vm", "", "Hyper-V VM to connect to")
flag.StringVar(&portstr, "port", "23a432c2-537a-4291-bcb5-d62504644739", "Hyper-V sockets service/port")
}
func main() {
log.SetFlags(log.LstdFlags)
flag.Parse()
vmid, err := hvsock.GuidFromString(vmstr)
if err != nil {
log.Fatalln("Failed to parse GUID", vmstr, err)
}
svcid, err := hvsock.GuidFromString(portstr)
if err != nil {
log.Fatalln("Failed to parse GUID", portstr, err)
}
c, err := hvsock.Dial(hvsock.HypervAddr{VmId: vmid, ServiceId: svcid})
if err != nil {
log.Fatalln("Failed to Dial:\n", vmstr, portstr, err)
}
fmt.Println("Send: hello")
l, err := fmt.Fprintf(c, "hello\n")
if err != nil {
log.Fatalln("Failed to send: ", err)
}
fmt.Println("Sent: %s bytes", l)
message, _ := bufio.NewReader(c).ReadString('\n')
fmt.Println("From SVR: " + message)
}

View File

@ -0,0 +1,131 @@
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net"
"strings"
"../hvsock"
)
var (
clientStr string
serverMode bool
svcid, _ = hvsock.GuidFromString("3049197C-9A4E-4FBF-9367-97F792F16994")
)
func init() {
flag.StringVar(&clientStr, "c", "", "Client")
flag.BoolVar(&serverMode, "s", false, "Start as a Server")
}
func server() {
l, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid})
if err != nil {
log.Fatalln("Listen():", err)
}
defer func() {
l.Close()
}()
for {
conn, err := l.Accept()
if err != nil {
log.Fatalln("Accept(): ", err)
}
fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr())
go handleRequest(conn)
}
}
func handleRequest(c net.Conn) {
defer func() {
fmt.Printf("Closing\n")
err := c.Close()
if err != nil {
log.Fatalln("Close():", err)
}
}()
n, err := io.Copy(c, c)
if err != nil {
log.Fatalln("Copy():", err)
}
fmt.Printf("Copied Bytes: %d\n", n)
fmt.Printf("Sending BYE message\n")
// The '\n' is important as the client use ReadString()
_, err = fmt.Fprintf(c, "Got %d bytes. Bye\n", n)
if err != nil {
log.Fatalln("Failed to send: ", err)
}
fmt.Printf("Sent bye\n")
}
func client(vmid hvsock.GUID) {
sa := hvsock.HypervAddr{VmId: vmid, ServiceId: svcid}
c, err := hvsock.Dial(sa)
if err != nil {
log.Fatalln("Failed to Dial:\n", sa.VmId.String(), sa.ServiceId.String(), err)
}
defer func() {
fmt.Printf("Closing\n")
c.Close()
}()
fmt.Printf("Send: hello\n")
// Note the '\n' is significant as we use ReadString below
l, err := fmt.Fprintf(c, "hello\n")
if err != nil {
log.Fatalln("Failed to send: ", err)
}
fmt.Printf("Sent: %d bytes\n", l)
message, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
log.Fatalln("Failed to receive: ", err)
}
fmt.Printf("From SVR: %s", message)
fmt.Printf("CloseWrite()\n")
c.CloseWrite()
fmt.Printf("Waiting for Bye message\n")
message, err = bufio.NewReader(c).ReadString('\n')
if err != nil {
log.Fatalln("Failed to receive: ", err)
}
fmt.Printf("From SVR: %s", message)
}
func main() {
log.SetFlags(log.LstdFlags)
flag.Parse()
if serverMode {
fmt.Printf("Starting server\n")
server()
}
vmid := hvsock.GUID_ZERO
var err error
if strings.Contains(clientStr, "-") {
vmid, err = hvsock.GuidFromString(clientStr)
if err != nil {
log.Fatalln("Can't parse GUID: ", clientStr)
}
} else if clientStr == "parent" {
vmid = hvsock.GUID_PARENT
} else {
vmid = hvsock.GUID_LOOPBACK
}
fmt.Printf("Client connecting to %s", vmid.String())
client(vmid)
}

View File

@ -0,0 +1,243 @@
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net"
"strings"
"sync"
"time"
"crypto/md5"
"math/rand"
"sync/atomic"
"../hvsock"
)
var (
clientStr string
serverMode bool
maxDataLen int
connections int
sleepTime int
verbose int
exitOnError bool
parallel int
svcid, _ = hvsock.GuidFromString("3049197C-9A4E-4FBF-9367-97F792F16994")
connCounter int32
)
func init() {
flag.StringVar(&clientStr, "c", "", "Client")
flag.BoolVar(&serverMode, "s", false, "Start as a Server")
flag.IntVar(&maxDataLen, "l", 64*1024, "Maximum Length of data")
flag.IntVar(&connections, "i", 100, "Total number of connections")
flag.IntVar(&sleepTime, "w", 0, "Sleep time in seconds between new connections")
flag.IntVar(&parallel, "p", 1, "Run n connections in parallel")
flag.BoolVar(&exitOnError, "e", false, "Exit when an error occurs")
flag.IntVar(&verbose, "v", 0, "Set the verbosity level")
rand.Seed(time.Now().UnixNano())
}
func main() {
log.SetFlags(log.LstdFlags)
flag.Parse()
if verbose > 2 {
hvsock.Debug = true
}
if serverMode {
fmt.Printf("Starting server\n")
server()
return
}
// Client mode
vmid := hvsock.GUID_ZERO
var err error
if strings.Contains(clientStr, "-") {
vmid, err = hvsock.GuidFromString(clientStr)
if err != nil {
log.Fatalln("Can't parse GUID: ", clientStr)
}
} else if clientStr == "parent" {
vmid = hvsock.GUID_PARENT
} else {
vmid = hvsock.GUID_LOOPBACK
}
if parallel <= 1 {
// No parallelism, run in the main thread.
fmt.Printf("Client connecting to %s\n", vmid.String())
for i := 0; i < connections; i++ {
client(vmid, i)
time.Sleep(time.Duration(sleepTime) * time.Second)
}
return
}
// Parallel clients
var wg sync.WaitGroup
for i := 0; i < parallel; i++ {
wg.Add(1)
go parClient(&wg, vmid)
}
wg.Wait()
}
func server() {
l, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid})
if err != nil {
log.Fatalln("Listen():", err)
}
defer func() {
l.Close()
}()
connid := 0
for {
conn, err := l.Accept()
if err != nil {
log.Fatalf("Accept(): %s\n", err)
}
prDebug("[%05d] accept(): %s -> %s \n", connid, conn.RemoteAddr(), conn.LocalAddr())
go handleRequest(conn, connid)
connid++
}
}
func handleRequest(c net.Conn, connid int) {
defer func() {
prDebug("[%05d] Closing\n", connid)
err := c.Close()
if err != nil {
prError("[%05d] Close(): %s\n", connid, err)
}
}()
n, err := io.Copy(c, c)
if err != nil {
prError("[%05d] Copy(): %s", connid, err)
return
}
prInfo("[%05d] Copied Bytes: %d\n", connid, n)
if n == 0 {
return
}
prDebug("[%05d] Sending BYE message\n", connid)
// The '\n' is important as the client use ReadString()
_, err = fmt.Fprintf(c, "Got %d bytes. Bye\n", n)
if err != nil {
prError("[%05d] Failed to send: %s", connid, err)
return
}
prDebug("[%05d] Sent bye\n", connid)
}
func parClient(wg *sync.WaitGroup, vmid hvsock.GUID) {
connid := int(atomic.AddInt32(&connCounter, 1))
for connid < connections {
client(vmid, connid)
connid = int(atomic.AddInt32(&connCounter, 1))
time.Sleep(time.Duration(sleepTime) * time.Second)
}
wg.Done()
}
func client(vmid hvsock.GUID, conid int) {
sa := hvsock.HypervAddr{VmId: vmid, ServiceId: svcid}
c, err := hvsock.Dial(sa)
if err != nil {
prError("[%05d] Failed to Dial: %s:%s %s\n", conid, sa.VmId.String(), sa.ServiceId.String(), err)
}
defer c.Close()
// Create buffer with random data and random length.
// Make sure the buffer is not zero-length
buflen := rand.Intn(maxDataLen-1) + 1
txbuf := randBuf(buflen)
csum0 := md5.Sum(txbuf)
prDebug("[%05d] TX: %d bytes, md5=%02x\n", conid, buflen, csum0)
w := make(chan int)
go func() {
l, err := c.Write(txbuf)
if err != nil {
prError("[%05d] Failed to send: %s\n", conid, err)
}
if l != buflen {
prError("[%05d] Failed to send enough data: %d\n", conid, l)
}
// Tell the other end that we are done
c.CloseWrite()
w <- l
}()
rxbuf := make([]byte, buflen)
n, err := io.ReadFull(bufio.NewReader(c), rxbuf)
if err != nil {
prError("[%05d] Failed to receive: %s\n", conid, err)
return
}
csum1 := md5.Sum(rxbuf)
totalSent := <-w
prInfo("[%05d] RX: %d bytes, md5=%02x (sent=%d)\n", conid, n, csum1, totalSent)
if csum0 != csum1 {
prError("[%05d] Checksums don't match", conid)
}
// Wait for Bye message
message, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
prError("[%05d] Failed to receive bye: %s\n", conid, err)
}
prDebug("[%05d] From SVR: %s", conid, message)
}
func randBuf(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = byte(rand.Intn(255))
}
return b
}
func prError(format string, args ...interface{}) {
if exitOnError {
log.Fatalf(format, args...)
} else {
log.Printf(format, args...)
}
}
func prInfo(format string, args ...interface{}) {
if verbose > 0 {
log.Printf(format, args...)
}
}
func prDebug(format string, args ...interface{}) {
if verbose > 1 {
log.Printf(format, args...)
}
}

View File

@ -0,0 +1,412 @@
package hvsock
import (
"errors"
"fmt"
"io"
"log"
"net"
"sync"
"syscall"
"encoding/binary"
)
// 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.
const (
maxMsgSize = 32 * 1024 // Maximum message size
)
// Hypper-V sockets use GUIDs for addresses and "ports"
type GUID [16]byte
// Convert a GUID into a string
func (g *GUID) String() string {
/* XXX This assume little endian */
return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
g[3], g[2], g[1], g[0],
g[5], g[4],
g[7], g[6],
g[8], g[9],
g[10], g[11], g[12], g[13], g[14], g[15])
}
// Parse a GUID string
func GuidFromString(s string) (GUID, error) {
var g GUID
var err error
_, err = fmt.Sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
&g[3], &g[2], &g[1], &g[0],
&g[5], &g[4],
&g[7], &g[6],
&g[8], &g[9],
&g[10], &g[11], &g[12], &g[13], &g[14], &g[15])
return g, err
}
type HypervAddr struct {
VmId GUID
ServiceId GUID
}
func (a HypervAddr) Network() string { return "hvsock" }
func (a HypervAddr) String() string {
vmid := a.VmId.String()
svc := a.ServiceId.String()
return vmid + ":" + svc
}
var (
Debug = false // Set to True to enable additional debug output
GUID_ZERO, _ = GuidFromString("00000000-0000-0000-0000-000000000000")
GUID_WILDCARD, _ = GuidFromString("00000000-0000-0000-0000-000000000000")
GUID_BROADCAST, _ = GuidFromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")
GUID_CHILDREN, _ = GuidFromString("90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd")
GUID_LOOPBACK, _ = GuidFromString("e0e16197-dd56-4a10-9195-5ee7a155a838")
GUID_PARENT, _ = GuidFromString("a42e7cda-d03f-480c-9cc2-a4de20abb878")
)
func Dial(raddr HypervAddr) (Conn, error) {
fd, err := syscall.Socket(AF_HYPERV, syscall.SOCK_STREAM, SHV_PROTO_RAW)
if err != nil {
return nil, err
}
err = connect(fd, &raddr)
if err != nil {
return nil, err
}
v, err := newHVsockConn(fd, HypervAddr{VmId: GUID_ZERO, ServiceId: GUID_ZERO}, raddr)
if err != nil {
return nil, err
}
v.wrlock = &sync.Mutex{}
return v, nil
}
func Listen(addr HypervAddr) (net.Listener, error) {
accept_fd, err := syscall.Socket(AF_HYPERV, syscall.SOCK_STREAM, SHV_PROTO_RAW)
if err != nil {
return nil, err
}
err = bind(accept_fd, addr)
if err != nil {
return nil, err
}
err = syscall.Listen(accept_fd, syscall.SOMAXCONN)
if err != nil {
return nil, err
}
return &hvsockListener{accept_fd, addr}, nil
}
const (
shutdownrd = 0xdeadbeef // Message for CloseRead()
shutdownwr = 0xbeefdead // Message for CloseWrite()
closemsg = 0xdeaddead // Message for Close()
)
// Conn is a hvsock connection which support half-close.
type Conn interface {
net.Conn
CloseRead() error
CloseWrite() error
}
func (v *hvsockListener) Accept() (net.Conn, error) {
var raddr HypervAddr
fd, err := accept(v.accept_fd, &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.accept_fd)
}
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 = errors.New("HvSocket has already been closed")
errSocketWriteClosed = errors.New("HvSocket has been closed for write")
errSocketReadClosed = errors.New("HvSocket has been closed for read")
errSocketMsgSize = errors.New("HvSocket message was of wrong size")
errSocketMsgWrite = errors.New("HvSocket writing message")
errSocketNotEnoughData = errors.New("HvSocket not enough data written")
errSocketUnImplemented = errors.New("Function not implemented")
)
type HVsockConn struct {
hvsockConn
wrlock *sync.Mutex
writeClosed bool
readClosed bool
bytesToRead int
}
func (v *HVsockConn) LocalAddr() net.Addr {
return v.local
}
func (v *HVsockConn) RemoteAddr() net.Addr {
return v.remote
}
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()
}
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
}
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. This function turns a stream interface into
// messages and 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
}
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...)
}
}

View File

@ -0,0 +1,69 @@
// Dummy implementation to compile on Mac OSX
package hvsock
import (
"errors"
"time"
)
const (
AF_HYPERV = 42
SHV_PROTO_RAW = 1
)
type hvsockListener struct {
accept_fd int
laddr HypervAddr
}
//
// System call wrapper
//
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")
}
// Internal representation. Complex mostly due to asynch send()/recv() syscalls.
type hvsockConn struct {
fd int
local HypervAddr
remote HypervAddr
}
// Main constructor
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")
}
func (v *HVsockConn) SetReadDeadline(t time.Time) error {
return nil // FIXME
}
func (v *HVsockConn) SetWriteDeadline(t time.Time) error {
return nil // FIXME
}
func (v *HVsockConn) SetDeadline(t time.Time) error {
return nil // FIXME
}

View File

@ -0,0 +1,147 @@
package hvsock
/*
#include <sys/socket.h>
struct sockaddr_hv {
unsigned short shv_family;
unsigned short reserved;
unsigned char shv_vm_id[16];
unsigned char shv_service_id[16];
};
int bind_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) {
return bind(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv));
}
int connect_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) {
return connect(fd, (const struct sockaddr*)sa_hv, sizeof(*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);
}
*/
import "C"
import (
"errors"
"fmt"
"os"
"strconv"
"time"
)
const (
AF_HYPERV = 43
SHV_PROTO_RAW = 1
)
type hvsockListener struct {
accept_fd int
laddr HypervAddr
}
//
// System call wrapper
//
func connect(s int, a *HypervAddr) (err error) {
sa := C.struct_sockaddr_hv{}
sa.shv_family = AF_HYPERV
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])
}
if ret := C.connect_sockaddr_hv(C.int(s), &sa); ret != 0 {
return errors.New("connect() returned " + strconv.Itoa(int(ret)))
}
return nil
}
func bind(s int, a HypervAddr) error {
sa := C.struct_sockaddr_hv{}
sa.shv_family = AF_HYPERV
sa.reserved = 0
for i := 0; i < 16; i++ {
// XXX this should take the address from `a` but Linux
// currently only support 0s
sa.shv_vm_id[i] = C.uchar(GUID_ZERO[i])
}
for i := 0; i < 16; i++ {
sa.shv_service_id[i] = C.uchar(a.ServiceId[i])
}
if ret := C.bind_sockaddr_hv(C.int(s), &sa); ret != 0 {
return errors.New("bind() returned " + strconv.Itoa(int(ret)))
}
return nil
}
func accept(s int, a *HypervAddr) (int, error) {
var accept_sa C.struct_sockaddr_hv
var accept_sa_len C.socklen_t
accept_sa_len = C.sizeof_struct_sockaddr_hv
fd, err := C.accept_hv(C.int(s), &accept_sa, &accept_sa_len)
if err != nil {
return -1, err
}
a.VmId = guidFromC(accept_sa.shv_vm_id)
a.ServiceId = guidFromC(accept_sa.shv_service_id)
return int(fd), nil
}
// Internal representation. Complex mostly due to asynch send()/recv() syscalls.
type hvsockConn struct {
fd int
hvsock *os.File
local HypervAddr
remote HypervAddr
}
// 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}
return &HVsockConn{hvsockConn: *v}, nil
}
func (v *HVsockConn) close() error {
return v.hvsock.Close()
}
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)
}
func (v *HVsockConn) SetReadDeadline(t time.Time) error {
return nil // FIXME
}
func (v *HVsockConn) SetWriteDeadline(t time.Time) error {
return nil // FIXME
}
func (v *HVsockConn) SetDeadline(t time.Time) error {
return nil // FIXME
}
func guidFromC(cg [16]C.uchar) GUID {
var g GUID
for i := 0; i < 16; i++ {
g[i] = byte(cg[i])
}
return g
}

View File

@ -0,0 +1,309 @@
package hvsock
import (
"errors"
"io"
"log"
"sync"
"syscall"
"time"
"unsafe"
)
// Make sure Winsock2 is initialised
func init() {
e := syscall.WSAStartup(uint32(0x202), &wsaData)
if e != nil {
log.Fatal("WSAStartup", e)
}
}
const (
AF_HYPERV = 34
SHV_PROTO_RAW = 1
socket_error = uintptr(^uint32(0))
)
// struck sockaddr equivalent
type rawSockaddrHyperv struct {
Family uint16
Reserved uint16
VmId GUID
ServiceId GUID
}
type hvsockListener struct {
accept_fd syscall.Handle
laddr HypervAddr
}
// Internal representation. Complex mostly due to asynch send()/recv() syscalls.
type hvsockConn struct {
fd syscall.Handle
local HypervAddr
remote HypervAddr
wg sync.WaitGroup
closing bool
readDeadline time.Time
writeDeadline time.Time
}
// Used for async system calls
const (
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
)
var (
errTimeout = &timeoutError{}
wsaData syscall.WSAData
)
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
// Main constructor
func newHVsockConn(h syscall.Handle, local HypervAddr, remote HypervAddr) (*HVsockConn, error) {
ioInitOnce.Do(initIo)
v := &hvsockConn{fd: h, local: local, remote: remote}
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
if err != nil {
return nil, err
}
err = setFileCompletionNotificationModes(h,
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
if err != nil {
return nil, err
}
return &HVsockConn{hvsockConn: *v}, nil
}
// Utility function to build a struct sockaddr for syscalls.
func (a HypervAddr) sockaddr(sa *rawSockaddrHyperv) (unsafe.Pointer, int32, error) {
sa.Family = AF_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
}
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)
}
func accept(s syscall.Handle, a *HypervAddr) (syscall.Handle, error) {
return 0, errors.New("accept(): Unimplemented")
}
//
// File IO/Socket interface
//
func (s *HVsockConn) close() error {
s.closeHandle()
return nil
}
// Underlying raw read() function.
func (v *HVsockConn) read(buf []byte) (int, error) {
var b syscall.WSABuf
var bytes uint32
var f uint32
b.Len = uint32(len(buf))
b.Buf = &buf[0]
c, err := v.prepareIo()
if err != nil {
return 0, err
}
err = syscall.WSARecv(v.fd, &b, 1, &bytes, &f, &c.o, nil)
n, err := v.asyncIo(c, v.readDeadline, bytes, err)
// Handle EOF conditions.
if err == nil && n == 0 && len(buf) != 0 {
return 0, io.EOF
}
if err == syscall.ERROR_BROKEN_PIPE {
return 0, io.EOF
}
return n, err
}
// Underlying raw write() function.
func (v *HVsockConn) write(buf []byte) (int, error) {
var b syscall.WSABuf
var f uint32
var bytes uint32
if len(buf) == 0 {
return 0, nil
}
f = 0
b.Len = uint32(len(buf))
b.Buf = &buf[0]
c, err := v.prepareIo()
if err != nil {
return 0, err
}
err = syscall.WSASend(v.fd, &b, 1, &bytes, f, &c.o, nil)
return v.asyncIo(c, v.writeDeadline, bytes, err)
}
func (v *HVsockConn) SetReadDeadline(t time.Time) error {
v.readDeadline = t
return nil
}
func (v *HVsockConn) SetWriteDeadline(t time.Time) error {
v.writeDeadline = t
return nil
}
func (v *HVsockConn) SetDeadline(t time.Time) error {
v.SetReadDeadline(t)
v.SetWriteDeadline(t)
return nil
}
// The code below here is adjusted from:
// https://github.com/Microsoft/go-winio/blob/master/file.go
var ioInitOnce sync.Once
var ioCompletionPort syscall.Handle
// ioResult contains the result of an asynchronous IO operation
type ioResult struct {
bytes uint32
err error
}
type ioOperation struct {
o syscall.Overlapped
ch chan ioResult
}
func initIo() {
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
if err != nil {
panic(err)
}
ioCompletionPort = h
go ioCompletionProcessor(h)
}
func (v *hvsockConn) closeHandle() {
if !v.closing {
// 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
}
}
// prepareIo prepares for a new IO operation
func (s *hvsockConn) prepareIo() (*ioOperation, error) {
s.wg.Add(1)
if s.closing {
return nil, errSocketClosed
}
c := &ioOperation{}
c.ch = make(chan ioResult)
return c, nil
}
// ioCompletionProcessor processes completed async IOs forever
func ioCompletionProcessor(h syscall.Handle) {
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
timeBeginPeriod(1)
for {
var bytes uint32
var key uintptr
var op *ioOperation
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
if op == nil {
panic(err)
}
op.ch <- ioResult{bytes, err}
}
}
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
// the operation has actually completed.
func (v *hvsockConn) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) {
if err != syscall.ERROR_IO_PENDING {
v.wg.Done()
return int(bytes), err
}
var r ioResult
wait := true
timedout := false
if v.closing {
cancelIoEx(v.fd, &c.o)
} else if !deadline.IsZero() {
now := time.Now()
if !deadline.After(now) {
timedout = true
} else {
timeout := time.After(deadline.Sub(now))
select {
case r = <-c.ch:
wait = false
case <-timeout:
timedout = true
}
}
}
if timedout {
cancelIoEx(v.fd, &c.o)
}
if wait {
r = <-c.ch
}
err = r.err
if err == syscall.ERROR_OPERATION_ABORTED {
if v.closing {
err = errSocketClosed
} else if timedout {
err = errTimeout
}
}
v.wg.Done()
return int(r.bytes), err
}

View File

@ -0,0 +1,100 @@
package hvsock
import (
"syscall"
"unsafe"
)
var (
modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
modwinmm = syscall.NewLazyDLL("winmm.dll")
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procConnect = modws2_32.NewProc("connect")
procbind = modws2_32.NewProc("bind")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
)
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 e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
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 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
newport = syscall.Handle(r0)
if newport == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func timeBeginPeriod(period uint32) (n int32) {
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
n = int32(r0)
return
}

View File

@ -0,0 +1,171 @@
package vsock
import (
"errors"
"fmt"
"net"
"os"
"syscall"
"time"
)
/* No way to teach net or syscall about vsock sockaddr, so go right to C */
/*
#include <sys/socket.h>
struct sockaddr_vm {
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_vm(int fd, const struct sockaddr_vm *sa_vm) {
return bind(fd, (const struct sockaddr*)sa_vm, sizeof(*sa_vm));
}
int connect_sockaddr_vm(int fd, const struct sockaddr_vm *sa_vm) {
return connect(fd, (const struct sockaddr*)sa_vm, sizeof(*sa_vm));
}
int accept_vm(int fd, struct sockaddr_vm *sa_vm, socklen_t *sa_vm_len) {
return accept4(fd, (struct sockaddr *)sa_vm, sa_vm_len, 0);
}
*/
import "C"
const (
AF_VSOCK = 40
VSOCK_CID_ANY = 4294967295 /* 2^32-1 */
VSOCK_CID_SELF = 3
)
// Listen returns a net.Listener which can accept connections on the given
// vhan port.
func Listen(port uint) (net.Listener, error) {
accept_fd, err := syscall.Socket(AF_VSOCK, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, err
}
sa := C.struct_sockaddr_vm{}
sa.svm_family = AF_VSOCK
sa.svm_port = C.uint(port)
sa.svm_cid = VSOCK_CID_ANY
if ret := C.bind_sockaddr_vm(C.int(accept_fd), &sa); ret != 0 {
return nil, errors.New(fmt.Sprintf("failed bind vsock connection to %08x.%08x, returned %d", sa.svm_cid, sa.svm_port, ret))
}
err = syscall.Listen(accept_fd, syscall.SOMAXCONN)
if err != nil {
return nil, err
}
return &vsockListener{accept_fd, port}, nil
}
// Conn is a vsock connection which support half-close.
type Conn interface {
net.Conn
CloseRead() error
CloseWrite() error
}
type vsockListener struct {
accept_fd int
port uint
}
func (v *vsockListener) Accept() (net.Conn, error) {
var accept_sa C.struct_sockaddr_vm
var accept_sa_len C.socklen_t
accept_sa_len = C.sizeof_struct_sockaddr_vm
fd, err := C.accept_vm(C.int(v.accept_fd), &accept_sa, &accept_sa_len)
if err != nil {
return nil, err
}
return newVsockConn(uintptr(fd), v.port)
}
func (v *vsockListener) Close() error {
// Note this won't cause the Accept to unblock.
return syscall.Close(v.accept_fd)
}
type VsockAddr struct {
Port uint
}
func (a VsockAddr) Network() string {
return "vsock"
}
func (a VsockAddr) String() string {
return fmt.Sprintf("%08x", a.Port)
}
func (v *vsockListener) Addr() net.Addr {
return VsockAddr{Port: v.port}
}
// a wrapper around FileConn which supports CloseRead and CloseWrite
type vsockConn struct {
vsock *os.File
fd uintptr
local VsockAddr
remote VsockAddr
}
type VsockConn struct {
vsockConn
}
func newVsockConn(fd uintptr, localPort uint) (*VsockConn, error) {
vsock := os.NewFile(fd, fmt.Sprintf("vsock:%d", fd))
local := VsockAddr{Port: localPort}
remote := VsockAddr{Port: uint(0)} // FIXME
return &VsockConn{vsockConn{vsock: vsock, fd: fd, local: local, remote: remote}}, nil
}
func (v *VsockConn) LocalAddr() net.Addr {
return v.local
}
func (v *VsockConn) RemoteAddr() net.Addr {
return v.remote
}
func (v *VsockConn) CloseRead() error {
return syscall.Shutdown(int(v.fd), syscall.SHUT_RD)
}
func (v *VsockConn) CloseWrite() error {
return syscall.Shutdown(int(v.fd), syscall.SHUT_WR)
}
func (v *VsockConn) Close() error {
return v.vsock.Close()
}
func (v *VsockConn) Read(buf []byte) (int, error) {
return v.vsock.Read(buf)
}
func (v *VsockConn) Write(buf []byte) (int, error) {
return v.vsock.Write(buf)
}
func (v *VsockConn) SetDeadline(t time.Time) error {
return nil // FIXME
}
func (v *VsockConn) SetReadDeadline(t time.Time) error {
return nil // FIXME
}
func (v *VsockConn) SetWriteDeadline(t time.Time) error {
return nil // FIXME
}

14
alpine/packages/go/vendor/manifest vendored Normal file
View File

@ -0,0 +1,14 @@
{
"version": 0,
"dependencies": [
{
"importpath": "github.com/rneugeba/virtsock/go",
"repository": "https://github.com/rneugeba/virtsock",
"vcs": "git",
"revision": "359bc27daab86588bfc7a304e78c6758b098d8e5",
"branch": "master",
"path": "/go",
"notests": true
}
]
}