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