feat: add VZVmnetNetworkDeviceAttachment support (macOS 26.0)#205
feat: add VZVmnetNetworkDeviceAttachment support (macOS 26.0)#205norio-nomura wants to merge 11 commits intoCode-Hex:mainfrom
VZVmnetNetworkDeviceAttachment support (macOS 26.0)#205Conversation
VmnetNetworkDeviceAttachment support (macOS 26.0)VZVmnetNetworkDeviceAttachment support (macOS 26.0)
6617c8f to
6a1f741
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
|
This can be used by multiple processes like this:
|
5a7a116 to
72cc1d4
Compare
In this procedure, I confirmed that VMs launched from multiple processes can share networks with each other. 👍🏻 |
72cc1d4 to
9506cbd
Compare
Added unit test and |
I'll try this added xpc package with lima to make it work. Until then, it's a draft. |
7bf24c1 to
007c2a5
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
aba95bd to
ba619f5
Compare
d3fad75 to
7a58378
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
7a58378 to
33858c0
Compare
nirs
left a comment
There was a problem hiding this comment.
I did not review most of this change, just the stange part of about marking bridged mode as depracated.
f048f6e to
3b512d7
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
c68bc89 to
f92e6f3
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
|
I think the approximately necessary APIs have been added. |
|
@norio-nomura Does the xpc package need to be public? If it fits within internal, I don't think we need to add tests. |
It depends on the |
We need the xpc.NewObject() for creating a vment_network_ref with serialization. Here is an example usage in vmnet-broker test vm: If vmnet.NewNetworkWithSerialization() will accept xpc_object_t (unsafe.Pointer), the xpc package is not needed in this project and can be part of Lima or available as separate package. |
|
Unit tests added to |
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com> # Conflicts: # go.sum
666a1d8 to
1b6b030
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com> # Conflicts: # go.sum
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com> # Conflicts: # go.sum # Conflicts: # go.sum
1b6b030 to
007de93
Compare
Based on `VMNET_SHARED_MODE`, and `VMNET_HOST_MODE` ```yaml networks: - vzShared: true - vzHost: true ``` But, to sharing network between multiple VMs, `VZVmnetNetworkDeviceAttachment` requires VMs are launched by same process. It depends on Code-Hex/vz#205 Signed-off-by: Norio Nomura <norio.nomura@gmail.com> # Conflicts: # go.sum # Conflicts: # go.sum
| return nil, err | ||
| case <-ctx.Done(): | ||
| return nil, ctx.Err() | ||
| } |
There was a problem hiding this comment.
If ctx.Done() fires before the reply, SendMessageWithReply returns and deletes the cgoReplyHandler, but the XPC runtime may call the callback later. In that case, callReplyHandler will reference the deleted cgo.Handle and panic. This only occurs when a timeout/cancellation occurs before the reply, so you may need to keep the handle alive until the callback is executed, or explicitly cancel the session/request.
Example code below:
type ReplyHandler func(*Dictionary, *RichError)
var replyHandlerRegistry struct {
mu sync.Mutex
next uint64
handlers map[uintptr]ReplyHandler
}
func registerReplyHandler(handler ReplyHandler) uintptr {
if handler == nil {
return 0
}
key := uintptr(atomic.AddUint64(&replyHandlerRegistry.next, 1))
replyHandlerRegistry.mu.Lock()
if replyHandlerRegistry.handlers == nil {
replyHandlerRegistry.handlers = make(map[uintptr]ReplyHandler)
}
replyHandlerRegistry.handlers[key] = handler
replyHandlerRegistry.mu.Unlock()
return key
}
func popReplyHandler(handle uintptr) ReplyHandler {
if handle == 0 {
return nil
}
replyHandlerRegistry.mu.Lock()
handler := replyHandlerRegistry.handlers[handle]
delete(replyHandlerRegistry.handlers, handle)
replyHandlerRegistry.mu.Unlock()
return handler
}
func deleteReplyHandler(handle uintptr) {
if handle == 0 {
return
}
replyHandlerRegistry.mu.Lock()
delete(replyHandlerRegistry.handlers, handle)
replyHandlerRegistry.mu.Unlock()
}
// callReplyHandler is called from C to handle reply messages.
//
//export callReplyHandler
func callReplyHandler(cgoReplyHandler uintptr, cgoReply, cgoError uintptr) {
reply := unwrapObject[*Dictionary](uintptr(cgoReply))
err := unwrapObject[*RichError](cgoError)
handler := popReplyHandler(cgoReplyHandler)
if handler == nil {
if reply != nil {
ReleaseOnCleanup(reply)
}
if err != nil {
ReleaseOnCleanup(err)
}
return
}
handler(reply, err)
}
// SendMessageWithReply sends a message *[Dictionary] to the [Session] and waits for a reply *[Dictionary]. (macOS 13.0+)
//
// Use [context.Context] to control cancellation and timeouts.
// - https://developer.apple.com/documentation/xpc/xpc_session_send_message_with_reply_async
func (s *Session) SendMessageWithReply(ctx context.Context, message *Dictionary) (*Dictionary, error) {
replyCh := make(chan *Dictionary, 1)
errCh := make(chan *RichError, 1)
replyHandler := (ReplyHandler)(func(reply *Dictionary, err *RichError) {
defer close(replyCh)
defer close(errCh)
if err != nil {
errCh <- ReleaseOnCleanup(Retain(err))
} else {
replyCh <- ReleaseOnCleanup(Retain(reply))
}
})
handle := registerReplyHandler(replyHandler)
C.xpcSessionSendMessageWithReplyAsync(objc.Ptr(s), objc.Ptr(message), C.uintptr_t(handle))
select {
case reply := <-replyCh:
return reply, nil
case err := <-errCh:
return nil, err
case <-ctx.Done():
deleteReplyHandler(handle)
return nil, ctx.Err()
}
}There's probably a better way...
| // Wait until 4-byte header is read | ||
| if _, err := conn.Read(v.backingBuffers[packetCount][:headerSize]); err != nil { | ||
| return 0, fmt.Errorf("conn.Read failed: %w", err) | ||
| } |
There was a problem hiding this comment.
A stream may return less than 4 bytes without error, you should read the full 4 bytes with io.ReadFull.
| // Wait until 4-byte header is read | |
| if _, err := conn.Read(v.backingBuffers[packetCount][:headerSize]); err != nil { | |
| return 0, fmt.Errorf("conn.Read failed: %w", err) | |
| } | |
| // Wait until 4-byte header is read | |
| if _, err := io.ReadFull(conn, v.backingBuffers[packetCount][:headerSize]); err != nil { | |
| return 0, fmt.Errorf("io.ReadFull failed: %w", err) | |
| } |
There was a problem hiding this comment.
The conn here was created with net.FileConn(), and the Read implementation uses syscall.Read.
man 2 read has the following description.
read()andpread()will fail if the parameternbyteexceedsINT_MAX, and they do not attempt a partial read.
| case 15: | ||
| target = 150000 // __MAC_15_0 |
There was a problem hiding this comment.
Sorry, please add this here 🙏
case 15.4:
target = 150400 // __MAC_15_4
case 26:
target = 260000 // __MAC_26_0
}
|
The performance of |
Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
`VZVmnetNetworkDeviceAttachment` is an API that creates vmnet devices on VMs added in macOS 26. see: https://developer.apple.com/documentation/virtualization/vzvmnetnetworkdeviceattachment?language=objc It does not require the `com.apple.vm.networking` entitlement nor root privileges. `HostMode` and `SharedMode` are supported. In order for multiple VMs to communicate with each other in `SharedMode`, they must be started in the same executable and the same `VmnetNetwork` must be passed to `NewVmnetNetworkDeviceAttachment()` to create an attachment. This change adds: - `vz.VmnetNetworkDeviceAttachment` represents `VZVmnetNetworkDeviceAttachment` in Go - `vmnet` package to use vmnet APIs that added on macOS 26.0 - `Return` represents `vmnet_return_t` as `error` - `ErrSuccess`, `ErrFailure`, ... - `Mode` represents `operating_modes_t` - `HostMode`, `SharedMode` - `NetworkConfiguration` represents `vmnet_network_configuration_t` - `Network` represents `vmnet_network_ref` - `vz_test.TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMs` - `vz_test.TestVmnetSharedModeWithConfiguringIPv4` Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
- Remove `FIXME`s for "Configuration file(s) do(es) not support C++: ..." Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
- Add `TestVmnetNetworkShareModeSharingOverXpc` to `vmnet_test.go` `TestVmnetNetworkShareModeSharingOverXpc` tests sharing `vmnet.Network` in `SharedMode` over XPC communication. This test registers test executable as an Mach service and launches it using `launchctl`. The launched Mach service provides `vmnet.Network` serialization to clients upon request, after booting a VM using the provided `vmnet.Network` to ensure the network is functional on the server side. The client boots VM using the provided `vmnet.Network` serialization. - Add `pkg/xpc` package that providing `<xpc/xpc.h>` APIs to support implementing Mach service server and client Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
- .github/workflows/compile.yml: Extend test job's timeout from 3m to 5m - Makefile: Extend test target's timeout from 2m to 4m Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
`xpc`: Use `internal/objc` `xpc`: Add missing APIs, hide `unsafe.Pointer`, and unexport `xpcObject` `xpc`: Change Fd from `int` to `uintptr` `xpc`: Remove calling `ReleaseOnCleanup()` in `xpcObject.retain()` Call `ReleaseOnCleanup()` explicitly if needed. `xpc`: minor tweak
- Fix `NewArray` with empty slices. - Change `New*` to return actual typed instance instead of `Object` Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
`vmnet`: Fix golangci-lint-v2 violations
`vmnet`: if iface.EnableVirtioHeader { packetSize += virtioNetHdrSize }
`vmnet`: Remove `object`
`vmnet`: Add doc comments to `*FileAdaptorForInterface`s
`vmnet`: Refactor `*FileAdaptorForInterface`s
- Remove written packet count from result of `WritePacketsTo*` in `PacketForwarder`
- Minimize differences between `pktDescsManager` and `msgHdrXArray`
`vmnet`: Handle `syscall.ENOBUFS` in `DatagramPacketForwarder.WritePacketsToConn`
`syscall.Sendmsg` may return `syscall.ENOBUFS` if there is not enough buffer set to the destination.
Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
Use `*FileAdaptorForInterface` APIs in `TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMs`: - `vmnet.DatagramFileAdaptorForInterface` - `vmnet.DatagramNextFileAdaptorForInterface` Since they are compatible with `vz.NewFileHandleNetworkDeviceAttachment`. Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
- `StreamFileAdaptorForInterface`: - Support partial read on `unix.Readv` in `readPacketsFromConn` - `Datagram*FileAdaptorForInterface`: - Add wait on `syscall.ENOBUFS` in `writePacketsToPacketConn` Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
007de93 to
9241433
Compare
feat: add
VZVmnetNetworkDeviceAttachmentsupport (macOS 26.0)VZVmnetNetworkDeviceAttachmentis an API that creates vmnet devices on VMs added in macOS 26.see: https://developer.apple.com/documentation/virtualization/vzvmnetnetworkdeviceattachment?language=objc
It does not require the
com.apple.vm.networkingentitlement nor root privileges.HostModeandSharedModeare supported.In order for multiple VMs to communicate with each other in
SharedMode, they must be started in the same executable and the sameVmnetNetworkmust be passed toNewVmnetNetworkDeviceAttachment()to create an attachment.This change adds:
vz.VmnetNetworkDeviceAttachmentrepresentsVZVmnetNetworkDeviceAttachmentin Govmnetpackage to use vmnet APIs that added on macOS 26.0Returnrepresentsvmnet_return_taserrorErrSuccess,ErrFailure, ...Moderepresentsoperating_modes_tHostMode,SharedModeNetworkConfigurationrepresentsvmnet_network_configuration_tNetworkrepresentsvmnet_network_refInterfacerepresentsinterface_ref*FileAdaptorForInterfaces support File Handle based network device APIs on QEMU, krunkit, andvz.NewFileHandleNetworkDeviceAttachmentxpcpackage that providing<xpc/xpc.h>APIs to support implementing Mach service server/client to sharing serializations ofvmnet.Networkand file descriptors ofvmnet.*FileAdaptorForInterfacesvz_test.TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMsvz_test.TestVmnetSharedModeWithConfiguringIPv4vz_test.TestVmnetNetworkShareModeSharingOverXpcTestVmnetNetworkShareModeSharingOverXpctests sharingvmnet.NetworkinSharedModeover XPC communication.This test registers test executable as an Mach service and launches it using
launchctl.The launched Mach service provides
vmnet.Networkserialization to clients upon request, after bootinga VM using the provided
vmnet.Networkto ensure the network is functional on the server side.The client boots VM using the provided
vmnet.Networkserialization.Edit: on macOS 26.2, "the same executable" restriction seems to be relaxed.
VZVmnetNetworkDeviceAttachmentseems to allow connecting to subnet created by a different executable.vmnet_interface_start_with_networkseems to allow connecting to subnet created by an executable at same path? (may changing CDHash not affect?)Which issue(s) this PR fixes:
Mentioned in #198 (comment)