...

Source file src/k8s.io/apimachinery/pkg/util/net/interface_test.go

Documentation: k8s.io/apimachinery/pkg/util/net

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package net
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  
    26  	netutils "k8s.io/utils/net"
    27  )
    28  
    29  const gatewayfirst = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT                                                       
    30  eth3	00000000	0100FE0A	0003	0	0	1024	00000000	0	0	0                                                                   
    31  eth3	0000FE0A	00000000	0001	0	0	0	0080FFFF	0	0	0                                                                      
    32  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    33  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0
    34  `
    35  const gatewaylast = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT  
    36  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    37  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0                                                                                                                     
    38  eth3	0000FE0A	00000000	0001	0	0	0	0080FFFF	0	0	0       
    39  eth3	00000000	0100FE0A	0003	0	0	1024	00000000	0	0	0                                                                 
    40  `
    41  const gatewaymiddle = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT                                                                                                                     
    42  eth3	0000FE0A	00000000	0001	0	0	0	0080FFFF	0	0	0                                                                      
    43  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0       
    44  eth3	00000000	0100FE0A	0003	0	0	1024	00000000	0	0	0                                                                         
    45  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0
    46  `
    47  const noInternetConnection = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT                                                       
    48  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    49  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0            
    50  `
    51  const nothing = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT                                                            
    52  `
    53  const badDestination = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT                                                       
    54  eth3	00000000	0100FE0A	0003	0	0	1024	00000000	0	0	0                                                                   
    55  eth3	0000FE0AA1	00000000	0001	0	0	0	0080FFFF	0	0	0                                                                      
    56  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    57  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0
    58  `
    59  const badGateway = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT
    60  eth3	00000000	0100FE0AA1	0003	0	0	1024	00000000	0	0	0                                                                   
    61  eth3	0000FE0A	00000000	0001	0	0	0	0080FFFF	0	0	0                                                                      
    62  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    63  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0
    64  `
    65  const route_Invalidhex = `Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT
    66  eth3	00000000	0100FE0AA	0003	0	0	1024	00000000	0	0	0                                                                   
    67  eth3	0000FE0A	00000000	0001	0	0	0	0080FFFF	0	0	0                                                                      
    68  docker0	000011AC	00000000	0001	0	0	0	0000FFFF	0	0	0                                                                            
    69  virbr0	007AA8C0	00000000	0001	0	0	0	00FFFFFF	0	0	0
    70  `
    71  
    72  const v6gatewayfirst = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
    73  20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
    74  00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200       lo
    75  `
    76  const v6gatewaylast = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
    77  00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200       lo
    78  00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
    79  `
    80  const v6gatewaymiddle = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
    81  00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
    82  00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200       lo
    83  `
    84  const v6noDefaultRoutes = `00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200       lo
    85  20010001000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00000001  docker0
    86  20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001   eth3
    87  fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001   eth3
    88  `
    89  const v6nothing = ``
    90  const v6badDestination = `2001000200000000 7a 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200       lo
    91  `
    92  const v6badGateway = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 200100010000000000000000000000000012 00000064 00000000 00000000 00000003 eth3
    93  `
    94  const v6route_Invalidhex = `000000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021fcafffea0ec00 00000064 00000000 00000000 00000003 enp1s0f0
    95  
    96  `
    97  
    98  const (
    99  	flagUp       = net.FlagUp | net.FlagBroadcast | net.FlagMulticast
   100  	flagDown     = net.FlagBroadcast | net.FlagMulticast
   101  	flagLoopback = net.FlagUp | net.FlagLoopback
   102  	flagP2P      = net.FlagUp | net.FlagPointToPoint
   103  )
   104  
   105  func makeIntf(index int, name string, flags net.Flags) net.Interface {
   106  	mac := net.HardwareAddr{0, 0x32, 0x7d, 0x69, 0xf7, byte(0x30 + index)}
   107  	return net.Interface{
   108  		Index:        index,
   109  		MTU:          1500,
   110  		Name:         name,
   111  		HardwareAddr: mac,
   112  		Flags:        flags}
   113  }
   114  
   115  var (
   116  	downIntf     = makeIntf(1, "eth3", flagDown)
   117  	loopbackIntf = makeIntf(1, "lo", flagLoopback)
   118  	p2pIntf      = makeIntf(1, "lo", flagP2P)
   119  	upIntf       = makeIntf(1, "eth3", flagUp)
   120  )
   121  
   122  var (
   123  	ipv4Route = Route{Interface: "eth3", Destination: netutils.ParseIPSloppy("0.0.0.0"), Gateway: netutils.ParseIPSloppy("10.254.0.1"), Family: familyIPv4}
   124  	ipv6Route = Route{Interface: "eth3", Destination: netutils.ParseIPSloppy("::"), Gateway: netutils.ParseIPSloppy("2001:1::1"), Family: familyIPv6}
   125  )
   126  
   127  var (
   128  	noRoutes   = []Route{}
   129  	routeV4    = []Route{ipv4Route}
   130  	routeV6    = []Route{ipv6Route}
   131  	bothRoutes = []Route{ipv4Route, ipv6Route}
   132  )
   133  
   134  func TestGetIPv4Routes(t *testing.T) {
   135  	testCases := []struct {
   136  		tcase      string
   137  		route      string
   138  		count      int
   139  		expected   *Route
   140  		errStrFrag string
   141  	}{
   142  		{"gatewayfirst", gatewayfirst, 1, &ipv4Route, ""},
   143  		{"gatewaymiddle", gatewaymiddle, 1, &ipv4Route, ""},
   144  		{"gatewaylast", gatewaylast, 1, &ipv4Route, ""},
   145  		{"no routes", nothing, 0, nil, ""},
   146  		{"badDestination", badDestination, 0, nil, "invalid IPv4"},
   147  		{"badGateway", badGateway, 0, nil, "invalid IPv4"},
   148  		{"route_Invalidhex", route_Invalidhex, 0, nil, "odd length hex string"},
   149  		{"no default routes", noInternetConnection, 0, nil, ""},
   150  	}
   151  	for _, tc := range testCases {
   152  		r := strings.NewReader(tc.route)
   153  		routes, err := getIPv4DefaultRoutes(r)
   154  		if err != nil {
   155  			if !strings.Contains(err.Error(), tc.errStrFrag) {
   156  				t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
   157  			}
   158  		} else if tc.errStrFrag != "" {
   159  			t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
   160  		} else {
   161  			if tc.count != len(routes) {
   162  				t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
   163  			} else if tc.count == 1 {
   164  				if !tc.expected.Gateway.Equal(routes[0].Gateway) {
   165  					t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
   166  				}
   167  				if !routes[0].Destination.Equal(net.IPv4zero) {
   168  					t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
   169  				}
   170  
   171  			}
   172  		}
   173  	}
   174  }
   175  
   176  func TestGetIPv6Routes(t *testing.T) {
   177  	testCases := []struct {
   178  		tcase      string
   179  		route      string
   180  		count      int
   181  		expected   *Route
   182  		errStrFrag string
   183  	}{
   184  		{"v6 gatewayfirst", v6gatewayfirst, 1, &ipv6Route, ""},
   185  		{"v6 gatewaymiddle", v6gatewaymiddle, 1, &ipv6Route, ""},
   186  		{"v6 gatewaylast", v6gatewaylast, 1, &ipv6Route, ""},
   187  		{"v6 no routes", v6nothing, 0, nil, ""},
   188  		{"v6 badDestination", v6badDestination, 0, nil, "invalid IPv6"},
   189  		{"v6 badGateway", v6badGateway, 0, nil, "invalid IPv6"},
   190  		{"v6 route_Invalidhex", v6route_Invalidhex, 0, nil, "odd length hex string"},
   191  		{"v6 no default routes", v6noDefaultRoutes, 0, nil, ""},
   192  	}
   193  	for _, tc := range testCases {
   194  		r := strings.NewReader(tc.route)
   195  		routes, err := getIPv6DefaultRoutes(r)
   196  		if err != nil {
   197  			if !strings.Contains(err.Error(), tc.errStrFrag) {
   198  				t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
   199  			}
   200  		} else if tc.errStrFrag != "" {
   201  			t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
   202  		} else {
   203  			if tc.count != len(routes) {
   204  				t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
   205  			} else if tc.count == 1 {
   206  				if !tc.expected.Gateway.Equal(routes[0].Gateway) {
   207  					t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
   208  				}
   209  				if !routes[0].Destination.Equal(net.IPv6zero) {
   210  					t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
   211  				}
   212  			}
   213  		}
   214  	}
   215  }
   216  
   217  func TestParseIP(t *testing.T) {
   218  	testCases := []struct {
   219  		tcase    string
   220  		ip       string
   221  		family   AddressFamily
   222  		success  bool
   223  		expected net.IP
   224  	}{
   225  		{"empty", "", familyIPv4, false, nil},
   226  		{"too short", "AA", familyIPv4, false, nil},
   227  		{"too long", "0011223344", familyIPv4, false, nil},
   228  		{"invalid", "invalid!", familyIPv4, false, nil},
   229  		{"zero", "00000000", familyIPv4, true, net.IP{0, 0, 0, 0}},
   230  		{"ffff", "FFFFFFFF", familyIPv4, true, net.IP{0xff, 0xff, 0xff, 0xff}},
   231  		{"valid v4", "12345678", familyIPv4, true, net.IP{120, 86, 52, 18}},
   232  		{"valid v6", "fe800000000000000000000000000000", familyIPv6, true, net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
   233  		{"v6 too short", "fe80000000000000021fcafffea0ec0", familyIPv6, false, nil},
   234  		{"v6 too long", "fe80000000000000021fcafffea0ec002", familyIPv6, false, nil},
   235  	}
   236  	for _, tc := range testCases {
   237  		ip, err := parseIP(tc.ip, tc.family)
   238  		if !ip.Equal(tc.expected) {
   239  			t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err)
   240  		}
   241  	}
   242  }
   243  
   244  func TestIsInterfaceUp(t *testing.T) {
   245  	testCases := []struct {
   246  		tcase    string
   247  		intf     *net.Interface
   248  		expected bool
   249  	}{
   250  		{"up", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true},
   251  		{"down", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false},
   252  		{"no interface", nil, false},
   253  	}
   254  	for _, tc := range testCases {
   255  		it := isInterfaceUp(tc.intf)
   256  		if it != tc.expected {
   257  			t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
   258  		}
   259  	}
   260  }
   261  
   262  type addrStruct struct{ val string }
   263  
   264  func (a addrStruct) Network() string {
   265  	return a.val
   266  }
   267  func (a addrStruct) String() string {
   268  	return a.val
   269  }
   270  
   271  func TestFinalIP(t *testing.T) {
   272  	testCases := []struct {
   273  		tcase    string
   274  		addr     []net.Addr
   275  		family   AddressFamily
   276  		expected net.IP
   277  	}{
   278  		{"no ipv4", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv4, nil},
   279  		{"no ipv6", []net.Addr{addrStruct{val: "10.128.0.4/32"}}, familyIPv6, nil},
   280  		{"invalidV4CIDR", []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}, familyIPv4, nil},
   281  		{"invalidV6CIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, familyIPv6, nil},
   282  		{"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, familyIPv4, nil},
   283  		{"loopbackv6", []net.Addr{addrStruct{val: "::1/128"}}, familyIPv6, nil},
   284  		{"link local v4", []net.Addr{addrStruct{val: "169.254.1.10/16"}}, familyIPv4, nil},
   285  		{"link local v6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, familyIPv6, nil},
   286  		{"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, familyIPv4, netutils.ParseIPSloppy("10.254.12.132")},
   287  		{"ip6", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv6, netutils.ParseIPSloppy("2001::5")},
   288  
   289  		{"no addresses", []net.Addr{}, familyIPv4, nil},
   290  	}
   291  	for _, tc := range testCases {
   292  		ip, err := getMatchingGlobalIP(tc.addr, tc.family)
   293  		if !ip.Equal(tc.expected) {
   294  			t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
   295  		}
   296  	}
   297  }
   298  
   299  func TestAddrs(t *testing.T) {
   300  	var nw networkInterfacer = validNetworkInterface{}
   301  	intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}
   302  	addrs, err := nw.Addrs(&intf)
   303  	if err != nil {
   304  		t.Errorf("expected no error got : %v", err)
   305  	}
   306  	if len(addrs) != 2 {
   307  		t.Errorf("expected addrs: 2 got null")
   308  	}
   309  }
   310  
   311  // Has a valid IPv4 address (IPv6 is LLA)
   312  type validNetworkInterface struct {
   313  }
   314  
   315  func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   316  	return &upIntf, nil
   317  }
   318  func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   319  	var ifat []net.Addr
   320  	ifat = []net.Addr{
   321  		addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
   322  	return ifat, nil
   323  }
   324  func (_ validNetworkInterface) Interfaces() ([]net.Interface, error) {
   325  	return []net.Interface{upIntf}, nil
   326  }
   327  
   328  // Both IPv4 and IPv6 addresses (expecting IPv4 to be used)
   329  type v4v6NetworkInterface struct {
   330  }
   331  
   332  func (_ v4v6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   333  	return &upIntf, nil
   334  }
   335  func (_ v4v6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   336  	var ifat []net.Addr
   337  	ifat = []net.Addr{
   338  		addrStruct{val: "2001::10/64"}, addrStruct{val: "10.254.71.145/17"}}
   339  	return ifat, nil
   340  }
   341  func (_ v4v6NetworkInterface) Interfaces() ([]net.Interface, error) {
   342  	return []net.Interface{upIntf}, nil
   343  }
   344  
   345  // Interface with only IPv6 address
   346  type ipv6NetworkInterface struct {
   347  }
   348  
   349  func (_ ipv6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   350  	return &upIntf, nil
   351  }
   352  func (_ ipv6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   353  	var ifat []net.Addr
   354  	ifat = []net.Addr{addrStruct{val: "2001::200/64"}}
   355  	return ifat, nil
   356  }
   357  
   358  func (_ ipv6NetworkInterface) Interfaces() ([]net.Interface, error) {
   359  	return []net.Interface{upIntf}, nil
   360  }
   361  
   362  // Only with link local addresses
   363  type networkInterfaceWithOnlyLinkLocals struct {
   364  }
   365  
   366  func (_ networkInterfaceWithOnlyLinkLocals) InterfaceByName(intfName string) (*net.Interface, error) {
   367  	return &upIntf, nil
   368  }
   369  func (_ networkInterfaceWithOnlyLinkLocals) Addrs(intf *net.Interface) ([]net.Addr, error) {
   370  	var ifat []net.Addr
   371  	ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
   372  	return ifat, nil
   373  }
   374  func (_ networkInterfaceWithOnlyLinkLocals) Interfaces() ([]net.Interface, error) {
   375  	return []net.Interface{upIntf}, nil
   376  }
   377  
   378  // Unable to get interface(s)
   379  type failGettingNetworkInterface struct {
   380  }
   381  
   382  func (_ failGettingNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   383  	return nil, fmt.Errorf("unable get Interface")
   384  }
   385  func (_ failGettingNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   386  	return nil, nil
   387  }
   388  func (_ failGettingNetworkInterface) Interfaces() ([]net.Interface, error) {
   389  	return nil, fmt.Errorf("mock failed getting all interfaces")
   390  }
   391  
   392  // No interfaces
   393  type noNetworkInterface struct {
   394  }
   395  
   396  func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   397  	return nil, fmt.Errorf("no such network interface")
   398  }
   399  func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   400  	return nil, nil
   401  }
   402  func (_ noNetworkInterface) Interfaces() ([]net.Interface, error) {
   403  	return []net.Interface{}, nil
   404  }
   405  
   406  // Interface is down
   407  type downNetworkInterface struct {
   408  }
   409  
   410  func (_ downNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   411  	return &downIntf, nil
   412  }
   413  func (_ downNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   414  	var ifat []net.Addr
   415  	ifat = []net.Addr{
   416  		addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
   417  	return ifat, nil
   418  }
   419  func (_ downNetworkInterface) Interfaces() ([]net.Interface, error) {
   420  	return []net.Interface{downIntf}, nil
   421  }
   422  
   423  // Loopback interface
   424  type loopbackNetworkInterface struct {
   425  }
   426  
   427  func (_ loopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   428  	return &loopbackIntf, nil
   429  }
   430  func (_ loopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   431  	var ifat []net.Addr
   432  	ifat = []net.Addr{
   433  		addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
   434  	return ifat, nil
   435  }
   436  func (_ loopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
   437  	return []net.Interface{loopbackIntf}, nil
   438  }
   439  
   440  // Point to point interface
   441  type p2pNetworkInterface struct {
   442  }
   443  
   444  func (_ p2pNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   445  	return &p2pIntf, nil
   446  }
   447  func (_ p2pNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   448  	var ifat []net.Addr
   449  	ifat = []net.Addr{
   450  		addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
   451  	return ifat, nil
   452  }
   453  func (_ p2pNetworkInterface) Interfaces() ([]net.Interface, error) {
   454  	return []net.Interface{p2pIntf}, nil
   455  }
   456  
   457  // Interface with link locals and loopback interface with global addresses
   458  type linkLocalLoopbackNetworkInterface struct {
   459  }
   460  
   461  func (_ linkLocalLoopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   462  	if intfName == LoopbackInterfaceName {
   463  		return &loopbackIntf, nil
   464  	}
   465  	return &upIntf, nil
   466  }
   467  func (_ linkLocalLoopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   468  	var ifat []net.Addr
   469  	ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
   470  	if intf.Name == LoopbackInterfaceName {
   471  		ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
   472  			// global addresses on loopback interface
   473  			addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
   474  	}
   475  	return ifat, nil
   476  }
   477  func (_ linkLocalLoopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
   478  	return []net.Interface{upIntf, loopbackIntf}, nil
   479  }
   480  
   481  // Interface and loopback interface with global addresses
   482  type globalsNetworkInterface struct {
   483  }
   484  
   485  func (_ globalsNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   486  	if intfName == LoopbackInterfaceName {
   487  		return &loopbackIntf, nil
   488  	}
   489  	return &upIntf, nil
   490  }
   491  func (_ globalsNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   492  	var ifat []net.Addr
   493  	ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"},
   494  		addrStruct{val: "192.168.1.1/31"}, addrStruct{val: "fd00::200/127"}}
   495  	if intf.Name == LoopbackInterfaceName {
   496  		ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
   497  			// global addresses on loopback interface
   498  			addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
   499  	}
   500  	return ifat, nil
   501  }
   502  func (_ globalsNetworkInterface) Interfaces() ([]net.Interface, error) {
   503  	return []net.Interface{upIntf, loopbackIntf}, nil
   504  }
   505  
   506  // Unable to get IP addresses for interface
   507  type networkInterfaceFailGetAddrs struct {
   508  }
   509  
   510  func (_ networkInterfaceFailGetAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
   511  	return &upIntf, nil
   512  }
   513  func (_ networkInterfaceFailGetAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
   514  	return nil, fmt.Errorf("unable to get Addrs")
   515  }
   516  func (_ networkInterfaceFailGetAddrs) Interfaces() ([]net.Interface, error) {
   517  	return []net.Interface{upIntf}, nil
   518  }
   519  
   520  // No addresses for interface
   521  type networkInterfaceWithNoAddrs struct {
   522  }
   523  
   524  func (_ networkInterfaceWithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
   525  	return &upIntf, nil
   526  }
   527  func (_ networkInterfaceWithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
   528  	ifat := []net.Addr{}
   529  	return ifat, nil
   530  }
   531  func (_ networkInterfaceWithNoAddrs) Interfaces() ([]net.Interface, error) {
   532  	return []net.Interface{upIntf}, nil
   533  }
   534  
   535  // Invalid addresses for interface
   536  type networkInterfaceWithInvalidAddr struct {
   537  }
   538  
   539  func (_ networkInterfaceWithInvalidAddr) InterfaceByName(intfName string) (*net.Interface, error) {
   540  	return &upIntf, nil
   541  }
   542  func (_ networkInterfaceWithInvalidAddr) Addrs(intf *net.Interface) ([]net.Addr, error) {
   543  	var ifat []net.Addr
   544  	ifat = []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}
   545  	return ifat, nil
   546  }
   547  func (_ networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) {
   548  	return []net.Interface{upIntf}, nil
   549  }
   550  
   551  func TestGetIPFromInterface(t *testing.T) {
   552  	testCases := []struct {
   553  		tcase      string
   554  		nwname     string
   555  		family     AddressFamily
   556  		nw         networkInterfacer
   557  		expected   net.IP
   558  		errStrFrag string
   559  	}{
   560  		{"ipv4", "eth3", familyIPv4, validNetworkInterface{}, netutils.ParseIPSloppy("10.254.71.145"), ""},
   561  		{"ipv6", "eth3", familyIPv6, ipv6NetworkInterface{}, netutils.ParseIPSloppy("2001::200"), ""},
   562  		{"no ipv4", "eth3", familyIPv4, ipv6NetworkInterface{}, nil, ""},
   563  		{"no ipv6", "eth3", familyIPv6, validNetworkInterface{}, nil, ""},
   564  		{"I/F down", "eth3", familyIPv4, downNetworkInterface{}, nil, ""},
   565  		{"I/F get fail", "eth3", familyIPv4, noNetworkInterface{}, nil, "no such network interface"},
   566  		{"fail get addr", "eth3", familyIPv4, networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
   567  		{"bad addr", "eth3", familyIPv4, networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
   568  	}
   569  	for _, tc := range testCases {
   570  		ip, err := getIPFromInterface(tc.nwname, tc.family, tc.nw)
   571  		if err != nil {
   572  			if !strings.Contains(err.Error(), tc.errStrFrag) {
   573  				t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
   574  			}
   575  		} else if tc.errStrFrag != "" {
   576  			t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
   577  		} else if !ip.Equal(tc.expected) {
   578  			t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
   579  		}
   580  	}
   581  }
   582  
   583  func TestGetIPFromLoopbackInterface(t *testing.T) {
   584  	testCases := []struct {
   585  		tcase      string
   586  		family     AddressFamily
   587  		nw         networkInterfacer
   588  		expected   net.IP
   589  		errStrFrag string
   590  	}{
   591  		{"ipv4", familyIPv4, linkLocalLoopbackNetworkInterface{}, netutils.ParseIPSloppy("10.1.1.1"), ""},
   592  		{"ipv6", familyIPv6, linkLocalLoopbackNetworkInterface{}, netutils.ParseIPSloppy("fd00:1:1::1"), ""},
   593  		{"no global ipv4", familyIPv4, loopbackNetworkInterface{}, nil, ""},
   594  		{"no global ipv6", familyIPv6, loopbackNetworkInterface{}, nil, ""},
   595  	}
   596  	for _, tc := range testCases {
   597  		ip, err := getIPFromLoopbackInterface(tc.family, tc.nw)
   598  		if err != nil {
   599  			if !strings.Contains(err.Error(), tc.errStrFrag) {
   600  				t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
   601  			}
   602  		} else if tc.errStrFrag != "" {
   603  			t.Errorf("case[%s]: Error %q expected, but seen %v", tc.tcase, tc.errStrFrag, err)
   604  		} else if !ip.Equal(tc.expected) {
   605  			t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
   606  		}
   607  	}
   608  }
   609  
   610  func TestChooseHostInterfaceFromRoute(t *testing.T) {
   611  	testCases := []struct {
   612  		tcase    string
   613  		routes   []Route
   614  		nw       networkInterfacer
   615  		order    AddressFamilyPreference
   616  		expected net.IP
   617  	}{
   618  		{"single-stack ipv4", routeV4, validNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145")},
   619  		{"single-stack ipv4, prefer v6", routeV4, validNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("10.254.71.145")},
   620  		{"single-stack ipv6", routeV6, ipv6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("2001::200")},
   621  		{"single-stack ipv6, prefer v6", routeV6, ipv6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::200")},
   622  		{"dual stack", bothRoutes, v4v6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145")},
   623  		{"dual stack, prefer v6", bothRoutes, v4v6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::10")},
   624  		{"LLA and loopback with global, IPv4", routeV4, linkLocalLoopbackNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.1.1.1")},
   625  		{"LLA and loopback with global, IPv6", routeV6, linkLocalLoopbackNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00:1:1::1")},
   626  		{"LLA and loopback with global, dual stack prefer IPv4", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.1.1.1")},
   627  		{"LLA and loopback with global, dual stack prefer IPv6", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00:1:1::1")},
   628  		{"LLA and loopback with global, no routes", noRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, nil},
   629  		{"interface and loopback with global, IPv4", routeV4, globalsNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("192.168.1.1")},
   630  		{"interface and loopback with global, IPv6", routeV6, globalsNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00::200")},
   631  		{"interface and loopback with global, dual stack prefer IPv4", bothRoutes, globalsNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("192.168.1.1")},
   632  		{"interface and loopback with global, dual stack prefer IPv6", bothRoutes, globalsNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00::200")},
   633  		{"interface and loopback with global, no routes", noRoutes, globalsNetworkInterface{}, preferIPv6, nil},
   634  		{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil},
   635  		{"no routes", noRoutes, validNetworkInterface{}, preferIPv4, nil},
   636  		{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
   637  	}
   638  	for _, tc := range testCases {
   639  		ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw, tc.order)
   640  		if !ip.Equal(tc.expected) {
   641  			t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
   642  		}
   643  	}
   644  }
   645  
   646  func TestMemberOf(t *testing.T) {
   647  	testCases := []struct {
   648  		tcase    string
   649  		ip       net.IP
   650  		family   AddressFamily
   651  		expected bool
   652  	}{
   653  		{"ipv4 is 4", netutils.ParseIPSloppy("10.20.30.40"), familyIPv4, true},
   654  		{"ipv4 is 6", netutils.ParseIPSloppy("10.10.10.10"), familyIPv6, false},
   655  		{"ipv6 is 4", netutils.ParseIPSloppy("2001::100"), familyIPv4, false},
   656  		{"ipv6 is 6", netutils.ParseIPSloppy("2001::100"), familyIPv6, true},
   657  	}
   658  	for _, tc := range testCases {
   659  		if memberOf(tc.ip, tc.family) != tc.expected {
   660  			t.Errorf("case[%s]: expected %+v", tc.tcase, tc.expected)
   661  		}
   662  	}
   663  }
   664  
   665  func TestGetIPFromHostInterfaces(t *testing.T) {
   666  	testCases := []struct {
   667  		tcase      string
   668  		nw         networkInterfacer
   669  		order      AddressFamilyPreference
   670  		expected   net.IP
   671  		errStrFrag string
   672  	}{
   673  		{"fail get I/Fs", failGettingNetworkInterface{}, preferIPv4, nil, "failed getting all interfaces"},
   674  		{"no interfaces", noNetworkInterface{}, preferIPv4, nil, "no interfaces"},
   675  		{"I/F not up", downNetworkInterface{}, preferIPv4, nil, "no acceptable"},
   676  		{"loopback only", loopbackNetworkInterface{}, preferIPv4, nil, "no acceptable"},
   677  		{"P2P I/F only", p2pNetworkInterface{}, preferIPv4, nil, "no acceptable"},
   678  		{"fail get addrs", networkInterfaceFailGetAddrs{}, preferIPv4, nil, "unable to get Addrs"},
   679  		{"no addresses", networkInterfaceWithNoAddrs{}, preferIPv4, nil, "no acceptable"},
   680  		{"invalid addr", networkInterfaceWithInvalidAddr{}, preferIPv4, nil, "invalid CIDR"},
   681  		{"no matches", networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil, "no acceptable"},
   682  		{"single-stack ipv4", validNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145"), ""},
   683  		{"single-stack ipv4, prefer ipv6", validNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("10.254.71.145"), ""},
   684  		{"single-stack ipv6", ipv6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("2001::200"), ""},
   685  		{"single-stack ipv6, prefer ipv6", ipv6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::200"), ""},
   686  		{"dual stack", v4v6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145"), ""},
   687  		{"dual stack, prefer ipv6", v4v6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::10"), ""},
   688  	}
   689  
   690  	for _, tc := range testCases {
   691  		ip, err := chooseIPFromHostInterfaces(tc.nw, tc.order)
   692  		if !ip.Equal(tc.expected) {
   693  			t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
   694  		}
   695  		if err != nil && !strings.Contains(err.Error(), tc.errStrFrag) {
   696  			t.Errorf("case[%s]: unable to find %q in error string %q", tc.tcase, tc.errStrFrag, err.Error())
   697  		}
   698  	}
   699  }
   700  
   701  func makeRouteFile(content string, t *testing.T) (*os.File, error) {
   702  	routeFile, err := os.CreateTemp("", "route")
   703  	if err != nil {
   704  		return nil, err
   705  	}
   706  
   707  	if _, err := routeFile.Write([]byte(content)); err != nil {
   708  		return routeFile, err
   709  	}
   710  	err = routeFile.Close()
   711  	return routeFile, err
   712  }
   713  
   714  func TestFailGettingIPv4Routes(t *testing.T) {
   715  	defer func() { v4File.name = ipv4RouteFile }()
   716  
   717  	// Try failure to open file (should not occur, as caller ensures we have IPv4 route file, but being thorough)
   718  	v4File.name = "no-such-file"
   719  	errStrFrag := "no such file"
   720  	_, err := v4File.extract()
   721  	if err == nil {
   722  		t.Errorf("Expected error trying to read non-existent v4 route file")
   723  	}
   724  	if !strings.Contains(err.Error(), errStrFrag) {
   725  		t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
   726  	}
   727  }
   728  
   729  func TestFailGettingIPv6Routes(t *testing.T) {
   730  	defer func() { v6File.name = ipv6RouteFile }()
   731  
   732  	// Try failure to open file (this would be ignored by caller)
   733  	v6File.name = "no-such-file"
   734  	errStrFrag := "no such file"
   735  	_, err := v6File.extract()
   736  	if err == nil {
   737  		t.Errorf("Expected error trying to read non-existent v6 route file")
   738  	}
   739  	if !strings.Contains(err.Error(), errStrFrag) {
   740  		t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
   741  	}
   742  }
   743  
   744  func TestGetAllDefaultRoutesFailNoV4RouteFile(t *testing.T) {
   745  	defer func() { v4File.name = ipv4RouteFile }()
   746  
   747  	// Should not occur, as caller ensures we have IPv4 route file, but being thorough
   748  	v4File.name = "no-such-file"
   749  	errStrFrag := "no such file"
   750  	_, err := getAllDefaultRoutes()
   751  	if err == nil {
   752  		t.Errorf("Expected error trying to read non-existent v4 route file")
   753  	}
   754  	if !strings.Contains(err.Error(), errStrFrag) {
   755  		t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
   756  	}
   757  }
   758  
   759  func TestGetAllDefaultRoutes(t *testing.T) {
   760  	testCases := []struct {
   761  		tcase      string
   762  		v4Info     string
   763  		v6Info     string
   764  		count      int
   765  		expected   []Route
   766  		errStrFrag string
   767  	}{
   768  		{"no routes", noInternetConnection, v6noDefaultRoutes, 0, nil, "no default routes"},
   769  		{"only v4 route", gatewayfirst, v6noDefaultRoutes, 1, routeV4, ""},
   770  		{"only v6 route", noInternetConnection, v6gatewayfirst, 1, routeV6, ""},
   771  		{"v4 and v6 routes", gatewayfirst, v6gatewayfirst, 2, bothRoutes, ""},
   772  	}
   773  	defer func() {
   774  		v4File.name = ipv4RouteFile
   775  		v6File.name = ipv6RouteFile
   776  	}()
   777  
   778  	for _, tc := range testCases {
   779  		routeFile, err := makeRouteFile(tc.v4Info, t)
   780  		if routeFile != nil {
   781  			defer os.Remove(routeFile.Name())
   782  		}
   783  		if err != nil {
   784  			t.Errorf("case[%s]: test setup failure for IPv4 route file: %v", tc.tcase, err)
   785  		}
   786  		v4File.name = routeFile.Name()
   787  		v6routeFile, err := makeRouteFile(tc.v6Info, t)
   788  		if v6routeFile != nil {
   789  			defer os.Remove(v6routeFile.Name())
   790  		}
   791  		if err != nil {
   792  			t.Errorf("case[%s]: test setup failure for IPv6 route file: %v", tc.tcase, err)
   793  		}
   794  		v6File.name = v6routeFile.Name()
   795  
   796  		routes, err := getAllDefaultRoutes()
   797  		if err != nil {
   798  			if !strings.Contains(err.Error(), tc.errStrFrag) {
   799  				t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
   800  			}
   801  		} else if tc.errStrFrag != "" {
   802  			t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
   803  		} else {
   804  			if tc.count != len(routes) {
   805  				t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
   806  			}
   807  			for i, expected := range tc.expected {
   808  				if !expected.Gateway.Equal(routes[i].Gateway) {
   809  					t.Errorf("case[%s]: at %d expected %v, got %v .err : %v", tc.tcase, i, tc.expected, routes, err)
   810  				}
   811  				zeroIP := net.IPv4zero
   812  				if expected.Family == familyIPv6 {
   813  					zeroIP = net.IPv6zero
   814  				}
   815  				if !routes[i].Destination.Equal(zeroIP) {
   816  					t.Errorf("case[%s}: at %d destination is not for default route (not %v)", tc.tcase, i, zeroIP)
   817  				}
   818  			}
   819  		}
   820  	}
   821  }
   822  

View as plain text