mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
interfaces: try larger read from /proc/net/route
Work around https://github.com/google/gvisor/issues/5732 by trying to read /proc/net/route with a larger bufsize if it fails the first time. Signed-off-by: Denton Gentry <dgentry@tailscale.com>
This commit is contained in:
parent
54ba6194f7
commit
a9745a0b68
@ -2,16 +2,85 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build linux darwin,!redo
|
// +build linux,!redo
|
||||||
|
|
||||||
package interfaces
|
package interfaces
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestDefaultRouteInterface(t *testing.T) {
|
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()
|
v, err := DefaultRouteInterface()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("got %q", v)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -135,14 +135,16 @@ func DefaultRouteInterface() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var zeroRouteBytes = []byte("00000000")
|
var zeroRouteBytes = []byte("00000000")
|
||||||
|
var procNetRoutePath = "/proc/net/route"
|
||||||
|
|
||||||
func defaultRouteInterfaceProcNet() (string, error) {
|
func defaultRouteInterfaceProcNetInternal(bufsize int) (string, error) {
|
||||||
f, err := os.Open("/proc/net/route")
|
f, err := os.Open(procNetRoutePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
br := bufio.NewReaderSize(f, 128)
|
|
||||||
|
br := bufio.NewReaderSize(f, bufsize)
|
||||||
for {
|
for {
|
||||||
line, err := br.ReadSlice('\n')
|
line, err := br.ReadSlice('\n')
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -170,7 +172,25 @@ func defaultRouteInterfaceProcNet() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("no default routes found")
|
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
|
// defaultRouteInterfaceAndroidIPRoute tries to find the machine's default route interface name
|
||||||
|
Loading…
Reference in New Issue
Block a user