...

Source file src/k8s.io/kubernetes/pkg/proxy/util/utils.go

Documentation: k8s.io/kubernetes/pkg/proxy/util

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

View as plain text