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  }