...

Source file src/k8s.io/kubernetes/pkg/proxy/ipvs/ipset.go

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

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

View as plain text