Add Qemu support

User can now choose between vpnkit or qemu protocol.

Qemu needs to be run with this argument:
`-netdev socket,id=vlan,connect=IP:PORT -device virtio-net-pci,netdev=vlan`
where IP and PORT represent the daemon on the host.

This extra port allocation can be avoided with the qemu-wrapper in cmd/.
The daemon can listen a unix domain socket, the wrapper will pass it to
Qemu as a file descriptor.
This commit is contained in:
Guillaume Rose
2021-04-30 10:55:17 +02:00
parent fc2545c42f
commit e29d81aabd
8 changed files with 141 additions and 10 deletions

View File

@@ -22,6 +22,7 @@ var (
mtu int
endpoints arrayFlags
vpnkitSocket string
qemuSocket string
)
func main() {
@@ -29,6 +30,7 @@ func main() {
flag.BoolVar(&debug, "debug", false, "debug")
flag.IntVar(&mtu, "mtu", 1500, "mtu")
flag.StringVar(&vpnkitSocket, "listen-vpnkit", "", "VPNKit socket to be used by Hyperkit")
flag.StringVar(&qemuSocket, "listen-qemu", "", "Socket to be used by Qemu")
flag.Parse()
if debug {
@@ -39,6 +41,15 @@ func main() {
endpoints = append(endpoints, transport.DefaultURL)
}
if vpnkitSocket != "" && qemuSocket != "" {
log.Fatal("cannot use qemu and vpnkit protocol at the same time")
}
protocol := types.HyperKitProtocol
if qemuSocket != "" {
protocol = types.QemuProtocol
}
if err := run(&types.Configuration{
Debug: debug,
CaptureFile: captureFile(),
@@ -89,6 +100,7 @@ func main() {
VpnKitUUIDMacAddresses: map[string]string{
"c3d68012-0208-11ea-9fd7-f2189899ab08": "5a:94:ef:e4:0c:ee",
},
Protocol: protocol,
}, endpoints); err != nil {
log.Fatal(err)
}
@@ -162,6 +174,27 @@ func run(configuration *types.Configuration, endpoints []string) error {
}()
}
if qemuSocket != "" {
qemuListener, err := transport.Listen(qemuSocket)
if err != nil {
return err
}
go func() {
for {
conn, err := qemuListener.Accept()
if err != nil {
log.Errorf("qemu accept error: %s", err)
continue
}
go func() {
if err := vn.AcceptQemu(conn); err != nil {
log.Errorf("qemu error: %s", err)
}
}()
}
}()
}
ln, err := vn.Listen("tcp", fmt.Sprintf("%s:8080", configuration.GatewayIP))
if err != nil {
return err

View File

@@ -0,0 +1,6 @@
Qemu doesn't accept a unix socket as netdev, only a file descriptro.
This wrapper is filling the gap.
```
$ ./qemu-wrapper /tmp/qemu.sock qemu-system-x86_64 [...] -netdev socket,id=vlan,fd=3 -device virtio-net-pci,netdev=vlan
```

27
cmd/qemu-wrapper/main.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import (
"log"
"net"
"os"
"os/exec"
)
func main() {
conn, err := net.Dial("unix", os.Args[1])
if err != nil {
log.Fatal(err)
}
fd, err := conn.(*net.UnixConn).File()
if err != nil {
log.Fatal(err)
}
cmd := exec.Command(os.Args[2], os.Args[3:]...)
cmd.ExtraFiles = append(cmd.ExtraFiles, fd)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}

39
pkg/tap/protocol.go Normal file
View File

@@ -0,0 +1,39 @@
package tap
import "encoding/binary"
type protocol interface {
Buf() []byte
Write(buf []byte, size int)
Read(buf []byte) int
}
type hyperkitProtocol struct {
}
func (s *hyperkitProtocol) Buf() []byte {
return make([]byte, 2)
}
func (s *hyperkitProtocol) Write(buf []byte, size int) {
binary.LittleEndian.PutUint16(buf, uint16(size))
}
func (s *hyperkitProtocol) Read(buf []byte) int {
return int(binary.LittleEndian.Uint16(buf[0:2]))
}
type qemuProtocol struct {
}
func (s *qemuProtocol) Buf() []byte {
return make([]byte, 4)
}
func (s *qemuProtocol) Write(buf []byte, size int) {
binary.BigEndian.PutUint32(buf, uint32(size))
}
func (s *qemuProtocol) Read(buf []byte) int {
return int(binary.BigEndian.Uint32(buf[0:4]))
}

View File

@@ -1,13 +1,13 @@
package tap
import (
"encoding/binary"
"fmt"
"io"
"net"
"sync"
"sync/atomic"
"github.com/code-ready/gvisor-tap-vsock/pkg/types"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/pkg/errors"
@@ -45,14 +45,17 @@ type Switch struct {
writeLock sync.Mutex
gateway VirtualDevice
protocol protocol
}
func NewSwitch(debug bool, mtu int) *Switch {
func NewSwitch(debug bool, mtu int, protocol types.Protocol) *Switch {
return &Switch{
debug: debug,
maxTransmissionUnit: mtu,
conns: make(map[int]net.Conn),
cam: make(map[tcpip.LinkAddress]int),
protocol: protocolImplementation(protocol),
}
}
@@ -108,8 +111,8 @@ func (e *Switch) connect(conn net.Conn) (int, bool) {
}
func (e *Switch) tx(src, dst tcpip.LinkAddress, pkt *stack.PacketBuffer) error {
size := make([]byte, 2)
binary.LittleEndian.PutUint16(size, uint16(pkt.Size()))
size := e.protocol.Buf()
e.protocol.Write(size, pkt.Size())
e.writeLock.Lock()
defer e.writeLock.Unlock()
@@ -179,17 +182,14 @@ func (e *Switch) disconnect(id int, conn net.Conn) {
}
func (e *Switch) rx(id int, conn net.Conn) error {
sizeBuf := make([]byte, 2)
sizeBuf := e.protocol.Buf()
for {
n, err := io.ReadFull(conn, sizeBuf)
if err != nil {
return errors.Wrap(err, "cannot read size from socket")
}
if n != 2 {
return fmt.Errorf("unexpected size %d", n)
}
size := int(binary.LittleEndian.Uint16(sizeBuf[0:2]))
size := int(e.protocol.Read(sizeBuf))
buf := make([]byte, size)
n, err = io.ReadFull(conn, buf)
@@ -235,3 +235,10 @@ func (e *Switch) rx(id int, conn net.Conn) error {
atomic.AddUint64(&e.Received, uint64(size))
}
}
func protocolImplementation(protocol types.Protocol) protocol {
if protocol == types.QemuProtocol {
return &qemuProtocol{}
}
return &hyperkitProtocol{}
}

View File

@@ -24,8 +24,17 @@ type Configuration struct {
DHCPStaticLeases map[string]string
VpnKitUUIDMacAddresses map[string]string
Protocol Protocol
}
type Protocol string
const (
HyperKitProtocol Protocol = "hyperkit"
QemuProtocol Protocol = "qemu"
)
type Zone struct {
Name string
Records []Record

View File

@@ -0,0 +1,10 @@
package virtualnetwork
import (
"net"
)
func (n *VirtualNetwork) AcceptQemu(conn net.Conn) error {
n.networkSwitch.Accept(conn)
return nil
}

View File

@@ -45,7 +45,7 @@ func New(configuration *types.Configuration) (*VirtualNetwork, error) {
if err != nil {
return nil, errors.Wrap(err, "cannot create tap endpoint")
}
networkSwitch := tap.NewSwitch(configuration.Debug, configuration.MTU)
networkSwitch := tap.NewSwitch(configuration.Debug, configuration.MTU, configuration.Protocol)
tapEndpoint.Connect(networkSwitch)
networkSwitch.Connect(tapEndpoint)