diff --git a/net/interfaces/interfaces_default_route_test.go b/net/interfaces/interfaces_default_route_test.go index d88bdf685..b3d4d2904 100644 --- a/net/interfaces/interfaces_default_route_test.go +++ b/net/interfaces/interfaces_default_route_test.go @@ -2,16 +2,85 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux darwin,!redo +// +build linux,!redo package interfaces -import "testing" +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" +) func TestDefaultRouteInterface(t *testing.T) { + // tests /proc/net/route on the local system, cannot make an assertion about + // the correct interface name, but good as a sanity check. v, err := DefaultRouteInterface() if err != nil { t.Fatal(err) } t.Logf("got %q", v) } + +// 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 <= 1000; 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) + } +} diff --git a/net/interfaces/interfaces_linux.go b/net/interfaces/interfaces_linux.go index 4b4dd08a3..20df83cd5 100644 --- a/net/interfaces/interfaces_linux.go +++ b/net/interfaces/interfaces_linux.go @@ -135,14 +135,16 @@ func DefaultRouteInterface() (string, error) { } var zeroRouteBytes = []byte("00000000") +var procNetRoutePath = "/proc/net/route" -func defaultRouteInterfaceProcNet() (string, error) { - f, err := os.Open("/proc/net/route") +func defaultRouteInterfaceProcNetInternal(bufsize int) (string, error) { + f, err := os.Open(procNetRoutePath) if err != nil { return "", err } defer f.Close() - br := bufio.NewReaderSize(f, 128) + + br := bufio.NewReaderSize(f, bufsize) for { line, err := br.ReadSlice('\n') if err == io.EOF { @@ -170,7 +172,25 @@ func defaultRouteInterfaceProcNet() (string, error) { } return "", errors.New("no default routes found") +} +func defaultRouteInterfaceProcNet() (string, error) { + rc, err := defaultRouteInterfaceProcNetInternal(128) + if rc == "" && (err == io.EOF || err == nil) { + // https://github.com/google/gvisor/issues/5732 + // On a regular Linux kernel you can read the first 128 bytes of /proc/net/route, + // then come back later to read the next 128 bytes and so on. + // + // In Google Cloud Run, where /proc/net/route comes from gVisor, you have to + // read it all at once. If you read only the first few bytes then the second + // read returns 0 bytes no matter how much originally appeared to be in the file. + // + // At the time of this writing (Mar 2021) Google Cloud Run has eth0 and eth1 + // with a 384 byte /proc/net/route. We allocate a large buffer to ensure we'll + // read it all in one call. + return defaultRouteInterfaceProcNetInternal(4096) + } + return rc, err } // defaultRouteInterfaceAndroidIPRoute tries to find the machine's default route interface name