github.com/cilium/cilium@v1.16.2/pkg/ipam/cidrset/cidr_set.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 // Copyright The Kubernetes Authors. 4 5 package cidrset 6 7 import ( 8 "encoding/binary" 9 "errors" 10 "fmt" 11 "math/big" 12 "math/bits" 13 "net" 14 "net/netip" 15 16 "go4.org/netipx" 17 18 "github.com/cilium/cilium/pkg/lock" 19 ) 20 21 // CidrSet manages a set of CIDR ranges from which blocks of IPs can 22 // be allocated from. 23 type CidrSet struct { 24 lock.Mutex 25 // clusterCIDR is the CIDR assigned to the cluster 26 clusterCIDR *net.IPNet 27 // clusterMaskSize is the mask size, in bits, assigned to the cluster 28 // caches the mask size to avoid the penalty of calling clusterCIDR.Mask.Size() 29 clusterMaskSize int 30 // nodeMask is the network mask assigned to the nodes 31 nodeMask net.IPMask 32 // nodeMaskSize is the mask size, in bits,assigned to the nodes 33 // caches the mask size to avoid the penalty of calling nodeMask.Size() 34 nodeMaskSize int 35 // maxCIDRs is the maximum number of CIDRs that can be allocated 36 maxCIDRs int 37 // allocatedCIDRs counts the number of CIDRs allocated 38 allocatedCIDRs int 39 // nextCandidate points to the next CIDR that should be free 40 nextCandidate int 41 // used is a bitmap used to track the CIDRs allocated 42 used big.Int 43 } 44 45 const ( 46 // The subnet mask size cannot be greater than 16 more than the cluster mask size 47 // TODO: https://github.com/kubernetes/kubernetes/issues/44918 48 // clusterSubnetMaxDiff limited to 16 due to the uncompressed bitmap 49 // Due to this limitation the subnet mask for IPv6 cluster cidr needs to be >= 48 50 // as default mask size for IPv6 is 64. 51 clusterSubnetMaxDiff = 16 52 // halfIPv6Len is the half of the IPv6 length 53 halfIPv6Len = net.IPv6len / 2 54 // the default subnet mask should be lower or equal to the max ipv4 netmask 55 maxSubNetMaskSizeIPv4 = 32 56 // the default subnet mask should be lower or equal to the max ipv6 netmask 57 maxSubNetMaskSizeIPv6 = 128 58 ) 59 60 var ( 61 // ErrCIDRRangeNoCIDRsRemaining occurs when there is no more space 62 // to allocate CIDR ranges. 63 ErrCIDRRangeNoCIDRsRemaining = errors.New( 64 "CIDR allocation failed; there are no remaining CIDRs left to allocate in the accepted range") 65 // ErrCIDRSetSubNetTooBig occurs when the subnet mask size is too 66 // big compared to the CIDR mask size. 67 ErrCIDRSetSubNetTooBig = errors.New( 68 "New CIDR set failed; the node CIDR size is too big") 69 // ErrSubNetMaskSizeInvalid occurs when the subnet mask size is invalid: 70 // bigger than 32 for IPv4 and bigger than 128 for IPv6 71 ErrSubNetMaskSizeInvalid = fmt.Errorf( 72 "SubNetMask is invalid, should be lower or equal to %d for IPv4 and to %d for IPv6", 73 maxSubNetMaskSizeIPv4, maxSubNetMaskSizeIPv6) 74 ) 75 76 // NewCIDRSet creates a new CidrSet. 77 func NewCIDRSet(clusterCIDR *net.IPNet, subNetMaskSize int) (*CidrSet, error) { 78 clusterMask := clusterCIDR.Mask 79 clusterMaskSize, bits := clusterMask.Size() 80 81 if clusterCIDR.IP.To4() == nil { 82 if subNetMaskSize > maxSubNetMaskSizeIPv6 { 83 return nil, ErrSubNetMaskSizeInvalid 84 } 85 if subNetMaskSize-clusterMaskSize > clusterSubnetMaxDiff { 86 return nil, ErrCIDRSetSubNetTooBig 87 } 88 } else if subNetMaskSize > maxSubNetMaskSizeIPv4 { 89 return nil, ErrSubNetMaskSizeInvalid 90 } 91 maxCIDRs := 1 << uint32(subNetMaskSize-clusterMaskSize) 92 return &CidrSet{ 93 clusterCIDR: clusterCIDR, 94 nodeMask: net.CIDRMask(subNetMaskSize, bits), 95 clusterMaskSize: clusterMaskSize, 96 maxCIDRs: maxCIDRs, 97 nodeMaskSize: subNetMaskSize, 98 }, nil 99 } 100 101 func (s *CidrSet) String() string { 102 return fmt.Sprintf("clusterCIDR: %s, nodeMask: %d", s.clusterCIDR.String(), s.nodeMaskSize) 103 } 104 105 func (s *CidrSet) indexToCIDRBlock(index int) *net.IPNet { 106 var ip []byte 107 switch /*v4 or v6*/ { 108 case s.clusterCIDR.IP.To4() != nil: 109 { 110 j := uint32(index) << uint32(32-s.nodeMaskSize) 111 ipInt := (binary.BigEndian.Uint32(s.clusterCIDR.IP)) | j 112 ip = make([]byte, net.IPv4len) 113 binary.BigEndian.PutUint32(ip, ipInt) 114 } 115 case s.clusterCIDR.IP.To16() != nil: 116 { 117 // leftClusterIP | rightClusterIP 118 // 2001:0DB8:1234:0000:0000:0000:0000:0000 119 const v6NBits = 128 120 const halfV6NBits = v6NBits / 2 121 leftClusterIP := binary.BigEndian.Uint64(s.clusterCIDR.IP[:halfIPv6Len]) 122 rightClusterIP := binary.BigEndian.Uint64(s.clusterCIDR.IP[halfIPv6Len:]) 123 124 ip = make([]byte, net.IPv6len) 125 126 if s.nodeMaskSize <= halfV6NBits { 127 // We only care about left side IP 128 leftClusterIP |= uint64(index) << uint(halfV6NBits-s.nodeMaskSize) 129 } else { 130 if s.clusterMaskSize < halfV6NBits { 131 // see how many bits are needed to reach the left side 132 btl := uint(s.nodeMaskSize - halfV6NBits) 133 indexMaxBit := uint(64 - bits.LeadingZeros64(uint64(index))) 134 if indexMaxBit > btl { 135 leftClusterIP |= uint64(index) >> btl 136 } 137 } 138 // the right side will be calculated the same way either the 139 // subNetMaskSize affects both left and right sides 140 rightClusterIP |= uint64(index) << uint(v6NBits-s.nodeMaskSize) 141 } 142 binary.BigEndian.PutUint64(ip[:halfIPv6Len], leftClusterIP) 143 binary.BigEndian.PutUint64(ip[halfIPv6Len:], rightClusterIP) 144 } 145 } 146 return &net.IPNet{ 147 IP: ip, 148 Mask: s.nodeMask, 149 } 150 } 151 152 // IsFull returns true if CidrSet does not have any more available CIDRs. 153 func (s *CidrSet) IsFull() bool { 154 s.Lock() 155 defer s.Unlock() 156 return s.allocatedCIDRs == s.maxCIDRs 157 } 158 159 // AllocateNext allocates the next free CIDR range. This will set the range 160 // as occupied and return the allocated range. 161 func (s *CidrSet) AllocateNext() (*net.IPNet, error) { 162 s.Lock() 163 defer s.Unlock() 164 165 if s.allocatedCIDRs == s.maxCIDRs { 166 return nil, ErrCIDRRangeNoCIDRsRemaining 167 } 168 candidate := s.nextCandidate 169 var i int 170 for i = 0; i < s.maxCIDRs; i++ { 171 if s.used.Bit(candidate) == 0 { 172 break 173 } 174 candidate = (candidate + 1) % s.maxCIDRs 175 } 176 177 s.nextCandidate = (candidate + 1) % s.maxCIDRs 178 s.used.SetBit(&s.used, candidate, 1) 179 s.allocatedCIDRs++ 180 181 return s.indexToCIDRBlock(candidate), nil 182 } 183 184 // InRange returns true if the given CIDR is inside the range of the allocatable 185 // CidrSet. 186 func (s *CidrSet) InRange(cidr *net.IPNet) bool { 187 return s.clusterCIDR.Contains(cidr.IP.Mask(s.clusterCIDR.Mask)) || cidr.Contains(s.clusterCIDR.IP.Mask(cidr.Mask)) 188 } 189 190 // IsClusterCIDR returns true if the given CIDR is equal to this CidrSet's cluster CIDR. 191 func (s *CidrSet) IsClusterCIDR(cidr netip.Prefix) bool { 192 clusterPrefix, _ := netipx.FromStdIPNet(s.clusterCIDR) 193 return cidr == clusterPrefix 194 } 195 196 // Prefix returns the CidrSet's prefix. 197 func (s *CidrSet) Prefix() netip.Prefix { 198 prefix, _ := netipx.FromStdIPNet(s.clusterCIDR) 199 return prefix 200 } 201 202 func (s *CidrSet) getBeginningAndEndIndices(cidr *net.IPNet) (begin, end int, err error) { 203 if cidr == nil { 204 return -1, -1, fmt.Errorf("error getting indices for cluster cidr %v, cidr is nil", s.clusterCIDR) 205 } 206 begin, end = 0, s.maxCIDRs-1 207 cidrMask := cidr.Mask 208 maskSize, _ := cidrMask.Size() 209 var ipSize int 210 211 if !s.InRange(cidr) { 212 return -1, -1, fmt.Errorf("cidr %v is out the range of cluster cidr %v", cidr, s.clusterCIDR) 213 } 214 215 if s.clusterMaskSize < maskSize { 216 217 ipSize = net.IPv4len 218 if cidr.IP.To4() == nil { 219 ipSize = net.IPv6len 220 } 221 begin, err = s.getIndexForIP(cidr.IP.Mask(s.nodeMask)) 222 if err != nil { 223 return -1, -1, err 224 } 225 ip := make([]byte, ipSize) 226 if cidr.IP.To4() != nil { 227 ipInt := binary.BigEndian.Uint32(cidr.IP) | (^binary.BigEndian.Uint32(cidr.Mask)) 228 binary.BigEndian.PutUint32(ip, ipInt) 229 } else { 230 // ipIntLeft | ipIntRight 231 // 2001:0DB8:1234:0000:0000:0000:0000:0000 232 ipIntLeft := binary.BigEndian.Uint64(cidr.IP[:net.IPv6len/2]) | (^binary.BigEndian.Uint64(cidr.Mask[:net.IPv6len/2])) 233 ipIntRight := binary.BigEndian.Uint64(cidr.IP[net.IPv6len/2:]) | (^binary.BigEndian.Uint64(cidr.Mask[net.IPv6len/2:])) 234 binary.BigEndian.PutUint64(ip[:net.IPv6len/2], ipIntLeft) 235 binary.BigEndian.PutUint64(ip[net.IPv6len/2:], ipIntRight) 236 } 237 end, err = s.getIndexForIP(net.IP(ip).Mask(s.nodeMask)) 238 if err != nil { 239 return -1, -1, err 240 } 241 } 242 return begin, end, nil 243 } 244 245 // IsAllocated verifies if the given CIDR is allocated 246 func (s *CidrSet) IsAllocated(cidr *net.IPNet) (bool, error) { 247 begin, end, err := s.getBeginningAndEndIndices(cidr) 248 if err != nil { 249 return false, err 250 } 251 s.Lock() 252 defer s.Unlock() 253 for i := begin; i <= end; i++ { 254 if s.used.Bit(i) == 0 { 255 return false, nil 256 } 257 } 258 return true, nil 259 } 260 261 // Release releases the given CIDR range. 262 func (s *CidrSet) Release(cidr *net.IPNet) error { 263 begin, end, err := s.getBeginningAndEndIndices(cidr) 264 if err != nil { 265 return err 266 } 267 s.Lock() 268 defer s.Unlock() 269 for i := begin; i <= end; i++ { 270 // Only change the counters if we change the bit to prevent 271 // double counting. 272 if s.used.Bit(i) != 0 { 273 s.used.SetBit(&s.used, i, 0) 274 s.allocatedCIDRs-- 275 } 276 } 277 return nil 278 } 279 280 // Occupy marks the given CIDR range as used. Occupy succeeds even if the CIDR 281 // range was previously used. 282 func (s *CidrSet) Occupy(cidr *net.IPNet) (err error) { 283 begin, end, err := s.getBeginningAndEndIndices(cidr) 284 if err != nil { 285 return err 286 } 287 s.Lock() 288 defer s.Unlock() 289 for i := begin; i <= end; i++ { 290 // Only change the counters if we change the bit to prevent 291 // double counting. 292 if s.used.Bit(i) == 0 { 293 s.used.SetBit(&s.used, i, 1) 294 s.allocatedCIDRs++ 295 } 296 } 297 298 return nil 299 } 300 301 func (s *CidrSet) getIndexForIP(ip net.IP) (int, error) { 302 if ip.To4() != nil { 303 cidrIndex := (binary.BigEndian.Uint32(s.clusterCIDR.IP) ^ binary.BigEndian.Uint32(ip.To4())) >> uint32(32-s.nodeMaskSize) 304 if cidrIndex >= uint32(s.maxCIDRs) { 305 return 0, fmt.Errorf("CIDR: %v/%v is out of the range of CIDR allocator", ip, s.nodeMaskSize) 306 } 307 return int(cidrIndex), nil 308 } 309 if ip.To16() != nil { 310 bigIP := big.NewInt(0).SetBytes(s.clusterCIDR.IP) 311 bigIP = bigIP.Xor(bigIP, big.NewInt(0).SetBytes(ip)) 312 cidrIndexBig := bigIP.Rsh(bigIP, uint(net.IPv6len*8-s.nodeMaskSize)) 313 cidrIndex := cidrIndexBig.Uint64() 314 if cidrIndex >= uint64(s.maxCIDRs) { 315 return 0, fmt.Errorf("CIDR: %v/%v is out of the range of CIDR allocator", ip, s.nodeMaskSize) 316 } 317 return int(cidrIndex), nil 318 } 319 320 return 0, fmt.Errorf("invalid IP: %v", ip) 321 }