gateway
This commit is contained in:
parent
5db26291ed
commit
7c6cac9569
131
net/gateway/gateway_common.go
Normal file
131
net/gateway/gateway_common.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNoGateway = errors.New("no gateway found")
|
||||||
|
|
||||||
|
func parseWindowsRoutePrint(output []byte) (net.IP, string, error) {
|
||||||
|
// Windows route output format is always like this:
|
||||||
|
// ===========================================================================
|
||||||
|
// Active Routes:
|
||||||
|
// Network Destination Netmask Gateway Interface Metric
|
||||||
|
// 0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.100 20
|
||||||
|
// ===========================================================================
|
||||||
|
// I'm trying to pick the active route,
|
||||||
|
// then jump 2 lines and pick the third IP
|
||||||
|
// Not using regex because output is quite standard from Windows XP to 8 (NEEDS TESTING)
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for idx, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "Active Routes:") {
|
||||||
|
if len(lines) <= idx+2 {
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(lines[idx+2])
|
||||||
|
if len(fields) < 3 {
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(fields[2])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinuxIPRoute(output []byte) (net.IP, string, 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
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 3 && fields[0] == "default" {
|
||||||
|
ip := net.ParseIP(fields[2])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[4], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinuxRoute(output []byte) (net.IP, string, error) {
|
||||||
|
// Linux route out format is always like this:
|
||||||
|
// Kernel IP routing table
|
||||||
|
// Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
// 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 && fields[0] == "0.0.0.0" {
|
||||||
|
ip := net.ParseIP(fields[1])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[7], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDarwinRouteGet(output []byte) (net.IP, string, error) {
|
||||||
|
// Darwin route out format is always like this:
|
||||||
|
// route to: default
|
||||||
|
// destination: default
|
||||||
|
// mask: default
|
||||||
|
// gateway: 192.168.1.1
|
||||||
|
// interface: tun0
|
||||||
|
// flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 && fields[0] == "gateway:" {
|
||||||
|
ip := net.ParseIP(fields[1])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBSDSolarisNetstat(output []byte) (net.IP, string, error) {
|
||||||
|
// netstat -rn produces the following on FreeBSD:
|
||||||
|
// Routing tables
|
||||||
|
//
|
||||||
|
// Internet:
|
||||||
|
// Destination Gateway Flags Netif Expire
|
||||||
|
// default 10.88.88.2 UGS em0
|
||||||
|
// 10.88.88.0/24 link#1 U em0
|
||||||
|
// 10.88.88.148 link#1 UHS lo0
|
||||||
|
// 127.0.0.1 link#2 UH lo0
|
||||||
|
//
|
||||||
|
// Internet6:
|
||||||
|
// Destination Gateway Flags Netif Expire
|
||||||
|
// ::/96 ::1 UGRS lo0
|
||||||
|
// ::1 link#2 UH lo0
|
||||||
|
// ::ffff:0.0.0.0/96 ::1 UGRS lo0
|
||||||
|
// fe80::/10 ::1 UGRS lo0
|
||||||
|
// ...
|
||||||
|
outputLines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range outputLines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 && fields[0] == "default" {
|
||||||
|
ip := net.ParseIP(fields[1])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
16
net/gateway/gateway_darwin.go
Normal file
16
net/gateway/gateway_darwin.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (net.IP, string, error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseDarwinRouteGet(output)
|
||||||
|
}
|
16
net/gateway/gateway_freebsd.go
Normal file
16
net/gateway/gateway_freebsd.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseBSDSolarisNetstat(output)
|
||||||
|
}
|
34
net/gateway/gateway_linux.go
Normal file
34
net/gateway/gateway_linux.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
ip, iface, err = discoverGatewayUsingRoute()
|
||||||
|
if err != nil {
|
||||||
|
ip, iface, err = discoverGatewayUsingIp()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverGatewayUsingIp() (net.IP, string, error) {
|
||||||
|
routeCmd := exec.Command("ip", "route", "show")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLinuxIPRoute(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverGatewayUsingRoute() (net.IP, string, error) {
|
||||||
|
routeCmd := exec.Command("route", "-n")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLinuxRoute(output)
|
||||||
|
}
|
16
net/gateway/gateway_solaris.go
Normal file
16
net/gateway/gateway_solaris.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseBSDSolarisNetstat(output)
|
||||||
|
}
|
248
net/gateway/gateway_test.go
Normal file
248
net/gateway/gateway_test.go
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testcase struct {
|
||||||
|
output []byte
|
||||||
|
ok bool
|
||||||
|
gateway string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseWindowsRoutePrint(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
Network Destination Netmask Gateway Interface Metric
|
||||||
|
0.0.0.0 0.0.0.0 10.88.88.2 10.88.88.149 10
|
||||||
|
===========================================================================
|
||||||
|
Persistent Routes:
|
||||||
|
`)
|
||||||
|
randomData := []byte(`
|
||||||
|
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(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
`)
|
||||||
|
badRoute1 := []byte(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
===========================================================================
|
||||||
|
Persistent Routes:
|
||||||
|
`)
|
||||||
|
badRoute2 := []byte(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
Network Destination Netmask Gateway Interface Metric
|
||||||
|
0.0.0.0 0.0.0.0 foo 10.88.88.149 10
|
||||||
|
===========================================================================
|
||||||
|
Persistent Routes:
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "10.88.88.2"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute1, false, ""},
|
||||||
|
{badRoute2, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseWindowsRoutePrint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLinuxIPRoutePrint(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
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
default via foo dev wlp3s0 metric 303
|
||||||
|
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "192.168.178.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseLinuxIPRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLinuxRoutePrint(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
Kernel IP routing table
|
||||||
|
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
Kernel IP routing table
|
||||||
|
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
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
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "192.168.1.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseLinuxRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDarwinRouteGet(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
route to: 0.0.0.0
|
||||||
|
destination: default
|
||||||
|
mask: default
|
||||||
|
gateway: 172.16.32.1
|
||||||
|
interface: en0
|
||||||
|
flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
|
||||||
|
0 0 0 0 0 0 1500 0
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
route to: 0.0.0.0
|
||||||
|
destination: default
|
||||||
|
mask: default
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
route to: 0.0.0.0
|
||||||
|
destination: default
|
||||||
|
mask: default
|
||||||
|
gateway: foo
|
||||||
|
interface: en0
|
||||||
|
flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
|
||||||
|
0 0 0 0 0 0 1500 0
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "172.16.32.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseDarwinRouteGet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseBSDSolarisNetstat(t *testing.T) {
|
||||||
|
correctDataFreeBSD := []byte(`
|
||||||
|
Routing tables
|
||||||
|
|
||||||
|
Internet:
|
||||||
|
Destination Gateway Flags Netif Expire
|
||||||
|
default 10.88.88.2 UGS em0
|
||||||
|
10.88.88.0/24 link#1 U em0
|
||||||
|
10.88.88.148 link#1 UHS lo0
|
||||||
|
127.0.0.1 link#2 UH lo0
|
||||||
|
|
||||||
|
Internet6:
|
||||||
|
Destination Gateway Flags Netif Expire
|
||||||
|
::/96 ::1 UGRS lo0
|
||||||
|
::1 link#2 UH lo0
|
||||||
|
::ffff:0.0.0.0/96 ::1 UGRS lo0
|
||||||
|
fe80::/10 ::1 UGRS lo0
|
||||||
|
`)
|
||||||
|
correctDataSolaris := []byte(`
|
||||||
|
Routing Table: IPv4
|
||||||
|
Destination Gateway Flags Ref Use Interface
|
||||||
|
-------------------- -------------------- ----- ----- ---------- ---------
|
||||||
|
default 172.16.32.1 UG 2 76419 net0
|
||||||
|
127.0.0.1 127.0.0.1 UH 2 36 lo0
|
||||||
|
172.16.32.0 172.16.32.17 U 4 8100 net0
|
||||||
|
|
||||||
|
Routing Table: IPv6
|
||||||
|
Destination/Mask Gateway Flags Ref Use If
|
||||||
|
--------------------------- --------------------------- ----- --- ------- -----
|
||||||
|
::1 ::1 UH 3 75382 lo0
|
||||||
|
2001:470:deeb:32::/64 2001:470:deeb:32::17 U 3 2744 net0
|
||||||
|
fe80::/10 fe80::6082:52ff:fedc:7df0 U 3 8430 net0
|
||||||
|
`)
|
||||||
|
randomData := []byte(`
|
||||||
|
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(`
|
||||||
|
Internet:
|
||||||
|
Destination Gateway Flags Netif Expire
|
||||||
|
10.88.88.0/24 link#1 U em0
|
||||||
|
10.88.88.148 link#1 UHS lo0
|
||||||
|
127.0.0.1 link#2 UH lo0
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
Internet:
|
||||||
|
Destination Gateway Flags Netif Expire
|
||||||
|
default foo UGS em0
|
||||||
|
10.88.88.0/24 link#1 U em0
|
||||||
|
10.88.88.148 link#1 UHS lo0
|
||||||
|
127.0.0.1 link#2 UH lo0
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctDataFreeBSD, true, "10.88.88.2"},
|
||||||
|
{correctDataSolaris, true, "172.16.32.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseBSDSolarisNetstat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test(t *testing.T, testcases []testcase, fn func([]byte) (net.IP, string, error)) {
|
||||||
|
for i, tc := range testcases {
|
||||||
|
net, iface, err := fn(tc.output)
|
||||||
|
if tc.ok {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error in test #%d: %v", i, err)
|
||||||
|
}
|
||||||
|
if net.String() != tc.gateway {
|
||||||
|
t.Errorf("Unexpected gateway address %v != %s", net, tc.gateway)
|
||||||
|
}
|
||||||
|
if "" == iface {
|
||||||
|
t.Errorf("Unexpected interface")
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
t.Errorf("Unexpected nil error in test #%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
net/gateway/gateway_unimplemented.go
Normal file
14
net/gateway/gateway_unimplemented.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build !darwin,!linux,!windows,!solaris,!freebsd
|
||||||
|
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
err = fmt.Errorf("DiscoverGateway not implemented for OS %s", runtime.GOOS)
|
||||||
|
return
|
||||||
|
}
|
16
net/gateway/gateway_windows.go
Normal file
16
net/gateway/gateway_windows.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("route", "print", "0.0.0.0")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseWindowsRoutePrint(output)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user