1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
|
package vsock
import (
"errors"
"fmt"
"io"
"net"
"os"
"strings"
"syscall"
"time"
)
const (
// Hypervisor specifies that a socket should communicate with the hypervisor
// process.
Hypervisor = 0x0
// Host specifies that a socket should communicate with processes other than
// the hypervisor on the host machine.
Host = 0x2
// cidReserved is a reserved context ID that is no longer in use,
// and cannot be used for socket communications.
cidReserved = 0x1
// shutRd and shutWr are arguments for unix.Shutdown, copied here to avoid
// importing x/sys/unix in cross-platform code.
shutRd = 0 // unix.SHUT_RD
shutWr = 1 // unix.SHUT_WR
// Error numbers we recognize, copied here to avoid importing x/sys/unix in
// cross-platform code.
ebadf = 9
enotconn = 107
// devVsock is the location of /dev/vsock. It is exposed on both the
// hypervisor and on virtual machines.
devVsock = "/dev/vsock"
// network is the vsock network reported in net.OpError.
network = "vsock"
// Operation names which may be returned in net.OpError.
opAccept = "accept"
opClose = "close"
opDial = "dial"
opListen = "listen"
opRawControl = "raw-control"
opRawRead = "raw-read"
opRawWrite = "raw-write"
opRead = "read"
opSet = "set"
opSyscallConn = "syscall-conn"
opWrite = "write"
)
// Listen opens a connection-oriented net.Listener for incoming VM sockets
// connections. The port parameter specifies the port for the Listener.
//
// To allow the server to assign a port automatically, specify 0 for port.
// The address of the server can be retrieved using the Addr method.
//
// When the Listener is no longer needed, Close must be called to free resources.
func Listen(port uint32) (*Listener, error) {
cid, err := ContextID()
if err != nil {
// No addresses available.
return nil, opError(opListen, err, nil, nil)
}
l, err := listen(cid, port)
if err != nil {
// No remote address available.
return nil, opError(opListen, err, &Addr{
ContextID: cid,
Port: port,
}, nil)
}
return l, nil
}
var _ net.Listener = &Listener{}
// A Listener is a VM sockets implementation of a net.Listener.
type Listener struct {
l *listener
}
// Accept implements the Accept method in the net.Listener interface; it waits
// for the next call and returns a generic net.Conn. The returned net.Conn will
// always be of type *Conn.
func (l *Listener) Accept() (net.Conn, error) {
c, err := l.l.Accept()
if err != nil {
return nil, l.opError(opAccept, err)
}
return c, nil
}
// Addr returns the listener's network address, a *Addr. The Addr returned is
// shared by all invocations of Addr, so do not modify it.
func (l *Listener) Addr() net.Addr { return l.l.Addr() }
// Close stops listening on the VM sockets address. Already Accepted connections
// are not closed.
func (l *Listener) Close() error {
return l.opError(opClose, l.l.Close())
}
// SetDeadline sets the deadline associated with the listener. A zero time value
// disables the deadline.
//
// SetDeadline only works with Go 1.12+.
func (l *Listener) SetDeadline(t time.Time) error {
return l.opError(opSet, l.l.SetDeadline(t))
}
// opError is a convenience for the function opError that also passes the local
// address of the Listener.
func (l *Listener) opError(op string, err error) error {
// No remote address for a Listener.
return opError(op, err, l.Addr(), nil)
}
// Dial dials a connection-oriented net.Conn to a VM sockets server.
// The contextID and port parameters specify the address of the server.
//
// If dialing a connection from the hypervisor to a virtual machine, the VM's
// context ID should be specified.
//
// If dialing from a VM to the hypervisor, Hypervisor should be used to
// communicate with the hypervisor process, or Host should be used to
// communicate with other processes on the host machine.
//
// When the connection is no longer needed, Close must be called to free resources.
func Dial(contextID, port uint32) (*Conn, error) {
c, err := dial(contextID, port)
if err != nil {
// No local address, but we have a remote address we can return.
return nil, opError(opDial, err, nil, &Addr{
ContextID: contextID,
Port: port,
})
}
return c, nil
}
var _ net.Conn = &Conn{}
var _ syscall.Conn = &Conn{}
// A Conn is a VM sockets implementation of a net.Conn.
type Conn struct {
fd connFD
local *Addr
remote *Addr
}
// Close closes the connection.
func (c *Conn) Close() error {
return c.opError(opClose, c.fd.Close())
}
// CloseRead shuts down the reading side of the VM sockets connection. Most
// callers should just use Close.
//
// CloseRead only works with Go 1.12+.
func (c *Conn) CloseRead() error {
return c.opError(opClose, c.fd.Shutdown(shutRd))
}
// CloseWrite shuts down the writing side of the VM sockets connection. Most
// callers should just use Close.
//
// CloseWrite only works with Go 1.12+.
func (c *Conn) CloseWrite() error {
return c.opError(opClose, c.fd.Shutdown(shutWr))
}
// LocalAddr returns the local network address. The Addr returned is shared by
// all invocations of LocalAddr, so do not modify it.
func (c *Conn) LocalAddr() net.Addr { return c.local }
// RemoteAddr returns the remote network address. The Addr returned is shared by
// all invocations of RemoteAddr, so do not modify it.
func (c *Conn) RemoteAddr() net.Addr { return c.remote }
// Read implements the net.Conn Read method.
func (c *Conn) Read(b []byte) (int, error) {
n, err := c.fd.Read(b)
if err != nil {
return n, c.opError(opRead, err)
}
return n, nil
}
// Write implements the net.Conn Write method.
func (c *Conn) Write(b []byte) (int, error) {
n, err := c.fd.Write(b)
if err != nil {
return n, c.opError(opWrite, err)
}
return n, nil
}
// A deadlineType specifies the type of deadline to set for a Conn.
type deadlineType int
// Possible deadlineType values.
const (
deadline deadlineType = iota
readDeadline
writeDeadline
)
// SetDeadline implements the net.Conn SetDeadline method.
func (c *Conn) SetDeadline(t time.Time) error {
return c.opError(opSet, c.fd.SetDeadline(t, deadline))
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.opError(opSet, c.fd.SetDeadline(t, readDeadline))
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (c *Conn) SetWriteDeadline(t time.Time) error {
return c.opError(opSet, c.fd.SetDeadline(t, writeDeadline))
}
// SyscallConn returns a raw network connection. This implements the
// syscall.Conn interface.
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
rc, err := c.fd.SyscallConn()
if err != nil {
return nil, c.opError(opSyscallConn, err)
}
return &rawConn{
rc: rc,
local: c.local,
remote: c.remote,
}, nil
}
// opError is a convenience for the function opError that also passes the local
// and remote addresses of the Conn.
func (c *Conn) opError(op string, err error) error {
return opError(op, err, c.local, c.remote)
}
var _ syscall.RawConn = &rawConn{}
// A rawConn is a syscall.RawConn that wraps an internal syscall.RawConn in order
// to produce net.OpError error values.
type rawConn struct {
rc syscall.RawConn
local *Addr
remote *Addr
}
// Control implements the syscall.RawConn Control method.
func (rc *rawConn) Control(fn func(fd uintptr)) error {
return rc.opError(opRawControl, rc.rc.Control(fn))
}
// Control implements the syscall.RawConn Read method.
func (rc *rawConn) Read(fn func(fd uintptr) (done bool)) error {
return rc.opError(opRawRead, rc.rc.Read(fn))
}
// Control implements the syscall.RawConn Write method.
func (rc *rawConn) Write(fn func(fd uintptr) (done bool)) error {
return rc.opError(opRawWrite, rc.rc.Write(fn))
}
// opError is a convenience for the function opError that also passes the local
// and remote addresses of the rawConn.
func (rc *rawConn) opError(op string, err error) error {
return opError(op, err, rc.local, rc.remote)
}
var _ net.Addr = &Addr{}
// An Addr is the address of a VM sockets endpoint.
type Addr struct {
ContextID uint32
Port uint32
}
// Network returns the address's network name, "vsock".
func (a *Addr) Network() string { return network }
// String returns a human-readable representation of Addr, and indicates if
// ContextID is meant to be used for a hypervisor, host, VM, etc.
func (a *Addr) String() string {
var host string
switch a.ContextID {
case Hypervisor:
host = fmt.Sprintf("hypervisor(%d)", a.ContextID)
case cidReserved:
host = fmt.Sprintf("reserved(%d)", a.ContextID)
case Host:
host = fmt.Sprintf("host(%d)", a.ContextID)
default:
host = fmt.Sprintf("vm(%d)", a.ContextID)
}
return fmt.Sprintf("%s:%d", host, a.Port)
}
// fileName returns a file name for use with os.NewFile for Addr.
func (a *Addr) fileName() string {
return fmt.Sprintf("%s:%s", a.Network(), a.String())
}
// ContextID retrieves the local VM sockets context ID for this system.
// ContextID can be used to directly determine if a system is capable of using
// VM sockets.
//
// If the kernel module is unavailable, access to the kernel module is denied,
// or VM sockets are unsupported on this system, it returns an error.
func ContextID() (uint32, error) {
return contextID()
}
// opError unpacks err if possible, producing a net.OpError with the input
// parameters in order to implement net.Conn. As a convenience, opError returns
// nil if the input error is nil.
func opError(op string, err error, local, remote net.Addr) error {
if err == nil {
return nil
}
// Unwrap inner errors from error types.
//
// TODO(mdlayher): errors.Cause or similar in Go 1.13.
switch xerr := err.(type) {
// os.PathError produced by os.File method calls.
case *os.PathError:
// Although we could make use of xerr.Op here, we're passing it manually
// for consistency, since some of the Conn calls we are making don't
// wrap an os.File, which would return an Op for us.
//
// As a special case, if the error is related to access to the /dev/vsock
// device, we don't unwrap it, so the caller has more context as to why
// their operation actually failed than "permission denied" or similar.
if xerr.Path != devVsock {
err = xerr.Err
}
}
switch {
case err == io.EOF, isErrno(err, enotconn):
// We may see a literal io.EOF as happens with x/net/nettest, but
// "transport not connected" also means io.EOF in Go.
return io.EOF
case err == os.ErrClosed, isErrno(err, ebadf), strings.Contains(err.Error(), "use of closed"):
// Different operations may return different errors that all effectively
// indicate a closed file.
//
// To rectify the differences, net.TCPConn uses an error with this text
// from internal/poll for the backing file already being closed.
err = errors.New("use of closed network connection")
default:
// Nothing to do, return this directly.
}
// Determine source and addr using the rules defined by net.OpError's
// documentation: https://golang.org/pkg/net/#OpError.
var source, addr net.Addr
switch op {
case opClose, opDial, opRawRead, opRawWrite, opRead, opWrite:
if local != nil {
source = local
}
if remote != nil {
addr = remote
}
case opAccept, opListen, opRawControl, opSet, opSyscallConn:
if local != nil {
addr = local
}
}
return &net.OpError{
Op: op,
Net: network,
Source: source,
Addr: addr,
Err: err,
}
}
|