all: make router setup/cleanup works more generally

This commit is contained in:
Cuong Manh Le
2023-07-17 18:57:30 +00:00
committed by Cuong Manh Le
parent 66cb7cc21d
commit 9ed8e49a08
13 changed files with 115 additions and 34 deletions
+31 -28
View File
@@ -275,22 +275,19 @@ func initCLI() {
if cp := router.CertPool(); cp != nil { if cp := router.CertPool(); cp != nil {
rootCertPool = cp rootCertPool = cp
} }
// Perform router setup/cleanup if ctrld could not be direct listener. p.onStarted = append(p.onStarted, func() {
if !couldBeDirectListener(cfg.FirstListener()) { mainLog.Debug().Msg("router setup")
p.onStarted = append(p.onStarted, func() { if err := p.router.Setup(); err != nil {
mainLog.Debug().Msg("router setup") mainLog.Error().Err(err).Msg("could not configure router")
if err := p.router.Setup(); err != nil { }
mainLog.Error().Err(err).Msg("could not configure router") })
} p.onStopped = append(p.onStopped, func() {
}) mainLog.Debug().Msg("router cleanup")
p.onStopped = append(p.onStopped, func() { if err := p.router.Cleanup(); err != nil {
mainLog.Debug().Msg("router cleanup") mainLog.Error().Err(err).Msg("could not cleanup router")
if err := p.router.Cleanup(); err != nil { }
mainLog.Error().Err(err).Msg("could not cleanup router") p.resetDNS()
} })
p.resetDNS()
})
}
} }
close(waitCh) close(waitCh)
@@ -404,7 +401,7 @@ func initCLI() {
return return
} }
if router.Name() != "" && !couldBeDirectListener(cfg.FirstListener()) { if router.Name() != "" {
mainLog.Debug().Msg("cleaning up router before installing") mainLog.Debug().Msg("cleaning up router before installing")
_ = p.router.Cleanup() _ = p.router.Cleanup()
} }
@@ -504,15 +501,18 @@ func initCLI() {
Short: "Stop the ctrld service", Short: "Stop the ctrld service",
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
prog := &prog{} tryReadingConfig(false)
s, err := newService(prog, svcConfig) v.Unmarshal(&cfg)
p := &prog{router: router.New(&cfg)}
s, err := newService(p, svcConfig)
if err != nil { if err != nil {
mainLog.Error().Msg(err.Error()) mainLog.Error().Msg(err.Error())
return return
} }
initLogging() initLogging()
if doTasks([]task{{s.Stop, true}}) { if doTasks([]task{{s.Stop, true}}) {
prog.resetDNS() p.router.Cleanup()
p.resetDNS()
mainLog.Notice().Msg("Service stopped") mainLog.Notice().Msg("Service stopped")
} }
}, },
@@ -591,7 +591,9 @@ func initCLI() {
NOTE: Uninstalling will set DNS to values provided by DHCP.`, NOTE: Uninstalling will set DNS to values provided by DHCP.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
p := &prog{} tryReadingConfig(false)
v.Unmarshal(&cfg)
p := &prog{router: router.New(&cfg)}
s, err := newService(p, svcConfig) s, err := newService(p, svcConfig)
if err != nil { if err != nil {
mainLog.Error().Msg(err.Error()) mainLog.Error().Msg(err.Error())
@@ -1157,19 +1159,20 @@ func uninstall(p *prog, s service.Service) {
} }
initLogging() initLogging()
if doTasks(tasks) { if doTasks(tasks) {
r := router.New(&cfg) if err := p.router.ConfigureService(svcConfig); err != nil {
if err := r.Uninstall(svcConfig); err != nil { mainLog.Fatal().Err(err).Msg("could not configure service")
}
if err := p.router.Uninstall(svcConfig); err != nil {
mainLog.Warn().Err(err).Msg("post uninstallation failed, please check system/service log for details error") mainLog.Warn().Err(err).Msg("post uninstallation failed, please check system/service log for details error")
return return
} }
// Stop already reset DNS on router. p.resetDNS()
if router.Name() == "" { if router.Name() != "" {
p.resetDNS() mainLog.Debug().Msg("Router cleanup")
} }
mainLog.Debug().Msg("Router cleanup")
// Stop already did router.Cleanup and report any error if happens, // Stop already did router.Cleanup and report any error if happens,
// ignoring error here to prevent false positive. // ignoring error here to prevent false positive.
_ = r.Cleanup() _ = p.router.Cleanup()
mainLog.Notice().Msg("Service uninstalled") mainLog.Notice().Msg("Service uninstalled")
return return
} }
+1 -1
View File
@@ -147,7 +147,7 @@ func resetDNS(iface *net.Interface) (err error) {
if ctrldnet.IPv6Available(ctx) { if ctrldnet.IPv6Available(ctx) {
c := client6.NewClient() c := client6.NewClient()
conversation, err := c.Exchange(iface.Name) conversation, err := c.Exchange(iface.Name)
if err != nil { if err != nil && !errAddrInUse(err) {
mainLog.Debug().Err(err).Msg("could not exchange DHCPv6") mainLog.Debug().Err(err).Msg("could not exchange DHCPv6")
} }
for _, packet := range conversation { for _, packet := range conversation {
+11 -1
View File
@@ -1,12 +1,14 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"os" "os"
"strconv" "strconv"
"sync" "sync"
"syscall"
"github.com/kardianos/service" "github.com/kardianos/service"
@@ -216,7 +218,7 @@ func (p *prog) setDNS() {
logger.Debug().Msg("setting DNS for interface") logger.Debug().Msg("setting DNS for interface")
ns := lc.IP ns := lc.IP
switch { switch {
case couldBeDirectListener(lc): case lc.IsDirectDnsListener():
// If ctrld is direct listener, use 127.0.0.1 as nameserver. // If ctrld is direct listener, use 127.0.0.1 as nameserver.
ns = "127.0.0.1" ns = "127.0.0.1"
case lc.Port != 53: case lc.Port != 53:
@@ -294,3 +296,11 @@ func runLogServer(sockPath string) net.Conn {
} }
return server return server
} }
func errAddrInUse(err error) bool {
opErr, ok := err.(*net.OpError)
if !ok {
return false
}
return errors.Is(opErr.Err, syscall.EADDRINUSE)
}
+18
View File
@@ -207,6 +207,24 @@ type ListenerConfig struct {
Policy *ListenerPolicyConfig `mapstructure:"policy" toml:"policy,omitempty"` Policy *ListenerPolicyConfig `mapstructure:"policy" toml:"policy,omitempty"`
} }
// IsDirectDnsListener reports whether ctrld can be a direct listener on port 53.
// It returns true only if ctrld can listen on port 53 for all interfaces. That means
// there's no other software listening on port 53.
//
// If someone listening on port 53, or ctrld could only listen on port 53 for a specific
// interface, ctrld could only be configured as a DNS forwarder.
func (lc *ListenerConfig) IsDirectDnsListener() bool {
if lc == nil || lc.Port != 53 {
return false
}
switch lc.IP {
case "", "::", "0.0.0.0":
return true
default:
return false
}
}
// ListenerPolicyConfig specifies the policy rules for ctrld to filter incoming requests. // ListenerPolicyConfig specifies the policy rules for ctrld to filter incoming requests.
type ListenerPolicyConfig struct { type ListenerPolicyConfig struct {
Name string `mapstructure:"name" toml:"name,omitempty"` Name string `mapstructure:"name" toml:"name,omitempty"`
+6
View File
@@ -58,6 +58,9 @@ func (d *Ddwrt) PreRun() error {
} }
func (d *Ddwrt) Setup() error { func (d *Ddwrt) Setup() error {
if d.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Already setup. // Already setup.
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" { if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
return nil return nil
@@ -81,6 +84,9 @@ func (d *Ddwrt) Setup() error {
} }
func (d *Ddwrt) Cleanup() error { func (d *Ddwrt) Cleanup() error {
if d.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" { if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
nvramKvMap["dnsmasq_options"] = "" nvramKvMap["dnsmasq_options"] = ""
// Restore old configs. // Restore old configs.
+6
View File
@@ -62,6 +62,9 @@ func (e *EdgeOS) PreRun() error {
} }
func (e *EdgeOS) Setup() error { func (e *EdgeOS) Setup() error {
if e.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
if e.isUSG { if e.isUSG {
return e.setupUSG() return e.setupUSG()
} }
@@ -69,6 +72,9 @@ func (e *EdgeOS) Setup() error {
} }
func (e *EdgeOS) Cleanup() error { func (e *EdgeOS) Cleanup() error {
if e.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
if e.isUSG { if e.isUSG {
return e.cleanupUSG() return e.cleanupUSG()
} }
+6
View File
@@ -54,6 +54,9 @@ func (f *Firewalla) PreRun() error {
} }
func (f *Firewalla) Setup() error { func (f *Firewalla) Setup() error {
if f.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
data, err := dnsmasq.FirewallaConfTmpl(dnsmasq.ConfigContentTmpl, f.cfg) data, err := dnsmasq.FirewallaConfTmpl(dnsmasq.ConfigContentTmpl, f.cfg)
if err != nil { if err != nil {
return fmt.Errorf("generating dnsmasq config: %w", err) return fmt.Errorf("generating dnsmasq config: %w", err)
@@ -71,6 +74,9 @@ func (f *Firewalla) Setup() error {
} }
func (f *Firewalla) Cleanup() error { func (f *Firewalla) Cleanup() error {
if f.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Removing current config. // Removing current config.
if err := os.Remove(firewallaDNSMasqConfigPath); err != nil { if err := os.Remove(firewallaDNSMasqConfigPath); err != nil {
return fmt.Errorf("removing ctrld config: %w", err) return fmt.Errorf("removing ctrld config: %w", err)
+6
View File
@@ -49,6 +49,9 @@ func (m *Merlin) PreRun() error {
} }
func (m *Merlin) Setup() error { func (m *Merlin) Setup() error {
if m.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
buf, err := os.ReadFile(dnsmasq.MerlinPostConfPath) buf, err := os.ReadFile(dnsmasq.MerlinPostConfPath)
// Already setup. // Already setup.
if bytes.Contains(buf, []byte(dnsmasq.MerlinPostConfMarker)) { if bytes.Contains(buf, []byte(dnsmasq.MerlinPostConfMarker)) {
@@ -86,6 +89,9 @@ func (m *Merlin) Setup() error {
} }
func (m *Merlin) Cleanup() error { func (m *Merlin) Cleanup() error {
if m.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" { if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
// Restore old configs. // Restore old configs.
if err := nvram.Restore(nvramKvMap, nvram.CtrldSetupKey); err != nil { if err := nvram.Restore(nvramKvMap, nvram.CtrldSetupKey); err != nil {
+6
View File
@@ -49,6 +49,9 @@ func (o *Openwrt) PreRun() error {
} }
func (o *Openwrt) Setup() error { func (o *Openwrt) Setup() error {
if o.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, o.cfg) data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, o.cfg)
if err != nil { if err != nil {
return err return err
@@ -68,6 +71,9 @@ func (o *Openwrt) Setup() error {
} }
func (o *Openwrt) Cleanup() error { func (o *Openwrt) Cleanup() error {
if o.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Remove the custom dnsmasq config // Remove the custom dnsmasq config
if err := os.Remove(openwrtDNSMasqConfigPath); err != nil { if err := os.Remove(openwrtDNSMasqConfigPath); err != nil {
return err return err
+6 -4
View File
@@ -66,6 +66,9 @@ func (p *Pfsense) Install(config *service.Config) error {
} }
func (p *Pfsense) Uninstall(config *service.Config) error { func (p *Pfsense) Uninstall(config *service.Config) error {
if err := os.Remove(filepath.Join(rcPath, p.svcName+".sh")); err != nil {
return fmt.Errorf("os.Remove: %w", err)
}
return nil return nil
} }
@@ -84,11 +87,10 @@ func (p *Pfsense) Setup() error {
} }
func (p *Pfsense) Cleanup() error { func (p *Pfsense) Cleanup() error {
if err := os.Remove(filepath.Join(rcPath, p.svcName+".sh")); err != nil { if p.cfg.FirstListener().IsDirectDnsListener() {
return fmt.Errorf("os.Remove: %w", err) _ = exec.Command(unboundRcPath, "onerestart").Run()
_ = exec.Command(dnsmasqRcPath, "onerestart").Run()
} }
_ = exec.Command(unboundRcPath, "onerestart").Run()
_ = exec.Command(dnsmasqRcPath, "onerestart").Run()
return nil return nil
} }
+6
View File
@@ -44,6 +44,9 @@ func (s *Synology) PreRun() error {
} }
func (s *Synology) Setup() error { func (s *Synology) Setup() error {
if s.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, s.cfg) data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, s.cfg)
if err != nil { if err != nil {
return err return err
@@ -61,6 +64,9 @@ func (s *Synology) Setup() error {
} }
func (s *Synology) Cleanup() error { func (s *Synology) Cleanup() error {
if s.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Remove the custom config files. // Remove the custom config files.
for _, f := range []string{synologyDNSMasqConfigPath, synologyDhcpdInfoPath} { for _, f := range []string{synologyDNSMasqConfigPath, synologyDhcpdInfoPath} {
if err := os.Remove(f); err != nil { if err := os.Remove(f); err != nil {
+6
View File
@@ -53,6 +53,9 @@ func (f *FreshTomato) PreRun() error {
} }
func (f *FreshTomato) Setup() error { func (f *FreshTomato) Setup() error {
if f.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Already setup. // Already setup.
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" { if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
return nil return nil
@@ -83,6 +86,9 @@ func (f *FreshTomato) Setup() error {
} }
func (f *FreshTomato) Cleanup() error { func (f *FreshTomato) Cleanup() error {
if f.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" { if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
nvramKvMap["dnsmasq_custom"] = "" nvramKvMap["dnsmasq_custom"] = ""
// Restore old configs. // Restore old configs.
+6
View File
@@ -47,6 +47,9 @@ func (u *Ubios) PreRun() error {
} }
func (u *Ubios) Setup() error { func (u *Ubios) Setup() error {
if u.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, u.cfg) data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, u.cfg)
if err != nil { if err != nil {
return err return err
@@ -62,6 +65,9 @@ func (u *Ubios) Setup() error {
} }
func (u *Ubios) Cleanup() error { func (u *Ubios) Cleanup() error {
if u.cfg.FirstListener().IsDirectDnsListener() {
return nil
}
// Remove the custom dnsmasq config // Remove the custom dnsmasq config
if err := os.Remove(ubiosDNSMasqConfigPath); err != nil { if err := os.Remove(ubiosDNSMasqConfigPath); err != nil {
return err return err