k8s.io/kubernetes@v1.29.3/pkg/proxy/ipvs/ipset.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 ipvs
    18  
    19  import (
    20  	"k8s.io/apimachinery/pkg/util/sets"
    21  	utilversion "k8s.io/apimachinery/pkg/util/version"
    22  	utilipset "k8s.io/kubernetes/pkg/proxy/ipvs/ipset"
    23  
    24  	"fmt"
    25  	"strings"
    26  
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  const (
    31  	// MinIPSetCheckVersion is the min ipset version we need.  IPv6 is supported in ipset 6.x
    32  	MinIPSetCheckVersion = "6.0"
    33  
    34  	kubeLoopBackIPSetComment = "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose"
    35  	kubeLoopBackIPSet        = "KUBE-LOOP-BACK"
    36  
    37  	kubeClusterIPSetComment = "Kubernetes service cluster ip + port for masquerade purpose"
    38  	kubeClusterIPSet        = "KUBE-CLUSTER-IP"
    39  
    40  	kubeExternalIPSetComment = "Kubernetes service external ip + port for masquerade and filter purpose"
    41  	kubeExternalIPSet        = "KUBE-EXTERNAL-IP"
    42  
    43  	kubeExternalIPLocalSetComment = "Kubernetes service external ip + port with externalTrafficPolicy=local"
    44  	kubeExternalIPLocalSet        = "KUBE-EXTERNAL-IP-LOCAL"
    45  
    46  	kubeLoadBalancerSetComment = "Kubernetes service lb portal"
    47  	kubeLoadBalancerSet        = "KUBE-LOAD-BALANCER"
    48  
    49  	kubeLoadBalancerLocalSetComment = "Kubernetes service load balancer ip + port with externalTrafficPolicy=local"
    50  	kubeLoadBalancerLocalSet        = "KUBE-LOAD-BALANCER-LOCAL"
    51  
    52  	kubeLoadBalancerFWSetComment = "Kubernetes service load balancer ip + port for load balancer with sourceRange"
    53  	kubeLoadBalancerFWSet        = "KUBE-LOAD-BALANCER-FW"
    54  
    55  	kubeLoadBalancerSourceIPSetComment = "Kubernetes service load balancer ip + port + source IP for packet filter purpose"
    56  	kubeLoadBalancerSourceIPSet        = "KUBE-LOAD-BALANCER-SOURCE-IP"
    57  
    58  	kubeLoadBalancerSourceCIDRSetComment = "Kubernetes service load balancer ip + port + source cidr for packet filter purpose"
    59  	kubeLoadBalancerSourceCIDRSet        = "KUBE-LOAD-BALANCER-SOURCE-CIDR"
    60  
    61  	kubeNodePortSetTCPComment = "Kubernetes nodeport TCP port for masquerade purpose"
    62  	kubeNodePortSetTCP        = "KUBE-NODE-PORT-TCP"
    63  
    64  	kubeNodePortLocalSetTCPComment = "Kubernetes nodeport TCP port with externalTrafficPolicy=local"
    65  	kubeNodePortLocalSetTCP        = "KUBE-NODE-PORT-LOCAL-TCP"
    66  
    67  	kubeNodePortSetUDPComment = "Kubernetes nodeport UDP port for masquerade purpose"
    68  	kubeNodePortSetUDP        = "KUBE-NODE-PORT-UDP"
    69  
    70  	kubeNodePortLocalSetUDPComment = "Kubernetes nodeport UDP port with externalTrafficPolicy=local"
    71  	kubeNodePortLocalSetUDP        = "KUBE-NODE-PORT-LOCAL-UDP"
    72  
    73  	kubeNodePortSetSCTPComment = "Kubernetes nodeport SCTP port for masquerade purpose with type 'hash ip:port'"
    74  	kubeNodePortSetSCTP        = "KUBE-NODE-PORT-SCTP-HASH"
    75  
    76  	kubeNodePortLocalSetSCTPComment = "Kubernetes nodeport SCTP port with externalTrafficPolicy=local with type 'hash ip:port'"
    77  	kubeNodePortLocalSetSCTP        = "KUBE-NODE-PORT-LOCAL-SCTP-HASH"
    78  
    79  	kubeHealthCheckNodePortSetComment = "Kubernetes health check node port"
    80  	kubeHealthCheckNodePortSet        = "KUBE-HEALTH-CHECK-NODE-PORT"
    81  
    82  	kubeIPVSSetComment = "Addresses on the ipvs interface"
    83  	kubeIPVSSet        = "KUBE-IPVS-IPS"
    84  )
    85  
    86  // IPSetVersioner can query the current ipset version.
    87  type IPSetVersioner interface {
    88  	// returns "X.Y"
    89  	GetVersion() (string, error)
    90  }
    91  
    92  // IPSet wraps util/ipset which is used by IPVS proxier.
    93  type IPSet struct {
    94  	utilipset.IPSet
    95  	// activeEntries is the current active entries of the ipset.
    96  	activeEntries sets.Set[string]
    97  	// handle is the util ipset interface handle.
    98  	handle utilipset.Interface
    99  }
   100  
   101  // NewIPSet initialize a new IPSet struct
   102  func NewIPSet(handle utilipset.Interface, name string, setType utilipset.Type, isIPv6 bool, comment string) *IPSet {
   103  	hashFamily := utilipset.ProtocolFamilyIPV4
   104  	if isIPv6 {
   105  		hashFamily = utilipset.ProtocolFamilyIPV6
   106  		// In dual-stack both ipv4 and ipv6 ipset's can co-exist. To
   107  		// ensure unique names the prefix for ipv6 is changed from
   108  		// "KUBE-" to "KUBE-6-". The "KUBE-" prefix is kept for
   109  		// backward compatibility. The maximum name length of an ipset
   110  		// is 31 characters which must be taken into account.  The
   111  		// ipv4 names are not altered to minimize the risk for
   112  		// problems on upgrades.
   113  		if strings.HasPrefix(name, "KUBE-") {
   114  			name = strings.Replace(name, "KUBE-", "KUBE-6-", 1)
   115  			if len(name) > 31 {
   116  				klog.InfoS("Ipset name truncated", "ipSetName", name, "truncatedName", name[:31])
   117  				name = name[:31]
   118  			}
   119  		}
   120  	}
   121  	set := &IPSet{
   122  		IPSet: utilipset.IPSet{
   123  			Name:       name,
   124  			SetType:    setType,
   125  			HashFamily: hashFamily,
   126  			Comment:    comment,
   127  		},
   128  		activeEntries: sets.New[string](),
   129  		handle:        handle,
   130  	}
   131  	return set
   132  }
   133  
   134  func (set *IPSet) validateEntry(entry *utilipset.Entry) bool {
   135  	return entry.Validate(&set.IPSet)
   136  }
   137  
   138  func (set *IPSet) isEmpty() bool {
   139  	return set.activeEntries.Len() == 0
   140  }
   141  
   142  func (set *IPSet) getComment() string {
   143  	return fmt.Sprintf("\"%s\"", set.Comment)
   144  }
   145  
   146  func (set *IPSet) resetEntries() {
   147  	set.activeEntries = sets.New[string]()
   148  }
   149  
   150  func (set *IPSet) syncIPSetEntries() {
   151  	appliedEntries, err := set.handle.ListEntries(set.Name)
   152  	if err != nil {
   153  		klog.ErrorS(err, "Failed to list ip set entries")
   154  		return
   155  	}
   156  
   157  	// currentIPSetEntries represents Endpoints watched from API Server.
   158  	currentIPSetEntries := sets.New[string]()
   159  	for _, appliedEntry := range appliedEntries {
   160  		currentIPSetEntries.Insert(appliedEntry)
   161  	}
   162  
   163  	if !set.activeEntries.Equal(currentIPSetEntries) {
   164  		// Clean legacy entries
   165  		for _, entry := range currentIPSetEntries.Difference(set.activeEntries).UnsortedList() {
   166  			if err := set.handle.DelEntry(entry, set.Name); err != nil {
   167  				if !utilipset.IsNotFoundError(err) {
   168  					klog.ErrorS(err, "Failed to delete ip set entry from ip set", "ipSetEntry", entry, "ipSet", set.Name)
   169  				}
   170  			} else {
   171  				klog.V(3).InfoS("Successfully deleted legacy ip set entry from ip set", "ipSetEntry", entry, "ipSet", set.Name)
   172  			}
   173  		}
   174  		// Create active entries
   175  		for _, entry := range set.activeEntries.Difference(currentIPSetEntries).UnsortedList() {
   176  			if err := set.handle.AddEntry(entry, &set.IPSet, true); err != nil {
   177  				klog.ErrorS(err, "Failed to add ip set entry to ip set", "ipSetEntry", entry, "ipSet", set.Name)
   178  			} else {
   179  				klog.V(3).InfoS("Successfully added ip set entry to ip set", "ipSetEntry", entry, "ipSet", set.Name)
   180  			}
   181  		}
   182  	}
   183  }
   184  
   185  func ensureIPSet(set *IPSet) error {
   186  	if err := set.handle.CreateSet(&set.IPSet, true); err != nil {
   187  		klog.ErrorS(err, "Failed to make sure existence of ip set", "ipSet", set)
   188  		return err
   189  	}
   190  	return nil
   191  }
   192  
   193  // checkMinVersion checks if ipset current version satisfies required min version
   194  func checkMinVersion(vstring string) bool {
   195  	version, err := utilversion.ParseGeneric(vstring)
   196  	if err != nil {
   197  		klog.ErrorS(err, "Got invalid version string", "versionString", vstring)
   198  		return false
   199  	}
   200  
   201  	minVersion, err := utilversion.ParseGeneric(MinIPSetCheckVersion)
   202  	if err != nil {
   203  		klog.ErrorS(err, "Got invalid version string", "versionString", MinIPSetCheckVersion)
   204  		return false
   205  	}
   206  	return !version.LessThan(minVersion)
   207  }