From a4edf266f0d7bad437d3913d64110a012ac93de1 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 11 Jul 2023 00:24:54 +0700 Subject: [PATCH] all: workaround problem with EdgeOS dnsmasq config --- cmd/ctrld/prog.go | 27 ++++++++++------ cmd/ctrld/prog_linux.go | 1 - internal/router/dnsmasq/conf.go | 30 ++++++++++++++++++ internal/router/dnsmasq/conf_test.go | 46 ++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 internal/router/dnsmasq/conf.go create mode 100644 internal/router/dnsmasq/conf_test.go diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index e94e0e9..83e71ae 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -16,6 +16,8 @@ import ( "github.com/Control-D-Inc/ctrld/internal/clientinfo" "github.com/Control-D-Inc/ctrld/internal/dnscache" "github.com/Control-D-Inc/ctrld/internal/router" + "github.com/Control-D-Inc/ctrld/internal/router/dnsmasq" + "github.com/Control-D-Inc/ctrld/internal/router/edgeos" "github.com/Control-D-Inc/ctrld/internal/router/firewalla" ) @@ -243,19 +245,26 @@ func (p *prog) setDNS() { } logger.Debug().Msg("setting DNS for interface") ns := lc.IP - ifaceName := defaultIfaceName() - isFirewalla := router.Name() == firewalla.Name - if isFirewalla { - // On Firewalla, the lo interface is excluded in all dnsmasq settings of all interfaces. - // Thus, we use "br0" as the nameserver in /etc/resolv.conf file. - ifaceName = "br0" - logger.Warn().Msg("using br0 interface IP address as DNS server") - } if couldBeDirectListener(lc) { // If ctrld is direct listener, use 127.0.0.1 as nameserver. ns = "127.0.0.1" } else if lc.Port != 53 { - logger.Warn().Msg("ctrld is not running on port 53, use default route interface as DNS server") + ifaceName := defaultIfaceName() + switch router.Name() { + case firewalla.Name: + // On Firewalla, the lo interface is excluded in all dnsmasq settings of all interfaces. + // Thus, we use "br0" as the nameserver in /etc/resolv.conf file. + ifaceName = "br0" + logger.Warn().Msg("using br0 interface IP address as DNS server") + case edgeos.Name: + // On EdgeOS, dnsmasq is run with "--local-service", so we need to get + // the proper interface from dnsmasq config. + if name, _ := dnsmasq.InterfaceNameFromConfig("/etc/dnsmasq.conf"); name != "" { + ifaceName = name + logger.Warn().Msgf("using %s interface IP address as DNS server", ifaceName) + } + } + logger.Warn().Msg("ctrld is not running on port 53, use interface %s IP as DNS server") netIface, err := net.InterfaceByName(ifaceName) if err != nil { mainLog.Fatal().Err(err).Msg("failed to get default route interface") diff --git a/cmd/ctrld/prog_linux.go b/cmd/ctrld/prog_linux.go index 0748b51..a3f2823 100644 --- a/cmd/ctrld/prog_linux.go +++ b/cmd/ctrld/prog_linux.go @@ -34,7 +34,6 @@ func setDependencies(svc *service.Config) { svc.Dependencies = append(svc.Dependencies, "Wants=vyatta-dhcpd.service") svc.Dependencies = append(svc.Dependencies, "After=vyatta-dhcpd.service") svc.Dependencies = append(svc.Dependencies, "Wants=dnsmasq.service") - svc.Dependencies = append(svc.Dependencies, "After=dnsmasq.service") } } diff --git a/internal/router/dnsmasq/conf.go b/internal/router/dnsmasq/conf.go new file mode 100644 index 0000000..b168042 --- /dev/null +++ b/internal/router/dnsmasq/conf.go @@ -0,0 +1,30 @@ +package dnsmasq + +import ( + "bufio" + "bytes" + "errors" + "io" + "os" + "strings" +) + +func InterfaceNameFromConfig(filename string) (string, error) { + buf, err := os.ReadFile(filename) + if err != nil { + return "", err + } + return interfaceNameFromReader(bytes.NewReader(buf)) +} + +func interfaceNameFromReader(r io.Reader) (string, error) { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + after, found := strings.CutPrefix(line, "interface=") + if found { + return after, nil + } + } + return "", errors.New("not found") +} diff --git a/internal/router/dnsmasq/conf_test.go b/internal/router/dnsmasq/conf_test.go new file mode 100644 index 0000000..99a0710 --- /dev/null +++ b/internal/router/dnsmasq/conf_test.go @@ -0,0 +1,46 @@ +package dnsmasq + +import ( + "strings" + "testing" +) + +func Test_interfaceNameFromReader(t *testing.T) { + tests := []struct { + name string + in string + wantIface string + }{ + { + "good", + `interface=lo`, + "lo", + }, + { + "multiple", + `interface=lo +interface=eth0 +`, + "lo", + }, + { + "no iface", + `cache-size=100`, + "", + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ifaceName, err := interfaceNameFromReader(strings.NewReader(tc.in)) + if tc.wantIface != "" && err != nil { + t.Errorf("unexpected error: %v", err) + return + } + if tc.wantIface != ifaceName { + t.Errorf("mismatched, want: %q, got: %q", tc.wantIface, ifaceName) + } + }) + } +}