725 lines
28 KiB
Go
725 lines
28 KiB
Go
/*
|
|
Copyright 2014 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package net
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const gatewayfirst = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
|
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
const gatewaylast = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
|
`
|
|
const gatewaymiddle = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
const noInternetConnection = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
const nothing = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
`
|
|
const badDestination = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
|
eth3 0000FE0AA1 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
const badGateway = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
eth3 00000000 0100FE0AA1 0003 0 0 1024 00000000 0 0 0
|
|
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
const route_Invalidhex = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
|
eth3 00000000 0100FE0AA 0003 0 0 1024 00000000 0 0 0
|
|
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
|
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
|
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
|
`
|
|
|
|
const v6gatewayfirst = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
|
20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
|
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
|
`
|
|
const v6gatewaylast = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
|
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
|
00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
|
`
|
|
const v6gatewaymiddle = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
|
00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
|
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
|
`
|
|
const v6noDefaultRoutes = `00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
|
20010001000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00000001 docker0
|
|
20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
|
fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
|
`
|
|
const v6nothing = ``
|
|
const v6badDestination = `2001000200000000 7a 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
|
`
|
|
const v6badGateway = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 200100010000000000000000000000000012 00000064 00000000 00000000 00000003 eth3
|
|
`
|
|
const v6route_Invalidhex = `000000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021fcafffea0ec00 00000064 00000000 00000000 00000003 enp1s0f0
|
|
|
|
`
|
|
|
|
const (
|
|
flagUp = net.FlagUp | net.FlagBroadcast | net.FlagMulticast
|
|
flagDown = net.FlagBroadcast | net.FlagMulticast
|
|
flagLoopback = net.FlagUp | net.FlagLoopback
|
|
flagP2P = net.FlagUp | net.FlagPointToPoint
|
|
)
|
|
|
|
func makeIntf(index int, name string, flags net.Flags) net.Interface {
|
|
mac := net.HardwareAddr{0, 0x32, 0x7d, 0x69, 0xf7, byte(0x30 + index)}
|
|
return net.Interface{
|
|
Index: index,
|
|
MTU: 1500,
|
|
Name: name,
|
|
HardwareAddr: mac,
|
|
Flags: flags}
|
|
}
|
|
|
|
var (
|
|
downIntf = makeIntf(1, "eth3", flagDown)
|
|
loopbackIntf = makeIntf(1, "lo", flagLoopback)
|
|
p2pIntf = makeIntf(1, "lo", flagP2P)
|
|
upIntf = makeIntf(1, "eth3", flagUp)
|
|
)
|
|
|
|
var (
|
|
ipv4Route = Route{Interface: "eth3", Destination: net.ParseIP("0.0.0.0"), Gateway: net.ParseIP("10.254.0.1"), Family: familyIPv4}
|
|
ipv6Route = Route{Interface: "eth3", Destination: net.ParseIP("::"), Gateway: net.ParseIP("2001:1::1"), Family: familyIPv6}
|
|
)
|
|
|
|
var (
|
|
noRoutes = []Route{}
|
|
routeV4 = []Route{ipv4Route}
|
|
routeV6 = []Route{ipv6Route}
|
|
bothRoutes = []Route{ipv4Route, ipv6Route}
|
|
)
|
|
|
|
func TestGetIPv4Routes(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
route string
|
|
count int
|
|
expected *Route
|
|
errStrFrag string
|
|
}{
|
|
{"gatewayfirst", gatewayfirst, 1, &ipv4Route, ""},
|
|
{"gatewaymiddle", gatewaymiddle, 1, &ipv4Route, ""},
|
|
{"gatewaylast", gatewaylast, 1, &ipv4Route, ""},
|
|
{"no routes", nothing, 0, nil, ""},
|
|
{"badDestination", badDestination, 0, nil, "invalid IPv4"},
|
|
{"badGateway", badGateway, 0, nil, "invalid IPv4"},
|
|
{"route_Invalidhex", route_Invalidhex, 0, nil, "odd length hex string"},
|
|
{"no default routes", noInternetConnection, 0, nil, ""},
|
|
}
|
|
for _, tc := range testCases {
|
|
r := strings.NewReader(tc.route)
|
|
routes, err := getIPv4DefaultRoutes(r)
|
|
if err != nil {
|
|
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
|
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
|
}
|
|
} else if tc.errStrFrag != "" {
|
|
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
|
} else {
|
|
if tc.count != len(routes) {
|
|
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
|
} else if tc.count == 1 {
|
|
if !tc.expected.Gateway.Equal(routes[0].Gateway) {
|
|
t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
|
|
}
|
|
if !routes[0].Destination.Equal(net.IPv4zero) {
|
|
t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetIPv6Routes(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
route string
|
|
count int
|
|
expected *Route
|
|
errStrFrag string
|
|
}{
|
|
{"v6 gatewayfirst", v6gatewayfirst, 1, &ipv6Route, ""},
|
|
{"v6 gatewaymiddle", v6gatewaymiddle, 1, &ipv6Route, ""},
|
|
{"v6 gatewaylast", v6gatewaylast, 1, &ipv6Route, ""},
|
|
{"v6 no routes", v6nothing, 0, nil, ""},
|
|
{"v6 badDestination", v6badDestination, 0, nil, "invalid IPv6"},
|
|
{"v6 badGateway", v6badGateway, 0, nil, "invalid IPv6"},
|
|
{"v6 route_Invalidhex", v6route_Invalidhex, 0, nil, "odd length hex string"},
|
|
{"v6 no default routes", v6noDefaultRoutes, 0, nil, ""},
|
|
}
|
|
for _, tc := range testCases {
|
|
r := strings.NewReader(tc.route)
|
|
routes, err := getIPv6DefaultRoutes(r)
|
|
if err != nil {
|
|
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
|
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
|
}
|
|
} else if tc.errStrFrag != "" {
|
|
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
|
} else {
|
|
if tc.count != len(routes) {
|
|
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
|
} else if tc.count == 1 {
|
|
if !tc.expected.Gateway.Equal(routes[0].Gateway) {
|
|
t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
|
|
}
|
|
if !routes[0].Destination.Equal(net.IPv6zero) {
|
|
t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseIP(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
ip string
|
|
family AddressFamily
|
|
success bool
|
|
expected net.IP
|
|
}{
|
|
{"empty", "", familyIPv4, false, nil},
|
|
{"too short", "AA", familyIPv4, false, nil},
|
|
{"too long", "0011223344", familyIPv4, false, nil},
|
|
{"invalid", "invalid!", familyIPv4, false, nil},
|
|
{"zero", "00000000", familyIPv4, true, net.IP{0, 0, 0, 0}},
|
|
{"ffff", "FFFFFFFF", familyIPv4, true, net.IP{0xff, 0xff, 0xff, 0xff}},
|
|
{"valid v4", "12345678", familyIPv4, true, net.IP{120, 86, 52, 18}},
|
|
{"valid v6", "fe800000000000000000000000000000", familyIPv6, true, net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
{"v6 too short", "fe80000000000000021fcafffea0ec0", familyIPv6, false, nil},
|
|
{"v6 too long", "fe80000000000000021fcafffea0ec002", familyIPv6, false, nil},
|
|
}
|
|
for _, tc := range testCases {
|
|
ip, err := parseIP(tc.ip, tc.family)
|
|
if !ip.Equal(tc.expected) {
|
|
t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsInterfaceUp(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
intf *net.Interface
|
|
expected bool
|
|
}{
|
|
{"up", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true},
|
|
{"down", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false},
|
|
{"no interface", nil, false},
|
|
}
|
|
for _, tc := range testCases {
|
|
it := isInterfaceUp(tc.intf)
|
|
if it != tc.expected {
|
|
t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
|
|
}
|
|
}
|
|
}
|
|
|
|
type addrStruct struct{ val string }
|
|
|
|
func (a addrStruct) Network() string {
|
|
return a.val
|
|
}
|
|
func (a addrStruct) String() string {
|
|
return a.val
|
|
}
|
|
|
|
func TestFinalIP(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
addr []net.Addr
|
|
family AddressFamily
|
|
expected net.IP
|
|
}{
|
|
{"no ipv4", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv4, nil},
|
|
{"no ipv6", []net.Addr{addrStruct{val: "10.128.0.4/32"}}, familyIPv6, nil},
|
|
{"invalidV4CIDR", []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}, familyIPv4, nil},
|
|
{"invalidV6CIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, familyIPv6, nil},
|
|
{"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, familyIPv4, nil},
|
|
{"loopbackv6", []net.Addr{addrStruct{val: "::1/128"}}, familyIPv6, nil},
|
|
{"link local v4", []net.Addr{addrStruct{val: "169.254.1.10/16"}}, familyIPv4, nil},
|
|
{"link local v6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, familyIPv6, nil},
|
|
{"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, familyIPv4, net.ParseIP("10.254.12.132")},
|
|
{"ip6", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv6, net.ParseIP("2001::5")},
|
|
|
|
{"no addresses", []net.Addr{}, familyIPv4, nil},
|
|
}
|
|
for _, tc := range testCases {
|
|
ip, err := getMatchingGlobalIP(tc.addr, tc.family)
|
|
if !ip.Equal(tc.expected) {
|
|
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddrs(t *testing.T) {
|
|
var nw networkInterfacer = validNetworkInterface{}
|
|
intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}
|
|
addrs, err := nw.Addrs(&intf)
|
|
if err != nil {
|
|
t.Errorf("expected no error got : %v", err)
|
|
}
|
|
if len(addrs) != 2 {
|
|
t.Errorf("expected addrs: 2 got null")
|
|
}
|
|
}
|
|
|
|
// Has a valid IPv4 address (IPv6 is LLA)
|
|
type validNetworkInterface struct {
|
|
}
|
|
|
|
func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{
|
|
addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ validNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// Both IPv4 and IPv6 addresses (expecting IPv4 to be used)
|
|
type v4v6NetworkInterface struct {
|
|
}
|
|
|
|
func (_ v4v6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ v4v6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{
|
|
addrStruct{val: "2001::10/64"}, addrStruct{val: "10.254.71.145/17"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ v4v6NetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// Interface with only IPv6 address
|
|
type ipv6NetworkInterface struct {
|
|
}
|
|
|
|
func (_ ipv6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ ipv6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{addrStruct{val: "2001::200/64"}}
|
|
return ifat, nil
|
|
}
|
|
|
|
func (_ ipv6NetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// Only with link local addresses
|
|
type networkInterfaceWithOnlyLinkLocals struct {
|
|
}
|
|
|
|
func (_ networkInterfaceWithOnlyLinkLocals) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ networkInterfaceWithOnlyLinkLocals) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ networkInterfaceWithOnlyLinkLocals) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// Unable to get interface(s)
|
|
type failGettingNetworkInterface struct {
|
|
}
|
|
|
|
func (_ failGettingNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return nil, fmt.Errorf("unable get Interface")
|
|
}
|
|
func (_ failGettingNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
return nil, nil
|
|
}
|
|
func (_ failGettingNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return nil, fmt.Errorf("mock failed getting all interfaces")
|
|
}
|
|
|
|
// No interfaces
|
|
type noNetworkInterface struct {
|
|
}
|
|
|
|
func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return nil, fmt.Errorf("no such network interface")
|
|
}
|
|
func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
return nil, nil
|
|
}
|
|
func (_ noNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{}, nil
|
|
}
|
|
|
|
// Interface is down
|
|
type downNetworkInterface struct {
|
|
}
|
|
|
|
func (_ downNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &downIntf, nil
|
|
}
|
|
func (_ downNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{
|
|
addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ downNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{downIntf}, nil
|
|
}
|
|
|
|
// Loopback interface
|
|
type loopbackNetworkInterface struct {
|
|
}
|
|
|
|
func (_ loopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &loopbackIntf, nil
|
|
}
|
|
func (_ loopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{
|
|
addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ loopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{loopbackIntf}, nil
|
|
}
|
|
|
|
// Point to point interface
|
|
type p2pNetworkInterface struct {
|
|
}
|
|
|
|
func (_ p2pNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &p2pIntf, nil
|
|
}
|
|
func (_ p2pNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{
|
|
addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ p2pNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{p2pIntf}, nil
|
|
}
|
|
|
|
// Unable to get IP addresses for interface
|
|
type networkInterfaceFailGetAddrs struct {
|
|
}
|
|
|
|
func (_ networkInterfaceFailGetAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ networkInterfaceFailGetAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
return nil, fmt.Errorf("unable to get Addrs")
|
|
}
|
|
func (_ networkInterfaceFailGetAddrs) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// No addresses for interface
|
|
type networkInterfaceWithNoAddrs struct {
|
|
}
|
|
|
|
func (_ networkInterfaceWithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ networkInterfaceWithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
ifat := []net.Addr{}
|
|
return ifat, nil
|
|
}
|
|
func (_ networkInterfaceWithNoAddrs) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
// Invalid addresses for interface
|
|
type networkInterfaceWithInvalidAddr struct {
|
|
}
|
|
|
|
func (_ networkInterfaceWithInvalidAddr) InterfaceByName(intfName string) (*net.Interface, error) {
|
|
return &upIntf, nil
|
|
}
|
|
func (_ networkInterfaceWithInvalidAddr) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
|
var ifat []net.Addr
|
|
ifat = []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}
|
|
return ifat, nil
|
|
}
|
|
func (_ networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) {
|
|
return []net.Interface{upIntf}, nil
|
|
}
|
|
|
|
func TestGetIPFromInterface(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
nwname string
|
|
family AddressFamily
|
|
nw networkInterfacer
|
|
expected net.IP
|
|
errStrFrag string
|
|
}{
|
|
{"ipv4", "eth3", familyIPv4, validNetworkInterface{}, net.ParseIP("10.254.71.145"), ""},
|
|
{"ipv6", "eth3", familyIPv6, ipv6NetworkInterface{}, net.ParseIP("2001::200"), ""},
|
|
{"no ipv4", "eth3", familyIPv4, ipv6NetworkInterface{}, nil, ""},
|
|
{"no ipv6", "eth3", familyIPv6, validNetworkInterface{}, nil, ""},
|
|
{"I/F down", "eth3", familyIPv4, downNetworkInterface{}, nil, ""},
|
|
{"I/F get fail", "eth3", familyIPv4, noNetworkInterface{}, nil, "no such network interface"},
|
|
{"fail get addr", "eth3", familyIPv4, networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
|
|
{"bad addr", "eth3", familyIPv4, networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
|
|
}
|
|
for _, tc := range testCases {
|
|
ip, err := getIPFromInterface(tc.nwname, tc.family, tc.nw)
|
|
if err != nil {
|
|
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
|
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
|
}
|
|
} else if tc.errStrFrag != "" {
|
|
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
|
} else if !ip.Equal(tc.expected) {
|
|
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChooseHostInterfaceFromRoute(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
routes []Route
|
|
nw networkInterfacer
|
|
expected net.IP
|
|
}{
|
|
{"ipv4", routeV4, validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
|
{"ipv6", routeV6, ipv6NetworkInterface{}, net.ParseIP("2001::200")},
|
|
{"prefer ipv4", bothRoutes, v4v6NetworkInterface{}, net.ParseIP("10.254.71.145")},
|
|
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, nil},
|
|
{"no routes", noRoutes, validNetworkInterface{}, nil},
|
|
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, nil},
|
|
}
|
|
for _, tc := range testCases {
|
|
ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw)
|
|
if !ip.Equal(tc.expected) {
|
|
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMemberOf(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
ip net.IP
|
|
family AddressFamily
|
|
expected bool
|
|
}{
|
|
{"ipv4 is 4", net.ParseIP("10.20.30.40"), familyIPv4, true},
|
|
{"ipv4 is 6", net.ParseIP("10.10.10.10"), familyIPv6, false},
|
|
{"ipv6 is 4", net.ParseIP("2001::100"), familyIPv4, false},
|
|
{"ipv6 is 6", net.ParseIP("2001::100"), familyIPv6, true},
|
|
}
|
|
for _, tc := range testCases {
|
|
if memberOf(tc.ip, tc.family) != tc.expected {
|
|
t.Errorf("case[%s]: expected %+v", tc.tcase, tc.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetIPFromHostInterfaces(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
nw networkInterfacer
|
|
expected net.IP
|
|
errStrFrag string
|
|
}{
|
|
{"fail get I/Fs", failGettingNetworkInterface{}, nil, "failed getting all interfaces"},
|
|
{"no interfaces", noNetworkInterface{}, nil, "no interfaces"},
|
|
{"I/F not up", downNetworkInterface{}, nil, "no acceptable"},
|
|
{"loopback only", loopbackNetworkInterface{}, nil, "no acceptable"},
|
|
{"P2P I/F only", p2pNetworkInterface{}, nil, "no acceptable"},
|
|
{"fail get addrs", networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
|
|
{"no addresses", networkInterfaceWithNoAddrs{}, nil, "no acceptable"},
|
|
{"invalid addr", networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
|
|
{"no matches", networkInterfaceWithOnlyLinkLocals{}, nil, "no acceptable"},
|
|
{"ipv4", validNetworkInterface{}, net.ParseIP("10.254.71.145"), ""},
|
|
{"ipv6", ipv6NetworkInterface{}, net.ParseIP("2001::200"), ""},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
ip, err := chooseIPFromHostInterfaces(tc.nw)
|
|
if !ip.Equal(tc.expected) {
|
|
t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
|
|
}
|
|
if err != nil && !strings.Contains(err.Error(), tc.errStrFrag) {
|
|
t.Errorf("case[%s]: unable to find %q in error string %q", tc.tcase, tc.errStrFrag, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeRouteFile(content string, t *testing.T) (*os.File, error) {
|
|
routeFile, err := ioutil.TempFile("", "route")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, err := routeFile.Write([]byte(content)); err != nil {
|
|
return routeFile, err
|
|
}
|
|
err = routeFile.Close()
|
|
return routeFile, err
|
|
}
|
|
|
|
func TestFailGettingIPv4Routes(t *testing.T) {
|
|
defer func() { v4File.name = ipv4RouteFile }()
|
|
|
|
// Try failure to open file (should not occur, as caller ensures we have IPv4 route file, but being thorough)
|
|
v4File.name = "no-such-file"
|
|
errStrFrag := "no such file"
|
|
_, err := v4File.extract()
|
|
if err == nil {
|
|
fmt.Errorf("Expected error trying to read non-existent v4 route file")
|
|
}
|
|
if !strings.Contains(err.Error(), errStrFrag) {
|
|
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
|
}
|
|
}
|
|
|
|
func TestFailGettingIPv6Routes(t *testing.T) {
|
|
defer func() { v6File.name = ipv6RouteFile }()
|
|
|
|
// Try failure to open file (this would be ignored by caller)
|
|
v6File.name = "no-such-file"
|
|
errStrFrag := "no such file"
|
|
_, err := v6File.extract()
|
|
if err == nil {
|
|
fmt.Errorf("Expected error trying to read non-existent v6 route file")
|
|
}
|
|
if !strings.Contains(err.Error(), errStrFrag) {
|
|
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
|
}
|
|
}
|
|
|
|
func TestGetAllDefaultRoutesFailNoV4RouteFile(t *testing.T) {
|
|
defer func() { v4File.name = ipv4RouteFile }()
|
|
|
|
// Should not occur, as caller ensures we have IPv4 route file, but being thorough
|
|
v4File.name = "no-such-file"
|
|
errStrFrag := "no such file"
|
|
_, err := getAllDefaultRoutes()
|
|
if err == nil {
|
|
fmt.Errorf("Expected error trying to read non-existent v4 route file")
|
|
}
|
|
if !strings.Contains(err.Error(), errStrFrag) {
|
|
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
|
}
|
|
}
|
|
|
|
func TestGetAllDefaultRoutes(t *testing.T) {
|
|
testCases := []struct {
|
|
tcase string
|
|
v4Info string
|
|
v6Info string
|
|
count int
|
|
expected []Route
|
|
errStrFrag string
|
|
}{
|
|
{"no routes", noInternetConnection, v6noDefaultRoutes, 0, nil, "No default routes"},
|
|
{"only v4 route", gatewayfirst, v6noDefaultRoutes, 1, routeV4, ""},
|
|
{"only v6 route", noInternetConnection, v6gatewayfirst, 1, routeV6, ""},
|
|
{"v4 and v6 routes", gatewayfirst, v6gatewayfirst, 2, bothRoutes, ""},
|
|
}
|
|
defer func() {
|
|
v4File.name = ipv4RouteFile
|
|
v6File.name = ipv6RouteFile
|
|
}()
|
|
|
|
for _, tc := range testCases {
|
|
routeFile, err := makeRouteFile(tc.v4Info, t)
|
|
if routeFile != nil {
|
|
defer os.Remove(routeFile.Name())
|
|
}
|
|
if err != nil {
|
|
t.Errorf("case[%s]: test setup failure for IPv4 route file: %v", tc.tcase, err)
|
|
}
|
|
v4File.name = routeFile.Name()
|
|
v6routeFile, err := makeRouteFile(tc.v6Info, t)
|
|
if v6routeFile != nil {
|
|
defer os.Remove(v6routeFile.Name())
|
|
}
|
|
if err != nil {
|
|
t.Errorf("case[%s]: test setup failure for IPv6 route file: %v", tc.tcase, err)
|
|
}
|
|
v6File.name = v6routeFile.Name()
|
|
|
|
routes, err := getAllDefaultRoutes()
|
|
if err != nil {
|
|
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
|
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
|
}
|
|
} else if tc.errStrFrag != "" {
|
|
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
|
} else {
|
|
if tc.count != len(routes) {
|
|
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
|
}
|
|
for i, expected := range tc.expected {
|
|
if !expected.Gateway.Equal(routes[i].Gateway) {
|
|
t.Errorf("case[%s]: at %d expected %v, got %v .err : %v", tc.tcase, i, tc.expected, routes, err)
|
|
}
|
|
zeroIP := net.IPv4zero
|
|
if expected.Family == familyIPv6 {
|
|
zeroIP = net.IPv6zero
|
|
}
|
|
if !routes[i].Destination.Equal(zeroIP) {
|
|
t.Errorf("case[%s}: at %d destination is not for default route (not %v)", tc.tcase, i, zeroIP)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|