unix: add SockaddrVM type and AF_VSOCK to anyToSockaddrGOOS to darwin

Change-Id: I511be555d92ac87a797949fc4c99fd8ef9515ace
Reviewed-on: https://go-review.googlesource.com/c/sys/+/354269
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/unix/syscall_darwin.go b/unix/syscall_darwin.go
index d59fb95..a8c1331 100644
--- a/unix/syscall_darwin.go
+++ b/unix/syscall_darwin.go
@@ -48,6 +48,30 @@
 	return unsafe.Pointer(&sa.raw), SizeofSockaddrCtl, nil
 }
 
+// SockaddrVM implements the Sockaddr interface for AF_VSOCK type sockets.
+// SockaddrVM provides access to Darwin VM sockets: a mechanism that enables
+// bidirectional communication between a hypervisor and its guest virtual
+// machines.
+type SockaddrVM struct {
+	// CID and Port specify a context ID and port address for a VM socket.
+	// Guests have a unique CID, and hosts may have a well-known CID of:
+	//  - VMADDR_CID_HYPERVISOR: refers to the hypervisor process.
+	//  - VMADDR_CID_LOCAL: refers to local communication (loopback).
+	//  - VMADDR_CID_HOST: refers to other processes on the host.
+	CID  uint32
+	Port uint32
+	raw  RawSockaddrVM
+}
+
+func (sa *SockaddrVM) sockaddr() (unsafe.Pointer, _Socklen, error) {
+	sa.raw.Len = SizeofSockaddrVM
+	sa.raw.Family = AF_VSOCK
+	sa.raw.Port = sa.Port
+	sa.raw.Cid = sa.CID
+
+	return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil
+}
+
 func anyToSockaddrGOOS(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
 	switch rsa.Addr.Family {
 	case AF_SYSTEM:
@@ -58,6 +82,13 @@
 			sa.Unit = pp.Sc_unit
 			return sa, nil
 		}
+	case AF_VSOCK:
+		pp := (*RawSockaddrVM)(unsafe.Pointer(rsa))
+		sa := &SockaddrVM{
+			CID:  pp.Cid,
+			Port: pp.Port,
+		}
+		return sa, nil
 	}
 	return nil, EAFNOSUPPORT
 }
diff --git a/unix/syscall_internal_darwin_test.go b/unix/syscall_internal_darwin_test.go
index e443144..d89ac2a 100644
--- a/unix/syscall_internal_darwin_test.go
+++ b/unix/syscall_internal_darwin_test.go
@@ -50,6 +50,23 @@
 				Unit: 0xC71,
 			},
 		},
+		{
+			name: "AF_VSOCK emtpy",
+			rsa:  sockaddrVMToAny(RawSockaddrVM{}),
+			err:  EAFNOSUPPORT,
+		},
+		{
+			name: "AF_VSOCK Cid and Port",
+			rsa: sockaddrVMToAny(RawSockaddrVM{
+				Family: AF_VSOCK,
+				Cid:    VMADDR_CID_HOST,
+				Port:   VMADDR_PORT_ANY,
+			}),
+			sa: &SockaddrVM{
+				CID:  VMADDR_CID_HOST,
+				Port: VMADDR_PORT_ANY,
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -121,6 +138,58 @@
 	}
 }
 
+func TestSockaddVM_sockaddr(t *testing.T) {
+	tests := []struct {
+		name string
+		sa   *SockaddrVM
+		raw  *RawSockaddrVM
+		err  error
+	}{
+		{
+			name: "empty",
+			sa:   &SockaddrVM{},
+			raw: &RawSockaddrVM{
+				Len:    SizeofSockaddrVM,
+				Family: AF_VSOCK,
+			},
+		},
+		{
+			name: "with CID and Port",
+			sa: &SockaddrVM{
+				CID:  VMADDR_CID_HOST,
+				Port: VMADDR_PORT_ANY,
+			},
+			raw: &RawSockaddrVM{
+				Len:    SizeofSockaddrVM,
+				Family: AF_VSOCK,
+				Port:   VMADDR_PORT_ANY,
+				Cid:    VMADDR_CID_HOST,
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			out, l, err := tt.sa.sockaddr()
+			if err != tt.err {
+				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
+			}
+
+			// Must be 0 on error or a fixed size otherwise.
+			if (tt.err != nil && l != 0) || (tt.raw != nil && l != SizeofSockaddrVM) {
+				t.Fatalf("unexpected Socklen: %d", l)
+			}
+
+			if out != nil {
+				raw := (*RawSockaddrVM)(out)
+				if !reflect.DeepEqual(raw, tt.raw) {
+					t.Fatalf("unexpected RawSockaddrVM:\n got: %#v\nwant: %#v", raw, tt.raw)
+				}
+			}
+		})
+	}
+}
+
 func sockaddrCtlToAny(in RawSockaddrCtl) *RawSockaddrAny {
 	var out RawSockaddrAny
 	copy(
@@ -129,3 +198,12 @@
 	)
 	return &out
 }
+
+func sockaddrVMToAny(in RawSockaddrVM) *RawSockaddrAny {
+	var out RawSockaddrAny
+	copy(
+		(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
+		(*(*[SizeofSockaddrVM]byte)(unsafe.Pointer(&in)))[:],
+	)
+	return &out
+}