k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/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 "fmt" 21 "net" 22 "strings" 23 24 v1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/types" 26 "k8s.io/apimachinery/pkg/util/sets" 27 utilfeature "k8s.io/apiserver/pkg/util/feature" 28 "k8s.io/client-go/tools/events" 29 utilsysctl "k8s.io/component-helpers/node/util/sysctl" 30 "k8s.io/klog/v2" 31 "k8s.io/kubernetes/pkg/apis/core/v1/helper" 32 "k8s.io/kubernetes/pkg/features" 33 netutils "k8s.io/utils/net" 34 ) 35 36 const ( 37 // IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space 38 IPv4ZeroCIDR = "0.0.0.0/0" 39 40 // IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space 41 IPv6ZeroCIDR = "::/0" 42 ) 43 44 // isValidEndpoint checks that the given host / port pair are valid endpoint 45 func isValidEndpoint(host string, port int) bool { 46 return host != "" && port > 0 47 } 48 49 // IsZeroCIDR checks whether the input CIDR string is either 50 // the IPv4 or IPv6 zero CIDR 51 func IsZeroCIDR(cidr string) bool { 52 if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR { 53 return true 54 } 55 return false 56 } 57 58 // ShouldSkipService checks if a given service should skip proxying 59 func ShouldSkipService(service *v1.Service) bool { 60 // if ClusterIP is "None" or empty, skip proxying 61 if !helper.IsServiceIPSet(service) { 62 klog.V(3).InfoS("Skipping service due to cluster IP", "service", klog.KObj(service), "clusterIP", service.Spec.ClusterIP) 63 return true 64 } 65 // Even if ClusterIP is set, ServiceTypeExternalName services don't get proxied 66 if service.Spec.Type == v1.ServiceTypeExternalName { 67 klog.V(3).InfoS("Skipping service due to Type=ExternalName", "service", klog.KObj(service)) 68 return true 69 } 70 return false 71 } 72 73 // AddressSet validates the addresses in the slice using the "isValid" function. 74 // Addresses that pass the validation are returned as a string Set. 75 func AddressSet(isValid func(ip net.IP) bool, addrs []net.Addr) sets.Set[string] { 76 ips := sets.New[string]() 77 for _, a := range addrs { 78 var ip net.IP 79 switch v := a.(type) { 80 case *net.IPAddr: 81 ip = v.IP 82 case *net.IPNet: 83 ip = v.IP 84 default: 85 continue 86 } 87 if isValid(ip) { 88 ips.Insert(ip.String()) 89 } 90 } 91 return ips 92 } 93 94 // LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event. 95 func LogAndEmitIncorrectIPVersionEvent(recorder events.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) { 96 errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName) 97 klog.ErrorS(nil, "Incorrect IP version", "service", klog.KRef(svcNamespace, svcName), "field", fieldName, "value", fieldValue) 98 if recorder != nil { 99 recorder.Eventf( 100 &v1.ObjectReference{ 101 Kind: "Service", 102 Name: svcName, 103 Namespace: svcNamespace, 104 UID: svcUID, 105 }, nil, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", "GatherEndpoints", errMsg) 106 } 107 } 108 109 // MapIPsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6) 110 func MapIPsByIPFamily(ipStrings []string) map[v1.IPFamily][]net.IP { 111 ipFamilyMap := map[v1.IPFamily][]net.IP{} 112 for _, ipStr := range ipStrings { 113 ip := netutils.ParseIPSloppy(ipStr) 114 if ip != nil { 115 // Since ip is parsed ok, GetIPFamilyFromIP will never return v1.IPFamilyUnknown 116 ipFamily := GetIPFamilyFromIP(ip) 117 ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], ip) 118 } else { 119 // ExternalIPs may not be validated by the api-server. 120 // Specifically empty strings validation, which yields into a lot 121 // of bad error logs. 122 if len(strings.TrimSpace(ipStr)) != 0 { 123 klog.ErrorS(nil, "Skipping invalid IP", "ip", ipStr) 124 } 125 } 126 } 127 return ipFamilyMap 128 } 129 130 // MapCIDRsByIPFamily maps a slice of CIDRs to their respective IP families (v4 or v6) 131 func MapCIDRsByIPFamily(cidrsStrings []string) map[v1.IPFamily][]*net.IPNet { 132 ipFamilyMap := map[v1.IPFamily][]*net.IPNet{} 133 for _, cidrStrUntrimmed := range cidrsStrings { 134 cidrStr := strings.TrimSpace(cidrStrUntrimmed) 135 _, cidr, err := netutils.ParseCIDRSloppy(cidrStr) 136 if err != nil { 137 // Ignore empty strings. Same as in MapIPsByIPFamily 138 if len(cidrStr) != 0 { 139 klog.ErrorS(err, "Invalid CIDR ignored", "CIDR", cidrStr) 140 } 141 continue 142 } 143 // since we just succefully parsed the CIDR, IPFamilyOfCIDR will never return "IPFamilyUnknown" 144 ipFamily := convertToV1IPFamily(netutils.IPFamilyOfCIDR(cidr)) 145 ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], cidr) 146 } 147 return ipFamilyMap 148 } 149 150 // GetIPFamilyFromIP Returns the IP family of ipStr, or IPFamilyUnknown if ipStr can't be parsed as an IP 151 func GetIPFamilyFromIP(ip net.IP) v1.IPFamily { 152 return convertToV1IPFamily(netutils.IPFamilyOf(ip)) 153 } 154 155 // Convert netutils.IPFamily to v1.IPFamily 156 func convertToV1IPFamily(ipFamily netutils.IPFamily) v1.IPFamily { 157 switch ipFamily { 158 case netutils.IPv4: 159 return v1.IPv4Protocol 160 case netutils.IPv6: 161 return v1.IPv6Protocol 162 } 163 164 return v1.IPFamilyUnknown 165 } 166 167 // OtherIPFamily returns the other ip family 168 func OtherIPFamily(ipFamily v1.IPFamily) v1.IPFamily { 169 if ipFamily == v1.IPv6Protocol { 170 return v1.IPv4Protocol 171 } 172 173 return v1.IPv6Protocol 174 } 175 176 // AppendPortIfNeeded appends the given port to IP address unless it is already in 177 // "ipv4:port" or "[ipv6]:port" format. 178 func AppendPortIfNeeded(addr string, port int32) string { 179 // Return if address is already in "ipv4:port" or "[ipv6]:port" format. 180 if _, _, err := net.SplitHostPort(addr); err == nil { 181 return addr 182 } 183 184 // Simply return for invalid case. This should be caught by validation instead. 185 ip := netutils.ParseIPSloppy(addr) 186 if ip == nil { 187 return addr 188 } 189 190 // Append port to address. 191 if ip.To4() != nil { 192 return fmt.Sprintf("%s:%d", addr, port) 193 } 194 return fmt.Sprintf("[%s]:%d", addr, port) 195 } 196 197 // EnsureSysctl sets a kernel sysctl to a given numeric value. 198 func EnsureSysctl(sysctl utilsysctl.Interface, name string, newVal int) error { 199 if oldVal, _ := sysctl.GetSysctl(name); oldVal != newVal { 200 if err := sysctl.SetSysctl(name, newVal); err != nil { 201 return fmt.Errorf("can't set sysctl %s to %d: %v", name, newVal, err) 202 } 203 klog.V(1).InfoS("Changed sysctl", "name", name, "before", oldVal, "after", newVal) 204 } 205 return nil 206 } 207 208 // GetClusterIPByFamily returns a service clusterip by family 209 func GetClusterIPByFamily(ipFamily v1.IPFamily, service *v1.Service) string { 210 // allowing skew 211 if len(service.Spec.IPFamilies) == 0 { 212 if len(service.Spec.ClusterIP) == 0 || service.Spec.ClusterIP == v1.ClusterIPNone { 213 return "" 214 } 215 216 IsIPv6Family := (ipFamily == v1.IPv6Protocol) 217 if IsIPv6Family == netutils.IsIPv6String(service.Spec.ClusterIP) { 218 return service.Spec.ClusterIP 219 } 220 221 return "" 222 } 223 224 for idx, family := range service.Spec.IPFamilies { 225 if family == ipFamily { 226 if idx < len(service.Spec.ClusterIPs) { 227 return service.Spec.ClusterIPs[idx] 228 } 229 } 230 } 231 232 return "" 233 } 234 235 func IsVIPMode(ing v1.LoadBalancerIngress) bool { 236 if !utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) { 237 return true // backwards compat 238 } 239 if ing.IPMode == nil { 240 return true 241 } 242 return *ing.IPMode == v1.LoadBalancerIPModeVIP 243 }