diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index 3de242e..805db1a 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -228,7 +228,6 @@ func initCLI() { {s.Start, true}, } if doTasks(tasks) { - disableAutoDNS(iface) prog.setDNS() mainLog.Info().Msg("Service started") } @@ -260,7 +259,6 @@ func initCLI() { } initLogging() if doTasks([]task{{s.Stop, true}}) { - enableAutoDNS(iface) prog.resetDNS() mainLog.Info().Msg("Service stopped") } @@ -330,7 +328,6 @@ func initCLI() { } initLogging() if doTasks(tasks) { - enableAutoDNS(iface) prog.resetDNS() mainLog.Info().Msg("Service uninstalled") return @@ -551,6 +548,10 @@ func processCDFlags() { } if netIface, _ := netInterface(iface); netIface != nil { + if err := restoreNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not restore NetworkManager") + return + } logger.Debug().Str("iface", netIface.Name).Msg("Restoring DNS for interface") if err := resetDNS(netIface); err != nil { logger.Warn().Err(err).Msg("something went wrong while restoring DNS") @@ -558,6 +559,7 @@ func processCDFlags() { logger.Debug().Str("iface", netIface.Name).Msg("Restoring DNS successfully") } } + tasks := []task{{s.Uninstall, true}} if doTasks(tasks) { logger.Info().Msg("uninstalled service") diff --git a/cmd/ctrld/network_manager.go b/cmd/ctrld/network_manager.go new file mode 100644 index 0000000..670fe9c --- /dev/null +++ b/cmd/ctrld/network_manager.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "os" + "path/filepath" + "runtime" + "time" + + "github.com/coreos/go-systemd/v22/dbus" +) + +const ( + nmConfDir = "/etc/NetworkManager/conf.d" + nmCtrldConfFilename = "99-ctrld.conf" + nmCtrldConfContent = `[main] +dns=none +systemd-resolved=false +` + nmSystemdUnitName = "NetworkManager.service" + systemdEnabledState = "enabled" +) + +var networkManagerCtrldConfFile = filepath.Join(nmConfDir, nmCtrldConfFilename) + +func setupNetworkManager() error { + if runtime.GOOS != "linux" { + mainLog.Debug().Msg("skipping NetworkManager setup, not on Linux") + return nil + } + if content, _ := os.ReadFile(nmCtrldConfContent); string(content) == nmCtrldConfContent { + mainLog.Debug().Msg("NetworkManager already setup, nothing to do") + return nil + } + err := os.WriteFile(networkManagerCtrldConfFile, []byte(nmCtrldConfContent), os.FileMode(0644)) + if os.IsNotExist(err) { + mainLog.Debug().Msg("NetworkManager is not available") + return nil + } + if err != nil { + mainLog.Debug().Err(err).Msg("could not write NetworkManager ctrld config file") + return err + } + + reloadNetworkManager() + mainLog.Debug().Msg("setup NetworkManager done") + return nil +} + +func restoreNetworkManager() error { + if runtime.GOOS != "linux" { + mainLog.Debug().Msg("skipping NetworkManager restoring, not on Linux") + return nil + } + err := os.Remove(networkManagerCtrldConfFile) + if os.IsNotExist(err) { + mainLog.Debug().Msg("NetworkManager is not available") + return nil + } + if err != nil { + mainLog.Debug().Err(err).Msg("could not remove NetworkManager ctrld config file") + return err + } + + reloadNetworkManager() + mainLog.Debug().Msg("restore NetworkManager done") + return nil +} + +func reloadNetworkManager() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + conn, err := dbus.NewSystemConnectionContext(ctx) + if err != nil { + mainLog.Error().Err(err).Msg("could not create new system connection") + return + } + defer conn.Close() + + waitCh := make(chan string) + if _, err := conn.ReloadUnitContext(ctx, nmSystemdUnitName, "ignore-dependencies", waitCh); err != nil { + mainLog.Debug().Err(err).Msg("could not reload NetworkManager") + } + <-waitCh +} diff --git a/cmd/ctrld/os_linux.go b/cmd/ctrld/os_linux.go index 01c52c0..582ae3c 100644 --- a/cmd/ctrld/os_linux.go +++ b/cmd/ctrld/os_linux.go @@ -9,7 +9,6 @@ import ( "net/netip" "os/exec" "reflect" - "runtime" "strings" "syscall" "time" @@ -175,25 +174,6 @@ func getDNSByNmcli(iface string) []string { return dns } -func getConnByNmcli(iface string) string { - if iface == "auto" { - iface = defaultIfaceName() - } - b, err := exec.Command("nmcli", "dev", "show", iface).Output() - if err != nil { - return "" - } - s := bufio.NewScanner(bytes.NewReader(b)) - for s.Scan() { - line := s.Text() - if _, connName, found := strings.Cut(line, "GENERAL.CONNECTION:"); found { - return strings.TrimSpace(connName) - } - - } - return "" -} - func ignoringEINTR(fn func() error) error { for { err := fn() @@ -202,22 +182,3 @@ func ignoringEINTR(fn func() error) error { } } } - -func disableAutoDNS(iface string) { - networkManagerIgnoreAutoDNS(iface, "yes") -} - -func enableAutoDNS(iface string) { - networkManagerIgnoreAutoDNS(iface, "no") -} - -func networkManagerIgnoreAutoDNS(iface, answer string) { - if runtime.GOOS != "linux" { - return - } - if connName := getConnByNmcli(iface); connName != "" { - mainLog.Debug().Msg("enable auto DNS from network manager") - _ = exec.Command("nmcli", "con", "mod", connName, "ipv4.ignore-auto-dns", answer).Run() - _ = exec.Command("nmcli", "con", "mod", connName, "ipv6.ignore-auto-dns", answer).Run() - } -} diff --git a/cmd/ctrld/os_mac.go b/cmd/ctrld/os_mac.go index ba9dc0d..95786f3 100644 --- a/cmd/ctrld/os_mac.go +++ b/cmd/ctrld/os_mac.go @@ -60,9 +60,3 @@ func resetDNS(iface *net.Interface) error { func currentDNS(_ *net.Interface) []string { return resolvconffile.NameServers("") } - -func disableAutoDNS(iface string) { -} - -func enableAutoDNS(iface string) { -} diff --git a/cmd/ctrld/os_windows.go b/cmd/ctrld/os_windows.go index 1652225..213c104 100644 --- a/cmd/ctrld/os_windows.go +++ b/cmd/ctrld/os_windows.go @@ -104,9 +104,3 @@ func currentDNS(iface *net.Interface) []string { } return ns } - -func disableAutoDNS(iface string) { -} - -func enableAutoDNS(iface string) { -} diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index 532f57e..8a9cd0e 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -209,6 +209,10 @@ func (p *prog) setDNS() { logger.Error().Err(err).Msg("could not get interface") return } + if err := setupNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not patch NetworkManager") + return + } logger.Debug().Msg("setting DNS for interface") if err := setDNS(netIface, []string{cfg.Listener["0"].IP}); err != nil { logger.Error().Err(err).Msgf("could not set DNS for interface") @@ -230,6 +234,10 @@ func (p *prog) resetDNS() { logger.Error().Err(err).Msg("could not get interface") return } + if err := restoreNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not restore NetworkManager") + return + } logger.Debug().Msg("Restoring DNS for interface") if err := resetDNS(netIface); err != nil { logger.Error().Err(err).Msgf("could not reset DNS") diff --git a/go.mod b/go.mod index 1e6f67c..433326b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Control-D-Inc/ctrld go 1.19 require ( + github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 github.com/go-playground/validator/v10 v10.11.1 github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e diff --git a/go.sum b/go.sum index 2a4fe13..a450fe2 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=