diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index e917f88..e09289a 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -236,10 +236,14 @@ func (p *prog) setDNS() { } logger.Debug().Msg("setting DNS for interface") ns := cfg.Listener["0"].IP - if router.Name() == router.Firewalla && ns == "127.0.0.1" { + if router.Name() == router.Firewalla && (ns == "127.0.0.1" || ns == "0.0.0.0" || ns == "") { // 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. - logger.Warn().Msg("127.0.0.1 won't work on Firewalla") + if ns == "127.0.0.1" { + logger.Warn().Msg("127.0.0.1 as DNS server won't work on Firewalla") + } else { + logger.Warn().Msgf("%q could not be used as DNS server", ns) + } if netIface, err := net.InterfaceByName("br0"); err == nil { addrs, _ := netIface.Addrs() for _, addr := range addrs { diff --git a/internal/router/dnsmasq.go b/internal/router/dnsmasq.go index 9df4132..68fc78a 100644 --- a/internal/router/dnsmasq.go +++ b/internal/router/dnsmasq.go @@ -7,7 +7,9 @@ import ( const dnsMasqConfigContentTmpl = `# GENERATED BY ctrld - DO NOT MODIFY no-resolv -server=127.0.0.1#5354 +{{- range .Upstreams}} +server={{ .Ip }}#{{ .Port }} +{{- end}} {{- if .SendClientInfo}} add-mac {{- end}} @@ -45,6 +47,11 @@ if [ -n "$pid" ] && [ -f "/proc/${pid}/cmdline" ]; then fi ` +type dnsmasqUpstream struct { + Ip string + Port int +} + func dnsMasqConf() (string, error) { var sb strings.Builder var tmplText string @@ -55,10 +62,18 @@ func dnsMasqConf() (string, error) { tmplText = merlinDNSMasqPostConfTmpl } tmpl := template.Must(template.New("").Parse(tmplText)) + upstreams := []dnsmasqUpstream{{ListenIP(), ListenPort()}} + if Name() == Firewalla { + if fu := firewallaDnsmasqUpstreams(); len(fu) > 0 { + upstreams = fu + } + } var to = &struct { SendClientInfo bool + Upstreams []dnsmasqUpstream }{ - routerPlatform.Load().sendClientInfo, + SendClientInfo: routerPlatform.Load().sendClientInfo, + Upstreams: upstreams, } if err := tmpl.Execute(&sb, to); err != nil { return "", err diff --git a/internal/router/firewalla.go b/internal/router/firewalla.go index 6d57409..7e81b24 100644 --- a/internal/router/firewalla.go +++ b/internal/router/firewalla.go @@ -2,8 +2,10 @@ package router import ( "fmt" + "net" "os" "os/exec" + "path/filepath" "strings" ) @@ -78,3 +80,27 @@ func writeFirewallStartupScript() error { script := fmt.Sprintf("#!/bin/bash\n\nsudo %q %s\n", exe, argStr) return os.WriteFile(firewallaCtrldInitScriptPath, []byte(script), 0755) } + +func firewallaDnsmasqUpstreams() []dnsmasqUpstream { + matches, err := filepath.Glob("/home/pi/firerouter/etc/dnsmasq.dns.*.conf") + if err != nil { + return nil + } + upstreams := make([]dnsmasqUpstream, 0, len(matches)) + for _, match := range matches { + // Trim prefix and suffix to get the iface name only. + ifaceName := strings.TrimSuffix(strings.TrimPrefix(match, "/home/pi/firerouter/etc/dnsmasq.dns."), ".conf") + if netIface, _ := net.InterfaceByName(ifaceName); netIface != nil { + addrs, _ := netIface.Addrs() + for _, addr := range addrs { + if netIP, ok := addr.(*net.IPNet); ok && netIP.IP.To4() != nil { + upstreams = append(upstreams, dnsmasqUpstream{ + Ip: netIP.IP.To4().String(), + Port: ListenPort(), + }) + } + } + } + } + return upstreams +} diff --git a/internal/router/router.go b/internal/router/router.go index c53178f..0c18f50 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -220,6 +220,13 @@ func Cleanup(svc *service.Config) error { // ListenIP returns the listener IP of ctrld on router. func ListenIP() string { + name := Name() + switch name { + case Firewalla: + // Firewalla excepts 127.0.0.1 in all interfaces config. So we need to listen on all interfaces, + // making dnsmasq to be able to forward DNS query to specific interface based on VLAN config. + return "0.0.0.0" + } return "127.0.0.1" }