summaryrefslogtreecommitdiffhomepage
path: root/net/interfaces/interfaces_linux.go
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 /net/interfaces/interfaces_linux.go
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>
Diffstat (limited to 'net/interfaces/interfaces_linux.go')
-rw-r--r--net/interfaces/interfaces_linux.go93
1 files changed, 93 insertions, 0 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
+}