mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #95790 from aojea/loopnet
autodetect global addresses on loopback interfaces
This commit is contained in:
commit
403d0cdc28
@ -266,6 +266,36 @@ func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInte
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any.
|
||||||
|
// The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
|
||||||
|
func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
|
||||||
|
intfs, err := nw.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, intf := range intfs {
|
||||||
|
if !isInterfaceUp(&intf) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if intf.Flags&(net.FlagLoopback) != 0 {
|
||||||
|
addrs, err := nw.Addrs(&intf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
|
||||||
|
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if matchingIP != nil {
|
||||||
|
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
|
||||||
|
return matchingIP, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// memberOf tells if the IP is of the desired family. Used for checking interface addresses.
|
// memberOf tells if the IP is of the desired family. Used for checking interface addresses.
|
||||||
func memberOf(ip net.IP, family AddressFamily) bool {
|
func memberOf(ip net.IP, family AddressFamily) bool {
|
||||||
if ip.To4() != nil {
|
if ip.To4() != nil {
|
||||||
@ -393,8 +423,9 @@ func getAllDefaultRoutes() ([]Route, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
||||||
// global IP address from the interface for the route. addressFamilies determines whether it
|
// global IP address from the interface for the route. If there are routes but no global
|
||||||
// prefers IPv4 or IPv6
|
// address is obtained from the interfaces, it checks if the loopback interface has a global address.
|
||||||
|
// addressFamilies determines whether it prefers IPv4 or IPv6
|
||||||
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||||
for _, family := range addressFamilies {
|
for _, family := range addressFamilies {
|
||||||
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
||||||
@ -411,6 +442,17 @@ func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressF
|
|||||||
klog.V(4).Infof("Found active IP %v ", finalIP)
|
klog.V(4).Infof("Found active IP %v ", finalIP)
|
||||||
return finalIP, nil
|
return finalIP, nil
|
||||||
}
|
}
|
||||||
|
// In case of network setups where default routes are present, but network
|
||||||
|
// interfaces use only link-local addresses (e.g. as described in RFC5549).
|
||||||
|
// the global IP is assigned to the loopback interface, and we should use it
|
||||||
|
loopbackIP, err := getIPFromLoopbackInterface(family, nw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if loopbackIP != nil {
|
||||||
|
klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
|
||||||
|
return loopbackIP, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
klog.V(4).Infof("No active IP found by looking at default routes")
|
klog.V(4).Infof("No active IP found by looking at default routes")
|
||||||
|
@ -453,6 +453,55 @@ func (_ p2pNetworkInterface) Interfaces() ([]net.Interface, error) {
|
|||||||
return []net.Interface{p2pIntf}, nil
|
return []net.Interface{p2pIntf}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interface with link locals and loopback interface with global addresses
|
||||||
|
type linkLocalLoopbackNetworkInterface struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ linkLocalLoopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||||
|
if intfName == LoopbackInterfaceName {
|
||||||
|
return &loopbackIntf, nil
|
||||||
|
}
|
||||||
|
return &upIntf, nil
|
||||||
|
}
|
||||||
|
func (_ linkLocalLoopbackNetworkInterface) 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"}}
|
||||||
|
if intf.Name == LoopbackInterfaceName {
|
||||||
|
ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
|
||||||
|
// global addresses on loopback interface
|
||||||
|
addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
|
||||||
|
}
|
||||||
|
return ifat, nil
|
||||||
|
}
|
||||||
|
func (_ linkLocalLoopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||||
|
return []net.Interface{upIntf, loopbackIntf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface and loopback interface with global addresses
|
||||||
|
type globalsNetworkInterface struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ globalsNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||||
|
if intfName == LoopbackInterfaceName {
|
||||||
|
return &loopbackIntf, nil
|
||||||
|
}
|
||||||
|
return &upIntf, nil
|
||||||
|
}
|
||||||
|
func (_ globalsNetworkInterface) 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"},
|
||||||
|
addrStruct{val: "192.168.1.1/31"}, addrStruct{val: "fd00::200/127"}}
|
||||||
|
if intf.Name == LoopbackInterfaceName {
|
||||||
|
ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
|
||||||
|
// global addresses on loopback interface
|
||||||
|
addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
|
||||||
|
}
|
||||||
|
return ifat, nil
|
||||||
|
}
|
||||||
|
func (_ globalsNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||||
|
return []net.Interface{upIntf, loopbackIntf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Unable to get IP addresses for interface
|
// Unable to get IP addresses for interface
|
||||||
type networkInterfaceFailGetAddrs struct {
|
type networkInterfaceFailGetAddrs struct {
|
||||||
}
|
}
|
||||||
@ -530,6 +579,33 @@ func TestGetIPFromInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetIPFromLoopbackInterface(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
tcase string
|
||||||
|
family AddressFamily
|
||||||
|
nw networkInterfacer
|
||||||
|
expected net.IP
|
||||||
|
errStrFrag string
|
||||||
|
}{
|
||||||
|
{"ipv4", familyIPv4, linkLocalLoopbackNetworkInterface{}, net.ParseIP("10.1.1.1"), ""},
|
||||||
|
{"ipv6", familyIPv6, linkLocalLoopbackNetworkInterface{}, net.ParseIP("fd00:1:1::1"), ""},
|
||||||
|
{"no global ipv4", familyIPv4, loopbackNetworkInterface{}, nil, ""},
|
||||||
|
{"no global ipv6", familyIPv6, loopbackNetworkInterface{}, nil, ""},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
ip, err := getIPFromLoopbackInterface(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 seen %v", tc.tcase, tc.errStrFrag, err)
|
||||||
|
} 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) {
|
func TestChooseHostInterfaceFromRoute(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
tcase string
|
tcase string
|
||||||
@ -544,6 +620,16 @@ func TestChooseHostInterfaceFromRoute(t *testing.T) {
|
|||||||
{"single-stack ipv6, prefer v6", routeV6, ipv6NetworkInterface{}, preferIPv6, net.ParseIP("2001::200")},
|
{"single-stack ipv6, prefer v6", routeV6, ipv6NetworkInterface{}, preferIPv6, net.ParseIP("2001::200")},
|
||||||
{"dual stack", bothRoutes, v4v6NetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145")},
|
{"dual stack", bothRoutes, v4v6NetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145")},
|
||||||
{"dual stack, prefer v6", bothRoutes, v4v6NetworkInterface{}, preferIPv6, net.ParseIP("2001::10")},
|
{"dual stack, prefer v6", bothRoutes, v4v6NetworkInterface{}, preferIPv6, net.ParseIP("2001::10")},
|
||||||
|
{"LLA and loopback with global, IPv4", routeV4, linkLocalLoopbackNetworkInterface{}, preferIPv4, net.ParseIP("10.1.1.1")},
|
||||||
|
{"LLA and loopback with global, IPv6", routeV6, linkLocalLoopbackNetworkInterface{}, preferIPv6, net.ParseIP("fd00:1:1::1")},
|
||||||
|
{"LLA and loopback with global, dual stack prefer IPv4", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv4, net.ParseIP("10.1.1.1")},
|
||||||
|
{"LLA and loopback with global, dual stack prefer IPv6", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, net.ParseIP("fd00:1:1::1")},
|
||||||
|
{"LLA and loopback with global, no routes", noRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, nil},
|
||||||
|
{"interface and loopback with global, IPv4", routeV4, globalsNetworkInterface{}, preferIPv4, net.ParseIP("192.168.1.1")},
|
||||||
|
{"interface and loopback with global, IPv6", routeV6, globalsNetworkInterface{}, preferIPv6, net.ParseIP("fd00::200")},
|
||||||
|
{"interface and loopback with global, dual stack prefer IPv4", bothRoutes, globalsNetworkInterface{}, preferIPv4, net.ParseIP("192.168.1.1")},
|
||||||
|
{"interface and loopback with global, dual stack prefer IPv6", bothRoutes, globalsNetworkInterface{}, preferIPv6, net.ParseIP("fd00::200")},
|
||||||
|
{"interface and loopback with global, no routes", noRoutes, globalsNetworkInterface{}, preferIPv6, nil},
|
||||||
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil},
|
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil},
|
||||||
{"no routes", noRoutes, validNetworkInterface{}, preferIPv4, nil},
|
{"no routes", noRoutes, validNetworkInterface{}, preferIPv4, nil},
|
||||||
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
|
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
|
||||||
|
Loading…
Reference in New Issue
Block a user