github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/ipam/allocator.go (about) 1 package ipam 2 3 import ( 4 "fmt" 5 "net" 6 "sort" 7 "sync" 8 9 "github.com/docker/docker/libnetwork/bitseq" 10 "github.com/docker/docker/libnetwork/datastore" 11 "github.com/docker/docker/libnetwork/discoverapi" 12 "github.com/docker/docker/libnetwork/ipamapi" 13 "github.com/docker/docker/libnetwork/ipamutils" 14 "github.com/docker/docker/libnetwork/types" 15 "github.com/sirupsen/logrus" 16 ) 17 18 const ( 19 localAddressSpace = "LocalDefault" 20 globalAddressSpace = "GlobalDefault" 21 // datastore keyes for ipam objects 22 dsConfigKey = "ipam/" + ipamapi.DefaultIPAM + "/config" 23 dsDataKey = "ipam/" + ipamapi.DefaultIPAM + "/data" 24 ) 25 26 // Allocator provides per address space ipv4/ipv6 book keeping 27 type Allocator struct { 28 // Predefined pools for default address spaces 29 // Separate from the addrSpace because they should not be serialized 30 predefined map[string][]*net.IPNet 31 predefinedStartIndices map[string]int 32 // The (potentially serialized) address spaces 33 addrSpaces map[string]*addrSpace 34 // stores []datastore.Datastore 35 // Allocated addresses in each address space's subnet 36 addresses map[SubnetKey]*bitseq.Handle 37 sync.Mutex 38 } 39 40 // NewAllocator returns an instance of libnetwork ipam 41 func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) { 42 a := &Allocator{} 43 44 // Load predefined subnet pools 45 46 a.predefined = map[string][]*net.IPNet{ 47 localAddressSpace: ipamutils.GetLocalScopeDefaultNetworks(), 48 globalAddressSpace: ipamutils.GetGlobalScopeDefaultNetworks(), 49 } 50 51 // Initialize asIndices map 52 a.predefinedStartIndices = make(map[string]int) 53 54 // Initialize bitseq map 55 a.addresses = make(map[SubnetKey]*bitseq.Handle) 56 57 // Initialize address spaces 58 a.addrSpaces = make(map[string]*addrSpace) 59 for _, aspc := range []struct { 60 as string 61 ds datastore.DataStore 62 }{ 63 {localAddressSpace, lcDs}, 64 {globalAddressSpace, glDs}, 65 } { 66 a.initializeAddressSpace(aspc.as, aspc.ds) 67 } 68 69 return a, nil 70 } 71 72 func (a *Allocator) refresh(as string) error { 73 aSpace, err := a.getAddressSpaceFromStore(as) 74 if err != nil { 75 return types.InternalErrorf("error getting pools config from store: %v", err) 76 } 77 78 if aSpace == nil { 79 return nil 80 } 81 82 a.Lock() 83 a.addrSpaces[as] = aSpace 84 a.Unlock() 85 86 return nil 87 } 88 89 func (a *Allocator) updateBitMasks(aSpace *addrSpace) error { 90 var inserterList []func() error 91 92 aSpace.Lock() 93 for k, v := range aSpace.subnets { 94 if v.Range == nil { 95 kk := k 96 vv := v 97 inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) }) 98 } 99 } 100 aSpace.Unlock() 101 102 // Add the bitmasks (data could come from datastore) 103 for _, f := range inserterList { 104 if err := f(); err != nil { 105 return err 106 } 107 } 108 109 return nil 110 } 111 112 // Checks for and fixes damaged bitmask. 113 func (a *Allocator) checkConsistency(as string) { 114 var sKeyList []SubnetKey 115 116 // Retrieve this address space's configuration and bitmasks from the datastore 117 a.refresh(as) 118 a.Lock() 119 aSpace, ok := a.addrSpaces[as] 120 a.Unlock() 121 if !ok { 122 return 123 } 124 a.updateBitMasks(aSpace) 125 126 aSpace.Lock() 127 for sk, pd := range aSpace.subnets { 128 if pd.Range != nil { 129 continue 130 } 131 sKeyList = append(sKeyList, sk) 132 } 133 aSpace.Unlock() 134 135 for _, sk := range sKeyList { 136 a.Lock() 137 bm := a.addresses[sk] 138 a.Unlock() 139 if err := bm.CheckConsistency(); err != nil { 140 logrus.Warnf("Error while running consistency check for %s: %v", sk, err) 141 } 142 } 143 } 144 145 func (a *Allocator) initializeAddressSpace(as string, ds datastore.DataStore) error { 146 scope := "" 147 if ds != nil { 148 scope = ds.Scope() 149 } 150 151 a.Lock() 152 if currAS, ok := a.addrSpaces[as]; ok { 153 if currAS.ds != nil { 154 a.Unlock() 155 return types.ForbiddenErrorf("a datastore is already configured for the address space %s", as) 156 } 157 } 158 a.addrSpaces[as] = &addrSpace{ 159 subnets: map[SubnetKey]*PoolData{}, 160 id: dsConfigKey + "/" + as, 161 scope: scope, 162 ds: ds, 163 alloc: a, 164 } 165 a.Unlock() 166 167 a.checkConsistency(as) 168 169 return nil 170 } 171 172 // DiscoverNew informs the allocator about a new global scope datastore 173 func (a *Allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { 174 if dType != discoverapi.DatastoreConfig { 175 return nil 176 } 177 178 dsc, ok := data.(discoverapi.DatastoreConfigData) 179 if !ok { 180 return types.InternalErrorf("incorrect data in datastore update notification: %v", data) 181 } 182 183 ds, err := datastore.NewDataStoreFromConfig(dsc) 184 if err != nil { 185 return err 186 } 187 188 return a.initializeAddressSpace(globalAddressSpace, ds) 189 } 190 191 // DiscoverDelete is a notification of no interest for the allocator 192 func (a *Allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { 193 return nil 194 } 195 196 // GetDefaultAddressSpaces returns the local and global default address spaces 197 func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) { 198 return localAddressSpace, globalAddressSpace, nil 199 } 200 201 // RequestPool returns an address pool along with its unique id. 202 // addressSpace must be a valid address space name and must not be the empty string. 203 // If pool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation. 204 // If subPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of pool. 205 // subPool must be empty if pool is empty. 206 func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { 207 logrus.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6) 208 209 k, nw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6) 210 if err != nil { 211 return "", nil, nil, types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, pool, subPool, err) 212 } 213 214 pdf := k == nil 215 216 retry: 217 if pdf { 218 if nw, err = a.getPredefinedPool(addressSpace, v6); err != nil { 219 return "", nil, nil, err 220 } 221 k = &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String()} 222 } 223 224 if err := a.refresh(addressSpace); err != nil { 225 return "", nil, nil, err 226 } 227 228 aSpace, err := a.getAddrSpace(addressSpace) 229 if err != nil { 230 return "", nil, nil, err 231 } 232 233 insert, err := aSpace.updatePoolDBOnAdd(*k, nw, ipr, pdf) 234 if err != nil { 235 if _, ok := err.(types.MaskableError); ok { 236 logrus.Debugf("Retrying predefined pool search: %v", err) 237 goto retry 238 } 239 return "", nil, nil, err 240 } 241 242 if err := a.writeToStore(aSpace); err != nil { 243 if _, ok := err.(types.RetryError); !ok { 244 return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error()) 245 } 246 247 goto retry 248 } 249 250 return k.String(), nw, nil, insert() 251 } 252 253 // ReleasePool releases the address pool identified by the passed id 254 func (a *Allocator) ReleasePool(poolID string) error { 255 logrus.Debugf("ReleasePool(%s)", poolID) 256 k := SubnetKey{} 257 if err := k.FromString(poolID); err != nil { 258 return types.BadRequestErrorf("invalid pool id: %s", poolID) 259 } 260 261 retry: 262 if err := a.refresh(k.AddressSpace); err != nil { 263 return err 264 } 265 266 aSpace, err := a.getAddrSpace(k.AddressSpace) 267 if err != nil { 268 return err 269 } 270 271 remove, err := aSpace.updatePoolDBOnRemoval(k) 272 if err != nil { 273 return err 274 } 275 276 if err = a.writeToStore(aSpace); err != nil { 277 if _, ok := err.(types.RetryError); !ok { 278 return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err) 279 } 280 goto retry 281 } 282 283 return remove() 284 } 285 286 // Given the address space, returns the local or global PoolConfig based on whether the 287 // address space is local or global. AddressSpace locality is registered with IPAM out of band. 288 func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) { 289 a.Lock() 290 defer a.Unlock() 291 aSpace, ok := a.addrSpaces[as] 292 if !ok { 293 return nil, types.BadRequestErrorf("cannot find address space %s (most likely the backing datastore is not configured)", as) 294 } 295 return aSpace, nil 296 } 297 298 // parsePoolRequest parses and validates a request to create a new pool under addressSpace and returns 299 // a SubnetKey, network and range describing the request. 300 func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, error) { 301 var ( 302 nw *net.IPNet 303 ipr *AddressRange 304 err error 305 ) 306 307 if addressSpace == "" { 308 return nil, nil, nil, ipamapi.ErrInvalidAddressSpace 309 } 310 311 if pool == "" && subPool != "" { 312 return nil, nil, nil, ipamapi.ErrInvalidSubPool 313 } 314 315 if pool == "" { 316 return nil, nil, nil, nil 317 } 318 319 if _, nw, err = net.ParseCIDR(pool); err != nil { 320 return nil, nil, nil, ipamapi.ErrInvalidPool 321 } 322 323 if subPool != "" { 324 if ipr, err = getAddressRange(subPool, nw); err != nil { 325 return nil, nil, nil, err 326 } 327 } 328 329 return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, ipr, nil 330 } 331 332 func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error { 333 //logrus.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String()) 334 335 store := a.getStore(key.AddressSpace) 336 ipVer := getAddressVersion(pool.IP) 337 ones, bits := pool.Mask.Size() 338 numAddresses := uint64(1 << uint(bits-ones)) 339 340 // Allow /64 subnet 341 if ipVer == v6 && numAddresses == 0 { 342 numAddresses-- 343 } 344 345 // Generate the new address masks. AddressMask content may come from datastore 346 h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses) 347 if err != nil { 348 return err 349 } 350 351 // Pre-reserve the network address on IPv4 networks large 352 // enough to have one (i.e., anything bigger than a /31. 353 if !(ipVer == v4 && numAddresses <= 2) { 354 h.Set(0) 355 } 356 357 // Pre-reserve the broadcast address on IPv4 networks large 358 // enough to have one (i.e., anything bigger than a /31). 359 if ipVer == v4 && numAddresses > 2 { 360 h.Set(numAddresses - 1) 361 } 362 363 a.Lock() 364 a.addresses[key] = h 365 a.Unlock() 366 return nil 367 } 368 369 func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) { 370 a.Lock() 371 bm, ok := a.addresses[k] 372 a.Unlock() 373 if !ok { 374 logrus.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String()) 375 if err := a.insertBitMask(k, n); err != nil { 376 return nil, types.InternalErrorf("could not find bitmask in datastore for %s", k.String()) 377 } 378 a.Lock() 379 bm = a.addresses[k] 380 a.Unlock() 381 } 382 return bm, nil 383 } 384 385 func (a *Allocator) getPredefineds(as string) []*net.IPNet { 386 a.Lock() 387 defer a.Unlock() 388 389 p := a.predefined[as] 390 i := a.predefinedStartIndices[as] 391 // defensive in case the list changed since last update 392 if i >= len(p) { 393 i = 0 394 } 395 return append(p[i:], p[:i]...) 396 } 397 398 func (a *Allocator) updateStartIndex(as string, amt int) { 399 a.Lock() 400 i := a.predefinedStartIndices[as] + amt 401 if i < 0 || i >= len(a.predefined[as]) { 402 i = 0 403 } 404 a.predefinedStartIndices[as] = i 405 a.Unlock() 406 } 407 408 func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) { 409 var v ipVersion 410 v = v4 411 if ipV6 { 412 v = v6 413 } 414 415 if as != localAddressSpace && as != globalAddressSpace { 416 return nil, types.NotImplementedErrorf("no default pool available for non-default address spaces") 417 } 418 419 aSpace, err := a.getAddrSpace(as) 420 if err != nil { 421 return nil, err 422 } 423 424 predefined := a.getPredefineds(as) 425 426 aSpace.Lock() 427 for i, nw := range predefined { 428 if v != getAddressVersion(nw.IP) { 429 continue 430 } 431 // Checks whether pool has already been allocated 432 if _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]; ok { 433 continue 434 } 435 // Shouldn't be necessary, but check prevents IP collisions should 436 // predefined pools overlap for any reason. 437 if !aSpace.contains(as, nw) { 438 aSpace.Unlock() 439 a.updateStartIndex(as, i+1) 440 return nw, nil 441 } 442 } 443 aSpace.Unlock() 444 445 return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v) 446 } 447 448 // RequestAddress returns an address from the specified pool ID 449 func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { 450 logrus.Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts) 451 k := SubnetKey{} 452 if err := k.FromString(poolID); err != nil { 453 return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID) 454 } 455 456 if err := a.refresh(k.AddressSpace); err != nil { 457 return nil, nil, err 458 } 459 460 aSpace, err := a.getAddrSpace(k.AddressSpace) 461 if err != nil { 462 return nil, nil, err 463 } 464 465 aSpace.Lock() 466 p, ok := aSpace.subnets[k] 467 if !ok { 468 aSpace.Unlock() 469 return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID) 470 } 471 472 if prefAddress != nil && !p.Pool.Contains(prefAddress) { 473 aSpace.Unlock() 474 return nil, nil, ipamapi.ErrIPOutOfRange 475 } 476 477 c := p 478 for c.Range != nil { 479 k = c.ParentKey 480 c = aSpace.subnets[k] 481 } 482 aSpace.Unlock() 483 484 bm, err := a.retrieveBitmask(k, c.Pool) 485 if err != nil { 486 return nil, nil, types.InternalErrorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v", 487 k.String(), prefAddress, poolID, err) 488 } 489 // In order to request for a serial ip address allocation, callers can pass in the option to request 490 // IP allocation serially or first available IP in the subnet 491 var serial bool 492 if opts != nil { 493 if val, ok := opts[ipamapi.AllocSerialPrefix]; ok { 494 serial = (val == "true") 495 } 496 } 497 ip, err := a.getAddress(p.Pool, bm, prefAddress, p.Range, serial) 498 if err != nil { 499 return nil, nil, err 500 } 501 502 return &net.IPNet{IP: ip, Mask: p.Pool.Mask}, nil, nil 503 } 504 505 // ReleaseAddress releases the address from the specified pool ID 506 func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error { 507 logrus.Debugf("ReleaseAddress(%s, %v)", poolID, address) 508 k := SubnetKey{} 509 if err := k.FromString(poolID); err != nil { 510 return types.BadRequestErrorf("invalid pool id: %s", poolID) 511 } 512 513 if err := a.refresh(k.AddressSpace); err != nil { 514 return err 515 } 516 517 aSpace, err := a.getAddrSpace(k.AddressSpace) 518 if err != nil { 519 return err 520 } 521 522 aSpace.Lock() 523 p, ok := aSpace.subnets[k] 524 if !ok { 525 aSpace.Unlock() 526 return types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID) 527 } 528 529 if address == nil { 530 aSpace.Unlock() 531 return types.BadRequestErrorf("invalid address: nil") 532 } 533 534 if !p.Pool.Contains(address) { 535 aSpace.Unlock() 536 return ipamapi.ErrIPOutOfRange 537 } 538 539 c := p 540 for c.Range != nil { 541 k = c.ParentKey 542 c = aSpace.subnets[k] 543 } 544 aSpace.Unlock() 545 546 mask := p.Pool.Mask 547 548 h, err := types.GetHostPartIP(address, mask) 549 if err != nil { 550 return types.InternalErrorf("failed to release address %s: %v", address.String(), err) 551 } 552 553 bm, err := a.retrieveBitmask(k, c.Pool) 554 if err != nil { 555 return types.InternalErrorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v", 556 k.String(), address, poolID, err) 557 } 558 defer logrus.Debugf("Released address PoolID:%s, Address:%v Sequence:%s", poolID, address, bm.String()) 559 560 return bm.Unset(ipToUint64(h)) 561 } 562 563 func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddress net.IP, ipr *AddressRange, serial bool) (net.IP, error) { 564 var ( 565 ordinal uint64 566 err error 567 base *net.IPNet 568 ) 569 570 logrus.Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", nw, bitmask.String(), serial, prefAddress) 571 base = types.GetIPNetCopy(nw) 572 573 if bitmask.Unselected() == 0 { 574 return nil, ipamapi.ErrNoAvailableIPs 575 } 576 if ipr == nil && prefAddress == nil { 577 ordinal, err = bitmask.SetAny(serial) 578 } else if prefAddress != nil { 579 hostPart, e := types.GetHostPartIP(prefAddress, base.Mask) 580 if e != nil { 581 return nil, types.InternalErrorf("failed to allocate requested address %s: %v", prefAddress.String(), e) 582 } 583 ordinal = ipToUint64(types.GetMinimalIP(hostPart)) 584 err = bitmask.Set(ordinal) 585 } else { 586 ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End, serial) 587 } 588 589 switch err { 590 case nil: 591 // Convert IP ordinal for this subnet into IP address 592 return generateAddress(ordinal, base), nil 593 case bitseq.ErrBitAllocated: 594 return nil, ipamapi.ErrIPAlreadyAllocated 595 case bitseq.ErrNoBitAvailable: 596 return nil, ipamapi.ErrNoAvailableIPs 597 default: 598 return nil, err 599 } 600 } 601 602 // DumpDatabase dumps the internal info 603 func (a *Allocator) DumpDatabase() string { 604 a.Lock() 605 aspaces := make(map[string]*addrSpace, len(a.addrSpaces)) 606 orderedAS := make([]string, 0, len(a.addrSpaces)) 607 for as, aSpace := range a.addrSpaces { 608 orderedAS = append(orderedAS, as) 609 aspaces[as] = aSpace 610 } 611 a.Unlock() 612 613 sort.Strings(orderedAS) 614 615 var s string 616 for _, as := range orderedAS { 617 aSpace := aspaces[as] 618 s = fmt.Sprintf("\n\n%s Config", as) 619 aSpace.Lock() 620 for k, config := range aSpace.subnets { 621 s += fmt.Sprintf("\n%v: %v", k, config) 622 if config.Range == nil { 623 a.retrieveBitmask(k, config.Pool) 624 } 625 } 626 aSpace.Unlock() 627 } 628 629 s = fmt.Sprintf("%s\n\nBitmasks", s) 630 for k, bm := range a.addresses { 631 s += fmt.Sprintf("\n%s: %s", k, bm) 632 } 633 634 return s 635 } 636 637 // IsBuiltIn returns true for builtin drivers 638 func (a *Allocator) IsBuiltIn() bool { 639 return true 640 }