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 }