|
@@ -0,0 +1,125 @@
|
|
|
|
|
+// Copyright 2018-present the CoreDHCP Authors. All rights reserved
|
|
|
|
|
+// This source code is licensed under the MIT license found in the
|
|
|
|
|
+// LICENSE file in the root directory of this source tree.
|
|
|
|
|
+
|
|
|
|
|
+package bitmap
|
|
|
|
|
+
|
|
|
|
|
+// This allocator handles IPv4 assignments with a similar logic to the base bitmap, but a simpler
|
|
|
|
|
+// implementation due to the ability to just use uint32 for IPv4 addresses
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "encoding/binary"
|
|
|
|
|
+ "errors"
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "net"
|
|
|
|
|
+ "sync"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/coredhcp/coredhcp/plugins/allocators"
|
|
|
|
|
+ "github.com/willf/bitset"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+var (
|
|
|
|
|
+ // ErrNoAddrAvail is returned when we can't allocate an IP
|
|
|
|
|
+ // because there's no unallocated space left
|
|
|
|
|
+ ErrNoAddrAvail = errors.New("No address available to allocate")
|
|
|
|
|
+
|
|
|
|
|
+ errNotInRange = errors.New("IP outside of allowed range")
|
|
|
|
|
+
|
|
|
|
|
+ errInvalidIP = errors.New("Invalid IP passed as input")
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// IPv4Allocator allocates IPv4 addresses, tracking utilization with a bitmap
|
|
|
|
|
+type IPv4Allocator struct {
|
|
|
|
|
+ start uint32
|
|
|
|
|
+ end uint32
|
|
|
|
|
+
|
|
|
|
|
+ bitmap *bitset.BitSet
|
|
|
|
|
+ l sync.Mutex
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (a *IPv4Allocator) toIP(offset uint32) net.IP {
|
|
|
|
|
+ if offset > a.end-a.start {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ r := make(net.IP, 4)
|
|
|
|
|
+ binary.BigEndian.PutUint32(r, a.start+offset)
|
|
|
|
|
+ return r
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (a *IPv4Allocator) toOffset(ip net.IP) (uint, error) {
|
|
|
|
|
+ if ip.To4() == nil {
|
|
|
|
|
+ return 0, errInvalidIP
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ intIP := binary.BigEndian.Uint32(ip.To4())
|
|
|
|
|
+ if intIP < a.start || intIP > a.end {
|
|
|
|
|
+ return 0, errNotInRange
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return uint(intIP - a.start), nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Allocate reserves an IP for a client
|
|
|
|
|
+func (a *IPv4Allocator) Allocate(hint net.IPNet) (n net.IPNet, err error) {
|
|
|
|
|
+ n.Mask = net.CIDRMask(32, 32)
|
|
|
|
|
+
|
|
|
|
|
+ // This is just a hint, ignore any error with it
|
|
|
|
|
+ hintOffset, _ := a.toOffset(hint.IP)
|
|
|
|
|
+
|
|
|
|
|
+ a.l.Lock()
|
|
|
|
|
+ defer a.l.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ var next uint
|
|
|
|
|
+ // First try the exact match
|
|
|
|
|
+ if a.bitmap.Test(hintOffset) {
|
|
|
|
|
+ next = hintOffset
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Then any available address
|
|
|
|
|
+ avail, ok := a.bitmap.NextClear(0)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return n, ErrNoAddrAvail
|
|
|
|
|
+ }
|
|
|
|
|
+ next = avail
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ a.bitmap.Set(next)
|
|
|
|
|
+ n.IP = a.toIP(uint32(next))
|
|
|
|
|
+ return
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Free releases the given IP
|
|
|
|
|
+func (a *IPv4Allocator) Free(n net.IPNet) error {
|
|
|
|
|
+ offset, err := a.toOffset(n.IP)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return errNotInRange
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ a.l.Lock()
|
|
|
|
|
+ defer a.l.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ if !a.bitmap.Test(uint(offset)) {
|
|
|
|
|
+ return &allocators.ErrDoubleFree{Loc: n}
|
|
|
|
|
+ }
|
|
|
|
|
+ a.bitmap.Clear(offset)
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// NewIPv4Allocator creates a new allocator suitable for giving out IPv4 addresses
|
|
|
|
|
+func NewIPv4Allocator(start, end net.IP) (*IPv4Allocator, error) {
|
|
|
|
|
+ if start.To4() == nil || end.To4() == nil {
|
|
|
|
|
+ return nil, fmt.Errorf("Invalid IPv4s given to create the allocator: [%s,%s]", start, end)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ alloc := IPv4Allocator{
|
|
|
|
|
+ start: binary.BigEndian.Uint32(start.To4()),
|
|
|
|
|
+ end: binary.BigEndian.Uint32(end.To4()),
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if alloc.start > alloc.end {
|
|
|
|
|
+ return nil, errors.New("No IPs in the given range to allocate")
|
|
|
|
|
+ }
|
|
|
|
|
+ alloc.bitmap = bitset.New(uint(alloc.end - alloc.start + 1))
|
|
|
|
|
+
|
|
|
|
|
+ return &alloc, nil
|
|
|
|
|
+}
|