Skip to content

Commit 72cc1d4

Browse files
committed
Use "192.168.x.1" to IPv4 subnet address instead of "192.168.x.0"
change `VmnetNetworkConfiguration.SetIPv4Subnet()` Signed-off-by: Norio Nomura <[email protected]>
1 parent 74eff93 commit 72cc1d4

File tree

2 files changed

+137
-7
lines changed

2 files changed

+137
-7
lines changed

vmnet.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,12 @@ func (c *VmnetNetworkConfiguration) SetIPv4Subnet(subnet netip.Prefix) error {
163163
if !subnet.Addr().Is4() {
164164
return fmt.Errorf("subnet is not ipv4")
165165
}
166-
ip := subnet.Addr().As4()
166+
if !netip.MustParsePrefix("192.168.0.0/16").Overlaps(subnet) {
167+
return fmt.Errorf("subnet %s is out of range", subnet.String())
168+
}
169+
// Use the first assignable address as the subnet address to avoid
170+
// Virtualization fails with error "Internal Virtualization error. Internal Network Error.".
171+
ip := subnet.Masked().Addr().Next().As4()
167172
mask := net.CIDRMask(subnet.Bits(), 32)
168173
var cSubnet C.struct_in_addr
169174
var cMask C.struct_in_addr

vmnet_test.go

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package vz_test
22

33
import (
44
"log"
5+
"net"
6+
"net/netip"
7+
"slices"
58
"testing"
69

710
"github.com/Code-Hex/vz/v3"
@@ -11,17 +14,31 @@ func TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMs(t *testing.T) {
1114
if vz.Available(26.0) {
1215
t.Skip("VmnetSharedMode is supported from macOS 26")
1316
}
17+
18+
// Create VmnetNetwork instance from configuration
1419
config, err := vz.NewVmnetNetworkConfiguration(vz.SharedMode)
1520
if err != nil {
1621
t.Fatal(err)
1722
}
18-
network, err := vz.NewVmnetNetwork(config)
23+
network1, err := vz.NewVmnetNetwork(config)
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
macaddress1 := randomMACAddress(t)
28+
29+
// Create another VmnetNetwork instance from serialization of the first one
30+
serialization, err := network1.CopySerialization()
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
network2, err := vz.NewVmnetNetworkWithSerialization(serialization)
1935
if err != nil {
2036
t.Fatal(err)
2137
}
38+
macaddress2 := randomMACAddress(t)
2239

23-
container1 := newVirtualizationMachine(t, ConfigureNetworkDeviceWithNetwork(network))
24-
container2 := newVirtualizationMachine(t, ConfigureNetworkDeviceWithNetwork(network))
40+
container1 := newVirtualizationMachine(t, configureNetworkDevice(network1, macaddress1))
41+
container2 := newVirtualizationMachine(t, configureNetworkDevice(network2, macaddress2))
2542
t.Cleanup(func() {
2643
if err := container1.Shutdown(); err != nil {
2744
log.Println(err)
@@ -32,12 +49,12 @@ func TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMs(t *testing.T) {
3249
})
3350

3451
// Log network information
35-
ipv4Subnet, err := network.IPv4Subnet()
52+
ipv4Subnet, err := network1.IPv4Subnet()
3653
if err != nil {
3754
t.Fatal(err)
3855
}
3956
t.Logf("Vmnet network IPv4 subnet: %s", ipv4Subnet.String())
40-
prefix, err := network.IPv6Prefix()
57+
prefix, err := network1.IPv6Prefix()
4158
if err != nil {
4259
t.Fatal(err)
4360
}
@@ -52,7 +69,68 @@ func TestVmnetSharedModeAllowsCommunicationBetweenMultipleVMs(t *testing.T) {
5269
container2.exec(t, "ping "+container1IPv4)
5370
}
5471

55-
func ConfigureNetworkDeviceWithNetwork(network *vz.VmnetNetwork) func(cfg *vz.VirtualMachineConfiguration) error {
72+
func TestVmnetSharedModeWithConfiguringIPv4(t *testing.T) {
73+
if vz.Available(26.0) {
74+
t.Skip("VmnetSharedMode is supported from macOS 26")
75+
}
76+
// Create VmnetNetwork instance from configuration
77+
config, err := vz.NewVmnetNetworkConfiguration(vz.SharedMode)
78+
if err != nil {
79+
t.Fatal(err)
80+
}
81+
// Configure IPv4 subnet
82+
ipv4Subnet := detectFreeIPv4Subnet(t, netip.MustParsePrefix("192.168.5.0/24"))
83+
if err := config.SetIPv4Subnet(ipv4Subnet); err != nil {
84+
t.Fatal(err)
85+
}
86+
// Configure DHCP reservation
87+
macaddress := randomMACAddress(t)
88+
ipv4 := "192.168.5.15"
89+
config.AddDhcpReservation(macaddress, netip.MustParseAddr(ipv4))
90+
91+
// Create VmnetNetwork instance
92+
network, err := vz.NewVmnetNetwork(config)
93+
if err != nil {
94+
t.Fatal(err)
95+
}
96+
97+
// Create VirtualizationMachine instance
98+
container := newVirtualizationMachine(t, configureNetworkDevice(network, macaddress))
99+
t.Cleanup(func() {
100+
if err := container.Shutdown(); err != nil {
101+
log.Println(err)
102+
}
103+
})
104+
105+
// Log network information
106+
ipv4SubnetConfigured, err := network.IPv4Subnet()
107+
if err != nil {
108+
t.Fatal(err)
109+
}
110+
t.Logf("Vmnet network IPv4 subnet: %s", ipv4SubnetConfigured.String())
111+
112+
// Verify the configured subnet
113+
// Compare with masked value to ignore host bits since Vmnet prefers to use first address as network address.
114+
if ipv4Subnet != ipv4SubnetConfigured.Masked() {
115+
t.Fatalf("expected IPv4 subnet %s, but got %s", ipv4Subnet.String(), ipv4SubnetConfigured.Masked().String())
116+
}
117+
118+
// Log IPv6 prefix
119+
prefix, err := network.IPv6Prefix()
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
t.Logf("Vmnet network IPv6 prefix: %s", prefix.String())
124+
125+
// Detect IP address and verify DHCP reservation
126+
containerIPv4 := container.DetectIPv4(t, "eth0")
127+
t.Logf("Container IPv4: %s", containerIPv4)
128+
if ipv4 != containerIPv4 {
129+
t.Fatalf("expected IPv4 %s, but got %s", ipv4, containerIPv4)
130+
}
131+
}
132+
133+
func configureNetworkDevice(network *vz.VmnetNetwork, macAddress *vz.MACAddress) func(cfg *vz.VirtualMachineConfiguration) error {
56134
return func(cfg *vz.VirtualMachineConfiguration) error {
57135
var configurations []*vz.VirtioNetworkDeviceConfiguration
58136
attachment, err := vz.NewVmnetNetworkDeviceAttachment(network)
@@ -63,8 +141,55 @@ func ConfigureNetworkDeviceWithNetwork(network *vz.VmnetNetwork) func(cfg *vz.Vi
63141
if err != nil {
64142
return err
65143
}
144+
config.SetMACAddress(macAddress)
66145
configurations = append(configurations, config)
67146
cfg.SetNetworkDevicesVirtualMachineConfiguration(configurations)
68147
return nil
69148
}
70149
}
150+
151+
func detectFreeIPv4Subnet(t *testing.T, prefer netip.Prefix) netip.Prefix {
152+
targetPrefix := netip.MustParsePrefix("192.168.0.0/16")
153+
hostNetIfs, err := net.Interfaces()
154+
if err != nil {
155+
t.Fatal(err)
156+
}
157+
candidates := make([]netip.Prefix, len(hostNetIfs))
158+
for _, hostNetIf := range hostNetIfs {
159+
hostNetAddrs, err := hostNetIf.Addrs()
160+
if err != nil {
161+
continue
162+
}
163+
for _, hostNetAddr := range hostNetAddrs {
164+
netIPNet, ok := hostNetAddr.(*net.IPNet)
165+
if !ok {
166+
continue
167+
}
168+
hostPrefix := netip.MustParsePrefix(netIPNet.String())
169+
if targetPrefix.Overlaps(hostPrefix) {
170+
candidates = append(candidates, hostPrefix)
171+
}
172+
}
173+
}
174+
slices.SortFunc(candidates, func(l, r netip.Prefix) int {
175+
if l.Addr().Less(r.Addr()) {
176+
return -1
177+
}
178+
return 1
179+
})
180+
for _, candidate := range candidates {
181+
if prefer.Addr() != candidate.Addr() {
182+
return prefer
183+
}
184+
}
185+
t.Fatal("no free IPv4 subnet found")
186+
return netip.Prefix{}
187+
}
188+
189+
func randomMACAddress(t *testing.T) *vz.MACAddress {
190+
mac, err := vz.NewRandomLocallyAdministeredMACAddress()
191+
if err != nil {
192+
t.Fatal(err)
193+
}
194+
return mac
195+
}

0 commit comments

Comments
 (0)