kubernetes/pkg/proxy/util/utils.go

220 lines
6.4 KiB
Go
Raw Normal View History

/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
2018-01-16 21:23:33 -05:00
"fmt"
"net"
"strconv"
2021-09-23 16:08:09 -04:00
"strings"
2025-01-08 07:00:18 -05:00
"time"
v1 "k8s.io/api/core/v1"
2018-01-16 21:23:33 -05:00
"k8s.io/apimachinery/pkg/util/sets"
utilsysctl "k8s.io/component-helpers/node/util/sysctl"
2024-05-17 09:11:39 -04:00
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
2021-08-19 19:16:14 -04:00
netutils "k8s.io/utils/net"
)
2018-01-16 21:23:33 -05:00
const (
2019-05-09 11:34:56 -04:00
// IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space
2018-01-16 21:23:33 -05:00
IPv4ZeroCIDR = "0.0.0.0/0"
2019-05-09 11:34:56 -04:00
// IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space
2018-01-16 21:23:33 -05:00
IPv6ZeroCIDR = "::/0"
2025-01-08 07:00:18 -05:00
// FullSyncPeriod is iptables and nftables proxier full sync period
FullSyncPeriod = 1 * time.Hour
2018-01-16 21:23:33 -05:00
)
2019-05-09 11:34:56 -04:00
// IsZeroCIDR checks whether the input CIDR string is either
// the IPv4 or IPv6 zero CIDR
func IsZeroCIDR(cidr *net.IPNet) bool {
if cidr == nil {
return false
2018-01-16 21:23:33 -05:00
}
prefixLen, _ := cidr.Mask.Size()
return prefixLen == 0
2018-01-16 21:23:33 -05:00
}
2019-05-09 11:34:56 -04:00
// ShouldSkipService checks if a given service should skip proxying
func ShouldSkipService(service *v1.Service) bool {
// if ClusterIP is "None" or empty, skip proxying
if !helper.IsServiceIPSet(service) {
klog.V(3).InfoS("Skipping service due to cluster IP", "service", klog.KObj(service), "clusterIP", service.Spec.ClusterIP)
return true
}
// Even if ClusterIP is set, ServiceTypeExternalName services don't get proxied
2018-08-15 09:51:19 -04:00
if service.Spec.Type == v1.ServiceTypeExternalName {
klog.V(3).InfoS("Skipping service due to Type=ExternalName", "service", klog.KObj(service))
return true
}
return false
}
2018-01-16 21:23:33 -05:00
// AddressSet validates the addresses in the slice using the "isValid" function.
// Addresses that pass the validation are returned as a string Set.
2023-02-19 12:08:57 -05:00
func AddressSet(isValid func(ip net.IP) bool, addrs []net.Addr) sets.Set[string] {
ips := sets.New[string]()
for _, a := range addrs {
var ip net.IP
switch v := a.(type) {
case *net.IPAddr:
ip = v.IP
case *net.IPNet:
ip = v.IP
default:
continue
}
if isValid(ip) {
ips.Insert(ip.String())
}
}
return ips
}
2020-11-17 02:13:51 -05:00
// MapIPsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6)
func MapIPsByIPFamily(ipStrings []string) map[v1.IPFamily][]net.IP {
ipFamilyMap := map[v1.IPFamily][]net.IP{}
for _, ipStr := range ipStrings {
ip := netutils.ParseIPSloppy(ipStr)
if ip != nil {
// Since ip is parsed ok, GetIPFamilyFromIP will never return v1.IPFamilyUnknown
ipFamily := GetIPFamilyFromIP(ip)
ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], ip)
} else {
// ExternalIPs may not be validated by the api-server.
// Specifically empty strings validation, which yields into a lot
// of bad error logs.
if len(strings.TrimSpace(ipStr)) != 0 {
klog.ErrorS(nil, "Skipping invalid IP", "ip", ipStr)
2021-09-23 16:08:09 -04:00
}
2020-11-17 02:13:51 -05:00
}
}
return ipFamilyMap
}
// MapCIDRsByIPFamily maps a slice of CIDRs to their respective IP families (v4 or v6)
func MapCIDRsByIPFamily(cidrsStrings []string) map[v1.IPFamily][]*net.IPNet {
ipFamilyMap := map[v1.IPFamily][]*net.IPNet{}
for _, cidrStrUntrimmed := range cidrsStrings {
cidrStr := strings.TrimSpace(cidrStrUntrimmed)
_, cidr, err := netutils.ParseCIDRSloppy(cidrStr)
if err != nil {
// Ignore empty strings. Same as in MapIPsByIPFamily
if len(cidrStr) != 0 {
klog.ErrorS(err, "Invalid CIDR ignored", "CIDR", cidrStr)
}
continue
2020-11-17 02:13:51 -05:00
}
// since we just succefully parsed the CIDR, IPFamilyOfCIDR will never return "IPFamilyUnknown"
ipFamily := convertToV1IPFamily(netutils.IPFamilyOfCIDR(cidr))
ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], cidr)
}
return ipFamilyMap
}
2023-07-06 04:48:52 -04:00
// GetIPFamilyFromIP Returns the IP family of ipStr, or IPFamilyUnknown if ipStr can't be parsed as an IP
func GetIPFamilyFromIP(ip net.IP) v1.IPFamily {
return convertToV1IPFamily(netutils.IPFamilyOf(ip))
}
2023-07-06 04:48:52 -04:00
// Convert netutils.IPFamily to v1.IPFamily
func convertToV1IPFamily(ipFamily netutils.IPFamily) v1.IPFamily {
switch ipFamily {
case netutils.IPv4:
return v1.IPv4Protocol
case netutils.IPv6:
return v1.IPv6Protocol
}
2023-07-06 04:48:52 -04:00
return v1.IPFamilyUnknown
2020-11-17 02:13:51 -05:00
}
// OtherIPFamily returns the other ip family
func OtherIPFamily(ipFamily v1.IPFamily) v1.IPFamily {
if ipFamily == v1.IPv6Protocol {
return v1.IPv4Protocol
}
return v1.IPv6Protocol
}
// AppendPortIfNeeded appends the given port to IP address unless it is already in
// "ipv4:port" or "[ipv6]:port" format.
func AppendPortIfNeeded(addr string, port int32) string {
// Return if address is already in "ipv4:port" or "[ipv6]:port" format.
if _, _, err := net.SplitHostPort(addr); err == nil {
return addr
}
// Simply return for invalid case. This should be caught by validation instead.
2021-08-19 19:16:14 -04:00
ip := netutils.ParseIPSloppy(addr)
if ip == nil {
return addr
}
// Append port to address.
return net.JoinHostPort(addr, strconv.Itoa(int(port)))
}
2020-04-10 11:47:03 -04:00
// EnsureSysctl sets a kernel sysctl to a given numeric value.
func EnsureSysctl(sysctl utilsysctl.Interface, name string, newVal int) error {
if oldVal, _ := sysctl.GetSysctl(name); oldVal != newVal {
if err := sysctl.SetSysctl(name, newVal); err != nil {
return fmt.Errorf("can't set sysctl %s to %d: %v", name, newVal, err)
}
klog.V(1).InfoS("Changed sysctl", "name", name, "before", oldVal, "after", newVal)
2020-04-10 11:47:03 -04:00
}
return nil
}
dual stack services (#91824) * api: structure change * api: defaulting, conversion, and validation * [FIX] validation: auto remove second ip/family when service changes to SingleStack * [FIX] api: defaulting, conversion, and validation * api-server: clusterIPs alloc, printers, storage and strategy * [FIX] clusterIPs default on read * alloc: auto remove second ip/family when service changes to SingleStack * api-server: repair loop handling for clusterIPs * api-server: force kubernetes default service into single stack * api-server: tie dualstack feature flag with endpoint feature flag * controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service * [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service * kube-proxy: feature-flag, utils, proxier, and meta proxier * [FIX] kubeproxy: call both proxier at the same time * kubenet: remove forced pod IP sorting * kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy * e2e: fix tests that depends on IPFamily field AND add dual stack tests * e2e: fix expected error message for ClusterIP immutability * add integration tests for dualstack the third phase of dual stack is a very complex change in the API, basically it introduces Dual Stack services. Main changes are: - It pluralizes the Service IPFamily field to IPFamilies, and removes the singular field. - It introduces a new field IPFamilyPolicyType that can take 3 values to express the "dual-stack(mad)ness" of the cluster: SingleStack, PreferDualStack and RequireDualStack - It pluralizes ClusterIP to ClusterIPs. The goal is to add coverage to the services API operations, taking into account the 6 different modes a cluster can have: - single stack: IP4 or IPv6 (as of today) - dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4 * [FIX] add integration tests for dualstack * generated data * generated files Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
// GetClusterIPByFamily returns a service clusterip by family
func GetClusterIPByFamily(ipFamily v1.IPFamily, service *v1.Service) string {
// allowing skew
if len(service.Spec.IPFamilies) == 0 {
if len(service.Spec.ClusterIP) == 0 || service.Spec.ClusterIP == v1.ClusterIPNone {
return ""
}
IsIPv6Family := (ipFamily == v1.IPv6Protocol)
2021-08-19 19:16:14 -04:00
if IsIPv6Family == netutils.IsIPv6String(service.Spec.ClusterIP) {
dual stack services (#91824) * api: structure change * api: defaulting, conversion, and validation * [FIX] validation: auto remove second ip/family when service changes to SingleStack * [FIX] api: defaulting, conversion, and validation * api-server: clusterIPs alloc, printers, storage and strategy * [FIX] clusterIPs default on read * alloc: auto remove second ip/family when service changes to SingleStack * api-server: repair loop handling for clusterIPs * api-server: force kubernetes default service into single stack * api-server: tie dualstack feature flag with endpoint feature flag * controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service * [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service * kube-proxy: feature-flag, utils, proxier, and meta proxier * [FIX] kubeproxy: call both proxier at the same time * kubenet: remove forced pod IP sorting * kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy * e2e: fix tests that depends on IPFamily field AND add dual stack tests * e2e: fix expected error message for ClusterIP immutability * add integration tests for dualstack the third phase of dual stack is a very complex change in the API, basically it introduces Dual Stack services. Main changes are: - It pluralizes the Service IPFamily field to IPFamilies, and removes the singular field. - It introduces a new field IPFamilyPolicyType that can take 3 values to express the "dual-stack(mad)ness" of the cluster: SingleStack, PreferDualStack and RequireDualStack - It pluralizes ClusterIP to ClusterIPs. The goal is to add coverage to the services API operations, taking into account the 6 different modes a cluster can have: - single stack: IP4 or IPv6 (as of today) - dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4 * [FIX] add integration tests for dualstack * generated data * generated files Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
return service.Spec.ClusterIP
}
return ""
}
for idx, family := range service.Spec.IPFamilies {
if family == ipFamily {
if idx < len(service.Spec.ClusterIPs) {
return service.Spec.ClusterIPs[idx]
}
}
}
return ""
}
2023-07-06 04:48:52 -04:00
func IsVIPMode(ing v1.LoadBalancerIngress) bool {
if ing.IPMode == nil {
return true
}
return *ing.IPMode == v1.LoadBalancerIPModeVIP
}