summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2020-08-10 12:40:00 -0700
committerBrad Fitzpatrick <bradfitz@tailscale.com>2020-08-10 13:01:49 -0700
commitc1024a5de2fc6695bc2acf1b3444ae6deda3fa2e (patch)
treeee151fa8d8e63c92404fc7feb0718dc85b87371d
parentd65e2632ab1940b9bae9fa14b012cf79161b9cbc (diff)
downloadtailscale-bradfitz/linux_default_route_interface.tar.xz
tailscale-bradfitz/linux_default_route_interface.zip
net/netns, net/interfaces: move defaultRouteInterface, add Android fallbackbradfitz/linux_default_route_interface
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
-rw-r--r--net/interfaces/interfaces_linux.go93
-rw-r--r--net/interfaces/interfaces_linux_test.go16
-rw-r--r--net/netns/netns_linux.go49
-rw-r--r--net/netns/netns_linux_test.go9
4 files changed, 111 insertions, 56 deletions
diff --git a/net/interfaces/interfaces_linux.go b/net/interfaces/interfaces_linux.go
index 0290c067d..4b4dd08a3 100644
--- a/net/interfaces/interfaces_linux.go
+++ b/net/interfaces/interfaces_linux.go
@@ -5,10 +5,15 @@
package interfaces
import (
+ "bufio"
"bytes"
+ "errors"
+ "io"
"log"
+ "os"
"os/exec"
"runtime"
+ "strings"
"go4.org/mem"
"inet.af/netaddr"
@@ -115,3 +120,91 @@ func likelyHomeRouterIPAndroid() (ret netaddr.IP, ok bool) {
cmd.Wait()
return ret, !ret.IsZero()
}
+
+// DefaultRouteInterface returns the name of the network interface that owns
+// the default route, not including any tailscale interfaces.
+func DefaultRouteInterface() (string, error) {
+ v, err := defaultRouteInterfaceProcNet()
+ if err == nil {
+ return v, nil
+ }
+ if runtime.GOOS == "android" {
+ return defaultRouteInterfaceAndroidIPRoute()
+ }
+ return v, err
+}
+
+var zeroRouteBytes = []byte("00000000")
+
+func defaultRouteInterfaceProcNet() (string, error) {
+ f, err := os.Open("/proc/net/route")
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+ br := bufio.NewReaderSize(f, 128)
+ for {
+ line, err := br.ReadSlice('\n')
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return "", err
+ }
+ if !bytes.Contains(line, zeroRouteBytes) {
+ continue
+ }
+ fields := strings.Fields(string(line))
+ ifc := fields[0]
+ ip := fields[1]
+ netmask := fields[7]
+
+ if strings.HasPrefix(ifc, "tailscale") ||
+ strings.HasPrefix(ifc, "wg") {
+ continue
+ }
+ if ip == "00000000" && netmask == "00000000" {
+ // default route
+ return ifc, nil // interface name
+ }
+ }
+
+ return "", errors.New("no default routes found")
+
+}
+
+// defaultRouteInterfaceAndroidIPRoute tries to find the machine's default route interface name
+// by parsing the "ip route" command output. We use this on Android where /proc/net/route
+// can be missing entries or have locked-down permissions.
+// See also comments in https://github.com/tailscale/tailscale/pull/666.
+func defaultRouteInterfaceAndroidIPRoute() (ifname string, err error) {
+ cmd := exec.Command("/system/bin/ip", "route", "show", "table", "0")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return "", err
+ }
+ if err := cmd.Start(); err != nil {
+ log.Printf("interfaces: running /system/bin/ip: %v", err)
+ return "", err
+ }
+ // Search for line like "default via 10.0.2.2 dev radio0 table 1016 proto static mtu 1500 "
+ lineread.Reader(out, func(line []byte) error {
+ const pfx = "default via "
+ if !mem.HasPrefix(mem.B(line), mem.S(pfx)) {
+ return nil
+ }
+ ff := strings.Fields(string(line))
+ for i, v := range ff {
+ if i > 0 && ff[i-1] == "dev" && ifname == "" {
+ ifname = v
+ }
+ }
+ return nil
+ })
+ cmd.Process.Kill()
+ cmd.Wait()
+ if ifname == "" {
+ return "", errors.New("no default routes found")
+ }
+ return ifname, nil
+}
diff --git a/net/interfaces/interfaces_linux_test.go b/net/interfaces/interfaces_linux_test.go
new file mode 100644
index 000000000..3aa69d2f6
--- /dev/null
+++ b/net/interfaces/interfaces_linux_test.go
@@ -0,0 +1,16 @@
+// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package interfaces
+
+import "testing"
+
+func BenchmarkDefaultRouteInterface(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ if _, err := DefaultRouteInterface(); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/net/netns/netns_linux.go b/net/netns/netns_linux.go
index 5a607066b..d24b5adfc 100644
--- a/net/netns/netns_linux.go
+++ b/net/netns/netns_linux.go
@@ -5,19 +5,15 @@
package netns
import (
- "bufio"
- "bytes"
- "errors"
"flag"
"fmt"
- "io"
"os"
"os/exec"
- "strings"
"sync"
"syscall"
"golang.org/x/sys/unix"
+ "tailscale.com/net/interfaces"
)
// tailscaleBypassMark is the mark indicating that packets originating
@@ -43,47 +39,6 @@ func ipRuleAvailable() bool {
return ipRuleOnce.v
}
-var zeroRouteBytes = []byte("00000000")
-
-// defaultRouteInterface returns the name of the network interface that owns
-// the default route, not including any tailscale interfaces. We only use
-// this in SO_BINDTODEVICE mode.
-func defaultRouteInterface() (string, error) {
- f, err := os.Open("/proc/net/route")
- if err != nil {
- return "", err
- }
- defer f.Close()
- br := bufio.NewReaderSize(f, 128)
- for {
- line, err := br.ReadSlice('\n')
- if err == io.EOF {
- break
- }
- if err != nil {
- return "", err
- }
- if !bytes.Contains(line, zeroRouteBytes) {
- continue
- }
- fields := strings.Fields(string(line))
- ifc := fields[0]
- ip := fields[1]
- netmask := fields[7]
-
- if strings.HasPrefix(ifc, "tailscale") ||
- strings.HasPrefix(ifc, "wg") {
- continue
- }
- if ip == "00000000" && netmask == "00000000" {
- // default route
- return ifc, nil // interface name
- }
- }
-
- return "", errors.New("no default routes found")
-}
-
// ignoreErrors returns true if we should ignore setsocketopt errors in
// this instance.
func ignoreErrors() bool {
@@ -133,7 +88,7 @@ func setBypassMark(fd uintptr) error {
}
func bindToDevice(fd uintptr) error {
- ifc, err := defaultRouteInterface()
+ ifc, err := interfaces.DefaultRouteInterface()
if err != nil {
// Make sure we bind to *some* interface,
// or we could get a routing loop.
diff --git a/net/netns/netns_linux_test.go b/net/netns/netns_linux_test.go
index 5afe647ef..8b050f7f5 100644
--- a/net/netns/netns_linux_test.go
+++ b/net/netns/netns_linux_test.go
@@ -49,12 +49,3 @@ func TestBypassMarkInSync(t *testing.T) {
}
t.Errorf("tailscaleBypassMark not found in router_linux.go")
}
-
-func BenchmarkDefaultRouteInterface(b *testing.B) {
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- if _, err := defaultRouteInterface(); err != nil {
- b.Fatal(err)
- }
- }
-}