diff --git a/internal/netif/tap_linux.go b/internal/netif/tap_linux.go new file mode 100644 index 0000000..a5abc9c --- /dev/null +++ b/internal/netif/tap_linux.go @@ -0,0 +1,28 @@ +//go:build linux + +package netif + +import ( + "fmt" + + "git.g3e.fr/syonad/two/internal/netns" + "github.com/vishvananda/netlink" +) + +func CreateTap(tapID int, bridgeName, vpcName string) error { + name := fmt.Sprintf("tap%d", tapID) + + return netns.Call(vpcName, func() error { + tap := &netlink.Tuntap{ + LinkAttrs: netlink.LinkAttrs{Name: name}, + Mode: netlink.TUNTAP_MODE_TAP, + } + if err := netlink.LinkAdd(tap); err != nil { + return err + } + if err := BridgeSetMaster(name, bridgeName); err != nil { + return err + } + return LinkSetUp(name) + }) +} diff --git a/internal/netif/tap_other.go b/internal/netif/tap_other.go new file mode 100644 index 0000000..a81fefb --- /dev/null +++ b/internal/netif/tap_other.go @@ -0,0 +1,9 @@ +//go:build !linux + +package netif + +import "errors" + +func CreateTap(_ int, _, _ string) error { + return errors.New("netif: tap not supported on this platform") +} diff --git a/internal/qmp/send.go b/internal/qmp/send.go new file mode 100644 index 0000000..8ce6711 --- /dev/null +++ b/internal/qmp/send.go @@ -0,0 +1,50 @@ +package qmp + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "strings" +) + +func Send(socketPath string, commands []string) ([]json.RawMessage, error) { + conn, err := net.Dial("unix", socketPath) + if err != nil { + return nil, fmt.Errorf("qmp dial: %w", err) + } + defer conn.Close() + + r := bufio.NewReader(conn) + + if _, err := r.ReadString('\n'); err != nil { + return nil, fmt.Errorf("qmp read greeting: %w", err) + } + + var sb strings.Builder + sb.WriteString(`{ "execute": "qmp_capabilities" }`) + for _, cmd := range commands { + sb.WriteByte('\n') + sb.WriteString(cmd) + } + sb.WriteByte('\n') + + if _, err := fmt.Fprint(conn, sb.String()); err != nil { + return nil, fmt.Errorf("qmp write: %w", err) + } + + if _, err := r.ReadString('\n'); err != nil { + return nil, fmt.Errorf("qmp read capabilities response: %w", err) + } + + var results []json.RawMessage + for range commands { + line, err := r.ReadString('\n') + if err != nil { + return nil, fmt.Errorf("qmp read response: %w", err) + } + results = append(results, json.RawMessage(line)) + } + + return results, nil +} diff --git a/internal/vm/start_linux.go b/internal/vm/start_linux.go new file mode 100644 index 0000000..9f59261 --- /dev/null +++ b/internal/vm/start_linux.go @@ -0,0 +1,53 @@ +//go:build linux + +package vm + +import ( + "fmt" + "os/exec" + + "git.g3e.fr/syonad/two/internal/netns" +) + +type Config struct { + Name string + VpcName string + TapID int + Mac string + VolumePath string + Memory int + CPUs int +} + +func Start(cfg Config) error { + memory := cfg.Memory + if memory == 0 { + memory = 512 + } + + cpus := cfg.CPUs + if cpus == 0 { + cpus = 1 + } + + return netns.Call(cfg.VpcName, func() error { + cmd := exec.Command("qemu-system-x86_64", + "-enable-kvm", + "-cpu", "host", + "-m", fmt.Sprintf("%d", memory), + "-smp", fmt.Sprintf("%d", cpus), + "-serial", fmt.Sprintf("unix:/tmp/%s.sock,server,nowait", cfg.Name), + "-monitor", fmt.Sprintf("unix:/tmp/%s.mon-sock,server,nowait", cfg.Name), + "-qmp", fmt.Sprintf("unix:/tmp/%s.qmp-sock,server,nowait", cfg.Name), + "-display", "none", + "-drive", fmt.Sprintf("file=%s,if=virtio", cfg.VolumePath), + "-netdev", fmt.Sprintf("tap,id=net0,ifname=tap%d,script=no,downscript=no", cfg.TapID), + "-device", fmt.Sprintf("virtio-net-pci,netdev=net0,mac=%s", cfg.Mac), + "-daemonize", + ) + if err := cmd.Run(); err != nil { + return fmt.Errorf("qemu-system-x86_64: %w", err) + } + return nil + }) +} diff --git a/internal/vm/start_other.go b/internal/vm/start_other.go new file mode 100644 index 0000000..9fb56fc --- /dev/null +++ b/internal/vm/start_other.go @@ -0,0 +1,14 @@ +//go:build !linux + +package vm + +import "errors" + +type Config struct { + Name, VpcName, Mac, VolumePath string + TapID, Memory, CPUs int +} + +func Start(cfg Config) error { + return errors.New("vm: not supported on this platform") +}