| package ipam | |
| import ( | |
| "fmt" | |
| "net/netip" | |
| "strings" | |
| "sync" | |
| "github.com/docker/docker/libnetwork/bitmap" | |
| "github.com/docker/docker/libnetwork/ipamapi" | |
| "github.com/docker/docker/libnetwork/types" | |
| ) | |
| // PoolID is the pointer to the configured pools in each address space | |
| type PoolID struct { | |
| AddressSpace string | |
| SubnetKey | |
| } | |
| // PoolData contains the configured pool data | |
| type PoolData struct { | |
| addrs *bitmap.Bitmap | |
| children map[netip.Prefix]struct{} | |
| // Whether to implicitly release the pool once it no longer has any children. | |
| autoRelease bool | |
| } | |
| // SubnetKey is the composite key to an address pool within an address space. | |
| type SubnetKey struct { | |
| Subnet, ChildSubnet netip.Prefix | |
| } | |
| // addrSpace contains the pool configurations for the address space | |
| type addrSpace struct { | |
| // Master subnet pools, indexed by the value's stringified PoolData.Pool field. | |
| subnets map[netip.Prefix]*PoolData | |
| // Predefined pool for the address space | |
| predefined []netip.Prefix | |
| predefinedStartIndex int | |
| sync.Mutex | |
| } | |
| // String returns the string form of the SubnetKey object | |
| func (s *PoolID) String() string { | |
| k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet) | |
| if s.ChildSubnet != (netip.Prefix{}) { | |
| k = fmt.Sprintf("%s/%s", k, s.ChildSubnet) | |
| } | |
| return k | |
| } | |
| // FromString populates the SubnetKey object reading it from string | |
| func (s *PoolID) FromString(str string) error { | |
| if str == "" || !strings.Contains(str, "/") { | |
| return types.BadRequestErrorf("invalid string form for subnetkey: %s", str) | |
| } | |
| p := strings.Split(str, "/") | |
| if len(p) != 3 && len(p) != 5 { | |
| return types.BadRequestErrorf("invalid string form for subnetkey: %s", str) | |
| } | |
| sub, err := netip.ParsePrefix(p[1] + "/" + p[2]) | |
| if err != nil { | |
| return types.BadRequestErrorf("%v", err) | |
| } | |
| var child netip.Prefix | |
| if len(p) == 5 { | |
| child, err = netip.ParsePrefix(p[3] + "/" + p[4]) | |
| if err != nil { | |
| return types.BadRequestErrorf("%v", err) | |
| } | |
| } | |
| *s = PoolID{ | |
| AddressSpace: p[0], | |
| SubnetKey: SubnetKey{ | |
| Subnet: sub, | |
| ChildSubnet: child, | |
| }, | |
| } | |
| return nil | |
| } | |
| // String returns the string form of the PoolData object | |
| func (p *PoolData) String() string { | |
| return fmt.Sprintf("PoolData[Children: %d]", len(p.children)) | |
| } | |
| // allocateSubnet adds the subnet k to the address space. | |
| func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error { | |
| aSpace.Lock() | |
| defer aSpace.Unlock() | |
| // Check if already allocated | |
| if pool, ok := aSpace.subnets[nw]; ok { | |
| var childExists bool | |
| if sub != (netip.Prefix{}) { | |
| _, childExists = pool.children[sub] | |
| } | |
| if sub == (netip.Prefix{}) || childExists { | |
| // This means the same pool is already allocated. allocateSubnet is called when there | |
| // is request for a pool/subpool. It should ensure there is no overlap with existing pools | |
| return ipamapi.ErrPoolOverlap | |
| } | |
| } | |
| return aSpace.allocateSubnetL(nw, sub) | |
| } | |
| func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error { | |
| // If master pool, check for overlap | |
| if sub == (netip.Prefix{}) { | |
| if aSpace.contains(nw) { | |
| return ipamapi.ErrPoolOverlap | |
| } | |
| // This is a new master pool, add it along with corresponding bitmask | |
| aSpace.subnets[nw] = newPoolData(nw) | |
| return nil | |
| } | |
| // This is a new non-master pool (subPool) | |
| if nw.Addr().BitLen() != sub.Addr().BitLen() { | |
| return fmt.Errorf("pool and subpool are of incompatible address families") | |
| } | |
| // Look for parent pool | |
| pp, ok := aSpace.subnets[nw] | |
| if !ok { | |
| // Parent pool does not exist, add it along with corresponding bitmask | |
| pp = newPoolData(nw) | |
| pp.autoRelease = true | |
| aSpace.subnets[nw] = pp | |
| } | |
| pp.children[sub] = struct{}{} | |
| return nil | |
| } | |
| func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error { | |
| aSpace.Lock() | |
| defer aSpace.Unlock() | |
| p, ok := aSpace.subnets[nw] | |
| if !ok { | |
| return ipamapi.ErrBadPool | |
| } | |
| if sub != (netip.Prefix{}) { | |
| if _, ok := p.children[sub]; !ok { | |
| return ipamapi.ErrBadPool | |
| } | |
| delete(p.children, sub) | |
| } else { | |
| p.autoRelease = true | |
| } | |
| if len(p.children) == 0 && p.autoRelease { | |
| delete(aSpace.subnets, nw) | |
| } | |
| return nil | |
| } | |
| // contains checks whether nw is a superset or subset of any of the existing subnets in this address space. | |
| func (aSpace *addrSpace) contains(nw netip.Prefix) bool { | |
| for pool := range aSpace.subnets { | |
| if nw.Contains(pool.Addr()) || pool.Contains(nw.Addr()) { | |
| return true | |
| } | |
| } | |
| return false | |
| } |