diff --git a/unix/syscall_darwin.go b/unix/syscall_darwin.go index d59fb95c..a8c13317 100644 --- a/unix/syscall_darwin.go +++ b/unix/syscall_darwin.go @@ -48,6 +48,30 @@ func (sa *SockaddrCtl) sockaddr() (unsafe.Pointer, _Socklen, error) { 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 @@ func anyToSockaddrGOOS(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { 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 e4431447..d89ac2ad 100644 --- a/unix/syscall_internal_darwin_test.go +++ b/unix/syscall_internal_darwin_test.go @@ -50,6 +50,23 @@ func Test_anyToSockaddr_darwin(t *testing.T) { 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 TestSockaddrCtl_sockaddr(t *testing.T) { } } +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 @@ func sockaddrCtlToAny(in RawSockaddrCtl) *RawSockaddrAny { ) return &out } + +func sockaddrVMToAny(in RawSockaddrVM) *RawSockaddrAny { + var out RawSockaddrAny + copy( + (*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:], + (*(*[SizeofSockaddrVM]byte)(unsafe.Pointer(&in)))[:], + ) + return &out +}