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 }