bitmap_ipv4.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2018-present the CoreDHCP Authors. All rights reserved
  2. // This source code is licensed under the MIT license found in the
  3. // LICENSE file in the root directory of this source tree.
  4. package bitmap
  5. // This allocator handles IPv4 assignments with a similar logic to the base bitmap, but a simpler
  6. // implementation due to the ability to just use uint32 for IPv4 addresses
  7. import (
  8. "encoding/binary"
  9. "errors"
  10. "fmt"
  11. "net"
  12. "sync"
  13. "github.com/coredhcp/coredhcp/plugins/allocators"
  14. "github.com/willf/bitset"
  15. )
  16. var (
  17. // ErrNoAddrAvail is returned when we can't allocate an IP
  18. // because there's no unallocated space left
  19. ErrNoAddrAvail = errors.New("No address available to allocate")
  20. errNotInRange = errors.New("IP outside of allowed range")
  21. errInvalidIP = errors.New("Invalid IP passed as input")
  22. )
  23. // IPv4Allocator allocates IPv4 addresses, tracking utilization with a bitmap
  24. type IPv4Allocator struct {
  25. start uint32
  26. end uint32
  27. bitmap *bitset.BitSet
  28. l sync.Mutex
  29. }
  30. func (a *IPv4Allocator) toIP(offset uint32) net.IP {
  31. if offset > a.end-a.start {
  32. return nil
  33. }
  34. r := make(net.IP, 4)
  35. binary.BigEndian.PutUint32(r, a.start+offset)
  36. return r
  37. }
  38. func (a *IPv4Allocator) toOffset(ip net.IP) (uint, error) {
  39. if ip.To4() == nil {
  40. return 0, errInvalidIP
  41. }
  42. intIP := binary.BigEndian.Uint32(ip.To4())
  43. if intIP < a.start || intIP > a.end {
  44. return 0, errNotInRange
  45. }
  46. return uint(intIP - a.start), nil
  47. }
  48. // Allocate reserves an IP for a client
  49. func (a *IPv4Allocator) Allocate(hint net.IPNet) (n net.IPNet, err error) {
  50. n.Mask = net.CIDRMask(32, 32)
  51. // This is just a hint, ignore any error with it
  52. hintOffset, _ := a.toOffset(hint.IP)
  53. a.l.Lock()
  54. defer a.l.Unlock()
  55. var next uint
  56. // First try the exact match
  57. if a.bitmap.Test(hintOffset) {
  58. next = hintOffset
  59. } else {
  60. // Then any available address
  61. avail, ok := a.bitmap.NextClear(0)
  62. if !ok {
  63. return n, ErrNoAddrAvail
  64. }
  65. next = avail
  66. }
  67. a.bitmap.Set(next)
  68. n.IP = a.toIP(uint32(next))
  69. return
  70. }
  71. // Free releases the given IP
  72. func (a *IPv4Allocator) Free(n net.IPNet) error {
  73. offset, err := a.toOffset(n.IP)
  74. if err != nil {
  75. return errNotInRange
  76. }
  77. a.l.Lock()
  78. defer a.l.Unlock()
  79. if !a.bitmap.Test(uint(offset)) {
  80. return &allocators.ErrDoubleFree{Loc: n}
  81. }
  82. a.bitmap.Clear(offset)
  83. return nil
  84. }
  85. // NewIPv4Allocator creates a new allocator suitable for giving out IPv4 addresses
  86. func NewIPv4Allocator(start, end net.IP) (*IPv4Allocator, error) {
  87. if start.To4() == nil || end.To4() == nil {
  88. return nil, fmt.Errorf("Invalid IPv4s given to create the allocator: [%s,%s]", start, end)
  89. }
  90. alloc := IPv4Allocator{
  91. start: binary.BigEndian.Uint32(start.To4()),
  92. end: binary.BigEndian.Uint32(end.To4()),
  93. }
  94. if alloc.start > alloc.end {
  95. return nil, errors.New("No IPs in the given range to allocate")
  96. }
  97. alloc.bitmap = bitset.New(uint(alloc.end - alloc.start + 1))
  98. return &alloc, nil
  99. }