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  }