diff --git a/cmd/host/main.go b/cmd/host/main.go index c7baeb03..7ff9276f 100644 --- a/cmd/host/main.go +++ b/cmd/host/main.go @@ -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 diff --git a/cmd/qemu-wrapper/README.md b/cmd/qemu-wrapper/README.md new file mode 100644 index 00000000..a6e48dcf --- /dev/null +++ b/cmd/qemu-wrapper/README.md @@ -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 +``` diff --git a/cmd/qemu-wrapper/main.go b/cmd/qemu-wrapper/main.go new file mode 100644 index 00000000..20cefd81 --- /dev/null +++ b/cmd/qemu-wrapper/main.go @@ -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) + } +} diff --git a/pkg/tap/protocol.go b/pkg/tap/protocol.go new file mode 100644 index 00000000..2736e343 --- /dev/null +++ b/pkg/tap/protocol.go @@ -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])) +} diff --git a/pkg/tap/switch.go b/pkg/tap/switch.go index 028658ea..f3fb97db 100644 --- a/pkg/tap/switch.go +++ b/pkg/tap/switch.go @@ -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{} +} diff --git a/pkg/types/configuration.go b/pkg/types/configuration.go index 5f10323b..f4c71eae 100644 --- a/pkg/types/configuration.go +++ b/pkg/types/configuration.go @@ -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 diff --git a/pkg/virtualnetwork/qemu.go b/pkg/virtualnetwork/qemu.go new file mode 100644 index 00000000..6f5e2fad --- /dev/null +++ b/pkg/virtualnetwork/qemu.go @@ -0,0 +1,10 @@ +package virtualnetwork + +import ( + "net" +) + +func (n *VirtualNetwork) AcceptQemu(conn net.Conn) error { + n.networkSwitch.Accept(conn) + return nil +} diff --git a/pkg/virtualnetwork/virtualnetwork.go b/pkg/virtualnetwork/virtualnetwork.go index 3180f4b2..2ce1d3d6 100644 --- a/pkg/virtualnetwork/virtualnetwork.go +++ b/pkg/virtualnetwork/virtualnetwork.go @@ -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)