wgengine/router: dns: unify on *BSD, multimode on Linux, Magic DNS (#536)

Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
Dmytro Shynkevych
2020-07-14 09:12:00 -04:00
committed by GitHub
parent 6e8f0860af
commit 30bbbe9467
19 changed files with 923 additions and 250 deletions

View File

@@ -5,20 +5,14 @@
package router
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/tailscale/wireguard-go/device"
"github.com/tailscale/wireguard-go/tun"
"inet.af/netaddr"
"tailscale.com/atomicfile"
"tailscale.com/types/logger"
)
@@ -31,6 +25,8 @@ type openbsdRouter struct {
tunname string
local netaddr.IPPrefix
routes map[netaddr.IPPrefix]struct{}
dnsConfig DNSConfig
}
func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) {
@@ -159,112 +155,28 @@ func (r *openbsdRouter) Set(cfg *Config) error {
r.local = localAddr
r.routes = newRoutes
if err := r.replaceResolvConf(cfg.DNS, cfg.DNSDomains); err != nil {
errq = fmt.Errorf("replacing resolv.conf failed: %v", err)
if !r.dnsConfig.EquivalentTo(cfg.DNSConfig) {
if err := dnsDirectUp(cfg.DNSConfig); err != nil {
errq = fmt.Errorf("dns up: direct: %v", err)
} else {
r.dnsConfig = cfg.DNSConfig
}
}
return errq
}
func (r *openbsdRouter) Close() error {
out, err := cmd("ifconfig", r.tunname, "down").CombinedOutput()
cleanup(r.logf, r.tunname)
return nil
}
func cleanup(logf logger.Logf, interfaceName string) {
if err := dnsDirectDown(); err != nil {
logf("dns down: direct: %v", err)
}
out, err := cmd("ifconfig", interfaceName, "down").CombinedOutput()
if err != nil {
r.logf("running ifconfig failed: %v\n%s", err, out)
logf("ifconfig down: %v\n%s", err, out)
}
if err := r.restoreResolvConf(); err != nil {
r.logf("failed to restore system resolv.conf: %v", err)
}
return nil
}
const (
tsConf = "/etc/resolv.tailscale.conf"
backupConf = "/etc/resolv.pre-tailscale-backup.conf"
resolvConf = "/etc/resolv.conf"
)
func (r *openbsdRouter) replaceResolvConf(servers []netaddr.IP, domains []string) error {
if len(servers) == 0 {
return r.restoreResolvConf()
}
// Write the tsConf file.
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "# resolv.conf(5) file generated by tailscale\n")
fmt.Fprintf(buf, "# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN\n\n")
for _, ns := range servers {
fmt.Fprintf(buf, "nameserver %s\n", ns)
}
if len(domains) > 0 {
fmt.Fprintf(buf, "search "+strings.Join(domains, " ")+"\n")
}
tf, err := ioutil.TempFile(filepath.Dir(tsConf), filepath.Base(tsConf)+".*")
if err != nil {
return err
}
tempName := tf.Name()
tf.Close()
if err := atomicfile.WriteFile(tempName, buf.Bytes(), 0644); err != nil {
return err
}
if err := os.Rename(tempName, tsConf); err != nil {
return err
}
if linkPath, err := os.Readlink(resolvConf); err != nil {
// Remove any old backup that may exist.
os.Remove(backupConf)
// Backup the existing /etc/resolv.conf file.
contents, err := ioutil.ReadFile(resolvConf)
if os.IsNotExist(err) {
// No existing /etc/resolv.conf file to backup.
// Nothing to do.
return nil
} else if err != nil {
return err
}
if err := atomicfile.WriteFile(backupConf, contents, 0644); err != nil {
return err
}
} else if linkPath != tsConf {
// Backup the existing symlink.
os.Remove(backupConf)
if err := os.Symlink(linkPath, backupConf); err != nil {
return err
}
} else {
// Nothing to do, resolvConf already points to tsConf.
return nil
}
os.Remove(resolvConf)
if err := os.Symlink(tsConf, resolvConf); err != nil {
return nil
}
return nil
}
func (r *openbsdRouter) restoreResolvConf() error {
if _, err := os.Stat(backupConf); err != nil {
if os.IsNotExist(err) {
return nil // No backup resolv.conf to restore.
}
return err
}
if ln, err := os.Readlink(resolvConf); err != nil {
return err
} else if ln != tsConf {
return fmt.Errorf("resolv.conf is not a symlink to %s", tsConf)
}
if err := os.Rename(backupConf, resolvConf); err != nil {
return err
}
os.Remove(tsConf) // Best effort removal.
return nil
}