From a3ad527ffd2bfc0e7c7373ed5930d5b84d1c0a15 Mon Sep 17 00:00:00 2001 From: Daman Arora Date: Sun, 19 May 2024 21:11:46 +0530 Subject: [PATCH] pkg/proxy: refactor npa.GetNodeIPs Signed-off-by: Daman Arora --- pkg/proxy/util/nodeport_addresses.go | 33 +--------------- pkg/proxy/util/utils.go | 46 ++++++++++++++++++++++ pkg/proxy/util/utils_test.go | 58 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 32 deletions(-) diff --git a/pkg/proxy/util/nodeport_addresses.go b/pkg/proxy/util/nodeport_addresses.go index c5332a07958..21769f0cea0 100644 --- a/pkg/proxy/util/nodeport_addresses.go +++ b/pkg/proxy/util/nodeport_addresses.go @@ -94,38 +94,7 @@ func (npa *NodePortAddresses) MatchAll() bool { // IPs are found, it returns an empty list. // NetworkInterfacer is injected for test purpose. func (npa *NodePortAddresses) GetNodeIPs(nw NetworkInterfacer) ([]net.IP, error) { - addrs, err := nw.InterfaceAddrs() - if err != nil { - return nil, fmt.Errorf("error listing all interfaceAddrs from host, error: %v", err) - } - - // Use a map to dedup matches - addresses := make(map[string]net.IP) - for _, cidr := range npa.cidrs { - for _, addr := range addrs { - var ip net.IP - // nw.InterfaceAddrs may return net.IPAddr or net.IPNet on windows, and it will return net.IPNet on linux. - switch v := addr.(type) { - case *net.IPAddr: - ip = v.IP - case *net.IPNet: - ip = v.IP - default: - continue - } - - if cidr.Contains(ip) { - addresses[ip.String()] = ip - } - } - } - - ips := make([]net.IP, 0, len(addresses)) - for _, ip := range addresses { - ips = append(ips, ip) - } - - return ips, nil + return FilterInterfaceAddrsByCIDRs(nw, npa.cidrs) } // ContainsIPv4Loopback returns true if npa's CIDRs contain an IPv4 loopback address. diff --git a/pkg/proxy/util/utils.go b/pkg/proxy/util/utils.go index 01d63eef132..14c85613ffa 100644 --- a/pkg/proxy/util/utils.go +++ b/pkg/proxy/util/utils.go @@ -241,3 +241,49 @@ func IsVIPMode(ing v1.LoadBalancerIngress) bool { } return *ing.IPMode == v1.LoadBalancerIPModeVIP } + +// FilterInterfaceAddrsByCIDRs filters the IP addresses of the provided NetworkInterfacer, +// returning only those that belong to any of the CIDRs specified in the given list. +func FilterInterfaceAddrsByCIDRs(nw NetworkInterfacer, cidrs []*net.IPNet) ([]net.IP, error) { + addrs, err := nw.InterfaceAddrs() + if err != nil { + return nil, fmt.Errorf("error listing all interfaceAddrs from host, error: %w", err) + } + + // Use a map to dedup matches + addresses := make(map[string]net.IP) + for _, cidr := range cidrs { + for _, addr := range addrs { + var ip net.IP + // nw.InterfaceAddrs may return net.IPAddr or net.IPNet on windows, and it will return net.IPNet on linux. + switch v := addr.(type) { + case *net.IPAddr: + ip = v.IP + case *net.IPNet: + ip = v.IP + default: + continue + } + + if cidr.Contains(ip) { + addresses[ip.String()] = ip + } + } + } + + ips := make([]net.IP, 0, len(addresses)) + for _, ip := range addresses { + ips = append(ips, ip) + } + + return ips, nil +} + +// FilterInterfaceAddrsByCIDRStrings is a wrapper around FilterInterfaceAddrsByCIDRs which accepts CIDRs as list of strings. +func FilterInterfaceAddrsByCIDRStrings(nw NetworkInterfacer, cidrStrings []string) ([]net.IP, error) { + cidrs, err := netutils.ParseCIDRs(cidrStrings) + if err != nil { + return nil, err + } + return FilterInterfaceAddrsByCIDRs(nw, cidrs) +} diff --git a/pkg/proxy/util/utils_test.go b/pkg/proxy/util/utils_test.go index 1f82de0fce9..d007825d785 100644 --- a/pkg/proxy/util/utils_test.go +++ b/pkg/proxy/util/utils_test.go @@ -21,9 +21,12 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" + proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing" netutils "k8s.io/utils/net" ) @@ -703,3 +706,58 @@ func TestIsZeroCIDR(t *testing.T) { }) } } + +func TestFilterInterfaceAddrsByCIDRs(t *testing.T) { + networkInterfacer := proxyutiltest.NewFakeNetwork() + var itf net.Interface + var addrs []net.Addr + itf = net.Interface{Index: 0, MTU: 0, Name: "eth1", HardwareAddr: nil, Flags: 0} + addrs = []net.Addr{ + &net.IPNet{IP: netutils.ParseIPSloppy("10.10.10.10"), Mask: net.CIDRMask(24, 32)}, + &net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)}, + } + networkInterfacer.AddInterfaceAddr(&itf, addrs) + + itf = net.Interface{Index: 0, MTU: 0, Name: "eth2", HardwareAddr: nil, Flags: 0} + addrs = []net.Addr{ + &net.IPNet{IP: netutils.ParseIPSloppy("192.168.0.2"), Mask: net.CIDRMask(24, 32)}, + &net.IPNet{IP: netutils.ParseIPSloppy("fd00:4321::2"), Mask: net.CIDRMask(64, 128)}, + } + networkInterfacer.AddInterfaceAddr(&itf, addrs) + + testCases := []struct { + name string + cidrStrings []string + expected []string + }{ + { + name: "ipv4", + cidrStrings: []string{"192.168.0.0/24"}, + expected: []string{"192.168.0.2"}, + }, + { + name: "ipv6", + cidrStrings: []string{"fd00:4321::/64"}, + expected: []string{"fd00:4321::2"}, + }, + { + name: "dual stack", + cidrStrings: []string{"10.10.0.0/16", "2001:db8::/64"}, + expected: []string{"10.10.10.10", "2001:db8::1"}, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cidrs, err := netutils.ParseCIDRs(tc.cidrStrings) + require.NoError(t, err) + + ips, err := FilterInterfaceAddrsByCIDRs(networkInterfacer, cidrs) + require.NoError(t, err) + var expected []net.IP + for i := range tc.expected { + expected = append(expected, netutils.ParseIPSloppy(tc.expected[i])) + } + require.Equal(t, expected, ips) + }) + } +}