cmd/cli: set DNS for all physical interfaces on Windows/Darwin

This commit is contained in:
Cuong Manh Le
2024-01-26 22:22:31 +07:00
committed by Cuong Manh Le
parent 67d74774a9
commit 0826671809
3 changed files with 86 additions and 0 deletions

View File

@@ -42,3 +42,21 @@ func networkServiceName(ifaceName string, r io.Reader) string {
}
return ""
}
// validInterface reports whether the *net.Interface is a valid one, which includes:
//
// - en0: physical wireless
// - en1: Thunderbolt 1
// - en2: Thunderbolt 2
// - en3: Thunderbolt 3
// - en4: Thunderbolt 4
//
// For full list, see: https://unix.stackexchange.com/questions/603506/what-are-these-ifconfig-interfaces-on-macos
func validInterface(iface *net.Interface) bool {
switch iface.Name {
case "en0", "en1", "en2", "en3", "en4":
return true
default:
return false
}
}

View File

@@ -5,3 +5,5 @@ package cli
import "net"
func patchNetIfaceName(iface *net.Interface) error { return nil }
func validInterface(iface *net.Interface) bool { return true }

View File

@@ -13,6 +13,7 @@ import (
"runtime"
"sort"
"strconv"
"strings"
"sync"
"syscall"
@@ -417,8 +418,14 @@ func (p *prog) setDNS() {
if iface == "" {
return
}
// allIfaces tracks whether we should set DNS for all physical interfaces.
allIfaces := false
if iface == "auto" {
iface = defaultIfaceName()
// If iface is "auto", it means user does not specify "--iface" flag.
// In this case, ctrld has to set DNS for all physical interfaces, so
// thing will still work when user switch from one to the other.
allIfaces = requiredMultiNICsConfig()
}
lc := cfg.FirstListener()
if lc == nil {
@@ -460,14 +467,22 @@ func (p *prog) setDNS() {
return
}
logger.Debug().Msg("setting DNS successfully")
if allIfaces {
withEachPhysicalInterfaces(netIface.Name, "set DNS", func(i *net.Interface) error {
return setDNS(i, nameservers)
})
}
}
func (p *prog) resetDNS() {
if iface == "" {
return
}
allIfaces := false
if iface == "auto" {
iface = defaultIfaceName()
// See corresponding comments in (*prog).setDNS function.
allIfaces = requiredMultiNICsConfig()
}
logger := mainLog.Load().With().Str("iface", iface).Logger()
netIface, err := netInterface(iface)
@@ -485,6 +500,9 @@ func (p *prog) resetDNS() {
return
}
logger.Debug().Msg("Restoring DNS successfully")
if allIfaces {
withEachPhysicalInterfaces(netIface.Name, "reset DNS", resetDNS)
}
}
func randomLocalIP() string {
@@ -649,3 +667,51 @@ func canBeLocalUpstream(addr string) bool {
}
return false
}
// withEachPhysicalInterfaces runs the function f with each physical interfaces, excluding
// the interface that matches excludeIfaceName. The context is used to clarify the
// log message when error happens.
func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net.Interface) error) {
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
// Skip not-running/local/virtual interface.
if !i.IsUp() || i.IsLoopback() || len(i.HardwareAddr) == 0 {
return
}
// Skip non-configured interfaces.
if addrs, _ := i.Addrs(); len(addrs) == 0 {
return
}
// Skip invalid interface.
if !validInterface(i.Interface) {
return
}
netIface := i.Interface
if err := patchNetIfaceName(netIface); err != nil {
mainLog.Load().Debug().Err(err).Msg("failed to patch net interface name")
return
}
// Skip excluded interface.
if netIface.Name == excludeIfaceName {
return
}
// Skip Windows Hyper-V Default Switch.
if strings.Contains(netIface.Name, "vEthernet") {
return
}
if err := f(netIface); err != nil {
mainLog.Load().Warn().Err(err).Msgf("failed to %s for interface: %q", context, i.Name)
} else {
mainLog.Load().Debug().Msgf("%s for interface %q successfully", context, i.Name)
}
})
}
// requiredMultiNicConfig reports whether ctrld needs to set/reset DNS for multiple NICs.
func requiredMultiNICsConfig() bool {
switch runtime.GOOS {
case "windows", "darwin":
return true
default:
return false
}
}