wgengine/router: support legacy resolvconf

Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
Dmytro Shynkevych 2020-07-23 14:34:39 -04:00 committed by Dmytro Shynkevych
parent 05a79d79ae
commit c53ab3111d
1 changed files with 57 additions and 2 deletions

View File

@ -57,13 +57,57 @@ func resolvconfIsActive() bool {
return false
}
// resolvconfImplementation enumerates supported implementations of the resolvconf CLI.
type resolvconfImplementation uint8
const (
// resolvconfOpenresolv is the implementation packaged as "openresolv" on Ubuntu.
// It supports exclusive mode and interface metrics.
resolvconfOpenresolv resolvconfImplementation = iota
// resolvconfLegacy is the implementation by Thomas Hood packaged as "resolvconf" on Ubuntu.
// It does not support exclusive mode or interface metrics.
resolvconfLegacy
)
// getResolvconfImplementation returns the implementation of resolvconf
// that appears to be in use.
func getResolvconfImplementation() resolvconfImplementation {
err := exec.Command("resolvconf", "-v").Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// Thomas Hood's resolvconf has a minimal flag set
// and exits with code 99 when passed an unknown flag.
if exitErr.ExitCode() == 99 {
return resolvconfLegacy
}
}
}
return resolvconfOpenresolv
}
// resolvconfConfigName is the name of the config submitted to resolvconf.
// It has this form to match the "tun*" rule in interface-order
// when running resolvconfLegacy, hopefully placing our config first.
const resolvconfConfigName = "tun-tailscale.inet"
// dnsResolvconfUp invokes the resolvconf binary to associate
// the given DNS configuration the Tailscale interface.
func dnsResolvconfUp(config DNSConfig, interfaceName string) error {
implementation := getResolvconfImplementation()
stdin := new(bytes.Buffer)
dnsWriteConfig(stdin, config.Nameservers, config.Domains) // dns_direct.go
cmd := exec.Command("resolvconf", "-m", "0", "-x", "-a", interfaceName+".inet")
var cmd *exec.Cmd
switch implementation {
case resolvconfOpenresolv:
// Request maximal priority (metric 0) and exclusive mode.
cmd = exec.Command("resolvconf", "-m", "0", "-x", "-a", resolvconfConfigName)
case resolvconfLegacy:
// This does not quite give us the desired behavior (queries leak),
// but there is nothing else we can do without messing with other interfaces' settings.
cmd = exec.Command("resolvconf", "-a", resolvconfConfigName)
}
cmd.Stdin = stdin
out, err := cmd.CombinedOutput()
if err != nil {
@ -75,10 +119,21 @@ func dnsResolvconfUp(config DNSConfig, interfaceName string) error {
// dnsResolvconfDown undoes the action of dnsResolvconfUp.
func dnsResolvconfDown(interfaceName string) error {
cmd := exec.Command("resolvconf", "-f", "-d", interfaceName+".inet")
implementation := getResolvconfImplementation()
var cmd *exec.Cmd
switch implementation {
case resolvconfOpenresolv:
cmd = exec.Command("resolvconf", "-f", "-d", resolvconfConfigName)
case resolvconfLegacy:
// resolvconfLegacy lacks the -f flag.
// Instead, it succeeds even when the config does not exist.
cmd = exec.Command("resolvconf", "-d", resolvconfConfigName)
}
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("running %s: %s", cmd, out)
}
return nil
}