Skip to content

Commit

Permalink
support "ip route get" as fallback on linux systems
Browse files Browse the repository at this point in the history
this fixes gateway lookups on android, where neither `netstat -rn`
nor `ip route show` display the gateway.

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.1.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0

$ ip route show
10.0.1.0/24 dev eth0  proto kernel  scope link  src 10.0.1.36

$ ip route get 8.8.8.8
8.8.8.8 via 10.0.1.1 dev eth0  src 10.0.1.36  uid 2000
    cache
  • Loading branch information
tmm1 committed Apr 7, 2018
1 parent 5795ac8 commit 25e5c05
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 6 deletions.
19 changes: 18 additions & 1 deletion gateway_common.go
Expand Up @@ -39,7 +39,7 @@ func parseWindowsRoutePrint(output []byte) (net.IP, error) {
return nil, errNoGateway
}

func parseLinuxIPRoute(output []byte) (net.IP, error) {
func parseLinuxIPRouteShow(output []byte) (net.IP, error) {
// Linux '/usr/bin/ip route show' format looks like this:
// default via 192.168.178.1 dev wlp3s0 metric 303
// 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
Expand All @@ -57,6 +57,23 @@ func parseLinuxIPRoute(output []byte) (net.IP, error) {
return nil, errNoGateway
}

func parseLinuxIPRouteGet(output []byte) (net.IP, error) {
// Linux '/usr/bin/ip route get 8.8.8.8' format looks like this:
// 8.8.8.8 via 10.0.1.1 dev eth0 src 10.0.1.36 uid 2000
lines := strings.Split(string(output), "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) >= 2 && fields[1] == "via" {
ip := net.ParseIP(fields[2])
if ip != nil {
return ip, nil
}
}
}

return nil, errNoGateway
}

func parseLinuxRoute(output []byte) (net.IP, error) {
// Linux route out format is always like this:
// Kernel IP routing table
Expand Down
19 changes: 16 additions & 3 deletions gateway_linux.go
Expand Up @@ -8,19 +8,32 @@ import (
func DiscoverGateway() (ip net.IP, err error) {
ip, err = discoverGatewayUsingRoute()
if err != nil {
ip, err = discoverGatewayUsingIp()
ip, err = discoverGatewayUsingIpRouteShow()
}
if err != nil {
ip, err = discoverGatewayUsingIpRouteGet()
}
return
}

func discoverGatewayUsingIp() (net.IP, error) {
func discoverGatewayUsingIpRouteShow() (net.IP, error) {
routeCmd := exec.Command("ip", "route", "show")
output, err := routeCmd.CombinedOutput()
if err != nil {
return nil, err
}

return parseLinuxIPRoute(output)
return parseLinuxIPRouteShow(output)
}

func discoverGatewayUsingIpRouteGet() (net.IP, error) {
routeCmd := exec.Command("ip", "route", "get", "8.8.8.8")
output, err := routeCmd.CombinedOutput()
if err != nil {
return nil, err
}

return parseLinuxIPRouteGet(output)
}

func discoverGatewayUsingRoute() (net.IP, error) {
Expand Down
43 changes: 41 additions & 2 deletions gateway_test.go
Expand Up @@ -59,7 +59,7 @@ Persistent Routes:
test(t, testcases, parseWindowsRoutePrint)
}

func TestParseLinuxIPRoutePrint(t *testing.T) {
func TestParseLinuxIPRouteShow(t *testing.T) {
correctData := []byte(`
default via 192.168.178.1 dev wlp3s0 metric 303
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
Expand All @@ -85,7 +85,40 @@ default via foo dev wlp3s0 metric 303
{badRoute, false, ""},
}

test(t, testcases, parseLinuxIPRoute)
test(t, testcases, parseLinuxIPRouteShow)
}

func TestParseLinuxIPRouteGet(t *testing.T) {
correctData := []byte(`
8.8.8.8 via 10.0.1.1 dev eth0 src 10.0.1.36 uid 2000
cache`)
randomData := []byte(`
test
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
`)
noRoute := []byte(`
broadcast 255.255.255.255 dev eth0 src 10.0.1.36 uid 2000
cache <local,brd>
`)
badRoute := []byte(`
local 0.0.0.0 dev lo src 127.0.0.1 uid 2000
cache <local>
`)
errorRoute := []byte(`
RTNETLINK answers: Invalid argument
`)

testcases := []testcase{
{correctData, true, "10.0.1.1"},
{randomData, false, ""},
{noRoute, false, ""},
{badRoute, false, ""},
{errorRoute, false, ""},
}

test(t, testcases, parseLinuxIPRouteGet)
}

func TestParseLinuxRoutePrint(t *testing.T) {
Expand All @@ -108,13 +141,19 @@ Destination Gateway Genmask Flags Metric Ref Use Iface
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 foo 0.0.0.0 UG 0 0 0 eth0
`)
missingRoute := []byte(`
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
`)

testcases := []testcase{
{correctData, true, "192.168.1.1"},
{randomData, false, ""},
{noRoute, false, ""},
{badRoute, false, ""},
{missingRoute, false, ""},
}

test(t, testcases, parseLinuxRoute)
Expand Down

0 comments on commit 25e5c05

Please sign in to comment.