Skip to content

Commit adf5456

Browse files
committed
feat: Add limactl vz-vmnet
It shares `VmnetNetwork` serialization between VMs. - `limactl vz-vmnet --enable-mach-service`: register Mach service and launch - `limactl vz-vmnet --enable-mach-service=false`: unregister Mach service `limactl vz-vmnet` does: - Receives a registration payload from VZ driver with fields: - `Network`: name of the network ("shared", "host", etc) - `CDHash`: `cdhash` bytes of the executable. - `Configuration`: `[]bytes@ representing `VzNetworkConfig` in JSON. - `Serialization`: serialization created by `VmnetNetwork.CopySerialization()` - Validates the provided cdhash matches to client's cdhash by using xpc_peer_requirement API. - Check the existence of the host interface using `VzNetworkConfig.Subnet`. - If the cdhash is valid and the interface is not exists, accepts registration to serialization entries. - If `Serialization` is not in payload and the network registration exists, reply the payload to client if the registered network still exists. - reply error on otherwise. VZ driver does: - Check the existence of the host interface using `VzNetworkConfig.Subnet`. - If exists: - Retrieves the existing registration payload from `lima vz-vmnet`. - Validate cdhash in payload matches with the self cdhash - If the `VzNetworkConfig` changed, produce a warning to log - Create a VmnetNetwork from the serialization - If not exists, Create a VmnetNetwork from `VzNetworkConfig`, and register them to `lima vz-vmnet` Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
1 parent 0f6d2d9 commit adf5456

22 files changed

+989
-81
lines changed

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ func newApp() *cobra.Command {
208208
newNetworkCommand(),
209209
newCloneCommand(),
210210
newRenameCommand(),
211+
newvzvmnetCommand(),
211212
)
212213
addPluginCommands(rootCmd)
213214

cmd/limactl/vz-vmnet.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func newvzvmnetCommand() *cobra.Command {
11+
newCommand := &cobra.Command{
12+
Use: "vz-vmnet",
13+
Short: "Run vz-vmnet",
14+
Args: cobra.ExactArgs(0),
15+
RunE: newvzvmnetAction,
16+
ValidArgsFunction: newvzvmnetComplete,
17+
Hidden: true,
18+
}
19+
newCommand.Flags().Bool("enable-mach-service", false, "Enable Mach service")
20+
newCommand.Flags().String("mach-service", "", "Run as Mach service")
21+
_ = newCommand.Flags().MarkHidden("mach-service")
22+
return newCommand
23+
}
24+
25+
func newvzvmnetComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
26+
return bashCompleteInstanceNames(cmd)
27+
}

cmd/limactl/vz-vmnet_darwin.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"errors"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
12+
"github.com/coreos/go-semver/semver"
13+
"github.com/spf13/cobra"
14+
15+
"github.com/lima-vm/lima/v2/pkg/osutil"
16+
"github.com/lima-vm/lima/v2/pkg/vzvmnet"
17+
)
18+
19+
func newvzvmnetAction(cmd *cobra.Command, _ []string) error {
20+
macOSProductVersion, err := osutil.ProductVersion()
21+
if err != nil {
22+
return err
23+
}
24+
if macOSProductVersion.LessThan(*semver.New("26.0.0")) {
25+
return errors.New("vz-vmnet requires macOS 26 or higher to run")
26+
}
27+
28+
if !cmd.HasLocalFlags() {
29+
return cmd.Help()
30+
}
31+
32+
ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGTERM)
33+
defer cancel()
34+
35+
if machServiceName, _ := cmd.Flags().GetString("mach-service"); machServiceName != "" {
36+
return vzvmnet.RunMachService(ctx, machServiceName)
37+
} else if enableMachService, _ := cmd.Flags().GetBool("enable-mach-service"); enableMachService {
38+
return vzvmnet.RegisterMachService(ctx)
39+
}
40+
return vzvmnet.UnregisterMachService(ctx)
41+
}

cmd/limactl/vz-vmnet_nodarwin.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !darwin
2+
3+
// SPDX-FileCopyrightText: Copyright The Lima Authors
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package main
7+
8+
import (
9+
"errors"
10+
11+
"github.com/spf13/cobra"
12+
)
13+
14+
func newvzvmnetAction(_ *cobra.Command, _ []string) error {
15+
return errors.New("vz-vmnet command is only supported on macOS")
16+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,4 @@ require (
148148
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
149149
)
150150

151-
replace github.com/Code-Hex/vz/v3 => github.com/norio-nomura/vz/v3 v3.7.2-0.20251122122159-6617c8faa123
151+
replace github.com/Code-Hex/vz/v3 => github.com/norio-nomura/vz/v3 v3.7.2-0.20251212095603-d3fad75f665e

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
207207
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
208208
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
209209
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
210-
github.com/norio-nomura/vz/v3 v3.7.2-0.20251122122159-6617c8faa123 h1:3Xzg1W5gel17So2d2NSA+flx6yoyknx5nG9Pb6eZU6s=
211-
github.com/norio-nomura/vz/v3 v3.7.2-0.20251122122159-6617c8faa123/go.mod h1:+0IVfZY7N/7Vv5KpZWbEgTRK6jMg4s7DVM+op2hdyrs=
210+
github.com/norio-nomura/vz/v3 v3.7.2-0.20251212095603-d3fad75f665e h1:6qEBUBrWQ/agtdr8rH9bolasuRpAPsbBcjTGlfzy3fA=
211+
github.com/norio-nomura/vz/v3 v3.7.2-0.20251212095603-d3fad75f665e/go.mod h1:+0IVfZY7N/7Vv5KpZWbEgTRK6jMg4s7DVM+op2hdyrs=
212212
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
213213
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
214214
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=

pkg/driver/vz/vm_darwin.go

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/lima-vm/lima/v2/pkg/networks/usernet"
3838
"github.com/lima-vm/lima/v2/pkg/osutil"
3939
"github.com/lima-vm/lima/v2/pkg/store"
40+
"github.com/lima-vm/lima/v2/pkg/vzvmnet"
4041
)
4142

4243
// diskImageCachingMode is set to DiskImageCachingModeCached so as to avoid disk corruption on ARM:
@@ -363,7 +364,8 @@ func attachNetwork(ctx context.Context, inst *limatype.Instance, vmConfig *vz.Vi
363364
}
364365

365366
for i, nw := range inst.Networks {
366-
if nw.VZNAT != nil && *nw.VZNAT {
367+
switch {
368+
case nw.VZNAT != nil && *nw.VZNAT:
367369
attachment, err := vz.NewNATNetworkDeviceAttachment()
368370
if err != nil {
369371
return err
@@ -373,30 +375,16 @@ func attachNetwork(ctx context.Context, inst *limatype.Instance, vmConfig *vz.Vi
373375
return err
374376
}
375377
configurations = append(configurations, networkConfig)
376-
} else if nw.VZShared != nil && *nw.VZShared {
377-
config, err := vz.NewVmnetNetworkConfiguration(vz.SharedMode)
378-
if err != nil {
379-
return err
380-
}
381-
network, err := vz.NewVmnetNetwork(config)
382-
if err != nil {
383-
return err
384-
}
385-
attachment, err := vz.NewVmnetNetworkDeviceAttachment(network)
386-
if err != nil {
387-
return err
388-
}
389-
networkConfig, err := newVirtioNetworkDeviceConfiguration(attachment, nw.MACAddress)
378+
case nw.Vz != "":
379+
nwCfg, err := networks.LoadConfig()
390380
if err != nil {
391381
return err
392382
}
393-
configurations = append(configurations, networkConfig)
394-
} else if nw.VZHost != nil && *nw.VZHost {
395-
config, err := vz.NewVmnetNetworkConfiguration(vz.HostMode)
396-
if err != nil {
397-
return err
383+
vzCfg, ok := nwCfg.Vz[nw.Vz]
384+
if !ok {
385+
return fmt.Errorf("networks.yaml: 'vz: %s' is not defined", nw.Vz)
398386
}
399-
network, err := vz.NewVmnetNetwork(config)
387+
network, err := vzvmnet.RequestVmnetNetwork(ctx, nw.Vz, vzCfg)
400388
if err != nil {
401389
return err
402390
}
@@ -409,7 +397,7 @@ func attachNetwork(ctx context.Context, inst *limatype.Instance, vmConfig *vz.Vi
409397
return err
410398
}
411399
configurations = append(configurations, networkConfig)
412-
} else if nw.Lima != "" {
400+
case nw.Lima != "":
413401
nwCfg, err := networks.LoadConfig()
414402
if err != nil {
415403
return err
@@ -461,7 +449,7 @@ func attachNetwork(ctx context.Context, inst *limatype.Instance, vmConfig *vz.Vi
461449
configurations = append(configurations, networkConfig)
462450
}
463451
}
464-
} else if nw.Socket != "" {
452+
case nw.Socket != "":
465453
clientFile, err := DialQemu(ctx, nw.Socket)
466454
if err != nil {
467455
return err

pkg/driver/vz/vz_driver_darwin.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,7 @@ func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
280280

281281
for i, nw := range cfg.Networks {
282282
if unknown := reflectutil.UnknownNonEmptyFields(nw, "VZNAT",
283-
"VZShared",
284-
"VZHost",
283+
"Vz",
285284
"Lima",
286285
"Socket",
287286
"MACAddress",
@@ -290,9 +289,9 @@ func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
290289
); len(unknown) > 0 {
291290
logrus.Warnf("vmType %s: ignoring networks[%d]: %+v", *cfg.VMType, i, unknown)
292291
}
293-
if (nw.VZShared != nil && *nw.VZShared) || (nw.VZHost != nil && *nw.VZHost) {
292+
if nw.Vz != "" {
294293
if macOSProductVersion.LessThan(*semver.New("26.0.0")) {
295-
return fmt.Errorf("networks[%d]: VZShared and VZHost require macOS 26.0 or later", i)
294+
return fmt.Errorf("networks[%d]: 'vz: %s' require macOS 26.0 or later", i, nw.Vz)
296295
}
297296
}
298297
}

pkg/limatmpl/embed.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,13 +543,9 @@ func (tmpl *Template) combineNetworks() {
543543
tmpl.copyListEntryField(networks, dst, src, "vzNAT")
544544
dest.VZNAT = nw.VZNAT
545545
}
546-
if dest.VZShared == nil && nw.VZShared != nil {
547-
tmpl.copyListEntryField(networks, dst, src, "vzShared")
548-
dest.VZShared = nw.VZShared
549-
}
550-
if dest.VZHost == nil && nw.VZHost != nil {
551-
tmpl.copyListEntryField(networks, dst, src, "vzHost")
552-
dest.VZHost = nw.VZHost
546+
if dest.Vz == "" && nw.Vz != "" {
547+
tmpl.copyListEntryField(networks, dst, src, "vz")
548+
dest.Vz = nw.Vz
553549
}
554550
if dest.Metric == nil && nw.Metric != nil {
555551
tmpl.copyListEntryField(networks, dst, src, "metric")

pkg/limatype/lima_yaml.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,9 @@ type Network struct {
317317
Socket string `yaml:"socket,omitempty" json:"socket,omitempty"`
318318
// VZNAT uses VZNATNetworkDeviceAttachment. Needs VZ. No root privilege is required.
319319
VZNAT *bool `yaml:"vzNAT,omitempty" json:"vzNAT,omitempty"`
320-
// VZShared, and VZHost use VZVmnetNetworkDeviceAttachment. Needs VZ. No root privilege is required.
320+
// Vz uses VZVmnetNetworkDeviceAttachment. Needs VZ. No root privilege is required.
321321
// Requires macOS 26.0 or later.
322-
VZShared *bool `yaml:"vzShared,omitempty" json:"vzShared,omitempty"`
323-
VZHost *bool `yaml:"vzHost,omitempty" json:"vzHost,omitempty"`
322+
Vz string `yaml:"vz,omitempty" json:"vz,omitempty"`
324323

325324
MACAddress string `yaml:"macAddress,omitempty" json:"macAddress,omitempty"`
326325
Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`

0 commit comments

Comments
 (0)