From 714be8ef249c68ebab706186e4e17680ebc6251f Mon Sep 17 00:00:00 2001 From: Balaji Vijayakumar Date: Sat, 7 Jan 2023 10:49:51 +0530 Subject: [PATCH] support socket_vmnet for vz driver Signed-off-by: Balaji Vijayakumar --- docs/network.md | 10 ++-- examples/vmnet.yaml | 3 +- pkg/vz/network_darwin.go | 107 +++++++++++++++++++++++++++++++++++++ pkg/vz/vm_darwin.go | 46 ++++++++++++++++ pkg/vz/vz_driver_darwin.go | 2 + 5 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 pkg/vz/network_darwin.go diff --git a/docs/network.md b/docs/network.md index 1484c8e3c3a..7f19924c66c 100644 --- a/docs/network.md +++ b/docs/network.md @@ -46,11 +46,11 @@ If `useHostResolver` is false, then DNS servers can be configured manually in `l VMNet assigns a "real" IP address that is reachable from the host. -The configuration steps are different across QEMU and VZ: -- [QEMU](#qemu) -- [VZ](#vz) +The configuration steps are different for each network type: +- [socket_vmnet](#socket_vmnet) +- [vzNAT](#vzNAT) -### QEMU +### socket_vmnet #### Managed (192.168.105.0/24) [`socket_vmnet`](https://github.com/lima-vm/socket_vmnet) is required for adding another guest IP that is accessible from the host and other guests. @@ -176,7 +176,7 @@ networks: -### VZ +### vzNAT > **Warning** > "vz" mode is experimental diff --git a/examples/vmnet.yaml b/examples/vmnet.yaml index aae6cf8d42b..e092640d62c 100644 --- a/examples/vmnet.yaml +++ b/examples/vmnet.yaml @@ -1,5 +1,4 @@ -# Example to enable vmnet.framework for QEMU. -# VZ users should refer to experimental/vz.yaml +# Example to enable vmnet.framework. # Usage: # brew install socket_vmnet diff --git a/pkg/vz/network_darwin.go b/pkg/vz/network_darwin.go new file mode 100644 index 00000000000..84f72d843fb --- /dev/null +++ b/pkg/vz/network_darwin.go @@ -0,0 +1,107 @@ +//go:build darwin && !no_vz +// +build darwin,!no_vz + +package vz + +import ( + "context" + "encoding/binary" + "io" + "net" + "os" + "time" + + "github.com/sirupsen/logrus" + "inet.af/tcpproxy" +) + +// DialQemu support connecting to QEMU supported network stack via unix socket +// Returns os.File, connected dgram connection to be used for vz +func DialQemu(unixSock string) (*os.File, error) { + unixConn, err := net.Dial("unix", unixSock) + if err != nil { + return nil, err + } + qemuConn := &QEMUPacketConn{unixConn: unixConn} + + server, client, err := createSockPair() + if err != nil { + return nil, err + } + dgramConn, err := net.FileConn(server) + if err != nil { + return nil, err + } + + remote := tcpproxy.DialProxy{ + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return dgramConn, nil + }, + } + go remote.HandleConn(qemuConn) + + return client, nil +} + +// QEMUPacketConn converts raw network packet to a QEMU supported network packet. +type QEMUPacketConn struct { + unixConn net.Conn +} + +var _ net.Conn = (*QEMUPacketConn)(nil) + +// Read gets rid of the QEMU header packet and returns the raw packet as response +func (v *QEMUPacketConn) Read(b []byte) (n int, err error) { + header := make([]byte, 4) + _, err = io.ReadFull(v.unixConn, header) + if err != nil { + logrus.Errorln("Failed to read header", err) + } + + size := binary.BigEndian.Uint32(header) + reader := io.LimitReader(v.unixConn, int64(size)) + _, err = reader.Read(b) + + if err != nil { + logrus.Errorln("Failed to read packet", err) + } + return int(size), nil +} + +// Write puts QEMU header packet first and then writes the raw packet +func (v *QEMUPacketConn) Write(b []byte) (n int, err error) { + header := make([]byte, 4) + binary.BigEndian.PutUint32(header, uint32(len(b))) + _, err = v.unixConn.Write(header) + if err != nil { + logrus.Errorln("Failed to write header", err) + } + + write, err := v.unixConn.Write(b) + if err != nil { + logrus.Errorln("Failed to write packet", err) + } + return write, nil +} +func (v *QEMUPacketConn) Close() error { + return v.unixConn.Close() +} +func (v *QEMUPacketConn) LocalAddr() net.Addr { + return v.unixConn.LocalAddr() +} + +func (v *QEMUPacketConn) RemoteAddr() net.Addr { + return v.unixConn.RemoteAddr() +} + +func (v *QEMUPacketConn) SetDeadline(t time.Time) error { + return v.unixConn.SetDeadline(t) +} + +func (v *QEMUPacketConn) SetReadDeadline(t time.Time) error { + return v.unixConn.SetReadDeadline(t) +} + +func (v *QEMUPacketConn) SetWriteDeadline(t time.Time) error { + return v.unixConn.SetWriteDeadline(t) +} diff --git a/pkg/vz/vm_darwin.go b/pkg/vz/vm_darwin.go index 4b013cc377d..2c2c6182cc3 100644 --- a/pkg/vz/vm_darwin.go +++ b/pkg/vz/vm_darwin.go @@ -234,6 +234,52 @@ func attachNetwork(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigu } configurations = append(configurations, networkConfig) } + + if nw.Lima != "" { + nwCfg, err := networks.Config() + if err != nil { + return err + } + socketVMNetOk, err := nwCfg.IsDaemonInstalled(networks.SocketVMNet) + if err != nil { + return err + } + if socketVMNetOk { + logrus.Debugf("Using socketVMNet (%q)", nwCfg.Paths.SocketVMNet) + sock, err := networks.Sock(nw.Lima) + if err != nil { + return err + } + + clientFile, err := DialQemu(sock) + if err != nil { + return err + } + attachment, err := vz.NewFileHandleNetworkDeviceAttachment(clientFile) + if err != nil { + return err + } + networkConfig, err = newVirtioNetworkDeviceConfiguration(attachment, nw.MACAddress) + if err != nil { + return err + } + configurations = append(configurations, networkConfig) + } + } else if nw.Socket != "" { + clientFile, err := DialQemu(nw.Socket) + if err != nil { + return err + } + attachment, err := vz.NewFileHandleNetworkDeviceAttachment(clientFile) + if err != nil { + return err + } + networkConfig, err = newVirtioNetworkDeviceConfiguration(attachment, nw.MACAddress) + if err != nil { + return err + } + configurations = append(configurations, networkConfig) + } } vmConfig.SetNetworkDevicesVirtualMachineConfiguration(configurations) return nil diff --git a/pkg/vz/vz_driver_darwin.go b/pkg/vz/vz_driver_darwin.go index f53c51b5366..48da1c6b958 100644 --- a/pkg/vz/vz_driver_darwin.go +++ b/pkg/vz/vz_driver_darwin.go @@ -86,6 +86,8 @@ func (l *LimaVzDriver) Validate() error { for i, network := range l.Yaml.Networks { if unknown := reflectutil.UnknownNonEmptyFields(network, "VZNAT", + "Lima", + "Socket", "MACAddress", "Interface", ); len(unknown) > 0 {