301 lines
12 KiB
Go
301 lines
12 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"
|
||
|
"net"
|
||
|
"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 gatewayfirstIpv6_1 = `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 gatewayfirstIpv6_2 = `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
|
||
|
`
|
||
|
|
||
|
// Based on DigitalOcean COREOS
|
||
|
const gatewayfirstLinkLocal = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||
|
eth0 00000000 0120372D 0001 0 0 0 00000000 0 0 0
|
||
|
eth0 00000000 00000000 0001 0 0 2048 00000000 0 0 0
|
||
|
`
|
||
|
|
||
|
func TestGetRoutes(t *testing.T) {
|
||
|
testCases := []struct {
|
||
|
tcase string
|
||
|
route string
|
||
|
expected int
|
||
|
}{
|
||
|
{"gatewayfirst", gatewayfirst, 4},
|
||
|
{"gatewaymiddle", gatewaymiddle, 4},
|
||
|
{"gatewaylast", gatewaylast, 4},
|
||
|
{"nothing", nothing, 0},
|
||
|
{"gatewayfirstIpv6_1", gatewayfirstIpv6_1, 0},
|
||
|
{"gatewayfirstIpv6_2", gatewayfirstIpv6_2, 0},
|
||
|
{"route_Invalidhex", route_Invalidhex, 0},
|
||
|
}
|
||
|
for _, tc := range testCases {
|
||
|
r := strings.NewReader(tc.route)
|
||
|
routes, err := getRoutes(r)
|
||
|
if len(routes) != tc.expected {
|
||
|
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, len(routes), err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestParseIP(t *testing.T) {
|
||
|
testCases := []struct {
|
||
|
tcase string
|
||
|
ip string
|
||
|
success bool
|
||
|
expected net.IP
|
||
|
}{
|
||
|
{"empty", "", false, nil},
|
||
|
{"too short", "AA", false, nil},
|
||
|
{"too long", "0011223344", false, nil},
|
||
|
{"invalid", "invalid!", false, nil},
|
||
|
{"zero", "00000000", true, net.IP{0, 0, 0, 0}},
|
||
|
{"ffff", "FFFFFFFF", true, net.IP{0xff, 0xff, 0xff, 0xff}},
|
||
|
{"valid", "12345678", true, net.IP{120, 86, 52, 18}},
|
||
|
}
|
||
|
for _, tc := range testCases {
|
||
|
ip, err := parseIP(tc.ip)
|
||
|
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},
|
||
|
{"nothing", net.Interface{}, 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
|
||
|
expected net.IP
|
||
|
}{
|
||
|
{"ipv6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, nil},
|
||
|
{"invalidCIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, nil},
|
||
|
{"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, nil},
|
||
|
{"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, net.ParseIP("10.254.12.132")},
|
||
|
|
||
|
{"nothing", []net.Addr{}, nil},
|
||
|
}
|
||
|
for _, tc := range testCases {
|
||
|
ip, err := getFinalIP(tc.addr)
|
||
|
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")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type validNetworkInterface struct {
|
||
|
}
|
||
|
|
||
|
func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
|
||
|
return &c, 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
|
||
|
}
|
||
|
|
||
|
type validNetworkInterfaceWithLinkLocal struct {
|
||
|
}
|
||
|
|
||
|
func (_ validNetworkInterfaceWithLinkLocal) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
c := net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: net.FlagUp}
|
||
|
return &c, nil
|
||
|
}
|
||
|
func (_ validNetworkInterfaceWithLinkLocal) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||
|
var ifat []net.Addr
|
||
|
ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "45.55.47.146/19"}}
|
||
|
return ifat, nil
|
||
|
}
|
||
|
|
||
|
type validNetworkInterfacewithIpv6Only struct {
|
||
|
}
|
||
|
|
||
|
func (_ validNetworkInterfacewithIpv6Only) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
|
||
|
return &c, nil
|
||
|
}
|
||
|
func (_ validNetworkInterfacewithIpv6Only) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||
|
var ifat []net.Addr
|
||
|
ifat = []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}
|
||
|
return ifat, nil
|
||
|
}
|
||
|
|
||
|
type noNetworkInterface struct {
|
||
|
}
|
||
|
|
||
|
func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
return nil, fmt.Errorf("unable get Interface")
|
||
|
}
|
||
|
func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
type networkInterfacewithNoAddrs struct {
|
||
|
}
|
||
|
|
||
|
func (_ networkInterfacewithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
|
||
|
return &c, nil
|
||
|
}
|
||
|
func (_ networkInterfacewithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||
|
return nil, fmt.Errorf("unable get Addrs")
|
||
|
}
|
||
|
|
||
|
type networkInterfacewithIpv6addrs struct {
|
||
|
}
|
||
|
|
||
|
func (_ networkInterfacewithIpv6addrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
||
|
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
|
||
|
return &c, nil
|
||
|
}
|
||
|
func (_ networkInterfacewithIpv6addrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||
|
var ifat []net.Addr
|
||
|
ifat = []net.Addr{addrStruct{val: "fe80::2f7:6ffff:fe6e:2956/64"}}
|
||
|
return ifat, nil
|
||
|
}
|
||
|
|
||
|
func TestGetIPFromInterface(t *testing.T) {
|
||
|
testCases := []struct {
|
||
|
tcase string
|
||
|
nwname string
|
||
|
nw networkInterfacer
|
||
|
expected net.IP
|
||
|
}{
|
||
|
{"valid", "eth3", validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||
|
{"ipv6", "eth3", validNetworkInterfacewithIpv6Only{}, nil},
|
||
|
{"nothing", "eth3", noNetworkInterface{}, nil},
|
||
|
}
|
||
|
for _, tc := range testCases {
|
||
|
ip, err := getIPFromInterface(tc.nwname, tc.nw)
|
||
|
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
|
||
|
inFile io.Reader
|
||
|
nw networkInterfacer
|
||
|
expected net.IP
|
||
|
}{
|
||
|
{"valid_routefirst", strings.NewReader(gatewayfirst), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||
|
{"valid_routelast", strings.NewReader(gatewaylast), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||
|
{"valid_routemiddle", strings.NewReader(gatewaymiddle), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||
|
{"valid_routemiddle_ipv6", strings.NewReader(gatewaymiddle), validNetworkInterfacewithIpv6Only{}, nil},
|
||
|
{"no internet connection", strings.NewReader(noInternetConnection), validNetworkInterface{}, nil},
|
||
|
{"no non-link-local ip", strings.NewReader(gatewayfirstLinkLocal), validNetworkInterfaceWithLinkLocal{}, net.ParseIP("45.55.47.146")},
|
||
|
{"no route", strings.NewReader(nothing), validNetworkInterface{}, nil},
|
||
|
{"no route file", nil, validNetworkInterface{}, nil},
|
||
|
{"no interfaces", nil, noNetworkInterface{}, nil},
|
||
|
{"no interface Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithNoAddrs{}, nil},
|
||
|
{"Invalid Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithIpv6addrs{}, nil},
|
||
|
}
|
||
|
for _, tc := range testCases {
|
||
|
ip, err := chooseHostInterfaceFromRoute(tc.inFile, tc.nw)
|
||
|
if !ip.Equal(tc.expected) {
|
||
|
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
||
|
}
|
||
|
}
|
||
|
}
|