From a5d6c9d6162f76457ea9d58c722d1917e0650137 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 31 May 2020 15:36:10 -0700 Subject: [PATCH] net/netns: optimize defaultRouteInterface a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It'll be called a bunch, so worth a bit of effort. Could go further, but not yet. (really, should hook into wgengine/monitor and only re-read on netlink changes?) name old time/op new time/op delta DefaultRouteInterface-8 60.8µs ±11% 44.6µs ± 5% -26.65% (p=0.000 n=20+19) name old alloc/op new alloc/op delta DefaultRouteInterface-8 3.29kB ± 0% 0.55kB ± 0% -83.21% (p=0.000 n=20+20) name old allocs/op new allocs/op delta DefaultRouteInterface-8 9.00 ± 0% 6.00 ± 0% -33.33% (p=0.000 n=20+20) --- net/netns/netns_linux.go | 25 ++++++++++++++++++++----- net/netns/netns_linux_test.go | 9 +++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/net/netns/netns_linux.go b/net/netns/netns_linux.go index 75d24eaff..1609320c4 100644 --- a/net/netns/netns_linux.go +++ b/net/netns/netns_linux.go @@ -5,10 +5,12 @@ package netns import ( + "bufio" + "bytes" "errors" "flag" "fmt" - "io/ioutil" + "io" "os" "os/exec" "strings" @@ -41,17 +43,30 @@ 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) { - b, err := ioutil.ReadFile("/proc/net/route") + f, err := os.Open("/proc/net/route") if err != nil { return "", err } - - for _, line := range strings.Split(string(b), "\n")[1:] { - fields := strings.Fields(line) + 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] diff --git a/net/netns/netns_linux_test.go b/net/netns/netns_linux_test.go index 8b050f7f5..5afe647ef 100644 --- a/net/netns/netns_linux_test.go +++ b/net/netns/netns_linux_test.go @@ -49,3 +49,12 @@ 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) + } + } +}