Skip to content

Commit b01d9df

Browse files
committed
feat: add VmnetNetworkDeviceAttachment support (macOS 26.0)
`VmnetNetworkDeviceAttachment` 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 process and the same `VmnetNetwork` must be passed to `NewVmnetNetworkDeviceAttachment()` to create an attachment. Add: - `VmnetReturn`: - `ErrVmnetSuccess` - ... - `VmnetMode`: - `HostMode` - `SharedMode` - `BridgedMode`(definition only since not supported. marked as deprecated) - `VmnetNetworkConfiguration`: `NewVmnetNetworkConfiguration()`,   The use of the instance method group is still unknown. Setting subnet seems to trigger disabling DHCP, etc. - `VmnetNetwork`: `NewVmnetNetwork()`, some APIs which using `xpc_object_t` are not implemented. - `VmnetNetworkDeviceAttachment`: `NewVmnetNetworkDeviceAttachment()` see: https://developer.apple.com/documentation/virtualization/vzvmnetnetworkdeviceattachment?language=objc
1 parent c17cd79 commit b01d9df

File tree

8 files changed

+722
-2
lines changed

8 files changed

+722
-2
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ require (
88
golang.org/x/mod v0.22.0
99
)
1010

11-
require golang.org/x/sys v0.36.0 // indirect
11+
require golang.org/x/sys v0.36.0

network.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package vz
22

33
/*
44
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5-
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization
5+
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization -framework vmnet
66
# include "virtualization_11.h"
77
# include "virtualization_13.h"
8+
# include "virtualization_26.h"
89
*/
910
import "C"
1011
import (
@@ -260,6 +261,55 @@ func (f *FileHandleNetworkDeviceAttachment) MaximumTransmissionUnit() int {
260261
return f.mtu
261262
}
262263

264+
// VmnetNetworkDeviceAttachment represents a vmnet network device attachment.
265+
//
266+
// This attachment is used to connect a virtual machine to a vmnet network.
267+
// The attachment is created with a VmnetNetwork and can be used with a VirtioNetworkDeviceConfiguration.
268+
// see: https://developer.apple.com/documentation/virtualization/vzvmnetnetworkdeviceattachment?language=objc
269+
//
270+
// This is only supported on macOS 26 and newer, error will
271+
// be returned on older versions.
272+
type VmnetNetworkDeviceAttachment struct {
273+
*pointer
274+
275+
*baseNetworkDeviceAttachment
276+
}
277+
278+
func (*VmnetNetworkDeviceAttachment) String() string {
279+
return "VmnetNetworkDeviceAttachment"
280+
}
281+
282+
func (v *VmnetNetworkDeviceAttachment) Network() VmnetNetwork {
283+
network := C.VZVmnetNetworkDeviceAttachment_network(objc.Ptr(v))
284+
return &baseVmnetNetwork{
285+
pointer: objc.NewPointer(network),
286+
}
287+
}
288+
289+
var _ NetworkDeviceAttachment = (*VmnetNetworkDeviceAttachment)(nil)
290+
291+
// NewVmnetNetworkDeviceAttachment creates a new VmnetNetworkDeviceAttachment with network.
292+
//
293+
// This is only supported on macOS 26 and newer, error will
294+
// be returned on older versions.
295+
func NewVmnetNetworkDeviceAttachment(network VmnetNetwork) (*VmnetNetworkDeviceAttachment, error) {
296+
if err := macOSAvailable(26); err != nil {
297+
return nil, err
298+
}
299+
300+
attachment := &VmnetNetworkDeviceAttachment{
301+
pointer: objc.NewPointer(
302+
C.newVZVmnetNetworkDeviceAttachment(
303+
objc.Ptr(network),
304+
),
305+
),
306+
}
307+
objc.SetFinalizer(attachment, func(self *VmnetNetworkDeviceAttachment) {
308+
objc.Release(self)
309+
})
310+
return attachment, nil
311+
}
312+
263313
// NetworkDeviceAttachment for a network device attachment.
264314
// see: https://developer.apple.com/documentation/virtualization/vznetworkdeviceattachment?language=objc
265315
type NetworkDeviceAttachment interface {
@@ -373,6 +423,10 @@ func (m *MACAddress) String() string {
373423
return cstring.String()
374424
}
375425

426+
func (m *MACAddress) EthernetAddress() C.ether_addr_t {
427+
return C.getVZMACAddressEthernetAddress(objc.Ptr(m))
428+
}
429+
376430
func (m *MACAddress) HardwareAddr() net.HardwareAddr {
377431
hw, _ := net.ParseMAC(m.String())
378432
return hw

virtualization_11.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ void *newVZVirtioSocketDeviceConfiguration();
105105
void *newVZMACAddress(const char *macAddress);
106106
void *newRandomLocallyAdministeredVZMACAddress();
107107
const char *getVZMACAddressString(void *macAddress);
108+
ether_addr_t getVZMACAddressEthernetAddress(void *macAddress);
108109
void *newVZVirtioSocketListener(uintptr_t cgoHandle);
109110
void *VZVirtualMachine_socketDevices(void *machine);
110111
void VZVirtioSocketDevice_setSocketListenerForPort(void *socketDevice, void *vmQueue, void *listener, uint32_t port);

virtualization_11.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,18 @@ VZVirtioSocketConnectionFlat convertVZVirtioSocketConnection2Flat(void *connecti
881881
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
882882
}
883883

884+
/*!
885+
@abstract The address represented as an ether_addr_t.
886+
*/
887+
ether_addr_t getVZMACAddressEthernetAddress(void *macAddress)
888+
{
889+
if (@available(macOS 11, *)) {
890+
return [(VZMACAddress *)macAddress ethernetAddress];
891+
}
892+
893+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
894+
}
895+
884896
/*!
885897
@abstract Create a valid, random, unicast, locally administered address.
886898
@discussion The generated address is not guaranteed to be unique.

virtualization_26.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// virtualization_26.h
3+
//
4+
// Created by codehex.
5+
//
6+
7+
#pragma once
8+
9+
// FIXME(codehex): this is dirty hack to avoid clang-format error like below
10+
// "Configuration file(s) do(es) not support C++: /github.com/Code-Hex/vz/.clang-format"
11+
#define NSURLComponents NSURLComponents
12+
13+
#import "virtualization_helper.h"
14+
#import <Virtualization/Virtualization.h>
15+
#import <vmnet/vmnet.h>
16+
17+
/* exported from cgo */
18+
19+
/* macOS 26 API */
20+
// VZVmnetNetworkConfiguration
21+
void *newVZVmnetNetworkConfiguration(uint32_t mode, uint32_t *status);
22+
uint32_t VZVmnetNetworkConfiguration_setExternalInterface(void *config, const char *ifname);
23+
void VZVmnetNetworkConfiguration_disableNat44(void *config);
24+
void VZVmnetNetworkConfiguration_disableNat66(void *config);
25+
void VZVmnetNetworkConfiguration_disableDhcp(void *config);
26+
void VZVmnetNetworkConfiguration_disableDnsProxy(void *config);
27+
void VZVmnetNetworkConfiguration_disableRouterAdvertisement(void *config);
28+
uint32_t VZVmnetNetworkConfiguration_setIPv4Subnet(void *config, struct in_addr const *subnet_addr, struct in_addr const *subnet_mask);
29+
uint32_t VZVmnetNetworkConfiguration_setIPv6Prefix(void *config, struct in6_addr const *prefix, uint8_t prefix_len);
30+
uint32_t VZVmnetNetworkConfiguration_addPortForwardingRule(void *config, uint8_t protocol, sa_family_t address_family, uint16_t internal_port, uint16_t external_port, void const *internal_address);
31+
uint32_t VZVmnetNetworkConfiguration_addDhcpReservation(void *config, ether_addr_t const *client, struct in_addr const *reservation);
32+
uint32_t VZVmnetNetworkConfiguration_setMtu(void *config, uint32_t mtu);
33+
// vmnet_network
34+
void *newVZVmnetNetwork(void *config, uint32_t *status);
35+
// VZVmnetNetworkDeviceAttachment
36+
void *newVZVmnetNetworkDeviceAttachment(void *network);
37+
void *VZVmnetNetworkDeviceAttachment_network(void *attachment);
38+
// vmnet_network get subnet
39+
void VZVmnetNetwork_getIPv6Prefix(void *network, struct in6_addr *prefix, uint8_t *prefix_len);
40+
void VZVmnetNetwork_getIPv4Subnet(void *network, struct in_addr *subnet, struct in_addr *mask);

virtualization_26.m

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
//
2+
// virtualization_11.m
3+
//
4+
// Created by codehex.
5+
//
6+
7+
#import "virtualization_26.h"
8+
9+
// VZVmnetNetworkConfiguration
10+
void *newVZVmnetNetworkConfiguration(uint32_t mode, uint32_t *status)
11+
{
12+
#ifdef INCLUDE_TARGET_OSX_26
13+
if (@available(macOS 26, *)) {
14+
return vmnet_network_configuration_create(mode, status);
15+
}
16+
#endif
17+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
18+
}
19+
20+
uint32_t VZVmnetNetworkConfiguration_setExternalInterface(void *config, const char *ifname)
21+
{
22+
#ifdef INCLUDE_TARGET_OSX_26
23+
if (@available(macOS 26, *)) {
24+
return vmnet_network_configuration_set_external_interface((vmnet_network_configuration_ref)config, ifname);
25+
}
26+
#endif
27+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
28+
}
29+
30+
void VZVmnetNetworkConfiguration_disableNat44(void *config)
31+
{
32+
#ifdef INCLUDE_TARGET_OSX_26
33+
if (@available(macOS 26, *)) {
34+
vmnet_network_configuration_disable_nat44((vmnet_network_configuration_ref)config);
35+
return;
36+
}
37+
#endif
38+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
39+
}
40+
41+
void VZVmnetNetworkConfiguration_disableNat66(void *config)
42+
{
43+
#ifdef INCLUDE_TARGET_OSX_26
44+
if (@available(macOS 26, *)) {
45+
vmnet_network_configuration_disable_nat66((vmnet_network_configuration_ref)config);
46+
return;
47+
}
48+
#endif
49+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
50+
}
51+
52+
void VZVmnetNetworkConfiguration_disableDhcp(void *config)
53+
{
54+
#ifdef INCLUDE_TARGET_OSX_26
55+
if (@available(macOS 26, *)) {
56+
vmnet_network_configuration_disable_dhcp((vmnet_network_configuration_ref)config);
57+
return;
58+
}
59+
#endif
60+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
61+
}
62+
63+
void VZVmnetNetworkConfiguration_disableDnsProxy(void *config)
64+
{
65+
#ifdef INCLUDE_TARGET_OSX_26
66+
if (@available(macOS 26, *)) {
67+
vmnet_network_configuration_disable_dns_proxy((vmnet_network_configuration_ref)config);
68+
return;
69+
}
70+
#endif
71+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
72+
}
73+
74+
void VZVmnetNetworkConfiguration_disableRouterAdvertisement(void *config)
75+
{
76+
#ifdef INCLUDE_TARGET_OSX_26
77+
if (@available(macOS 26, *)) {
78+
vmnet_network_configuration_disable_router_advertisement((vmnet_network_configuration_ref)config);
79+
return;
80+
}
81+
#endif
82+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
83+
}
84+
85+
uint32_t VZVmnetNetworkConfiguration_setIPv4Subnet(void *config, struct in_addr const *subnet_addr, struct in_addr const *subnet_mask)
86+
{
87+
#ifdef INCLUDE_TARGET_OSX_26
88+
if (@available(macOS 26, *)) {
89+
return vmnet_network_configuration_set_ipv4_subnet((vmnet_network_configuration_ref)config, subnet_addr, subnet_mask);
90+
}
91+
#endif
92+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
93+
}
94+
95+
uint32_t VZVmnetNetworkConfiguration_setIPv6Prefix(void *config, struct in6_addr const *prefix, uint8_t prefix_len)
96+
{
97+
#ifdef INCLUDE_TARGET_OSX_26
98+
if (@available(macOS 26, *)) {
99+
return vmnet_network_configuration_set_ipv6_prefix((vmnet_network_configuration_ref)config, prefix, prefix_len);
100+
}
101+
#endif
102+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
103+
}
104+
105+
uint32_t VZVmnetNetworkConfiguration_addPortForwardingRule(void *config, uint8_t protocol, sa_family_t address_family, uint16_t internal_port, uint16_t external_port, void const *internal_address)
106+
{
107+
#ifdef INCLUDE_TARGET_OSX_26
108+
if (@available(macOS 26, *)) {
109+
return vmnet_network_configuration_add_port_forwarding_rule((vmnet_network_configuration_ref)config, protocol, address_family, internal_port, external_port, internal_address);
110+
}
111+
#endif
112+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
113+
}
114+
115+
uint32_t VZVmnetNetworkConfiguration_addDhcpReservation(void *config, ether_addr_t const *client, struct in_addr const *reservation)
116+
{
117+
#ifdef INCLUDE_TARGET_OSX_26
118+
if (@available(macOS 26, *)) {
119+
return vmnet_network_configuration_add_dhcp_reservation((vmnet_network_configuration_ref)config, client, reservation);
120+
}
121+
#endif
122+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
123+
}
124+
125+
uint32_t VZVmnetNetworkConfiguration_setMtu(void *config, uint32_t mtu)
126+
{
127+
#ifdef INCLUDE_TARGET_OSX_26
128+
if (@available(macOS 26, *)) {
129+
return vmnet_network_configuration_set_mtu((vmnet_network_configuration_ref)config, mtu);
130+
}
131+
#endif
132+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
133+
}
134+
135+
// vmnet_network
136+
void *newVZVmnetNetwork(void *config, uint32_t *status)
137+
{
138+
#ifdef INCLUDE_TARGET_OSX_26
139+
if (@available(macOS 26, *)) {
140+
return vmnet_network_create((vmnet_network_configuration_ref)config, status);
141+
}
142+
#endif
143+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
144+
}
145+
146+
// VZVmnetNetworkDeviceAttachment
147+
void *newVZVmnetNetworkDeviceAttachment(void *network)
148+
{
149+
#ifdef INCLUDE_TARGET_OSX_26
150+
if (@available(macOS 26, *)) {
151+
return [[VZVmnetNetworkDeviceAttachment alloc] initWithNetwork:network];
152+
}
153+
#endif
154+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
155+
}
156+
157+
void *VZVmnetNetworkDeviceAttachment_network(void *attachment)
158+
{
159+
#ifdef INCLUDE_TARGET_OSX_26
160+
if (@available(macOS 26, *)) {
161+
return [(VZVmnetNetworkDeviceAttachment *)attachment network];
162+
}
163+
#endif
164+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
165+
}
166+
167+
// vmnet_network get subnet
168+
void VZVmnetNetwork_getIPv6Prefix(void *network, struct in6_addr *prefix, uint8_t *prefix_len)
169+
{
170+
#ifdef INCLUDE_TARGET_OSX_26
171+
if (@available(macOS 26, *)) {
172+
vmnet_network_get_ipv6_prefix((vmnet_network_ref)network, prefix, prefix_len);
173+
return;
174+
}
175+
#endif
176+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
177+
}
178+
void VZVmnetNetwork_getIPv4Subnet(void *network, struct in_addr *subnet, struct in_addr *mask)
179+
{
180+
#ifdef INCLUDE_TARGET_OSX_26
181+
if (@available(macOS 26, *)) {
182+
vmnet_network_get_ipv4_subnet((vmnet_network_ref)network, subnet, mask);
183+
return;
184+
}
185+
#endif
186+
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
187+
}

virtualization_helper.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ NSDictionary *dumpProcessinfo();
4646
#pragma message("macOS 15 API has been disabled")
4747
#endif
4848

49+
// for macOS 26 API
50+
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
51+
#define INCLUDE_TARGET_OSX_26 1
52+
#else
53+
#pragma message("macOS 26 API has been disabled")
54+
#endif
55+
4956
static inline int mac_os_x_version_max_allowed()
5057
{
5158
#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED

0 commit comments

Comments
 (0)