//go:build linux package main // inspired by https://github.com/firecracker-microvm/firectl/blob/main/main.go#L64 import ( "antoine-roux.tk/projects/go/firecracker-netns/internal/netlink" "antoine-roux.tk/projects/go/firecracker-netns/internal/netns" "context" "fmt" "github.com/firecracker-microvm/firecracker-go-sdk" "github.com/firecracker-microvm/firecracker-go-sdk/client/models" "github.com/sirupsen/logrus" "net" "os" "runtime" ) func setupEnv() int { log := logrus.New() log.SetLevel(logrus.DebugLevel) newNs, err := netns.New() if err != nil { fmt.Println("new ns error", err) return 1 } defer func(handle netns.NsHandle) { err := handle.Close() if err != nil { fmt.Println("close ns error", err) } }(newNs) defer func(ns netns.NsHandle) { err := netns.Delete(ns) if err != nil { fmt.Println("delete ns error", err) } }(newNs) vethPair, err := netlink.NewVirtualPairing(newNs, "wlp3s0") if err != nil { fmt.Println("new Veth error", err) return 1 } defer func(veth *netlink.PairLink) { err = veth.DeleteVirtualPairing() if err != nil { fmt.Println("delete vethPair error", err) } }(vethPair) err = netns.Set(newNs) if err != nil { fmt.Println("set guest ns error", err) return 1 } firstIpTapNetwork := net.IPv4(172, 16, 0, 1) tapNetwork := net.IPNet{ IP: firstIpTapNetwork, Mask: net.CIDRMask(30, 32), } tap, err := netlink.CreateTap(tapNetwork, vethPair.Guest.Link.Attrs().Name) if err != nil { fmt.Println("create tap in guest ns error", err) return 1 } defer func(tap *netlink.Tap) { err := tap.DeleteTap() if err != nil { fmt.Println("delete tap error", err) } }(tap) // Do something with the network namespace interfaces, _ := net.Interfaces() log.Debugf("Interfaces: %v\n", interfaces) ctx := context.Background() cancel, cancelFunc := context.WithCancel(ctx) defer cancelFunc() cpuCount := int64(4) memorySize := int64(1024) isSmt := true socketPath := "/tmp/firecracker.socket" cfg := firecracker.Config{ SocketPath: socketPath, KernelImagePath: "./out/vmlinux", LogPath: "./out/firecracker.log", LogLevel: "Debug", KernelArgs: "console=ttyS0 reboot=k panic=1 pci=off", Drives: []models.Drive{ { DriveID: firecracker.String("rootfs"), PathOnHost: firecracker.String("./out/rootfs.ext4"), IsReadOnly: firecracker.Bool(false), IsRootDevice: firecracker.Bool(true), }, }, NetworkInterfaces: firecracker.NetworkInterfaces{ firecracker.NetworkInterface{ StaticConfiguration: &firecracker.StaticNetworkConfiguration{ MacAddress: "06:00:AC:10:00:02", HostDevName: tap.Link.Attrs().Name, }, }, }, MachineCfg: models.MachineConfiguration{ VcpuCount: &cpuCount, MemSizeMib: &memorySize, Smt: &isSmt, TrackDirtyPages: true, }, } firecrackerOpts := []firecracker.Opt{ firecracker.WithProcessRunner( firecracker.VMCommandBuilder{}. WithBin("firecracker"). WithSocketPath(socketPath). Build(ctx), ), firecracker.WithLogger(logrus.NewEntry(log)), } vm, err := firecracker.NewMachine(cancel, cfg, firecrackerOpts...) if err != nil { log.Errorln("create vm error", err) return 1 } defer os.Remove(cfg.SocketPath) defer vm.StopVMM() if err := vm.Start(ctx); err != nil { log.Errorln("start vm error", err) return 1 } if err := vm.Wait(ctx); err != nil { log.Errorln("wait vm error", err) return 1 } return 0 } func main() { // Lock the OS Thread, so we don't accidentally switch namespaces runtime.LockOSThread() defer runtime.UnlockOSThread() os.Exit(setupEnv()) }