k8s.io/kubernetes@v1.29.3/pkg/proxy/util/utils.go (about) 1 /* 2 Copyright 2017 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 "context" 21 "fmt" 22 "net" 23 "strconv" 24 "strings" 25 26 v1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/types" 28 utilrand "k8s.io/apimachinery/pkg/util/rand" 29 "k8s.io/apimachinery/pkg/util/sets" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 "k8s.io/client-go/tools/events" 32 utilsysctl "k8s.io/component-helpers/node/util/sysctl" 33 helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" 34 "k8s.io/kubernetes/pkg/features" 35 netutils "k8s.io/utils/net" 36 37 "k8s.io/klog/v2" 38 ) 39 40 const ( 41 // IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space 42 IPv4ZeroCIDR = "0.0.0.0/0" 43 44 // IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space 45 IPv6ZeroCIDR = "::/0" 46 ) 47 48 // isValidEndpoint checks that the given host / port pair are valid endpoint 49 func isValidEndpoint(host string, port int) bool { 50 return host != "" && port > 0 51 } 52 53 // BuildPortsToEndpointsMap builds a map of portname -> all ip:ports for that 54 // portname. Explode Endpoints.Subsets[*] into this structure. 55 func BuildPortsToEndpointsMap(endpoints *v1.Endpoints) map[string][]string { 56 portsToEndpoints := map[string][]string{} 57 for i := range endpoints.Subsets { 58 ss := &endpoints.Subsets[i] 59 for i := range ss.Ports { 60 port := &ss.Ports[i] 61 for i := range ss.Addresses { 62 addr := &ss.Addresses[i] 63 if isValidEndpoint(addr.IP, int(port.Port)) { 64 portsToEndpoints[port.Name] = append(portsToEndpoints[port.Name], net.JoinHostPort(addr.IP, strconv.Itoa(int(port.Port)))) 65 } 66 } 67 } 68 } 69 return portsToEndpoints 70 } 71 72 // IsZeroCIDR checks whether the input CIDR string is either 73 // the IPv4 or IPv6 zero CIDR 74 func IsZeroCIDR(cidr string) bool { 75 if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR { 76 return true 77 } 78 return false 79 } 80 81 // IsLoopBack checks if a given IP address is a loopback address. 82 func IsLoopBack(ip string) bool { 83 netIP := netutils.ParseIPSloppy(ip) 84 if netIP != nil { 85 return netIP.IsLoopback() 86 } 87 return false 88 } 89 90 // Resolver is an interface for net.Resolver 91 type Resolver interface { 92 LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) 93 } 94 95 // GetLocalAddrs returns a list of all network addresses on the local system 96 func GetLocalAddrs() ([]net.IP, error) { 97 var localAddrs []net.IP 98 99 addrs, err := net.InterfaceAddrs() 100 if err != nil { 101 return nil, err 102 } 103 104 for _, addr := range addrs { 105 ip, _, err := netutils.ParseCIDRSloppy(addr.String()) 106 if err != nil { 107 return nil, err 108 } 109 110 localAddrs = append(localAddrs, ip) 111 } 112 113 return localAddrs, nil 114 } 115 116 // GetLocalAddrSet return a local IPSet. 117 // If failed to get local addr, will assume no local ips. 118 func GetLocalAddrSet() netutils.IPSet { 119 localAddrs, err := GetLocalAddrs() 120 if err != nil { 121 klog.ErrorS(err, "Failed to get local addresses assuming no local IPs") 122 } else if len(localAddrs) == 0 { 123 klog.InfoS("No local addresses were found") 124 } 125 126 localAddrSet := netutils.IPSet{} 127 localAddrSet.Insert(localAddrs...) 128 return localAddrSet 129 } 130 131 // ShouldSkipService checks if a given service should skip proxying 132 func ShouldSkipService(service *v1.Service) bool { 133 // if ClusterIP is "None" or empty, skip proxying 134 if !helper.IsServiceIPSet(service) { 135 klog.V(3).InfoS("Skipping service due to cluster IP", "service", klog.KObj(service), "clusterIP", service.Spec.ClusterIP) 136 return true 137 } 138 // Even if ClusterIP is set, ServiceTypeExternalName services don't get proxied 139 if service.Spec.Type == v1.ServiceTypeExternalName { 140 klog.V(3).InfoS("Skipping service due to Type=ExternalName", "service", klog.KObj(service)) 141 return true 142 } 143 return false 144 } 145 146 // AddressSet validates the addresses in the slice using the "isValid" function. 147 // Addresses that pass the validation are returned as a string Set. 148 func AddressSet(isValid func(ip net.IP) bool, addrs []net.Addr) sets.Set[string] { 149 ips := sets.New[string]() 150 for _, a := range addrs { 151 var ip net.IP 152 switch v := a.(type) { 153 case *net.IPAddr: 154 ip = v.IP 155 case *net.IPNet: 156 ip = v.IP 157 default: 158 continue 159 } 160 if isValid(ip) { 161 ips.Insert(ip.String()) 162 } 163 } 164 return ips 165 } 166 167 // LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event. 168 func LogAndEmitIncorrectIPVersionEvent(recorder events.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) { 169 errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName) 170 klog.ErrorS(nil, "Incorrect IP version", "service", klog.KRef(svcNamespace, svcName), "field", fieldName, "value", fieldValue) 171 if recorder != nil { 172 recorder.Eventf( 173 &v1.ObjectReference{ 174 Kind: "Service", 175 Name: svcName, 176 Namespace: svcNamespace, 177 UID: svcUID, 178 }, nil, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", "GatherEndpoints", errMsg) 179 } 180 } 181 182 // MapIPsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6) 183 func MapIPsByIPFamily(ipStrings []string) map[v1.IPFamily][]string { 184 ipFamilyMap := map[v1.IPFamily][]string{} 185 for _, ip := range ipStrings { 186 // Handle only the valid IPs 187 if ipFamily := GetIPFamilyFromIP(ip); ipFamily != v1.IPFamilyUnknown { 188 ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], ip) 189 } else { 190 // this function is called in multiple places. All of which 191 // have sanitized data. Except the case of ExternalIPs which is 192 // not validated by api-server. Specifically empty strings 193 // validation. Which yields into a lot of bad error logs. 194 // check for empty string 195 if len(strings.TrimSpace(ip)) != 0 { 196 klog.ErrorS(nil, "Skipping invalid IP", "ip", ip) 197 198 } 199 } 200 } 201 return ipFamilyMap 202 } 203 204 // MapCIDRsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6) 205 func MapCIDRsByIPFamily(cidrStrings []string) map[v1.IPFamily][]string { 206 ipFamilyMap := map[v1.IPFamily][]string{} 207 for _, cidr := range cidrStrings { 208 // Handle only the valid CIDRs 209 if ipFamily := getIPFamilyFromCIDR(cidr); ipFamily != v1.IPFamilyUnknown { 210 ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], cidr) 211 } else { 212 klog.ErrorS(nil, "Skipping invalid CIDR", "cidr", cidr) 213 } 214 } 215 return ipFamilyMap 216 } 217 218 // GetIPFamilyFromIP Returns the IP family of ipStr, or IPFamilyUnknown if ipStr can't be parsed as an IP 219 func GetIPFamilyFromIP(ipStr string) v1.IPFamily { 220 return convertToV1IPFamily(netutils.IPFamilyOfString(ipStr)) 221 } 222 223 // Returns the IP family of cidrStr, or IPFamilyUnknown if cidrStr can't be parsed as a CIDR 224 func getIPFamilyFromCIDR(cidrStr string) v1.IPFamily { 225 return convertToV1IPFamily(netutils.IPFamilyOfCIDRString(cidrStr)) 226 } 227 228 // Convert netutils.IPFamily to v1.IPFamily 229 func convertToV1IPFamily(ipFamily netutils.IPFamily) v1.IPFamily { 230 switch ipFamily { 231 case netutils.IPv4: 232 return v1.IPv4Protocol 233 case netutils.IPv6: 234 return v1.IPv6Protocol 235 } 236 237 return v1.IPFamilyUnknown 238 } 239 240 // OtherIPFamily returns the other ip family 241 func OtherIPFamily(ipFamily v1.IPFamily) v1.IPFamily { 242 if ipFamily == v1.IPv6Protocol { 243 return v1.IPv4Protocol 244 } 245 246 return v1.IPv6Protocol 247 } 248 249 // AppendPortIfNeeded appends the given port to IP address unless it is already in 250 // "ipv4:port" or "[ipv6]:port" format. 251 func AppendPortIfNeeded(addr string, port int32) string { 252 // Return if address is already in "ipv4:port" or "[ipv6]:port" format. 253 if _, _, err := net.SplitHostPort(addr); err == nil { 254 return addr 255 } 256 257 // Simply return for invalid case. This should be caught by validation instead. 258 ip := netutils.ParseIPSloppy(addr) 259 if ip == nil { 260 return addr 261 } 262 263 // Append port to address. 264 if ip.To4() != nil { 265 return fmt.Sprintf("%s:%d", addr, port) 266 } 267 return fmt.Sprintf("[%s]:%d", addr, port) 268 } 269 270 // ShuffleStrings copies strings from the specified slice into a copy in random 271 // order. It returns a new slice. 272 func ShuffleStrings(s []string) []string { 273 if s == nil { 274 return nil 275 } 276 shuffled := make([]string, len(s)) 277 perm := utilrand.Perm(len(s)) 278 for i, j := range perm { 279 shuffled[j] = s[i] 280 } 281 return shuffled 282 } 283 284 // EnsureSysctl sets a kernel sysctl to a given numeric value. 285 func EnsureSysctl(sysctl utilsysctl.Interface, name string, newVal int) error { 286 if oldVal, _ := sysctl.GetSysctl(name); oldVal != newVal { 287 if err := sysctl.SetSysctl(name, newVal); err != nil { 288 return fmt.Errorf("can't set sysctl %s to %d: %v", name, newVal, err) 289 } 290 klog.V(1).InfoS("Changed sysctl", "name", name, "before", oldVal, "after", newVal) 291 } 292 return nil 293 } 294 295 // GetClusterIPByFamily returns a service clusterip by family 296 func GetClusterIPByFamily(ipFamily v1.IPFamily, service *v1.Service) string { 297 // allowing skew 298 if len(service.Spec.IPFamilies) == 0 { 299 if len(service.Spec.ClusterIP) == 0 || service.Spec.ClusterIP == v1.ClusterIPNone { 300 return "" 301 } 302 303 IsIPv6Family := (ipFamily == v1.IPv6Protocol) 304 if IsIPv6Family == netutils.IsIPv6String(service.Spec.ClusterIP) { 305 return service.Spec.ClusterIP 306 } 307 308 return "" 309 } 310 311 for idx, family := range service.Spec.IPFamilies { 312 if family == ipFamily { 313 if idx < len(service.Spec.ClusterIPs) { 314 return service.Spec.ClusterIPs[idx] 315 } 316 } 317 } 318 319 return "" 320 } 321 322 // RevertPorts is closing ports in replacementPortsMap but not in originalPortsMap. In other words, it only 323 // closes the ports opened in this sync. 324 func RevertPorts(replacementPortsMap, originalPortsMap map[netutils.LocalPort]netutils.Closeable) { 325 for k, v := range replacementPortsMap { 326 // Only close newly opened local ports - leave ones that were open before this update 327 if originalPortsMap[k] == nil { 328 klog.V(2).InfoS("Closing local port", "port", k.String()) 329 v.Close() 330 } 331 } 332 } 333 334 func IsVIPMode(ing v1.LoadBalancerIngress) bool { 335 if !utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) { 336 return true // backwards compat 337 } 338 if ing.IPMode == nil { 339 return true 340 } 341 return *ing.IPMode == v1.LoadBalancerIPModeVIP 342 }