mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #101429 from Nordix/issues-93858
Kube-proxy/ipvs; Use go "net" lib to get nodeIPs
This commit is contained in:
commit
67a352e85f
@ -32,7 +32,12 @@ type NetLinkHandle interface {
|
|||||||
DeleteDummyDevice(devName string) error
|
DeleteDummyDevice(devName string) error
|
||||||
// ListBindAddress will list all IP addresses which are bound in a given interface
|
// ListBindAddress will list all IP addresses which are bound in a given interface
|
||||||
ListBindAddress(devName string) ([]string, error)
|
ListBindAddress(devName string) ([]string, error)
|
||||||
// GetLocalAddresses returns all unique local type IP addresses based on specified device and filter device
|
// GetAllLocalAddresses return all local addresses on the node.
|
||||||
// If device is not specified, it will list all unique local type addresses except filter device addresses
|
// Only the addresses of the current family are returned.
|
||||||
GetLocalAddresses(dev, filterDev string) (sets.String, error)
|
// IPv6 link-local and loopback addresses are excluded.
|
||||||
|
GetAllLocalAddresses() (sets.String, error)
|
||||||
|
// GetLocalAddresses return all local addresses for an interface.
|
||||||
|
// Only the addresses of the current family are returned.
|
||||||
|
// IPv6 link-local and loopback addresses are excluded.
|
||||||
|
GetLocalAddresses(dev string) (sets.String, error)
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,10 @@ package ipvs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
@ -124,72 +126,41 @@ func (h *netlinkHandle) ListBindAddress(devName string) ([]string, error) {
|
|||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLocalAddresses lists all LOCAL type IP addresses from host based on filter device.
|
// GetAllLocalAddresses return all local addresses on the node.
|
||||||
// If dev is not specified, it's equivalent to exec:
|
// Only the addresses of the current family are returned.
|
||||||
// $ ip route show table local type local proto kernel
|
// IPv6 link-local and loopback addresses are excluded.
|
||||||
// 10.0.0.1 dev kube-ipvs0 scope host src 10.0.0.1
|
func (h *netlinkHandle) GetAllLocalAddresses() (sets.String, error) {
|
||||||
// 10.0.0.10 dev kube-ipvs0 scope host src 10.0.0.10
|
addr, err := net.InterfaceAddrs()
|
||||||
// 10.0.0.252 dev kube-ipvs0 scope host src 10.0.0.252
|
|
||||||
// 100.106.89.164 dev eth0 scope host src 100.106.89.164
|
|
||||||
// 127.0.0.0/8 dev lo scope host src 127.0.0.1
|
|
||||||
// 127.0.0.1 dev lo scope host src 127.0.0.1
|
|
||||||
// 172.17.0.1 dev docker0 scope host src 172.17.0.1
|
|
||||||
// 192.168.122.1 dev virbr0 scope host src 192.168.122.1
|
|
||||||
// Then cut the unique src IP fields,
|
|
||||||
// --> result set: [10.0.0.1, 10.0.0.10, 10.0.0.252, 100.106.89.164, 127.0.0.1, 172.17.0.1, 192.168.122.1]
|
|
||||||
|
|
||||||
// If dev is specified, it's equivalent to exec:
|
|
||||||
// $ ip route show table local type local proto kernel dev kube-ipvs0
|
|
||||||
// 10.0.0.1 scope host src 10.0.0.1
|
|
||||||
// 10.0.0.10 scope host src 10.0.0.10
|
|
||||||
// Then cut the unique src IP fields,
|
|
||||||
// --> result set: [10.0.0.1, 10.0.0.10]
|
|
||||||
|
|
||||||
// If filterDev is specified, the result will discard route of specified device and cut src from other routes.
|
|
||||||
func (h *netlinkHandle) GetLocalAddresses(dev, filterDev string) (sets.String, error) {
|
|
||||||
chosenLinkIndex, filterLinkIndex := -1, -1
|
|
||||||
if dev != "" {
|
|
||||||
link, err := h.LinkByName(dev)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error get device %s, err: %v", dev, err)
|
|
||||||
}
|
|
||||||
chosenLinkIndex = link.Attrs().Index
|
|
||||||
} else if filterDev != "" {
|
|
||||||
link, err := h.LinkByName(filterDev)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error get filter device %s, err: %v", filterDev, err)
|
|
||||||
}
|
|
||||||
filterLinkIndex = link.Attrs().Index
|
|
||||||
}
|
|
||||||
|
|
||||||
routeFilter := &netlink.Route{
|
|
||||||
Table: unix.RT_TABLE_LOCAL,
|
|
||||||
Type: unix.RTN_LOCAL,
|
|
||||||
Protocol: unix.RTPROT_KERNEL,
|
|
||||||
}
|
|
||||||
filterMask := netlink.RT_FILTER_TABLE | netlink.RT_FILTER_TYPE | netlink.RT_FILTER_PROTOCOL
|
|
||||||
|
|
||||||
// find chosen device
|
|
||||||
if chosenLinkIndex != -1 {
|
|
||||||
routeFilter.LinkIndex = chosenLinkIndex
|
|
||||||
filterMask |= netlink.RT_FILTER_OIF
|
|
||||||
}
|
|
||||||
routes, err := h.RouteListFiltered(netlink.FAMILY_ALL, routeFilter, filterMask)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error list route table, err: %v", err)
|
return nil, fmt.Errorf("Could not get addresses: %v", err)
|
||||||
}
|
}
|
||||||
res := sets.NewString()
|
return utilproxy.AddressSet(h.isValidForSet, addr), nil
|
||||||
for _, route := range routes {
|
}
|
||||||
if route.LinkIndex == filterLinkIndex {
|
|
||||||
continue
|
// GetLocalAddresses return all local addresses for an interface.
|
||||||
}
|
// Only the addresses of the current family are returned.
|
||||||
if h.isIPv6 {
|
// IPv6 link-local and loopback addresses are excluded.
|
||||||
if route.Dst.IP.To4() == nil && !route.Dst.IP.IsLinkLocalUnicast() {
|
func (h *netlinkHandle) GetLocalAddresses(dev string) (sets.String, error) {
|
||||||
res.Insert(route.Dst.IP.String())
|
ifi, err := net.InterfaceByName(dev)
|
||||||
}
|
if err != nil {
|
||||||
} else if route.Src != nil {
|
return nil, fmt.Errorf("Could not get interface %s: %v", dev, err)
|
||||||
res.Insert(route.Src.String())
|
}
|
||||||
}
|
addr, err := ifi.Addrs()
|
||||||
}
|
if err != nil {
|
||||||
return res, nil
|
return nil, fmt.Errorf("Can't get addresses from %s: %v", ifi.Name, err)
|
||||||
|
}
|
||||||
|
return utilproxy.AddressSet(h.isValidForSet, addr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *netlinkHandle) isValidForSet(ip net.IP) bool {
|
||||||
|
if h.isIPv6 != netutils.IsIPv6(ip) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if h.isIPv6 && ip.IsLinkLocalUnicast() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ip.IsLoopback() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
@ -21,44 +21,57 @@ package ipvs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
type emptyHandle struct {
|
// The type must match the one in proxier_test.go
|
||||||
|
type netlinkHandle struct {
|
||||||
|
isIPv6 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNetLinkHandle will create an EmptyHandle
|
// NewNetLinkHandle will create an EmptyHandle
|
||||||
func NewNetLinkHandle(ipv6 bool) NetLinkHandle {
|
func NewNetLinkHandle(ipv6 bool) NetLinkHandle {
|
||||||
return &emptyHandle{}
|
return &netlinkHandle{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureAddressBind checks if address is bound to the interface and, if not, binds it. If the address is already bound, return true.
|
// EnsureAddressBind checks if address is bound to the interface and, if not, binds it. If the address is already bound, return true.
|
||||||
func (h *emptyHandle) EnsureAddressBind(address, devName string) (exist bool, err error) {
|
func (h *netlinkHandle) EnsureAddressBind(address, devName string) (exist bool, err error) {
|
||||||
return false, fmt.Errorf("netlink not supported for this platform")
|
return false, fmt.Errorf("netlink not supported for this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnbindAddress unbind address from the interface
|
// UnbindAddress unbind address from the interface
|
||||||
func (h *emptyHandle) UnbindAddress(address, devName string) error {
|
func (h *netlinkHandle) UnbindAddress(address, devName string) error {
|
||||||
return fmt.Errorf("netlink not supported for this platform")
|
return fmt.Errorf("netlink not supported for this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureDummyDevice is part of interface
|
// EnsureDummyDevice is part of interface
|
||||||
func (h *emptyHandle) EnsureDummyDevice(devName string) (bool, error) {
|
func (h *netlinkHandle) EnsureDummyDevice(devName string) (bool, error) {
|
||||||
return false, fmt.Errorf("netlink is not supported in this platform")
|
return false, fmt.Errorf("netlink is not supported in this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDummyDevice is part of interface.
|
// DeleteDummyDevice is part of interface.
|
||||||
func (h *emptyHandle) DeleteDummyDevice(devName string) error {
|
func (h *netlinkHandle) DeleteDummyDevice(devName string) error {
|
||||||
return fmt.Errorf("netlink is not supported in this platform")
|
return fmt.Errorf("netlink is not supported in this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListBindAddress is part of interface.
|
// ListBindAddress is part of interface.
|
||||||
func (h *emptyHandle) ListBindAddress(devName string) ([]string, error) {
|
func (h *netlinkHandle) ListBindAddress(devName string) ([]string, error) {
|
||||||
|
return nil, fmt.Errorf("netlink is not supported in this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllLocalAddresses is part of interface.
|
||||||
|
func (h *netlinkHandle) GetAllLocalAddresses() (sets.String, error) {
|
||||||
return nil, fmt.Errorf("netlink is not supported in this platform")
|
return nil, fmt.Errorf("netlink is not supported in this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLocalAddresses is part of interface.
|
// GetLocalAddresses is part of interface.
|
||||||
func (h *emptyHandle) GetLocalAddresses(dev, filterDev string) (sets.String, error) {
|
func (h *netlinkHandle) GetLocalAddresses(dev string) (sets.String, error) {
|
||||||
return nil, fmt.Errorf("netlink is not supported in this platform")
|
return nil, fmt.Errorf("netlink is not supported in this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must match the one in proxier_test.go
|
||||||
|
func (h *netlinkHandle) isValidForSet(ip net.IP) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -289,33 +289,31 @@ type realIPGetter struct {
|
|||||||
nl NetLinkHandle
|
nl NetLinkHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeIPs returns all LOCAL type IP addresses from host which are taken as the Node IPs of NodePort service.
|
// NodeIPs returns all LOCAL type IP addresses from host which are
|
||||||
// It will list source IP exists in local route table with `kernel` protocol type, and filter out IPVS proxier
|
// taken as the Node IPs of NodePort service. Filtered addresses:
|
||||||
// created dummy device `kube-ipvs0` For example,
|
//
|
||||||
// $ ip route show table local type local proto kernel
|
// * Loopback addresses
|
||||||
// 10.0.0.1 dev kube-ipvs0 scope host src 10.0.0.1
|
// * Addresses of the "other" family (not handled by this proxier instance)
|
||||||
// 10.0.0.10 dev kube-ipvs0 scope host src 10.0.0.10
|
// * Link-local IPv6 addresses
|
||||||
// 10.0.0.252 dev kube-ipvs0 scope host src 10.0.0.252
|
// * Addresses on the created dummy device `kube-ipvs0`
|
||||||
// 100.106.89.164 dev eth0 scope host src 100.106.89.164
|
//
|
||||||
// 127.0.0.0/8 dev lo scope host src 127.0.0.1
|
|
||||||
// 127.0.0.1 dev lo scope host src 127.0.0.1
|
|
||||||
// 172.17.0.1 dev docker0 scope host src 172.17.0.1
|
|
||||||
// 192.168.122.1 dev virbr0 scope host src 192.168.122.1
|
|
||||||
// Then filter out dev==kube-ipvs0, and cut the unique src IP fields,
|
|
||||||
// Node IP set: [100.106.89.164, 172.17.0.1, 192.168.122.1]
|
|
||||||
// Note that loopback addresses are excluded.
|
|
||||||
func (r *realIPGetter) NodeIPs() (ips []net.IP, err error) {
|
func (r *realIPGetter) NodeIPs() (ips []net.IP, err error) {
|
||||||
// Pass in empty filter device name for list all LOCAL type addresses.
|
|
||||||
nodeAddress, err := r.nl.GetLocalAddresses("", DefaultDummyDevice)
|
nodeAddress, err := r.nl.GetAllLocalAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error listing LOCAL type addresses from host, error: %v", err)
|
return nil, fmt.Errorf("error listing LOCAL type addresses from host, error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We must exclude the addresses on the IPVS dummy interface
|
||||||
|
bindedAddress, err := r.BindedIPs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ipset := nodeAddress.Difference(bindedAddress)
|
||||||
|
|
||||||
// translate ip string to IP
|
// translate ip string to IP
|
||||||
for _, ipStr := range nodeAddress.UnsortedList() {
|
for _, ipStr := range ipset.UnsortedList() {
|
||||||
a := netutils.ParseIPSloppy(ipStr)
|
a := netutils.ParseIPSloppy(ipStr)
|
||||||
if a.IsLoopback() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ips = append(ips, a)
|
ips = append(ips, a)
|
||||||
}
|
}
|
||||||
return ips, nil
|
return ips, nil
|
||||||
@ -323,7 +321,7 @@ func (r *realIPGetter) NodeIPs() (ips []net.IP, err error) {
|
|||||||
|
|
||||||
// BindedIPs returns all addresses that are binded to the IPVS dummy interface kube-ipvs0
|
// BindedIPs returns all addresses that are binded to the IPVS dummy interface kube-ipvs0
|
||||||
func (r *realIPGetter) BindedIPs() (sets.String, error) {
|
func (r *realIPGetter) BindedIPs() (sets.String, error) {
|
||||||
return r.nl.GetLocalAddresses(DefaultDummyDevice, "")
|
return r.nl.GetLocalAddresses(DefaultDummyDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxier implements proxy.Provider
|
// Proxier implements proxy.Provider
|
||||||
|
@ -385,6 +385,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetNodeIPs(t *testing.T) {
|
func TestGetNodeIPs(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
isIPv6 bool
|
||||||
devAddresses map[string][]string
|
devAddresses map[string][]string
|
||||||
expectIPs []string
|
expectIPs []string
|
||||||
}{
|
}{
|
||||||
@ -405,22 +406,22 @@ func TestGetNodeIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// case 3
|
// case 3
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"encap0": {"10.20.30.40"}, "lo": {"127.0.0.1"}, "docker0": {"172.17.0.1"}},
|
devAddresses: map[string][]string{"encap0": {"10.20.30.40", "fe80::200:ff:fe01:1"}, "lo": {"127.0.0.1", "::1"}, "docker0": {"172.17.0.1"}},
|
||||||
expectIPs: []string{"10.20.30.40", "172.17.0.1"},
|
expectIPs: []string{"10.20.30.40", "172.17.0.1"},
|
||||||
},
|
},
|
||||||
// case 4
|
// case 4
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"encaps9": {"10.20.30.40"}, "lo": {"127.0.0.1"}, "encap7": {"10.20.30.31"}},
|
devAddresses: map[string][]string{"encaps9": {"10.20.30.40"}, "lo": {"127.0.0.1", "::1"}, "encap7": {"1000::", "10.20.30.31"}},
|
||||||
expectIPs: []string{"10.20.30.40", "10.20.30.31"},
|
expectIPs: []string{"10.20.30.40", "10.20.30.31"},
|
||||||
},
|
},
|
||||||
// case 5
|
// case 5
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"kube-ipvs0": {"1.2.3.4"}, "lo": {"127.0.0.1"}, "encap7": {"10.20.30.31"}},
|
devAddresses: map[string][]string{"kube-ipvs0": {"2000::", "1.2.3.4"}, "lo": {"127.0.0.1", "::1"}, "encap7": {"1000::", "10.20.30.31"}},
|
||||||
expectIPs: []string{"10.20.30.31"},
|
expectIPs: []string{"10.20.30.31"},
|
||||||
},
|
},
|
||||||
// case 6
|
// case 6
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"kube-ipvs0": {"1.2.3.4", "2.3.4.5"}, "lo": {"127.0.0.1"}},
|
devAddresses: map[string][]string{"kube-ipvs0": {"1.2.3.4", "2.3.4.5"}, "lo": {"127.0.0.1", "::1"}},
|
||||||
expectIPs: []string{},
|
expectIPs: []string{},
|
||||||
},
|
},
|
||||||
// case 7
|
// case 7
|
||||||
@ -430,18 +431,31 @@ func TestGetNodeIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// case 8
|
// case 8
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"kube-ipvs0": {"1.2.3.4", "2.3.4.5"}, "eth5": {"3.4.5.6"}, "lo": {"127.0.0.1"}},
|
devAddresses: map[string][]string{"kube-ipvs0": {"1.2.3.4", "2.3.4.5"}, "eth5": {"3.4.5.6"}, "lo": {"127.0.0.1", "::1"}},
|
||||||
expectIPs: []string{"3.4.5.6"},
|
expectIPs: []string{"3.4.5.6"},
|
||||||
},
|
},
|
||||||
// case 9
|
// case 9
|
||||||
{
|
{
|
||||||
devAddresses: map[string][]string{"ipvs0": {"1.2.3.4"}, "lo": {"127.0.0.1"}, "encap7": {"10.20.30.31"}},
|
devAddresses: map[string][]string{"ipvs0": {"1.2.3.4"}, "lo": {"127.0.0.1", "::1"}, "encap7": {"10.20.30.31"}},
|
||||||
expectIPs: []string{"10.20.30.31", "1.2.3.4"},
|
expectIPs: []string{"10.20.30.31", "1.2.3.4"},
|
||||||
},
|
},
|
||||||
|
// case 10
|
||||||
|
{
|
||||||
|
isIPv6: true,
|
||||||
|
devAddresses: map[string][]string{"ipvs0": {"1.2.3.4", "1000::"}, "lo": {"127.0.0.1", "::1"}, "encap7": {"10.20.30.31", "2000::", "fe80::200:ff:fe01:1"}},
|
||||||
|
expectIPs: []string{"1000::", "2000::"},
|
||||||
|
},
|
||||||
|
// case 11
|
||||||
|
{
|
||||||
|
isIPv6: true,
|
||||||
|
devAddresses: map[string][]string{"ipvs0": {"1.2.3.4", "1000::"}, "lo": {"127.0.0.1", "::1"}, "encap7": {"10.20.30.31", "2000::", "fe80::200:ff:fe01:1"}, "kube-ipvs0": {"1.2.3.4", "2.3.4.5", "2000::"}},
|
||||||
|
expectIPs: []string{"1000::"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range testCases {
|
for i := range testCases {
|
||||||
fake := netlinktest.NewFakeNetlinkHandle()
|
fake := netlinktest.NewFakeNetlinkHandle()
|
||||||
|
fake.IsIPv6 = testCases[i].isIPv6
|
||||||
for dev, addresses := range testCases[i].devAddresses {
|
for dev, addresses := range testCases[i].devAddresses {
|
||||||
fake.SetLocalAddresses(dev, addresses...)
|
fake.SetLocalAddresses(dev, addresses...)
|
||||||
}
|
}
|
||||||
@ -5354,3 +5368,94 @@ func Test_EndpointSliceOnlyReadyAndTerminatingLocalWithFeatureGateDisabled(t *te
|
|||||||
assert.Nil(t, rsErr2, "Expected no error getting real servers")
|
assert.Nil(t, rsErr2, "Expected no error getting real servers")
|
||||||
assert.Len(t, realServers2, 0, "Expected 0 real servers")
|
assert.Len(t, realServers2, 0, "Expected 0 real servers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIpIsValidForSet(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
isIPv6 bool
|
||||||
|
ip string
|
||||||
|
res bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"127.0.0.1",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"127.0.0.0",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"127.6.7.8",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"8.8.8.8",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"192.168.0.1",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"169.254.0.0",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"::ffff:169.254.0.0", // IPv6 mapped IPv4
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"1000::",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
// IPv6
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"::1",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"1000::",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"fe80::200:ff:fe01:1",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"8.8.8.8",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"::ffff:8.8.8.8",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
v := &netlinkHandle{}
|
||||||
|
v.isIPv6 = tc.isIPv6
|
||||||
|
ip := netutils.ParseIPSloppy(tc.ip)
|
||||||
|
if ip == nil {
|
||||||
|
t.Errorf("Parse error: %s", tc.ip)
|
||||||
|
}
|
||||||
|
if v.isValidForSet(ip) != tc.res {
|
||||||
|
if tc.isIPv6 {
|
||||||
|
t.Errorf("IPv6: %s", tc.ip)
|
||||||
|
} else {
|
||||||
|
t.Errorf("IPv4: %s", tc.ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ package testing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"k8s.io/utils/net"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
@ -27,6 +28,8 @@ type FakeNetlinkHandle struct {
|
|||||||
// localAddresses is a network interface name to all of its IP addresses map, e.g.
|
// localAddresses is a network interface name to all of its IP addresses map, e.g.
|
||||||
// eth0 -> [1.2.3.4, 10.20.30.40]
|
// eth0 -> [1.2.3.4, 10.20.30.40]
|
||||||
localAddresses map[string][]string
|
localAddresses map[string][]string
|
||||||
|
|
||||||
|
IsIPv6 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFakeNetlinkHandle will create a new FakeNetlinkHandle
|
// NewFakeNetlinkHandle will create a new FakeNetlinkHandle
|
||||||
@ -112,23 +115,25 @@ func (h *FakeNetlinkHandle) ListBindAddress(devName string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLocalAddresses is a mock implementation
|
// GetLocalAddresses is a mock implementation
|
||||||
func (h *FakeNetlinkHandle) GetLocalAddresses(dev, filterDev string) (sets.String, error) {
|
func (h *FakeNetlinkHandle) GetLocalAddresses(dev string) (sets.String, error) {
|
||||||
res := sets.NewString()
|
res := sets.NewString()
|
||||||
if len(dev) != 0 {
|
// list all addresses from a given network interface.
|
||||||
// list all addresses from a given network interface.
|
for _, addr := range h.localAddresses[dev] {
|
||||||
for _, addr := range h.localAddresses[dev] {
|
if h.isValidForSet(addr) {
|
||||||
res.Insert(addr)
|
res.Insert(addr)
|
||||||
}
|
}
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
// If filterDev is not given, will list all addresses from all available network interface.
|
return res, nil
|
||||||
|
}
|
||||||
|
func (h *FakeNetlinkHandle) GetAllLocalAddresses() (sets.String, error) {
|
||||||
|
res := sets.NewString()
|
||||||
|
// List all addresses from all available network interfaces.
|
||||||
for linkName := range h.localAddresses {
|
for linkName := range h.localAddresses {
|
||||||
if linkName == filterDev {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// list all addresses from a given network interface.
|
// list all addresses from a given network interface.
|
||||||
for _, addr := range h.localAddresses[linkName] {
|
for _, addr := range h.localAddresses[linkName] {
|
||||||
res.Insert(addr)
|
if h.isValidForSet(addr) {
|
||||||
|
res.Insert(addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
@ -146,3 +151,17 @@ func (h *FakeNetlinkHandle) SetLocalAddresses(dev string, ips ...string) error {
|
|||||||
h.localAddresses[dev] = append(h.localAddresses[dev], ips...)
|
h.localAddresses[dev] = append(h.localAddresses[dev], ips...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *FakeNetlinkHandle) isValidForSet(ipString string) bool {
|
||||||
|
ip := net.ParseIPSloppy(ipString)
|
||||||
|
if h.IsIPv6 != (ip.To4() == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if h.IsIPv6 && ip.IsLinkLocalUnicast() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ip.IsLoopback() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -27,22 +27,22 @@ func TestSetGetLocalAddresses(t *testing.T) {
|
|||||||
fake := NewFakeNetlinkHandle()
|
fake := NewFakeNetlinkHandle()
|
||||||
fake.SetLocalAddresses("eth0", "1.2.3.4")
|
fake.SetLocalAddresses("eth0", "1.2.3.4")
|
||||||
expected := sets.NewString("1.2.3.4")
|
expected := sets.NewString("1.2.3.4")
|
||||||
addr, _ := fake.GetLocalAddresses("eth0", "")
|
addr, _ := fake.GetLocalAddresses("eth0")
|
||||||
if !reflect.DeepEqual(expected, addr) {
|
if !reflect.DeepEqual(expected, addr) {
|
||||||
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, addr)
|
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, addr)
|
||||||
}
|
}
|
||||||
list, _ := fake.GetLocalAddresses("", "")
|
list, _ := fake.GetAllLocalAddresses()
|
||||||
if !reflect.DeepEqual(expected, list) {
|
if !reflect.DeepEqual(expected, list) {
|
||||||
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, list)
|
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, list)
|
||||||
}
|
}
|
||||||
fake.SetLocalAddresses("lo", "127.0.0.1")
|
fake.SetLocalAddresses("lo", "127.0.0.1")
|
||||||
expected = sets.NewString("127.0.0.1")
|
expected = sets.NewString()
|
||||||
addr, _ = fake.GetLocalAddresses("lo", "")
|
addr, _ = fake.GetLocalAddresses("lo")
|
||||||
if !reflect.DeepEqual(expected, addr) {
|
if !reflect.DeepEqual(expected, addr) {
|
||||||
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, addr)
|
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, addr)
|
||||||
}
|
}
|
||||||
list, _ = fake.GetLocalAddresses("", "")
|
list, _ = fake.GetAllLocalAddresses()
|
||||||
expected = sets.NewString("1.2.3.4", "127.0.0.1")
|
expected = sets.NewString("1.2.3.4")
|
||||||
if !reflect.DeepEqual(expected, list) {
|
if !reflect.DeepEqual(expected, list) {
|
||||||
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, list)
|
t.Errorf("Unexpected mismatch, expected: %v, got: %v", expected, list)
|
||||||
}
|
}
|
||||||
|
@ -252,6 +252,27 @@ func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error)
|
|||||||
return uniqueAddressList, nil
|
return uniqueAddressList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddressSet validates the addresses in the slice using the "isValid" function.
|
||||||
|
// Addresses that pass the validation are returned as a string Set.
|
||||||
|
func AddressSet(isValid func(ip net.IP) bool, addrs []net.Addr) sets.String {
|
||||||
|
ips := sets.NewString()
|
||||||
|
for _, a := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isValid(ip) {
|
||||||
|
ips.Insert(ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
// LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event.
|
// LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event.
|
||||||
func LogAndEmitIncorrectIPVersionEvent(recorder events.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) {
|
func LogAndEmitIncorrectIPVersionEvent(recorder events.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) {
|
||||||
errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName)
|
errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName)
|
||||||
|
@ -1283,3 +1283,119 @@ func randSeq() string {
|
|||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustParseIPAddr(str string) net.Addr {
|
||||||
|
a, err := net.ResolveIPAddr("ip", str)
|
||||||
|
if err != nil {
|
||||||
|
panic("mustParseIPAddr")
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
func mustParseIPNet(str string) net.Addr {
|
||||||
|
_, n, err := netutils.ParseCIDRSloppy(str)
|
||||||
|
if err != nil {
|
||||||
|
panic("mustParseIPNet")
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func mustParseUnix(str string) net.Addr {
|
||||||
|
n, err := net.ResolveUnixAddr("unix", str)
|
||||||
|
if err != nil {
|
||||||
|
panic("mustParseUnix")
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type cidrValidator struct {
|
||||||
|
cidr *net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *cidrValidator) isValid(ip net.IP) bool {
|
||||||
|
return v.cidr.Contains(ip)
|
||||||
|
}
|
||||||
|
func newCidrValidator(cidr string) func(ip net.IP) bool {
|
||||||
|
_, n, err := netutils.ParseCIDRSloppy(cidr)
|
||||||
|
if err != nil {
|
||||||
|
panic("mustParseIPNet")
|
||||||
|
}
|
||||||
|
obj := cidrValidator{n}
|
||||||
|
return obj.isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddressSet(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
validator func(ip net.IP) bool
|
||||||
|
input []net.Addr
|
||||||
|
expected sets.String
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Empty",
|
||||||
|
func(ip net.IP) bool { return false },
|
||||||
|
nil,
|
||||||
|
sets.NewString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Reject IPAddr x 2",
|
||||||
|
func(ip net.IP) bool { return false },
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseIPAddr("8.8.8.8"),
|
||||||
|
mustParseIPAddr("1000::"),
|
||||||
|
},
|
||||||
|
sets.NewString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Accept IPAddr x 2",
|
||||||
|
func(ip net.IP) bool { return true },
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseIPAddr("8.8.8.8"),
|
||||||
|
mustParseIPAddr("1000::"),
|
||||||
|
},
|
||||||
|
sets.NewString("8.8.8.8", "1000::"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Accept IPNet x 2",
|
||||||
|
func(ip net.IP) bool { return true },
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseIPNet("8.8.8.8/32"),
|
||||||
|
mustParseIPNet("1000::/128"),
|
||||||
|
},
|
||||||
|
sets.NewString("8.8.8.8", "1000::"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Accept Unix x 2",
|
||||||
|
func(ip net.IP) bool { return true },
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseUnix("/tmp/sock1"),
|
||||||
|
mustParseUnix("/tmp/sock2"),
|
||||||
|
},
|
||||||
|
sets.NewString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Cidr IPv4",
|
||||||
|
newCidrValidator("192.168.1.0/24"),
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseIPAddr("8.8.8.8"),
|
||||||
|
mustParseIPAddr("1000::"),
|
||||||
|
mustParseIPAddr("192.168.1.1"),
|
||||||
|
},
|
||||||
|
sets.NewString("192.168.1.1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Cidr IPv6",
|
||||||
|
newCidrValidator("1000::/64"),
|
||||||
|
[]net.Addr{
|
||||||
|
mustParseIPAddr("8.8.8.8"),
|
||||||
|
mustParseIPAddr("1000::"),
|
||||||
|
mustParseIPAddr("192.168.1.1"),
|
||||||
|
},
|
||||||
|
sets.NewString("1000::"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
if !tc.expected.Equal(AddressSet(tc.validator, tc.input)) {
|
||||||
|
t.Errorf("%s", tc.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user