From a6fbc1707295849bc6c3dcdc12943175bcdaf762 Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Fri, 6 Oct 2023 10:47:55 -0700 Subject: [PATCH 1/2] Create veth pair Adds a veth pair with one end attached to the default network namespace and the other end attached to the rancher desktop namespace. Signed-off-by: Nino Kodabande --- cmd/network/setup_linux.go | 162 +++++++++++++++++++++++++++++-------- 1 file changed, 129 insertions(+), 33 deletions(-) diff --git a/cmd/network/setup_linux.go b/cmd/network/setup_linux.go index 17d647f..3b5a5e4 100644 --- a/cmd/network/setup_linux.go +++ b/cmd/network/setup_linux.go @@ -18,12 +18,14 @@ import ( "fmt" "io" "io/fs" + "net" "os" "os/exec" "strconv" "github.com/linuxkit/virtsock/pkg/vsock" "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" "github.com/vishvananda/netns" "github.com/rancher-sandbox/rancher-desktop-networking/pkg/config" @@ -43,33 +45,22 @@ var ( ) const ( - nsenter = "/usr/bin/nsenter" - unshare = "/usr/bin/unshare" - vsockHandshakePort = 6669 - vsockDialPort = 6656 - defaultTapDevice = "eth0" + nsenter = "/usr/bin/nsenter" + unshare = "/usr/bin/unshare" + vsockHandshakePort = 6669 + vsockDialPort = 6656 + defaultTapDevice = "eth0" + defaultNSVeth = "veth0" + rancherDesktopNSVeth = "veth1" + defaultNamespacePID = 1 + cidrOnes = 24 + cidrBits = 32 ) func main() { - flag.BoolVar(&debug, "debug", false, "enable additional debugging") - flag.StringVar(&tapIface, "tap-interface", defaultTapDevice, "tap interface name, eg. eth0, eth1") - flag.StringVar(&subnet, "subnet", config.DefaultSubnet, - fmt.Sprintf("Subnet range with CIDR suffix that is associated to the tap interface, e,g: %s", config.DefaultSubnet)) - flag.StringVar(&tapDeviceMacAddr, "tap-mac-address", config.TapDeviceMacAddr, - "MAC address that is associated to the tap interface") - flag.StringVar(&vmSwitchPath, "vm-switch-path", "", "the path to the vm-switch binary that will run in a new namespace") - flag.StringVar(&vmSwitchLogFile, "vm-switch-logfile", "", "path to the logfile for vm-switch process") - flag.StringVar(&unshareArg, "unshare-arg", "", "the command argument to pass to the unshare program") - flag.StringVar(&logFile, "logfile", "/var/log/network-setup.log", "path to the logfile for network setup process") - flag.Parse() + initializeFlags() - if err := log.SetOutputFile(logFile, logrus.StandardLogger()); err != nil { - logrus.Fatalf("setting logger's output file failed: %v", err) - } - - if debug { - logrus.SetLevel(logrus.DebugLevel) - } + setupLogging(logFile) if vmSwitchPath == "" { logrus.Fatal("path to the vm-switch process must be provided") @@ -89,6 +80,8 @@ func main() { } logrus.Debugf("successful connection to host on CID: %v and Port: %d: connection: %+v", vsock.CIDHost, vsockDialPort, vsockConn) + originNS, _ := netns.Get() + // setup network namespace ns, err := configureNamespace() if err != nil { @@ -99,6 +92,82 @@ func main() { logrus.Fatal(err) } + connFile, err := vsockConn.File() + if err != nil { + logrus.Fatal(err) + } + + vmSwitchCmd := configureVMSwitch(ns, + vmSwitchLogFile, + vmSwitchPath, + tapIface, + subnet, + tapDeviceMacAddr, + connFile) + + if err := vmSwitchCmd.Start(); err != nil { + logrus.Fatalf("could not start the vm-switch process: %v", err) + } + logrus.Infof("successfully started the vm-switch running with a PID: %v", vmSwitchCmd.Process.Pid) + + if err := createVethPair(defaultNamespacePID, vmSwitchCmd.Process.Pid); err != nil { + logrus.Fatal(err) + } + logrus.Infof("created veth pair %s and %s", defaultNSVeth, rancherDesktopNSVeth) + + if err := configureVethPair(rancherDesktopNSVeth, "192.168.1.2"); err != nil { + logrus.Fatalf("failed setting up veth: %s for rancher desktop namespace: %v", rancherDesktopNSVeth, err) + } + + // switch back to the original namespace to configure veth0 + if err := netns.Set(originNS); err != nil { + logrus.Fatal(err) + } + if err := configureVethPair(defaultNSVeth, "192.168.1.1"); err != nil { + logrus.Fatalf("failed setting up veth: %s for rancher desktop namespace: %v", rancherDesktopNSVeth, err) + } + + if err := originNS.Close(); err != nil { + logrus.Error(err) + } + + if err := vmSwitchCmd.Wait(); err != nil { + logrus.Errorf("vm-switch exited with error: %v", err) + } +} + +func initializeFlags() { + flag.BoolVar(&debug, "debug", false, "enable additional debugging") + flag.StringVar(&tapIface, "tap-interface", defaultTapDevice, "tap interface name, eg. eth0, eth1") + flag.StringVar(&subnet, "subnet", config.DefaultSubnet, + fmt.Sprintf("Subnet range with CIDR suffix that is associated to the tap interface, e,g: %s", config.DefaultSubnet)) + flag.StringVar(&tapDeviceMacAddr, "tap-mac-address", config.TapDeviceMacAddr, + "MAC address that is associated to the tap interface") + flag.StringVar(&vmSwitchPath, "vm-switch-path", "", "the path to the vm-switch binary that will run in a new namespace") + flag.StringVar(&vmSwitchLogFile, "vm-switch-logfile", "", "path to the logfile for vm-switch process") + flag.StringVar(&unshareArg, "unshare-arg", "", "the command argument to pass to the unshare program") + flag.StringVar(&logFile, "logfile", "/var/log/network-setup.log", "path to the logfile for network setup process") + flag.Parse() +} + +func setupLogging(logFile string) { + if err := log.SetOutputFile(logFile, logrus.StandardLogger()); err != nil { + logrus.Fatalf("setting logger's output file failed: %v", err) + } + + if debug { + logrus.SetLevel(logrus.DebugLevel) + } +} + +func configureVMSwitch( + ns netns.NsHandle, + vmSwitchLogFile, + vmSwitchPath, + tapIface, + subnet, + tapDevMacAddr string, + connFile *os.File) *exec.Cmd { // Start the vm-switch process in the new namespace; we do // this as the golang runtime can switch threads at will, so it // is safer to have a whole process in a consistent namespace. @@ -111,7 +180,7 @@ func main() { "-subnet", subnet, "-tap-mac-address", - tapDeviceMacAddr, + tapDevMacAddr, } if vmSwitchLogFile != "" { args = append(args, "-logfile", vmSwitchLogFile) @@ -121,21 +190,48 @@ func main() { } vmSwitchCmd := exec.Command(nsenter, args...) - connFile, err := vsockConn.File() - if err != nil { - logrus.Fatal(err) - } // pass in the vsock connection as a FD to the // vm-switch process in the newely created namespace vmSwitchCmd.ExtraFiles = []*os.File{connFile} - if err := vmSwitchCmd.Start(); err != nil { - logrus.Fatalf("could not start the vm-switch process: %v", err) + return vmSwitchCmd +} + +func createVethPair(defaultNsPid, peerNsPid int) error { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: defaultNSVeth, + Namespace: netlink.NsPid(defaultNsPid), + }, + PeerName: rancherDesktopNSVeth, + PeerNamespace: netlink.NsPid(peerNsPid), } - logrus.Infof("successfully started the vm-switch running with a PID: %v", vmSwitchCmd.Process.Pid) + if err := netlink.LinkAdd(veth); err != nil { + return err + } + logrus.Infof("created veth pair %s and %s", defaultNSVeth, rancherDesktopNSVeth) + return nil +} - if err := vmSwitchCmd.Wait(); err != nil { - logrus.Errorf("vm-switch exited with error: %v", err) +func configureVethPair(vethName, ipAddr string) error { + veth, err := netlink.LinkByName(vethName) + if err != nil { + return err + } + + vethIP := net.IPNet{ + IP: net.ParseIP(ipAddr), + Mask: net.CIDRMask(cidrOnes, cidrBits), + } + + addr := &netlink.Addr{IPNet: &vethIP, Label: ""} + if err := netlink.AddrAdd(veth, addr); err != nil { + return err + } + + if err := netlink.LinkSetUp(veth); err != nil { + return err } + return nil } func unshareCmd(ns netns.NsHandle, args string) error { From 3d9bf1175ceea420db031328759afcb002e88bcf Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Tue, 10 Oct 2023 09:48:27 -0700 Subject: [PATCH 2/2] addresses PR comments Signed-off-by: Nino Kodabande --- cmd/network/setup_linux.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cmd/network/setup_linux.go b/cmd/network/setup_linux.go index 3b5a5e4..e925ecf 100644 --- a/cmd/network/setup_linux.go +++ b/cmd/network/setup_linux.go @@ -50,8 +50,8 @@ const ( vsockHandshakePort = 6669 vsockDialPort = 6656 defaultTapDevice = "eth0" - defaultNSVeth = "veth0" - rancherDesktopNSVeth = "veth1" + defaultNSVeth = "veth-rd0" + rancherDesktopNSVeth = "veth-rd1" defaultNamespacePID = 1 cidrOnes = 24 cidrBits = 32 @@ -80,7 +80,10 @@ func main() { } logrus.Debugf("successful connection to host on CID: %v and Port: %d: connection: %+v", vsock.CIDHost, vsockDialPort, vsockConn) - originNS, _ := netns.Get() + originNS, err := netns.Get() + if err != nil { + logrus.Errorf("failed getting a handle to the current namespace: %v", err) + } // setup network namespace ns, err := configureNamespace() @@ -110,10 +113,13 @@ func main() { } logrus.Infof("successfully started the vm-switch running with a PID: %v", vmSwitchCmd.Process.Pid) - if err := createVethPair(defaultNamespacePID, vmSwitchCmd.Process.Pid); err != nil { + err = createVethPair(defaultNamespacePID, + vmSwitchCmd.Process.Pid, + defaultNSVeth, + rancherDesktopNSVeth) + if err != nil { logrus.Fatal(err) } - logrus.Infof("created veth pair %s and %s", defaultNSVeth, rancherDesktopNSVeth) if err := configureVethPair(rancherDesktopNSVeth, "192.168.1.2"); err != nil { logrus.Fatalf("failed setting up veth: %s for rancher desktop namespace: %v", rancherDesktopNSVeth, err) @@ -196,7 +202,7 @@ func configureVMSwitch( return vmSwitchCmd } -func createVethPair(defaultNsPid, peerNsPid int) error { +func createVethPair(defaultNsPid, peerNsPid int, defaultNSVeth, rancherDesktopNSVeth string) error { veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ Name: defaultNSVeth,