mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 11:35:35 +00:00
2a67beaacf
tailscaled was using 100% CPU on a machine with ~1M lines, 100MB+ of /proc/net/route data. Two problems: in likelyHomeRouterIPLinux, we didn't stop reading the file once we found the default route (which is on the first non-header line when present). Which meant it was finding the answer and then parsing 100MB over 1M lines unnecessarily. Second was that if the default route isn't present, it'd read to the end of the file looking for it. If it's not in the first 1,000 lines, it ain't coming, or at least isn't worth having. (it's only used for discovering a potential UPnP/PMP/PCP server, which is very unlikely to be present in the environment of a machine with a ton of routes) Change-Id: I2c4a291ab7f26aedc13885d79237b8f05c2fd8e4 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
// 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 (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// test the specific /proc/net/route path as found on Google Cloud Run instances
|
|
func TestGoogleCloudRunDefaultRouteInterface(t *testing.T) {
|
|
dir := t.TempDir()
|
|
savedProcNetRoutePath := procNetRoutePath
|
|
defer func() { procNetRoutePath = savedProcNetRoutePath }()
|
|
procNetRoutePath = filepath.Join(dir, "CloudRun")
|
|
buf := []byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n" +
|
|
"eth0\t8008FEA9\t00000000\t0001\t0\t0\t0\t01FFFFFF\t0\t0\t0\n" +
|
|
"eth1\t00000000\t00000000\t0001\t0\t0\t0\t00000000\t0\t0\t0\n")
|
|
err := ioutil.WriteFile(procNetRoutePath, buf, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DefaultRouteInterface()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got != "eth1" {
|
|
t.Fatalf("got %s, want eth1", got)
|
|
}
|
|
}
|
|
|
|
// we read chunks of /proc/net/route at a time, test that files longer than the chunk
|
|
// size can be handled.
|
|
func TestExtremelyLongProcNetRoute(t *testing.T) {
|
|
dir := t.TempDir()
|
|
savedProcNetRoutePath := procNetRoutePath
|
|
defer func() { procNetRoutePath = savedProcNetRoutePath }()
|
|
procNetRoutePath = filepath.Join(dir, "VeryLong")
|
|
f, err := os.Create(procNetRoutePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = f.Write([]byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for n := 0; n <= 900; n++ {
|
|
line := fmt.Sprintf("eth%d\t8008FEA9\t00000000\t0001\t0\t0\t0\t01FFFFFF\t0\t0\t0\n", n)
|
|
_, err := f.Write([]byte(line))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
_, err = f.Write([]byte("tokenring1\t00000000\t00000000\t0001\t0\t0\t0\t00000000\t0\t0\t0\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := DefaultRouteInterface()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got != "tokenring1" {
|
|
t.Fatalf("got %q, want tokenring1", got)
|
|
}
|
|
}
|
|
|
|
// test the specific /proc/net/route path as found on AWS App Runner instances
|
|
func TestAwsAppRunnerDefaultRouteInterface(t *testing.T) {
|
|
dir := t.TempDir()
|
|
savedProcNetRoutePath := procNetRoutePath
|
|
defer func() { procNetRoutePath = savedProcNetRoutePath }()
|
|
procNetRoutePath = filepath.Join(dir, "CloudRun")
|
|
buf := []byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n" +
|
|
"eth0\t00000000\tF9AFFEA9\t0003\t0\t0\t0\t00000000\t0\t0\t0\n" +
|
|
"*\tFEA9FEA9\t00000000\t0005\t0\t0\t0\tFFFFFFFF\t0\t0\t0\n" +
|
|
"ecs-eth0\t02AAFEA9\t01ACFEA9\t0007\t0\t0\t0\tFFFFFFFF\t0\t0\t0\n" +
|
|
"ecs-eth0\t00ACFEA9\t00000000\t0001\t0\t0\t0\t00FFFFFF\t0\t0\t0\n" +
|
|
"eth0\t00AFFEA9\t00000000\t0001\t0\t0\t0\t00FFFFFF\t0\t0\t0\n")
|
|
err := ioutil.WriteFile(procNetRoutePath, buf, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DefaultRouteInterface()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got != "eth0" {
|
|
t.Fatalf("got %s, want eth0", got)
|
|
}
|
|
}
|
|
|
|
func BenchmarkDefaultRouteInterface(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err := DefaultRouteInterface(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|