allocator.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package ipam
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "net/netip"
  7. "strings"
  8. "github.com/containerd/containerd/log"
  9. "github.com/docker/docker/libnetwork/bitmap"
  10. "github.com/docker/docker/libnetwork/ipamapi"
  11. "github.com/docker/docker/libnetwork/ipbits"
  12. "github.com/docker/docker/libnetwork/types"
  13. )
  14. const (
  15. localAddressSpace = "LocalDefault"
  16. globalAddressSpace = "GlobalDefault"
  17. )
  18. // Allocator provides per address space ipv4/ipv6 book keeping
  19. type Allocator struct {
  20. // The address spaces
  21. local, global *addrSpace
  22. }
  23. // NewAllocator returns an instance of libnetwork ipam
  24. func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
  25. var (
  26. a Allocator
  27. err error
  28. )
  29. a.local, err = newAddrSpace(lcAs)
  30. if err != nil {
  31. return nil, fmt.Errorf("could not construct local address space: %w", err)
  32. }
  33. a.global, err = newAddrSpace(glAs)
  34. if err != nil {
  35. return nil, fmt.Errorf("could not construct global address space: %w", err)
  36. }
  37. return &a, nil
  38. }
  39. func newAddrSpace(predefined []*net.IPNet) (*addrSpace, error) {
  40. pdf := make([]netip.Prefix, len(predefined))
  41. for i, n := range predefined {
  42. var ok bool
  43. pdf[i], ok = toPrefix(n)
  44. if !ok {
  45. return nil, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
  46. }
  47. }
  48. return &addrSpace{
  49. subnets: map[netip.Prefix]*PoolData{},
  50. predefined: pdf,
  51. }, nil
  52. }
  53. // GetDefaultAddressSpaces returns the local and global default address spaces
  54. func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
  55. return localAddressSpace, globalAddressSpace, nil
  56. }
  57. // RequestPool returns an address pool along with its unique id.
  58. // addressSpace must be a valid address space name and must not be the empty string.
  59. // 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.
  60. // If subPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of pool.
  61. // subPool must be empty if pool is empty.
  62. func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
  63. log.G(context.TODO()).Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6)
  64. parseErr := func(err error) (string, *net.IPNet, map[string]string, error) {
  65. return "", nil, nil, types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, pool, subPool, err)
  66. }
  67. if addressSpace == "" {
  68. return parseErr(ipamapi.ErrInvalidAddressSpace)
  69. }
  70. aSpace, err := a.getAddrSpace(addressSpace)
  71. if err != nil {
  72. return "", nil, nil, err
  73. }
  74. k := PoolID{AddressSpace: addressSpace}
  75. if pool == "" {
  76. if subPool != "" {
  77. return parseErr(ipamapi.ErrInvalidSubPool)
  78. }
  79. k.Subnet, err = aSpace.allocatePredefinedPool(v6)
  80. if err != nil {
  81. return "", nil, nil, err
  82. }
  83. return k.String(), toIPNet(k.Subnet), nil, nil
  84. }
  85. if k.Subnet, err = netip.ParsePrefix(pool); err != nil {
  86. return parseErr(ipamapi.ErrInvalidPool)
  87. }
  88. if subPool != "" {
  89. var err error
  90. k.ChildSubnet, err = netip.ParsePrefix(subPool)
  91. if err != nil {
  92. return parseErr(ipamapi.ErrInvalidSubPool)
  93. }
  94. }
  95. k.Subnet, k.ChildSubnet = k.Subnet.Masked(), k.ChildSubnet.Masked()
  96. err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
  97. if err != nil {
  98. return "", nil, nil, err
  99. }
  100. return k.String(), toIPNet(k.Subnet), nil, nil
  101. }
  102. // ReleasePool releases the address pool identified by the passed id
  103. func (a *Allocator) ReleasePool(poolID string) error {
  104. log.G(context.TODO()).Debugf("ReleasePool(%s)", poolID)
  105. k := PoolID{}
  106. if err := k.FromString(poolID); err != nil {
  107. return types.BadRequestErrorf("invalid pool id: %s", poolID)
  108. }
  109. aSpace, err := a.getAddrSpace(k.AddressSpace)
  110. if err != nil {
  111. return err
  112. }
  113. return aSpace.releaseSubnet(k.Subnet, k.ChildSubnet)
  114. }
  115. // Given the address space, returns the local or global PoolConfig based on whether the
  116. // address space is local or global. AddressSpace locality is registered with IPAM out of band.
  117. func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
  118. switch as {
  119. case localAddressSpace:
  120. return a.local, nil
  121. case globalAddressSpace:
  122. return a.global, nil
  123. }
  124. return nil, types.BadRequestErrorf("cannot find address space %s", as)
  125. }
  126. func newPoolData(pool netip.Prefix) *PoolData {
  127. ones, bits := pool.Bits(), pool.Addr().BitLen()
  128. numAddresses := uint64(1 << uint(bits-ones))
  129. // Allow /64 subnet
  130. if pool.Addr().Is6() && numAddresses == 0 {
  131. numAddresses--
  132. }
  133. // Generate the new address masks.
  134. h := bitmap.New(numAddresses)
  135. // Pre-reserve the network address on IPv4 networks large
  136. // enough to have one (i.e., anything bigger than a /31.
  137. if !(pool.Addr().Is4() && numAddresses <= 2) {
  138. h.Set(0)
  139. }
  140. // Pre-reserve the broadcast address on IPv4 networks large
  141. // enough to have one (i.e., anything bigger than a /31).
  142. if pool.Addr().Is4() && numAddresses > 2 {
  143. h.Set(numAddresses - 1)
  144. }
  145. return &PoolData{addrs: h, children: map[netip.Prefix]struct{}{}}
  146. }
  147. // getPredefineds returns the predefined subnets for the address space.
  148. //
  149. // It should not be called concurrently with any other method on the addrSpace.
  150. func (aSpace *addrSpace) getPredefineds() []netip.Prefix {
  151. i := aSpace.predefinedStartIndex
  152. // defensive in case the list changed since last update
  153. if i >= len(aSpace.predefined) {
  154. i = 0
  155. }
  156. return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
  157. }
  158. // updatePredefinedStartIndex rotates the predefined subnet list by amt.
  159. //
  160. // It should not be called concurrently with any other method on the addrSpace.
  161. func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
  162. i := aSpace.predefinedStartIndex + amt
  163. if i < 0 || i >= len(aSpace.predefined) {
  164. i = 0
  165. }
  166. aSpace.predefinedStartIndex = i
  167. }
  168. func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (netip.Prefix, error) {
  169. aSpace.Lock()
  170. defer aSpace.Unlock()
  171. for i, nw := range aSpace.getPredefineds() {
  172. if ipV6 != nw.Addr().Is6() {
  173. continue
  174. }
  175. // Checks whether pool has already been allocated
  176. if _, ok := aSpace.subnets[nw]; ok {
  177. continue
  178. }
  179. // Shouldn't be necessary, but check prevents IP collisions should
  180. // predefined pools overlap for any reason.
  181. if !aSpace.contains(nw) {
  182. aSpace.updatePredefinedStartIndex(i + 1)
  183. err := aSpace.allocateSubnetL(nw, netip.Prefix{})
  184. if err != nil {
  185. return netip.Prefix{}, err
  186. }
  187. return nw, nil
  188. }
  189. }
  190. v := 4
  191. if ipV6 {
  192. v = 6
  193. }
  194. return netip.Prefix{}, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
  195. }
  196. // RequestAddress returns an address from the specified pool ID
  197. func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
  198. log.G(context.TODO()).Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts)
  199. k := PoolID{}
  200. if err := k.FromString(poolID); err != nil {
  201. return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID)
  202. }
  203. aSpace, err := a.getAddrSpace(k.AddressSpace)
  204. if err != nil {
  205. return nil, nil, err
  206. }
  207. var pref netip.Addr
  208. if prefAddress != nil {
  209. var ok bool
  210. pref, ok = netip.AddrFromSlice(prefAddress)
  211. if !ok {
  212. return nil, nil, types.BadRequestErrorf("invalid preferred address: %v", prefAddress)
  213. }
  214. }
  215. p, err := aSpace.requestAddress(k.Subnet, k.ChildSubnet, pref.Unmap(), opts)
  216. if err != nil {
  217. return nil, nil, err
  218. }
  219. return &net.IPNet{
  220. IP: p.AsSlice(),
  221. Mask: net.CIDRMask(k.Subnet.Bits(), k.Subnet.Addr().BitLen()),
  222. }, nil, nil
  223. }
  224. func (aSpace *addrSpace) requestAddress(nw, sub netip.Prefix, prefAddress netip.Addr, opts map[string]string) (netip.Addr, error) {
  225. aSpace.Lock()
  226. defer aSpace.Unlock()
  227. p, ok := aSpace.subnets[nw]
  228. if !ok {
  229. return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
  230. }
  231. if prefAddress != (netip.Addr{}) && !nw.Contains(prefAddress) {
  232. return netip.Addr{}, ipamapi.ErrIPOutOfRange
  233. }
  234. if sub != (netip.Prefix{}) {
  235. if _, ok := p.children[sub]; !ok {
  236. return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
  237. }
  238. }
  239. // In order to request for a serial ip address allocation, callers can pass in the option to request
  240. // IP allocation serially or first available IP in the subnet
  241. serial := opts[ipamapi.AllocSerialPrefix] == "true"
  242. ip, err := getAddress(nw, p.addrs, prefAddress, sub, serial)
  243. if err != nil {
  244. return netip.Addr{}, err
  245. }
  246. return ip, nil
  247. }
  248. // ReleaseAddress releases the address from the specified pool ID
  249. func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
  250. log.G(context.TODO()).Debugf("ReleaseAddress(%s, %v)", poolID, address)
  251. k := PoolID{}
  252. if err := k.FromString(poolID); err != nil {
  253. return types.BadRequestErrorf("invalid pool id: %s", poolID)
  254. }
  255. aSpace, err := a.getAddrSpace(k.AddressSpace)
  256. if err != nil {
  257. return err
  258. }
  259. addr, ok := netip.AddrFromSlice(address)
  260. if !ok {
  261. return types.BadRequestErrorf("invalid address: %v", address)
  262. }
  263. return aSpace.releaseAddress(k.Subnet, k.ChildSubnet, addr.Unmap())
  264. }
  265. func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr) error {
  266. aSpace.Lock()
  267. defer aSpace.Unlock()
  268. p, ok := aSpace.subnets[nw]
  269. if !ok {
  270. return types.NotFoundErrorf("cannot find address pool for %v/%v", nw, sub)
  271. }
  272. if sub != (netip.Prefix{}) {
  273. if _, ok := p.children[sub]; !ok {
  274. return types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
  275. }
  276. }
  277. if !address.IsValid() {
  278. return types.BadRequestErrorf("invalid address")
  279. }
  280. if !nw.Contains(address) {
  281. return ipamapi.ErrIPOutOfRange
  282. }
  283. defer log.G(context.TODO()).Debugf("Released address Address:%v Sequence:%s", address, p.addrs)
  284. return p.addrs.Unset(hostID(address, uint(nw.Bits())))
  285. }
  286. func getAddress(base netip.Prefix, bitmask *bitmap.Bitmap, prefAddress netip.Addr, ipr netip.Prefix, serial bool) (netip.Addr, error) {
  287. var (
  288. ordinal uint64
  289. err error
  290. )
  291. log.G(context.TODO()).Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", base, bitmask, serial, prefAddress)
  292. if bitmask.Unselected() == 0 {
  293. return netip.Addr{}, ipamapi.ErrNoAvailableIPs
  294. }
  295. if ipr == (netip.Prefix{}) && prefAddress == (netip.Addr{}) {
  296. ordinal, err = bitmask.SetAny(serial)
  297. } else if prefAddress != (netip.Addr{}) {
  298. ordinal = hostID(prefAddress, uint(base.Bits()))
  299. err = bitmask.Set(ordinal)
  300. } else {
  301. start, end := subnetRange(base, ipr)
  302. ordinal, err = bitmask.SetAnyInRange(start, end, serial)
  303. }
  304. switch err {
  305. case nil:
  306. // Convert IP ordinal for this subnet into IP address
  307. return ipbits.Add(base.Addr(), ordinal, 0), nil
  308. case bitmap.ErrBitAllocated:
  309. return netip.Addr{}, ipamapi.ErrIPAlreadyAllocated
  310. case bitmap.ErrNoBitAvailable:
  311. return netip.Addr{}, ipamapi.ErrNoAvailableIPs
  312. default:
  313. return netip.Addr{}, err
  314. }
  315. }
  316. // DumpDatabase dumps the internal info
  317. func (a *Allocator) DumpDatabase() string {
  318. aspaces := map[string]*addrSpace{
  319. localAddressSpace: a.local,
  320. globalAddressSpace: a.global,
  321. }
  322. var b strings.Builder
  323. for _, as := range []string{localAddressSpace, globalAddressSpace} {
  324. fmt.Fprintf(&b, "\n### %s\n", as)
  325. b.WriteString(aspaces[as].DumpDatabase())
  326. }
  327. return b.String()
  328. }
  329. func (aSpace *addrSpace) DumpDatabase() string {
  330. aSpace.Lock()
  331. defer aSpace.Unlock()
  332. var b strings.Builder
  333. for k, config := range aSpace.subnets {
  334. fmt.Fprintf(&b, "%v: %v\n", k, config)
  335. fmt.Fprintf(&b, " Bitmap: %v\n", config.addrs)
  336. for k := range config.children {
  337. fmt.Fprintf(&b, " - Subpool: %v\n", k)
  338. }
  339. }
  340. return b.String()
  341. }
  342. // IsBuiltIn returns true for builtin drivers
  343. func (a *Allocator) IsBuiltIn() bool {
  344. return true
  345. }