Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion network.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package vz

/*
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization -framework vmnet
# include <vmnet/vmnet.h>
# include "virtualization_11.h"
# include "virtualization_13.h"
# include "virtualization_26.h"
*/
import "C"
import (
Expand All @@ -14,6 +16,7 @@ import (
"syscall"

"github.com/Code-Hex/vz/v3/internal/objc"
"github.com/Code-Hex/vz/v3/vmnet"
)

// BridgedNetwork defines a network interface that bridges a physical interface with a virtual machine.
Expand Down Expand Up @@ -266,6 +269,54 @@ func (f *FileHandleNetworkDeviceAttachment) MaximumTransmissionUnit() int {
return f.mtu
}

// VmnetNetworkDeviceAttachment is a network device attachment backed
// by an in-process vmnet logical network. The attachment binds the
// vmnet network to the virtio-net device in-kernel — no userspace
// pump. The caller builds the network via the vmnet subpackage,
// which exposes operating mode (host / shared / bridged), IPv4
// subnet, and DHCP reservations.
//
// see: https://developer.apple.com/documentation/virtualization/vzvmnetnetworkdeviceattachment
type VmnetNetworkDeviceAttachment struct {
*pointer

*baseNetworkDeviceAttachment
}

func (*VmnetNetworkDeviceAttachment) String() string {
return "VmnetNetworkDeviceAttachment"
}

// Network returns the vmnet network underlying the attachment. The
// returned wrapper owns its own +1 retain count.
func (v *VmnetNetworkDeviceAttachment) Network() *vmnet.Network {
ptr := C.VZVmnetNetworkDeviceAttachment_network(objc.Ptr(v))
return vmnet.NewNetworkFromPointer(objc.NewPointer(ptr))
}

var _ NetworkDeviceAttachment = (*VmnetNetworkDeviceAttachment)(nil)

// NewVmnetNetworkDeviceAttachment creates a new
// VmnetNetworkDeviceAttachment wrapping the given vmnet.Network.
//
// This is only supported on macOS 26 and newer, error will
// be returned on older versions.
func NewVmnetNetworkDeviceAttachment(network *vmnet.Network) (*VmnetNetworkDeviceAttachment, error) {
if err := macOSAvailable(26); err != nil {
return nil, err
}

attachment := &VmnetNetworkDeviceAttachment{
pointer: objc.NewPointer(
C.newVZVmnetNetworkDeviceAttachment(objc.Ptr(network)),
),
}
objc.SetFinalizer(attachment, func(self *VmnetNetworkDeviceAttachment) {
objc.Release(self)
})
return attachment, nil
}

// NetworkDeviceAttachment for a network device attachment.
// see: https://developer.apple.com/documentation/virtualization/vznetworkdeviceattachment?language=objc
type NetworkDeviceAttachment interface {
Expand Down
2 changes: 2 additions & 0 deletions osversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func macOSBuildTargetAvailable(version float64) error {
target = 140000 // __MAC_14_0
case 15:
target = 150000 // __MAC_15_0
case 26:
target = 260000 // __MAC_26_0
}
if allowedVersion < target {
return fmt.Errorf("%w for %.1f (the binary was built with __MAC_OS_X_VERSION_MAX_ALLOWED=%d; needs recompilation)",
Expand Down
23 changes: 23 additions & 0 deletions virtualization_26.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// virtualization_26.h
//
// Created by codehex.
//

#pragma once

#import "virtualization_helper.h"
#import <vmnet/vmnet.h>
#import <Virtualization/Virtualization.h>

// newVZVmnetNetworkDeviceAttachment wraps a vmnet_network_ref
// (created via the vmnet subpackage, passed in as void *) in a
// VZVmnetNetworkDeviceAttachment. The attachment does not retain
// the network; the caller's Network wrapper owns the +1.
void *newVZVmnetNetworkDeviceAttachment(void *network);

// VZVmnetNetworkDeviceAttachment_network reads the network back from
// an attachment. CFRetain'd before return so the Go-side Network
// wrapper can own a refcount independent of the attachment's
// storage.
void *VZVmnetNetworkDeviceAttachment_network(void *attachment);
38 changes: 38 additions & 0 deletions virtualization_26.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// virtualization_26.m
//
// Created by codehex.
//

#import "virtualization_26.h"

#ifdef INCLUDE_TARGET_OSX_26
#import <CoreFoundation/CoreFoundation.h>
#import <vmnet/vmnet.h>
#endif // INCLUDE_TARGET_OSX_26

void *newVZVmnetNetworkDeviceAttachment(void *network)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
return [[VZVmnetNetworkDeviceAttachment alloc]
initWithNetwork:(vmnet_network_ref)network];
}
#endif
(void)network;
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
return NULL;
}

void *VZVmnetNetworkDeviceAttachment_network(void *attachment)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
vmnet_network_ref network = ((VZVmnetNetworkDeviceAttachment *)attachment).network;
if (network) CFRetain(network);
return network;
}
#endif
(void)attachment;
return NULL;
}
13 changes: 13 additions & 0 deletions virtualization_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

#import <Availability.h>
#import <Foundation/Foundation.h>
// vmnet.h declares vmnet_network_ref, which Virtualization.framework's
// VZVmnetNetworkDeviceAttachment.h references via a @property. Every
// compilation unit that ends up pulling in <Virtualization/Virtualization.h>
// needs this in scope first, so include it here in the helper that
// every per-version header imports.
#import <vmnet/vmnet.h>

NSDictionary *dumpProcessinfo();
NSFileHandle *newFileHandleDupFd(int fileDescriptor, void **error);
Expand Down Expand Up @@ -47,6 +53,13 @@ NSFileHandle *newFileHandleDupFd(int fileDescriptor, void **error);
#pragma message("macOS 15 API has been disabled")
#endif

// for macOS 26 API
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
#define INCLUDE_TARGET_OSX_26 1
#else
#pragma message("macOS 26 API has been disabled")
#endif

static inline int mac_os_x_version_max_allowed()
{
#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
Expand Down
51 changes: 51 additions & 0 deletions vmnet/cgo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// vmnet.h
//
// Created by codehex.
//

#pragma once

#import <Availability.h>
#include <stdint.h>
#include <stdlib.h>

// SDK guard, mirroring the parent package's virtualization_helper.h.
// Duplicated because cgo includes are per-package (subpackages don't
// share headers with the root unless explicitly wired). Keep in sync.
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
#define INCLUDE_TARGET_OSX_26 1
#endif

// --- vmnet_network_configuration_* wrappers ------------------------------

// newNetworkConfiguration returns a +1-retained
// vmnet_network_configuration_ref for the given operating mode.
// On older macOS / older SDKs returns NULL; status (if non-NULL)
// gets the vmnet_return_t (or -1 when the API isn't compiled in).
void *newNetworkConfiguration(int mode, int *status);

// releaseNetworkConfiguration CFReleases a config ref. Safe on NULL.
void releaseNetworkConfiguration(void *config);

// setIPv4Subnet calls vmnet_network_configuration_set_ipv4_subnet.
// gatewayIP is the first usable address of the range (e.g.
// "192.168.200.1" for /24) — Apple's docstring calls the param
// "subnet_addr" but the actual semantic is the gateway IP. mask is
// dotted-quad ("255.255.255.0"). Returns vmnet_return_t (0=success).
int setIPv4Subnet(void *config, const char *gatewayIP, const char *mask);

// addDhcpReservation calls vmnet_network_configuration_add_dhcp_reservation.
// mac is exactly 6 bytes; reservationIP is a dotted-quad IPv4 string.
// Returns vmnet_return_t (0=success).
int addDhcpReservation(void *config, const uint8_t *mac, const char *reservationIP);

// --- vmnet_network_* wrappers --------------------------------------------

// newNetwork returns a +1-retained vmnet_network_ref from the given
// configuration. The configuration may be released after this call.
// On failure or older macOS / SDK returns NULL.
void *newNetwork(void *config, int *status);

// releaseNetwork CFReleases a network ref. Safe on NULL.
void releaseNetwork(void *network);
100 changes: 100 additions & 0 deletions vmnet/cgo.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// vmnet.m
//
// Created by codehex.
//

#import "cgo.h"

#ifdef INCLUDE_TARGET_OSX_26
#import <CoreFoundation/CoreFoundation.h>
#import <arpa/inet.h>
#import <net/ethernet.h>
#import <string.h>
#import <vmnet/vmnet.h>
#endif // INCLUDE_TARGET_OSX_26

void *newNetworkConfiguration(int mode, int *status)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
vmnet_return_t s = VMNET_SUCCESS;
vmnet_network_configuration_ref config =
vmnet_network_configuration_create((vmnet_mode_t)mode, &s);
if (status) *status = (int)s;
return config;
}
#endif
if (status) *status = -1;
return NULL;
}

void releaseNetworkConfiguration(void *config)
{
#ifdef INCLUDE_TARGET_OSX_26
if (config) CFRelease((vmnet_network_configuration_ref)config);
#else
(void)config;
#endif
}

// setIPv4Subnet / addDhcpReservation return 0 on success, the raw
// vmnet_return_t on failure (or -1 if the API isn't compiled in).
// vmnet's VMNET_SUCCESS is 1000, not 0 — callers can't compare the
// raw return against 0, so we normalize here.
int setIPv4Subnet(void *config, const char *gatewayIP, const char *mask)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
struct in_addr s, m;
if (inet_pton(AF_INET, gatewayIP, &s) != 1) return (int)VMNET_INVALID_ARGUMENT;
if (inet_pton(AF_INET, mask, &m) != 1) return (int)VMNET_INVALID_ARGUMENT;
vmnet_return_t rc = vmnet_network_configuration_set_ipv4_subnet(
(vmnet_network_configuration_ref)config, &s, &m);
return rc == VMNET_SUCCESS ? 0 : (int)rc;
}
#endif
(void)config; (void)gatewayIP; (void)mask;
return -1;
}

int addDhcpReservation(void *config, const uint8_t *mac, const char *reservationIP)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
ether_addr_t client;
memcpy(&client, mac, 6);
struct in_addr res;
if (inet_pton(AF_INET, reservationIP, &res) != 1) return (int)VMNET_INVALID_ARGUMENT;
vmnet_return_t rc = vmnet_network_configuration_add_dhcp_reservation(
(vmnet_network_configuration_ref)config, &client, &res);
return rc == VMNET_SUCCESS ? 0 : (int)rc;
}
#endif
(void)config; (void)mac; (void)reservationIP;
return -1;
}

void *newNetwork(void *config, int *status)
{
#ifdef INCLUDE_TARGET_OSX_26
if (@available(macOS 26, *)) {
vmnet_return_t s = VMNET_SUCCESS;
vmnet_network_ref network = vmnet_network_create(
(vmnet_network_configuration_ref)config, &s);
if (status) *status = (int)s;
return network;
}
#endif
if (status) *status = -1;
return NULL;
}

void releaseNetwork(void *network)
{
#ifdef INCLUDE_TARGET_OSX_26
if (network) CFRelease((vmnet_network_ref)network);
#else
(void)network;
#endif
}
Loading