k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/proxy/ipvs/ipset.go (about) 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 }