k8s.io/kubernetes@v1.29.3/pkg/proxy/util/nodeport_addresses.go (about)

     1  /*
     2  Copyright 2022 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 util
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  
    23  	"k8s.io/api/core/v1"
    24  	netutils "k8s.io/utils/net"
    25  )
    26  
    27  // NodePortAddresses is used to handle the --nodeport-addresses flag
    28  type NodePortAddresses struct {
    29  	cidrStrings []string
    30  
    31  	cidrs                []*net.IPNet
    32  	containsIPv4Loopback bool
    33  	matchAll             bool
    34  }
    35  
    36  // RFC 5735 127.0.0.0/8 - This block is assigned for use as the Internet host loopback address
    37  var ipv4LoopbackStart = net.IPv4(127, 0, 0, 0)
    38  
    39  // NewNodePortAddresses takes an IP family and the `--nodeport-addresses` value (which is
    40  // assumed to contain only valid CIDRs, potentially of both IP families) and returns a
    41  // NodePortAddresses object for the given family. If there are no CIDRs of the given
    42  // family then the CIDR "0.0.0.0/0" or "::/0" will be added (even if there are CIDRs of
    43  // the other family).
    44  func NewNodePortAddresses(family v1.IPFamily, cidrStrings []string) *NodePortAddresses {
    45  	npa := &NodePortAddresses{}
    46  
    47  	// Filter CIDRs to correct family
    48  	for _, str := range cidrStrings {
    49  		if (family == v1.IPv4Protocol) == netutils.IsIPv4CIDRString(str) {
    50  			npa.cidrStrings = append(npa.cidrStrings, str)
    51  		}
    52  	}
    53  	if len(npa.cidrStrings) == 0 {
    54  		if family == v1.IPv4Protocol {
    55  			npa.cidrStrings = []string{IPv4ZeroCIDR}
    56  		} else {
    57  			npa.cidrStrings = []string{IPv6ZeroCIDR}
    58  		}
    59  	}
    60  
    61  	// Now parse
    62  	for _, str := range npa.cidrStrings {
    63  		_, cidr, _ := netutils.ParseCIDRSloppy(str)
    64  
    65  		if netutils.IsIPv4CIDR(cidr) {
    66  			if cidr.IP.IsLoopback() || cidr.Contains(ipv4LoopbackStart) {
    67  				npa.containsIPv4Loopback = true
    68  			}
    69  		}
    70  
    71  		if IsZeroCIDR(str) {
    72  			// Ignore everything else
    73  			npa.cidrs = []*net.IPNet{cidr}
    74  			npa.matchAll = true
    75  			break
    76  		}
    77  
    78  		npa.cidrs = append(npa.cidrs, cidr)
    79  	}
    80  
    81  	return npa
    82  }
    83  
    84  func (npa *NodePortAddresses) String() string {
    85  	return fmt.Sprintf("%v", npa.cidrStrings)
    86  }
    87  
    88  // MatchAll returns true if npa matches all node IPs (of npa's given family)
    89  func (npa *NodePortAddresses) MatchAll() bool {
    90  	return npa.matchAll
    91  }
    92  
    93  // GetNodeIPs return all matched node IP addresses for npa's CIDRs. If no matching
    94  // IPs are found, it returns an empty list.
    95  // NetworkInterfacer is injected for test purpose.
    96  func (npa *NodePortAddresses) GetNodeIPs(nw NetworkInterfacer) ([]net.IP, error) {
    97  	addrs, err := nw.InterfaceAddrs()
    98  	if err != nil {
    99  		return nil, fmt.Errorf("error listing all interfaceAddrs from host, error: %v", err)
   100  	}
   101  
   102  	// Use a map to dedup matches
   103  	addresses := make(map[string]net.IP)
   104  	for _, cidr := range npa.cidrs {
   105  		for _, addr := range addrs {
   106  			var ip net.IP
   107  			// nw.InterfaceAddrs may return net.IPAddr or net.IPNet on windows, and it will return net.IPNet on linux.
   108  			switch v := addr.(type) {
   109  			case *net.IPAddr:
   110  				ip = v.IP
   111  			case *net.IPNet:
   112  				ip = v.IP
   113  			default:
   114  				continue
   115  			}
   116  
   117  			if cidr.Contains(ip) {
   118  				addresses[ip.String()] = ip
   119  			}
   120  		}
   121  	}
   122  
   123  	ips := make([]net.IP, 0, len(addresses))
   124  	for _, ip := range addresses {
   125  		ips = append(ips, ip)
   126  	}
   127  
   128  	return ips, nil
   129  }
   130  
   131  // ContainsIPv4Loopback returns true if npa's CIDRs contain an IPv4 loopback address.
   132  func (npa *NodePortAddresses) ContainsIPv4Loopback() bool {
   133  	return npa.containsIPv4Loopback
   134  }