Compare commits
1 Commits
main
...
dshynkev/d
Author | SHA1 | Date |
---|---|---|
![]() |
dd933a3a60 |
|
@ -963,13 +963,8 @@ func (b *LocalBackend) authReconfig() {
|
||||||
domains := nm.DNS.Domains
|
domains := nm.DNS.Domains
|
||||||
proxied := nm.DNS.Proxied
|
proxied := nm.DNS.Proxied
|
||||||
if proxied {
|
if proxied {
|
||||||
if len(nm.DNS.Nameservers) == 0 {
|
// Domains for proxying should come first to avoid leaking queries.
|
||||||
b.logf("[unexpected] dns proxied but no nameservers")
|
domains = append(domainsForProxying(nm), domains...)
|
||||||
proxied = false
|
|
||||||
} else {
|
|
||||||
// Domains for proxying should come first to avoid leaking queries.
|
|
||||||
domains = append(domainsForProxying(nm), domains...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rcfg.DNS = dns.Config{
|
rcfg.DNS = dns.Config{
|
||||||
Nameservers: nm.DNS.Nameservers,
|
Nameservers: nm.DNS.Nameservers,
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"inet.af/netaddr"
|
"net"
|
||||||
|
|
||||||
|
"inet.af/netaddr"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,12 +66,11 @@ func (lhs Config) Equal(rhs Config) bool {
|
||||||
type ManagerConfig struct {
|
type ManagerConfig struct {
|
||||||
// logf is the logger for the manager to use.
|
// logf is the logger for the manager to use.
|
||||||
Logf logger.Logf
|
Logf logger.Logf
|
||||||
// InterfaceNAme is the name of the interface with which DNS settings should be associated.
|
// InterfaceName is the name of the interface with which DNS settings should be associated.
|
||||||
InterfaceName string
|
InterfaceName string
|
||||||
|
// SetNameservers is the function to which upstream nameservers should be passed.
|
||||||
|
SetUpstreams func([]net.Addr)
|
||||||
// Cleanup indicates that the manager is created for cleanup only.
|
// Cleanup indicates that the manager is created for cleanup only.
|
||||||
// A no-op manager will be instantiated if the system needs no cleanup.
|
// A no-op manager will be instantiated if the system needs no cleanup.
|
||||||
Cleanup bool
|
Cleanup bool
|
||||||
// PerDomain indicates that a manager capable of per-domain configuration is preferred.
|
|
||||||
// Certain managers are per-domain only; they will not be considered if this is false.
|
|
||||||
PerDomain bool
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -100,21 +101,38 @@ func isResolvedRunning() bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// directManager is a managerImpl which replaces /etc/resolv.conf with a file
|
// directManager is a Manager which replaces /etc/resolv.conf with a file
|
||||||
// generated from the given configuration, creating a backup of its old state.
|
// generated from the given configuration, creating a backup of its old state.
|
||||||
//
|
//
|
||||||
// This way of configuring DNS is precarious, since it does not react
|
// This way of configuring DNS is precarious, since it does not react
|
||||||
// to the disappearance of the Tailscale interface.
|
// to the disappearance of the Tailscale interface.
|
||||||
// The caller must call Down before program shutdown
|
// The caller must call Down before program shutdown
|
||||||
// or as cleanup if the program terminates unexpectedly.
|
// or as cleanup if the program terminates unexpectedly.
|
||||||
type directManager struct{}
|
type directManager struct {
|
||||||
|
oldConfig Config
|
||||||
func newDirectManager(mconfig ManagerConfig) managerImpl {
|
setUpstreams func([]net.Addr)
|
||||||
return directManager{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up implements managerImpl.
|
func newDirectManager(mconfig ManagerConfig) Manager {
|
||||||
func (m directManager) Up(config Config) error {
|
oldConfig, err := readResolvConf()
|
||||||
|
if err != nil {
|
||||||
|
mconfig.Logf("reading old config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return directManager{
|
||||||
|
oldConfig: oldConfig,
|
||||||
|
setUpstreams: mconfig.SetUpstreams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements Manager.
|
||||||
|
func (m directManager) Set(config Config) error {
|
||||||
|
if len(config.Nameservers) == 0 && len(config.Domains) == 0 {
|
||||||
|
return m.Down()
|
||||||
|
}
|
||||||
|
|
||||||
|
config = prepareGlobalConfig(config, m.oldConfig, m.setUpstreams)
|
||||||
|
|
||||||
// Write the tsConf file.
|
// Write the tsConf file.
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
writeResolvConf(buf, config.Nameservers, config.Domains)
|
writeResolvConf(buf, config.Nameservers, config.Domains)
|
||||||
|
@ -159,7 +177,7 @@ func (m directManager) Up(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Down implements managerImpl.
|
// Down implements Manager.
|
||||||
func (m directManager) Down() error {
|
func (m directManager) Down() error {
|
||||||
if _, err := os.Stat(backupConf); err != nil {
|
if _, err := os.Stat(backupConf); err != nil {
|
||||||
// If the backup file does not exist, then Up never ran successfully.
|
// If the backup file does not exist, then Up never ran successfully.
|
||||||
|
|
|
@ -5,90 +5,50 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// reconfigTimeout is the time interval within which Manager.{Up,Down} should complete.
|
// Manager manages system DNS settings.
|
||||||
//
|
type Manager interface {
|
||||||
// This is particularly useful because certain conditions can cause indefinite hangs
|
// Set updates system DNS settings to match the given configuration.
|
||||||
// (such as improper dbus auth followed by contextless dbus.Object.Call).
|
Set(Config) error
|
||||||
// Such operations should be wrapped in a timeout context.
|
// Down undoes the effects of Set.
|
||||||
const reconfigTimeout = time.Second //lint:ignore U1000 used on Linux at least, maybe others later
|
// It is idempotent and performs no action if Set has never been called.
|
||||||
|
|
||||||
type managerImpl interface {
|
|
||||||
// Up updates system DNS settings to match the given configuration.
|
|
||||||
Up(Config) error
|
|
||||||
// Down undoes the effects of Up.
|
|
||||||
// It is idempotent and performs no action if Up has never been called.
|
|
||||||
Down() error
|
Down() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager manages system DNS settings.
|
type wrappedManager struct {
|
||||||
type Manager struct {
|
logf logger.Logf
|
||||||
logf logger.Logf
|
impl Manager
|
||||||
|
config Config
|
||||||
impl managerImpl
|
|
||||||
|
|
||||||
config Config
|
|
||||||
mconfig ManagerConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManagers created a new manager from the given config.
|
// NewManager creates a new manager from the given config.
|
||||||
func NewManager(mconfig ManagerConfig) *Manager {
|
func NewManager(mconfig ManagerConfig) Manager {
|
||||||
mconfig.Logf = logger.WithPrefix(mconfig.Logf, "dns: ")
|
mconfig.Logf = logger.WithPrefix(mconfig.Logf, "dns: ")
|
||||||
m := &Manager{
|
m := &wrappedManager{
|
||||||
logf: mconfig.Logf,
|
logf: mconfig.Logf,
|
||||||
impl: newManager(mconfig),
|
impl: newManager(mconfig),
|
||||||
|
|
||||||
config: Config{PerDomain: mconfig.PerDomain},
|
|
||||||
mconfig: mconfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.logf("using %T", m.impl)
|
m.logf("using %T", m.impl)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Set(config Config) error {
|
func (m *wrappedManager) Set(config Config) error {
|
||||||
if config.Equal(m.config) {
|
if config.Equal(m.config) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.logf("Set: %+v", config)
|
m.logf("set: %+v", config)
|
||||||
|
|
||||||
if len(config.Nameservers) == 0 {
|
err := m.impl.Set(config)
|
||||||
err := m.impl.Down()
|
|
||||||
// If we save the config, we will not retry next time. Only do this on success.
|
|
||||||
if err == nil {
|
|
||||||
m.config = config
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switching to and from per-domain mode may require a change of manager.
|
|
||||||
if config.PerDomain != m.config.PerDomain {
|
|
||||||
if err := m.impl.Down(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.mconfig.PerDomain = config.PerDomain
|
|
||||||
m.impl = newManager(m.mconfig)
|
|
||||||
m.logf("switched to %T", m.impl)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := m.impl.Up(config)
|
|
||||||
// If we save the config, we will not retry next time. Only do this on success.
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.config = config
|
m.config = config
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Up() error {
|
func (m *wrappedManager) Down() error {
|
||||||
return m.impl.Up(m.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Down() error {
|
|
||||||
return m.impl.Down()
|
return m.impl.Down()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
func newManager(mconfig ManagerConfig) managerImpl {
|
func newManager(mconfig ManagerConfig) Manager {
|
||||||
// TODO(dmytro): on darwin, we should use a macOS-specific method such as scutil.
|
// TODO(dmytro): on darwin, we should use a macOS-specific method such as scutil.
|
||||||
// This is currently not implemented. Editing /etc/resolv.conf does not work,
|
// This is currently not implemented. Editing /etc/resolv.conf does not work,
|
||||||
// as most applications use the system resolver, which disregards it.
|
// as most applications use the system resolver, which disregards it.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
func newManager(mconfig ManagerConfig) managerImpl {
|
func newManager(mconfig ManagerConfig) Manager {
|
||||||
switch {
|
switch {
|
||||||
case isResolvconfActive():
|
case isResolvconfActive():
|
||||||
return newResolvconfManager(mconfig)
|
return newResolvconfManager(mconfig)
|
||||||
|
|
|
@ -4,24 +4,177 @@
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
func newManager(mconfig ManagerConfig) managerImpl {
|
import (
|
||||||
switch {
|
"context"
|
||||||
// systemd-resolved should only activate per-domain.
|
"sync"
|
||||||
case isResolvedActive() && mconfig.PerDomain:
|
"time"
|
||||||
if mconfig.Cleanup {
|
|
||||||
return newNoopManager(mconfig)
|
"tailscale.com/logtail/backoff"
|
||||||
} else {
|
"tailscale.com/types/logger"
|
||||||
return newResolvedManager(mconfig)
|
)
|
||||||
}
|
|
||||||
case isNMActive():
|
var reconfigTimeout = 5 * time.Second
|
||||||
if mconfig.Cleanup {
|
|
||||||
return newNoopManager(mconfig)
|
type dnsMode uint8
|
||||||
} else {
|
|
||||||
return newNMManager(mconfig)
|
const (
|
||||||
}
|
noMode dnsMode = iota
|
||||||
case isResolvconfActive():
|
nmMode
|
||||||
return newResolvconfManager(mconfig)
|
resolvedMode
|
||||||
|
resolvconfMode
|
||||||
|
directMode
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m dnsMode) String() string {
|
||||||
|
switch m {
|
||||||
|
case noMode:
|
||||||
|
return "none"
|
||||||
|
case nmMode:
|
||||||
|
return "NetworkManager"
|
||||||
|
case resolvedMode:
|
||||||
|
return "systemd-resolved"
|
||||||
|
case resolvconfMode:
|
||||||
|
return "resolvconf"
|
||||||
|
case directMode:
|
||||||
|
return "direct"
|
||||||
default:
|
default:
|
||||||
return newDirectManager(mconfig)
|
return "???"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// linuxManager manages system configuration asynchronously with backoff on errors.
|
||||||
|
// This is useful because nmManager and resolvedManager cannot be used
|
||||||
|
// until the Tailscale network interface is ready from the point of view
|
||||||
|
// of NetworkManager/systemd-resolved, which can take a unspecified amount of time.
|
||||||
|
type linuxManager struct {
|
||||||
|
logf logger.Logf
|
||||||
|
mconfig ManagerConfig
|
||||||
|
|
||||||
|
config chan Config
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
wg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func configToMode(config Config) dnsMode {
|
||||||
|
switch {
|
||||||
|
case isNMActive():
|
||||||
|
return nmMode
|
||||||
|
case isResolvedActive() && config.PerDomain:
|
||||||
|
return resolvedMode
|
||||||
|
case isResolvconfActive():
|
||||||
|
return resolvconfMode
|
||||||
|
default:
|
||||||
|
return directMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *linuxManager) modeToImpl(mode dnsMode) Manager {
|
||||||
|
switch mode {
|
||||||
|
case nmMode:
|
||||||
|
return newNMManager(m.mconfig)
|
||||||
|
case resolvedMode:
|
||||||
|
return newResolvedManager(m.mconfig)
|
||||||
|
case resolvconfMode:
|
||||||
|
return newResolvconfManager(m.mconfig)
|
||||||
|
case directMode:
|
||||||
|
return newDirectManager(m.mconfig)
|
||||||
|
default:
|
||||||
|
return newNoopManager(m.mconfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newManager(mconfig ManagerConfig) Manager {
|
||||||
|
// For cleanup, don't try anything fancy.
|
||||||
|
if mconfig.Cleanup {
|
||||||
|
switch {
|
||||||
|
case isNMActive(), isResolvedActive():
|
||||||
|
return newNoopManager(mconfig)
|
||||||
|
case isResolvconfActive():
|
||||||
|
return newResolvconfManager(mconfig)
|
||||||
|
default:
|
||||||
|
return newDirectManager(mconfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &linuxManager{
|
||||||
|
logf: mconfig.Logf,
|
||||||
|
mconfig: mconfig,
|
||||||
|
config: make(chan Config, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *linuxManager) background() {
|
||||||
|
defer m.wg.Done()
|
||||||
|
|
||||||
|
var mode dnsMode
|
||||||
|
var impl Manager
|
||||||
|
var config Config
|
||||||
|
|
||||||
|
bo := backoff.NewBackoff("dns", m.logf, 30*time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-m.ctx.Done():
|
||||||
|
if err := impl.Down(); err != nil {
|
||||||
|
m.logf("stop: down: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case config = <-m.config:
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newMode := configToMode(config)
|
||||||
|
if newMode != mode {
|
||||||
|
m.logf("changing mode: %v -> %v", mode, newMode)
|
||||||
|
// If a non-noop manager was active, deactivate it first.
|
||||||
|
if mode != noMode {
|
||||||
|
if err := impl.Down(); err != nil {
|
||||||
|
m.logf("mode change: down: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode = newMode
|
||||||
|
impl = m.modeToImpl(newMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := impl.Set(config)
|
||||||
|
if err != nil {
|
||||||
|
m.logf("set: %v", err)
|
||||||
|
// Force another iteration.
|
||||||
|
select {
|
||||||
|
case m.config <- config:
|
||||||
|
// continue
|
||||||
|
default:
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bo.BackOff(m.ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements Manager.
|
||||||
|
func (m *linuxManager) Set(config Config) error {
|
||||||
|
if m.ctx == nil {
|
||||||
|
m.ctx, m.cancel = context.WithCancel(context.Background())
|
||||||
|
m.wg.Add(1)
|
||||||
|
go m.background()
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-m.ctx.Done():
|
||||||
|
return nil
|
||||||
|
case m.config <- config:
|
||||||
|
// continue
|
||||||
|
default:
|
||||||
|
<-m.config
|
||||||
|
m.config <- config
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Down implements Manager.
|
||||||
|
func (m *linuxManager) Down() error {
|
||||||
|
m.cancel()
|
||||||
|
m.wg.Wait()
|
||||||
|
m.ctx = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
func newManager(mconfig ManagerConfig) managerImpl {
|
func newManager(mconfig ManagerConfig) Manager {
|
||||||
return newDirectManager(mconfig)
|
return newDirectManager(mconfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ type windowsManager struct {
|
||||||
guid string
|
guid string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newManager(mconfig ManagerConfig) managerImpl {
|
func newManager(mconfig ManagerConfig) Manager {
|
||||||
return windowsManager{
|
return windowsManager{
|
||||||
logf: mconfig.Logf,
|
logf: mconfig.Logf,
|
||||||
guid: tun.WintunGUID,
|
guid: tun.WintunGUID,
|
||||||
|
@ -110,7 +110,8 @@ func (m windowsManager) setDomains(path string, oldDomains, newDomains []string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m windowsManager) Up(config Config) error {
|
// Set implements Manager.
|
||||||
|
func (m windowsManager) Set(config Config) error {
|
||||||
var ipsv4 []string
|
var ipsv4 []string
|
||||||
var ipsv6 []string
|
var ipsv6 []string
|
||||||
|
|
||||||
|
@ -150,6 +151,7 @@ func (m windowsManager) Up(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Down implements Manager.
|
||||||
func (m windowsManager) Down() error {
|
func (m windowsManager) Down() error {
|
||||||
return m.Up(Config{Nameservers: nil, Domains: nil})
|
return m.Set(Config{Nameservers: nil, Domains: nil})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
|
@ -36,14 +36,6 @@ func init() {
|
||||||
|
|
||||||
// isNMActive determines if NetworkManager is currently managing system DNS settings.
|
// isNMActive determines if NetworkManager is currently managing system DNS settings.
|
||||||
func isNMActive() bool {
|
func isNMActive() bool {
|
||||||
// This is somewhat tricky because NetworkManager supports a number
|
|
||||||
// of DNS configuration modes. In all cases, we expect it to be installed
|
|
||||||
// and /etc/resolv.conf to contain a mention of NetworkManager in the comments.
|
|
||||||
_, err := exec.LookPath("NetworkManager")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open("/etc/resolv.conf")
|
f, err := os.Open("/etc/resolv.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -64,21 +56,73 @@ func isNMActive() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// nmManager uses the NetworkManager DBus API.
|
func nmDNSMode() string {
|
||||||
type nmManager struct {
|
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
||||||
interfaceName string
|
defer cancel()
|
||||||
|
|
||||||
|
// conn is a shared connection whose lifecycle is managed by the dbus package.
|
||||||
|
// We should not interfere with that by closing it.
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsManager := conn.Object(
|
||||||
|
"org.freedesktop.NetworkManager",
|
||||||
|
dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"),
|
||||||
|
)
|
||||||
|
|
||||||
|
var dnsMode string
|
||||||
|
err = dnsManager.CallWithContext(
|
||||||
|
ctx, "org.freedesktop.DBus.Properties.Get", 0,
|
||||||
|
"org.freedesktop.NetworkManager.DnsManager", "Mode",
|
||||||
|
).Store(&dnsMode)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return dnsMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNMManager(mconfig ManagerConfig) managerImpl {
|
// nmManager uses the NetworkManager DBus API.
|
||||||
return nmManager{
|
type nmManager struct {
|
||||||
|
global bool
|
||||||
|
interfaceName string
|
||||||
|
oldConfig Config
|
||||||
|
setUpstreams func([]net.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNMManager(mconfig ManagerConfig) Manager {
|
||||||
|
mode := nmDNSMode()
|
||||||
|
mconfig.Logf("NetworkManager DNS mode is %q", mode)
|
||||||
|
|
||||||
|
global := mode == "default"
|
||||||
|
m := &nmManager{
|
||||||
|
global: global,
|
||||||
interfaceName: mconfig.InterfaceName,
|
interfaceName: mconfig.InterfaceName,
|
||||||
|
setUpstreams: mconfig.SetUpstreams,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if global {
|
||||||
|
oldConfig, err := readResolvConf()
|
||||||
|
if err != nil {
|
||||||
|
mconfig.Logf("reading old config: %v", err)
|
||||||
|
} else {
|
||||||
|
m.oldConfig = oldConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
type nmConnectionSettings map[string]map[string]dbus.Variant
|
type nmConnectionSettings map[string]map[string]dbus.Variant
|
||||||
|
|
||||||
// Up implements managerImpl.
|
// Set implements Manager.
|
||||||
func (m nmManager) Up(config Config) error {
|
func (m nmManager) Set(config Config) error {
|
||||||
|
if m.global && !(len(config.Nameservers) == 0 && len(config.Domains) == 0) {
|
||||||
|
config = prepareGlobalConfig(config, m.oldConfig, m.setUpstreams)
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -215,7 +259,7 @@ func (m nmManager) Up(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Down implements managerImpl.
|
// Down implements Manager.
|
||||||
func (m nmManager) Down() error {
|
func (m nmManager) Down() error {
|
||||||
return m.Up(Config{Nameservers: nil, Domains: nil})
|
return m.Set(Config{Nameservers: nil, Domains: nil})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@ package dns
|
||||||
|
|
||||||
type noopManager struct{}
|
type noopManager struct{}
|
||||||
|
|
||||||
// Up implements managerImpl.
|
// Set implements Manager.
|
||||||
func (m noopManager) Up(Config) error { return nil }
|
func (noopManager) Set(Config) error { return nil }
|
||||||
|
|
||||||
// Down implements managerImpl.
|
// Down implements Manager.
|
||||||
func (m noopManager) Down() error { return nil }
|
func (noopManager) Down() error { return nil }
|
||||||
|
|
||||||
func newNoopManager(mconfig ManagerConfig) managerImpl {
|
func newNoopManager(mconfig ManagerConfig) Manager {
|
||||||
return noopManager{}
|
return noopManager{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
@ -96,15 +97,24 @@ func getResolvconfImpl() resolvconfImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolvconfManager struct {
|
type resolvconfManager struct {
|
||||||
impl resolvconfImpl
|
impl resolvconfImpl
|
||||||
|
oldConfig Config
|
||||||
|
setUpstreams func([]net.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResolvconfManager(mconfig ManagerConfig) managerImpl {
|
func newResolvconfManager(mconfig ManagerConfig) Manager {
|
||||||
impl := getResolvconfImpl()
|
impl := getResolvconfImpl()
|
||||||
mconfig.Logf("resolvconf implementation is %s", impl)
|
mconfig.Logf("resolvconf implementation is %s", impl)
|
||||||
|
|
||||||
return resolvconfManager{
|
oldConfig, err := readResolvConf()
|
||||||
impl: impl,
|
if err != nil {
|
||||||
|
mconfig.Logf("reading old config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resolvconfManager{
|
||||||
|
impl: impl,
|
||||||
|
oldConfig: oldConfig,
|
||||||
|
setUpstreams: mconfig.SetUpstreams,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,8 +123,14 @@ func newResolvconfManager(mconfig ManagerConfig) managerImpl {
|
||||||
// when running resolvconfLegacy, hopefully placing our config first.
|
// when running resolvconfLegacy, hopefully placing our config first.
|
||||||
const resolvconfConfigName = "tun-tailscale.inet"
|
const resolvconfConfigName = "tun-tailscale.inet"
|
||||||
|
|
||||||
// Up implements managerImpl.
|
// Set implements Manager.
|
||||||
func (m resolvconfManager) Up(config Config) error {
|
func (m *resolvconfManager) Set(config Config) error {
|
||||||
|
if len(config.Nameservers) == 0 && len(config.Domains) == 0 {
|
||||||
|
return m.Down()
|
||||||
|
}
|
||||||
|
|
||||||
|
config = prepareGlobalConfig(config, m.oldConfig, m.setUpstreams)
|
||||||
|
|
||||||
stdin := new(bytes.Buffer)
|
stdin := new(bytes.Buffer)
|
||||||
writeResolvConf(stdin, config.Nameservers, config.Domains) // dns_direct.go
|
writeResolvConf(stdin, config.Nameservers, config.Domains) // dns_direct.go
|
||||||
|
|
||||||
|
@ -137,8 +153,8 @@ func (m resolvconfManager) Up(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Down implements managerImpl.
|
// Down implements Manager.
|
||||||
func (m resolvconfManager) Down() error {
|
func (m *resolvconfManager) Down() error {
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
switch m.impl {
|
switch m.impl {
|
||||||
case resolvconfOpenresolv:
|
case resolvconfOpenresolv:
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
@ -49,18 +48,6 @@ type resolvedLinkDomain struct {
|
||||||
|
|
||||||
// isResolvedActive determines if resolved is currently managing system DNS settings.
|
// isResolvedActive determines if resolved is currently managing system DNS settings.
|
||||||
func isResolvedActive() bool {
|
func isResolvedActive() bool {
|
||||||
// systemd-resolved is never installed without systemd.
|
|
||||||
_, err := exec.LookPath("systemctl")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// is-active exits with code 3 if the service is not active.
|
|
||||||
err = exec.Command("systemctl", "is-active", "systemd-resolved").Run()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := readResolvConf()
|
config, err := readResolvConf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -77,12 +64,12 @@ func isResolvedActive() bool {
|
||||||
// resolvedManager uses the systemd-resolved DBus API.
|
// resolvedManager uses the systemd-resolved DBus API.
|
||||||
type resolvedManager struct{}
|
type resolvedManager struct{}
|
||||||
|
|
||||||
func newResolvedManager(mconfig ManagerConfig) managerImpl {
|
func newResolvedManager(mconfig ManagerConfig) Manager {
|
||||||
return resolvedManager{}
|
return resolvedManager{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up implements managerImpl.
|
// Set implements Manager.
|
||||||
func (m resolvedManager) Up(config Config) error {
|
func (m resolvedManager) Set(config Config) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -151,7 +138,7 @@ func (m resolvedManager) Up(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Down implements managerImpl.
|
// Down implements Manager.
|
||||||
func (m resolvedManager) Down() error {
|
func (m resolvedManager) Down() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/wgengine/router/dns"
|
"tailscale.com/wgengine/router/dns"
|
||||||
|
"tailscale.com/wgengine/tsdns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Router is responsible for managing the system network stack.
|
// Router is responsible for managing the system network stack.
|
||||||
|
@ -30,11 +31,17 @@ type Router interface {
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Router for the current platform, using the
|
type InitConfig struct {
|
||||||
// provided tun device.
|
Logf logger.Logf
|
||||||
func New(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (Router, error) {
|
Wgdev *device.Device
|
||||||
logf = logger.WithPrefix(logf, "router: ")
|
Tun tun.Device
|
||||||
return newUserspaceRouter(logf, wgdev, tundev)
|
Resolver *tsdns.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Router for the current platform, using the provided config.
|
||||||
|
func New(cfg InitConfig) (Router, error) {
|
||||||
|
cfg.Logf = logger.WithPrefix(cfg.Logf, "router: ")
|
||||||
|
return newUserspaceRouter(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup restores the system network configuration to its original state
|
// Cleanup restores the system network configuration to its original state
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (Router, error) {
|
func newUserspaceRouter(cfg InitConfig) (Router, error) {
|
||||||
return newUserspaceBSDRouter(logf, wgdev, tundev)
|
return newUserspaceBSDRouter(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanup(logger.Logf, string) {
|
func cleanup(logger.Logf, string) {
|
||||||
|
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, tunname string, dev *device.Device, tuntap tun.Device, netChanged func()) Router {
|
func newUserspaceRouter(cfg InitConfig) Router {
|
||||||
return NewFakeRouter(logf, tunname, dev, tuntap, netChanged)
|
return NewFakeRouter(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanup(logf logger.Logf, interfaceName string) {
|
func cleanup(logger.Logf, string) {
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,13 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tailscale/wireguard-go/device"
|
|
||||||
"github.com/tailscale/wireguard-go/tun"
|
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewFakeRouter returns a Router that does nothing when called and
|
// NewFakeRouter returns a Router that does nothing when called and
|
||||||
// always returns nil errors.
|
// always returns nil errors.
|
||||||
func NewFake(logf logger.Logf, _ *device.Device, _ tun.Device) (Router, error) {
|
func NewFake(cfg InitConfig) (Router, error) {
|
||||||
return fakeRouter{logf: logf}, nil
|
return fakeRouter{logf: cfg.Logf}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeRouter struct {
|
type fakeRouter struct {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
// Work is currently underway for an in-kernel FreeBSD implementation of wireguard
|
// Work is currently underway for an in-kernel FreeBSD implementation of wireguard
|
||||||
// https://svnweb.freebsd.org/base?view=revision&revision=357986
|
// https://svnweb.freebsd.org/base?view=revision&revision=357986
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) {
|
func newUserspaceRouter(cfg InitConfig) (Router, error) {
|
||||||
return newUserspaceBSDRouter(logf, nil, tundev)
|
return newUserspaceBSDRouter(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanup(logf logger.Logf, interfaceName string) {
|
func cleanup(logf logger.Logf, interfaceName string) {
|
||||||
|
|
|
@ -10,8 +10,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/tailscale/wireguard-go/device"
|
|
||||||
"github.com/tailscale/wireguard-go/tun"
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
|
@ -85,14 +83,14 @@ type linuxRouter struct {
|
||||||
snatSubnetRoutes bool
|
snatSubnetRoutes bool
|
||||||
netfilterMode NetfilterMode
|
netfilterMode NetfilterMode
|
||||||
|
|
||||||
dns *dns.Manager
|
dns dns.Manager
|
||||||
|
|
||||||
ipt4 netfilterRunner
|
ipt4 netfilterRunner
|
||||||
cmd commandRunner
|
cmd commandRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, _ *device.Device, tunDev tun.Device) (Router, error) {
|
func newUserspaceRouter(cfg InitConfig) (Router, error) {
|
||||||
tunname, err := tunDev.Name()
|
tunname, err := cfg.Tun.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -102,20 +100,21 @@ func newUserspaceRouter(logf logger.Logf, _ *device.Device, tunDev tun.Device) (
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newUserspaceRouterAdvanced(logf, tunname, ipt4, osCommandRunner{})
|
return newUserspaceRouterAdvanced(cfg, tunname, ipt4, osCommandRunner{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, netfilter netfilterRunner, cmd commandRunner) (Router, error) {
|
func newUserspaceRouterAdvanced(cfg InitConfig, tunname string, netfilter netfilterRunner, cmd commandRunner) (Router, error) {
|
||||||
_, err := exec.Command("ip", "rule").Output()
|
_, err := exec.Command("ip", "rule").Output()
|
||||||
ipRuleAvailable := (err == nil)
|
ipRuleAvailable := (err == nil)
|
||||||
|
|
||||||
mconfig := dns.ManagerConfig{
|
mconfig := dns.ManagerConfig{
|
||||||
Logf: logf,
|
Logf: cfg.Logf,
|
||||||
InterfaceName: tunname,
|
InterfaceName: tunname,
|
||||||
|
SetUpstreams: cfg.Resolver.SetUpstreams,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &linuxRouter{
|
return &linuxRouter{
|
||||||
logf: logf,
|
logf: cfg.Logf,
|
||||||
ipRuleAvailable: ipRuleAvailable,
|
ipRuleAvailable: ipRuleAvailable,
|
||||||
tunname: tunname,
|
tunname: tunname,
|
||||||
netfilterMode: NetfilterOff,
|
netfilterMode: NetfilterOff,
|
||||||
|
|
|
@ -241,7 +241,7 @@ nat/POSTROUTING -j ts-postrouting
|
||||||
}
|
}
|
||||||
|
|
||||||
fake := NewFakeOS(t)
|
fake := NewFakeOS(t)
|
||||||
router, err := newUserspaceRouterAdvanced(t.Logf, "tailscale0", fake, fake)
|
router, err := newUserspaceRouterAdvanced(InitConfig{Logf: t.Logf}, "tailscale0", fake, fake)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create router: %v", err)
|
t.Fatalf("failed to create router: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,22 +27,23 @@ type openbsdRouter struct {
|
||||||
local netaddr.IPPrefix
|
local netaddr.IPPrefix
|
||||||
routes map[netaddr.IPPrefix]struct{}
|
routes map[netaddr.IPPrefix]struct{}
|
||||||
|
|
||||||
dns *dns.Manager
|
dns dns.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) {
|
func newUserspaceRouter(cfg InitConfig) (Router, error) {
|
||||||
tunname, err := tundev.Name()
|
tunname, err := cfg.Tun.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mconfig := dns.ManagerConfig{
|
mconfig := dns.ManagerConfig{
|
||||||
Logf: logf,
|
Logf: cfg.Logf,
|
||||||
InterfaceName: tunname,
|
InterfaceName: tunname,
|
||||||
|
SetUpstreams: cfg.Resolver.SetUpstreams,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &openbsdRouter{
|
return &openbsdRouter{
|
||||||
logf: logf,
|
logf: cfg.Logf,
|
||||||
tunname: tunname,
|
tunname: tunname,
|
||||||
dns: dns.NewManager(mconfig),
|
dns: dns.NewManager(mconfig),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -26,22 +26,23 @@ type userspaceBSDRouter struct {
|
||||||
local netaddr.IPPrefix
|
local netaddr.IPPrefix
|
||||||
routes map[netaddr.IPPrefix]struct{}
|
routes map[netaddr.IPPrefix]struct{}
|
||||||
|
|
||||||
dns *dns.Manager
|
dns dns.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUserspaceBSDRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) {
|
func newUserspaceBSDRouter(cfg InitConfig) (Router, error) {
|
||||||
tunname, err := tundev.Name()
|
tunname, err := cfg.Tun.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mconfig := dns.ManagerConfig{
|
mconfig := dns.ManagerConfig{
|
||||||
Logf: logf,
|
Logf: cfg.Logf,
|
||||||
InterfaceName: tunname,
|
InterfaceName: tunname,
|
||||||
|
SetUpstreams: cfg.Resolver.Upstreams,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &userspaceBSDRouter{
|
return &userspaceBSDRouter{
|
||||||
logf: logf,
|
logf: cfg.Logf,
|
||||||
tunname: tunname,
|
tunname: tunname,
|
||||||
dns: dns.NewManager(mconfig),
|
dns: dns.NewManager(mconfig),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -21,25 +21,25 @@ type winRouter struct {
|
||||||
nativeTun *tun.NativeTun
|
nativeTun *tun.NativeTun
|
||||||
wgdev *device.Device
|
wgdev *device.Device
|
||||||
routeChangeCallback *winipcfg.RouteChangeCallback
|
routeChangeCallback *winipcfg.RouteChangeCallback
|
||||||
dns *dns.Manager
|
dns dns.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUserspaceRouter(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (Router, error) {
|
func newUserspaceRouter(cfg InitConfig) (Router, error) {
|
||||||
tunname, err := tundev.Name()
|
tunname, err := cfg.Tun.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nativeTun := tundev.(*tun.NativeTun)
|
nativeTun := cfg.Tun.(*tun.NativeTun)
|
||||||
guid := nativeTun.GUID().String()
|
guid := nativeTun.GUID().String()
|
||||||
mconfig := dns.ManagerConfig{
|
mconfig := dns.ManagerConfig{
|
||||||
Logf: logf,
|
Logf: cfg.Logf,
|
||||||
InterfaceName: guid,
|
InterfaceName: guid,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &winRouter{
|
return &winRouter{
|
||||||
logf: logf,
|
logf: cfg.Logf,
|
||||||
wgdev: wgdev,
|
wgdev: cfg.Wgdev,
|
||||||
tunname: tunname,
|
tunname: tunname,
|
||||||
nativeTun: nativeTun,
|
nativeTun: nativeTun,
|
||||||
dns: dns.NewManager(mconfig),
|
dns: dns.NewManager(mconfig),
|
||||||
|
|
|
@ -152,6 +152,7 @@ func (r *Resolver) SetMap(m *Map) {
|
||||||
func (r *Resolver) SetUpstreams(upstreams []net.Addr) {
|
func (r *Resolver) SetUpstreams(upstreams []net.Addr) {
|
||||||
if r.forwarder != nil {
|
if r.forwarder != nil {
|
||||||
r.forwarder.setUpstreams(upstreams)
|
r.forwarder.setUpstreams(upstreams)
|
||||||
|
r.logf("set upstreams: %v", upstreams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ type userspaceEngine struct {
|
||||||
|
|
||||||
// RouterGen is the signature for a function that creates a
|
// RouterGen is the signature for a function that creates a
|
||||||
// router.Router.
|
// router.Router.
|
||||||
type RouterGen func(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (router.Router, error)
|
type RouterGen func(router.InitConfig) (router.Router, error)
|
||||||
|
|
||||||
type EngineConfig struct {
|
type EngineConfig struct {
|
||||||
// Logf is the logging function used by the engine.
|
// Logf is the logging function used by the engine.
|
||||||
|
@ -309,9 +309,15 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Pass the underlying tun.(*NativeDevice) to the router:
|
routerCfg := router.InitConfig{
|
||||||
// routers do not Read or Write, but do access native interfaces.
|
Logf: logf,
|
||||||
e.router, err = conf.RouterGen(logf, e.wgdev, e.tundev.Unwrap())
|
Wgdev: e.wgdev,
|
||||||
|
// Pass the unwrapped tun.(*NativeDevice) to the router:
|
||||||
|
// routers do not Read or Write, but do access native interfaces.
|
||||||
|
Tun: conf.TUN,
|
||||||
|
Resolver: e.resolver,
|
||||||
|
}
|
||||||
|
e.router, err = conf.RouterGen(routerCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.magicConn.Close()
|
e.magicConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -857,17 +863,21 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config)
|
||||||
|
|
||||||
if routerChanged {
|
if routerChanged {
|
||||||
if routerCfg.DNS.Proxied {
|
if routerCfg.DNS.Proxied {
|
||||||
ips := routerCfg.DNS.Nameservers
|
if len(routerCfg.DNS.Nameservers) == 0 {
|
||||||
upstreams := make([]net.Addr, len(ips))
|
routerCfg.DNS.PerDomain = true
|
||||||
for i, ip := range ips {
|
} else {
|
||||||
stdIP := ip.IPAddr()
|
ips := routerCfg.DNS.Nameservers
|
||||||
upstreams[i] = &net.UDPAddr{
|
upstreams := make([]net.Addr, len(ips))
|
||||||
IP: stdIP.IP,
|
for i, ip := range ips {
|
||||||
Port: 53,
|
stdIP := ip.IPAddr()
|
||||||
Zone: stdIP.Zone,
|
upstreams[i] = &net.UDPAddr{
|
||||||
|
IP: stdIP.IP,
|
||||||
|
Port: 53,
|
||||||
|
Zone: stdIP.Zone,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
e.resolver.SetUpstreams(upstreams)
|
||||||
}
|
}
|
||||||
e.resolver.SetUpstreams(upstreams)
|
|
||||||
routerCfg.DNS.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
|
routerCfg.DNS.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
|
||||||
}
|
}
|
||||||
e.logf("wgengine: Reconfig: configuring router")
|
e.logf("wgengine: Reconfig: configuring router")
|
||||||
|
|
Loading…
Reference in New Issue