From 03f4aae1bfec7a0d34fbb3d4d74ff68bc643b1b7 Mon Sep 17 00:00:00 2001 From: Dip J Date: Wed, 11 Jan 2023 16:06:56 +0900 Subject: [PATCH 1/7] Initial commit for IPAM --- ipalloc.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 ipalloc.go diff --git a/ipalloc.go b/ipalloc.go new file mode 100644 index 0000000..ead71da --- /dev/null +++ b/ipalloc.go @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache 2.0 +// Copyright (c) 2023 NetLOX Inc + +package loxilib + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "net" + "os" + "strconv" + "strings" + "syscall" + "unsafe" +) + +const ( + IPZoneDefault = "default" +) + +type IpRange struct { + ipNet net.IPNet + freeID *Counter + ident map[uint16]struct{} +} + +type IpZone struct { + name string + pool map[string]*IpRange +} + +type IpAllocator struct { + ipBlocks map[string]*IpZone +} + +func (ipa *IpAllocator) AddNewRange(zone string, cidr string) error { + ip, ipn, err:= net.ParseCIDR(cidr) + + if err != nil { + return errors.New("Invalid CIDR") + } + + ipz := ipa.ipBlocks[IPZoneDefault] + + if zone != IPZoneDefault { + if ipz = ipa.ipBlocks[zone]; ipz == nil { + ipz := new(IpZone) + ipa.ipBlocks[IPZoneDefault] = ipz + } + } + + if ipz == nil { + return errors.New("Can't find IP Zone") + } + + for _, ipr := range ipz.pool { + if ipr.ipNet.Contains(ip) { + return errors.New("Existing IP Pool") + } + } + +} + +func IpAllocatorNew() *IpAllocator { + ipa := new(IpAllocator) + ipa.ipBlocks = make(map[string]*IpZone) + + ipz := new(IpZone) + ipa.ipBlocks[IPZoneDefault] = ipz + + return ipa +} \ No newline at end of file From eb2eb8482b697962043f6ac67ea947110890236a Mon Sep 17 00:00:00 2001 From: dj-nlx Date: Mon, 16 Jan 2023 18:23:16 +0900 Subject: [PATCH 2/7] ipalloc implementation --- ipalloc.go | 118 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/ipalloc.go b/ipalloc.go index ead71da..8d93e52 100644 --- a/ipalloc.go +++ b/ipalloc.go @@ -4,71 +4,127 @@ package loxilib import ( - "bufio" - "bytes" - "encoding/binary" "errors" "net" - "os" - "strconv" - "strings" - "syscall" - "unsafe" ) const ( - IPZoneDefault = "default" + IPClusterDefault = "default" ) type IpRange struct { - ipNet net.IPNet - freeID *Counter - ident map[uint16]struct{} + ipNet net.IPNet + freeID *Counter + ident map[uint16]struct{} } -type IpZone struct { - name string - pool map[string]*IpRange +type IpClusterPool struct { + name string + pool map[string]*IpRange } type IpAllocator struct { - ipBlocks map[string]*IpZone + ipBlocks map[string]*IpClusterPool } -func (ipa *IpAllocator) AddNewRange(zone string, cidr string) error { - ip, ipn, err:= net.ParseCIDR(cidr) - +func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string) (net.IP, error) { + var ipCPool *IpClusterPool + _, _, err := net.ParseCIDR(cidr) + + if err != nil { + return net.IP{0, 0, 0, 0}, errors.New("Invalid CIDR") + } + + if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { + return net.IP{0, 0, 0, 0}, errors.New("No such IP Cluster Pool") + } + + if ipr := ipCPool.pool[cidr]; ipr == nil { + return net.IP{0, 0, 0, 0}, errors.New("No such IP Range") + } + + return net.IP{0, 0, 0, 0}, nil +} + +func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { + var ipCPool *IpClusterPool + ip, ipn, err := net.ParseCIDR(cidr) + if err != nil { return errors.New("Invalid CIDR") } - ipz := ipa.ipBlocks[IPZoneDefault] + ipCPool = ipa.ipBlocks[IPClusterDefault] - if zone != IPZoneDefault { - if ipz = ipa.ipBlocks[zone]; ipz == nil { - ipz := new(IpZone) - ipa.ipBlocks[IPZoneDefault] = ipz + if cluster != IPClusterDefault { + if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { + ipCPool = new(IpClusterPool) + ipCPool.name = cluster + ipa.ipBlocks[cluster] = ipCPool } } - if ipz == nil { - return errors.New("Can't find IP Zone") + if ipCPool == nil { + return errors.New("Can't find IP Cluster Pool") } - for _, ipr := range ipz.pool { + for _, ipr := range ipCPool.pool { if ipr.ipNet.Contains(ip) { return errors.New("Existing IP Pool") } } + ipr := new(IpRange) + ipr.ipNet = *ipn + iprSz := 0 + sz, _ := ipn.Mask.Size() + if IsNetIPv4(ip.String()) { + iprSz = (1 << (32 - sz)) - 1 + } else { + iprSz = (1 << (128 - sz)) - 1 + } + + // If it is a x.x.x.0/8, then we will allocate + // from x.x.x.1 to x.x.x.255 + ipr.freeID = NewCounter(1, iprSz) + + if ipr.freeID == nil { + return errors.New("IP Pool alloc failed") + } + + ipr.ident = make(map[uint16]struct{}) + ipCPool.pool[cidr] = ipr + + return nil +} + +func (ipa *IpAllocator) DeleteIPRange(cluster string, cidr string) error { + var ipCPool *IpClusterPool + _, _, err := net.ParseCIDR(cidr) + + if err != nil { + return errors.New("Invalid CIDR") + } + + if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { + return errors.New("No such IP Cluster Pool") + } + + if ipr := ipCPool.pool[cidr]; ipr == nil { + return errors.New("No such IP Range") + } + + delete(ipCPool.pool, cidr) + + return nil } func IpAllocatorNew() *IpAllocator { ipa := new(IpAllocator) - ipa.ipBlocks = make(map[string]*IpZone) + ipa.ipBlocks = make(map[string]*IpClusterPool) - ipz := new(IpZone) - ipa.ipBlocks[IPZoneDefault] = ipz + ipz := new(IpClusterPool) + ipa.ipBlocks[IPClusterDefault] = ipz return ipa -} \ No newline at end of file +} From 61b9463fc0e12785c2c95c975302c8c3cbdac6af Mon Sep 17 00:00:00 2001 From: dj-nlx Date: Tue, 17 Jan 2023 00:28:28 +0900 Subject: [PATCH 3/7] ipalloc implementation --- ipalloc.go | 148 +++++++++++++++++++++++++++++++++++++++++++++++----- lib_test.go | 90 +++++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 15 deletions(-) diff --git a/ipalloc.go b/ipalloc.go index 8d93e52..d1fdcd7 100644 --- a/ipalloc.go +++ b/ipalloc.go @@ -15,7 +15,7 @@ const ( type IpRange struct { ipNet net.IPNet freeID *Counter - ident map[uint16]struct{} + ident map[uint32]struct{} } type IpClusterPool struct { @@ -27,23 +27,126 @@ type IpAllocator struct { ipBlocks map[string]*IpClusterPool } -func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string) (net.IP, error) { +func addIPIndex(ip net.IP, index uint64) net.IP { + retIP := ip + + v := index + c := uint64(0) + arrIndex := len(ip) - 1 + + for i := 0; i < 8 && arrIndex >= 0 && v > 0; i++ { + c = v / 255 + retIP[arrIndex] += uint8((v + c) % 255) + arrIndex-- + v >>= 8 + } + + return retIP +} + +func diffIPIndex(baseIP net.IP, IP net.IP) uint64 { + var index uint64 + iplen := 0 + if IsNetIPv4(baseIP.String()) { + iplen = 4 + } else { + iplen = 16 + } + arrIndex := len(baseIP) - iplen + arrIndex1 := len(IP) - iplen + + for i := 0; i < 8 && arrIndex < iplen; i++ { + basev := uint8(baseIP[arrIndex]) + ipv := uint8(IP[arrIndex1]) + if basev > ipv { + return 0 + } + + index = uint64(ipv - basev) + arrIndex++ + arrIndex1++ + index |= index << (8 * (iplen - i - 1)) + } + + return index +} + +func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (net.IP, error) { var ipCPool *IpClusterPool - _, _, err := net.ParseCIDR(cidr) + var ipr *IpRange + _, ipn, err := net.ParseCIDR(cidr) if err != nil { return net.IP{0, 0, 0, 0}, errors.New("Invalid CIDR") } if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { - return net.IP{0, 0, 0, 0}, errors.New("No such IP Cluster Pool") + if err := ipa.AddIPRange(cluster, cidr); err != nil { + return net.IP{0, 0, 0, 0}, errors.New("No such IP Cluster Pool") + } + if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { + return net.IP{0, 0, 0, 0}, errors.New("IP Range allocation failure") + } } - if ipr := ipCPool.pool[cidr]; ipr == nil { + if ipr = ipCPool.pool[cidr]; ipr == nil { return net.IP{0, 0, 0, 0}, errors.New("No such IP Range") } - return net.IP{0, 0, 0, 0}, nil + if _, ok := ipr.ident[id]; ok { + return net.IP{0, 0, 0, 0}, errors.New("IP Range,Ident exists") + } + + newIndex, err := ipr.freeID.GetCounter() + if err != nil { + return net.IP{0, 0, 0, 0}, errors.New("IP Alloc counter failure") + } + + ipr.ident[id] = struct{}{} + + retIP := addIPIndex(ipn.IP, uint64(newIndex)) + + return retIP, nil +} + +func (ipa *IpAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPString string) error { + var ipCPool *IpClusterPool + var ipr *IpRange + _, _, err := net.ParseCIDR(cidr) + if err != nil { + return errors.New("Invalid CIDR") + } + + IP := net.ParseIP(IPString) + if IP == nil { + return errors.New("Invalid IP String") + } + + if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { + return errors.New("IP Cluster not found") + } + + if ipr = ipCPool.pool[cidr]; ipr == nil { + return errors.New("No such IP Range") + } + + if _, ok := ipr.ident[id]; !ok { + return errors.New("IP Range - Ident not found") + } + + retIndex := diffIPIndex(ipr.ipNet.IP, IP) + if retIndex <= 0 { + return errors.New("IP return index not found") + } + + err = ipr.freeID.PutCounter(retIndex) + if err != nil { + return errors.New("IP Range counter failure") + } + + delete(ipr.ident, id) + + return nil } func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { @@ -76,23 +179,39 @@ func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { ipr := new(IpRange) ipr.ipNet = *ipn - iprSz := 0 + iprSz := uint64(0) sz, _ := ipn.Mask.Size() if IsNetIPv4(ip.String()) { - iprSz = (1 << (32 - sz)) - 1 + if sz == 31 { + iprSz = 1 + } else { + iprSz = (1 << (32 - sz)) - 2 + } } else { - iprSz = (1 << (128 - sz)) - 1 + if sz == 127 { + iprSz = 1 + } else { + iprSz = (1 << (128 - sz)) - 2 + } + } + + if iprSz < 1 { + return errors.New("IP Pool subnet error") + } + + if iprSz > uint64(^uint16(0)) { + iprSz = uint64(^uint16(0)) } - // If it is a x.x.x.0/8, then we will allocate - // from x.x.x.1 to x.x.x.255 + // If it is a x.x.x.0/24, then we will allocate + // from x.x.x.1 to x.x.x.254 ipr.freeID = NewCounter(1, iprSz) if ipr.freeID == nil { return errors.New("IP Pool alloc failed") } - ipr.ident = make(map[uint16]struct{}) + ipr.ident = make(map[uint32]struct{}) ipCPool.pool[cidr] = ipr return nil @@ -123,8 +242,9 @@ func IpAllocatorNew() *IpAllocator { ipa := new(IpAllocator) ipa.ipBlocks = make(map[string]*IpClusterPool) - ipz := new(IpClusterPool) - ipa.ipBlocks[IPClusterDefault] = ipz + ipCPool := new(IpClusterPool) + ipCPool.pool = make(map[string]*IpRange) + ipa.ipBlocks[IPClusterDefault] = ipCPool return ipa } diff --git a/lib_test.go b/lib_test.go index c7b04f9..1094292 100644 --- a/lib_test.go +++ b/lib_test.go @@ -263,7 +263,7 @@ func TestCounter(t *testing.T) { t.Errorf("Able to put invalid Counter %d", 15) } - var idx int + var idx uint64 idx, err = cR.GetCounter() if idx != 5 || err != nil { t.Errorf("Counter get got %d of expected %d", idx, 5) @@ -283,3 +283,91 @@ func TestIfStat(t *testing.T) { t.Errorf("Get stats failed for eth0") } } + +func TestIPAlloc(t *testing.T) { + ipa := IpAllocatorNew() + + ipa.AddIPRange(IPClusterDefault, "123.123.123.0/24") + for i := 0; i < 255; i++ { + ip, err := ipa.AllocateNewIP(IPClusterDefault, "123.123.123.0/24", uint32(i)) + if i >= 254 && err == nil { + t.Fatal("Failed IP Alloc for 123.123.123.0/24 - Check Alloc Algo") + } else if i < 254 && err != nil { + t.Fatalf("Failed IP Alloc for 123.123.123.0/24 : %d:%s", i, err) + } else if err == nil { + expected := fmt.Sprintf("123.123.123.%d", i+1) + if ip.String() != expected { + t.Fatalf("Failed IP Alloc for 123.123.123.0/24: %s:%s", ip.String(), expected) + } + } + } + + err := ipa.DeAllocateIP(IPClusterDefault, "123.123.123.0/24", 0, "123.123.123.1") + if err != nil { + t.Fatalf("IP DeAlloc failed for %s:%s", "123.123.123.1", err) + } + + ip, err := ipa.AllocateNewIP(IPClusterDefault, "123.123.123.0/24", uint32(0)) + if err != nil || ip.String() != "123.123.123.1" { + t.Fatalf("Failed IP Alloc for 123.123.123.0/24:%s", "123.123.123.1") + } + + ipa.AddIPRange(IPClusterDefault, "11.11.11.0/31") + ip, err = ipa.AllocateNewIP(IPClusterDefault, "11.11.11.0/31", 0) + if err != nil { + t.Fatal("Failed IP Alloc for 11.11.11.0/31 - Check Alloc Algo") + } + + if ip.String() != "11.11.11.1" { + t.Fatalf("Failed IP Alloc for 11.11.11.0/31: %s:%s", ip.String(), "11.11.11.1") + } + + ip, err = ipa.AllocateNewIP(IPClusterDefault, "11.11.11.0/31", 0) + if err == nil { + t.Fatal("Invalid IP Alloc for 11.11.11.0/31 - Check Alloc Algo") + } + + err = ipa.DeleteIPRange(IPClusterDefault, "11.11.11.0/31") + if err != nil { + t.Fatal("Failed to delete IP Alloc for 11.11.11.0/31 - Check Alloc Algo") + } + + err = ipa.AddIPRange(IPClusterDefault, "12.12.0.0/16") + if err != nil { + t.Fatal("Failed to Add IP Range for 12.12.0.0/16") + } + + ip1, err := ipa.AllocateNewIP(IPClusterDefault, "12.12.0.0/16", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 12.12.0.0/16:1:%s", err) + } + + _, err = ipa.AllocateNewIP(IPClusterDefault, "12.12.0.0/16", 1) + if err != nil { + t.Fatalf("IP Alloc failed for 12.12.0.0/16:2:%s", err) + } + + err = ipa.DeAllocateIP(IPClusterDefault, "12.12.0.0/16", 0, ip1.String()) + if err != nil { + t.Fatalf("IP DeAlloc failed for %s:%s", ip1.String(), err) + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "12.12.0.0/16", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 12.12.0.0/16:1:%s", err) + } + + err = ipa.AddIPRange(IPClusterDefault, "3ffe::/64") + if err != nil { + t.Fatal("Failed to Add IP Range for 3ffe::/64") + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "3ffe::/64", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 3ffe::/64:%s", err) + } + + if ip1.String() != "3ffe::1" { + t.Fatalf("IP Alloc failed - 3ffe::1:%s", ip1.String()) + } +} From 45b6ae100e799c50c2025ee55e7ad1cfaff33416 Mon Sep 17 00:00:00 2001 From: dj-nlx Date: Tue, 17 Jan 2023 00:50:00 +0900 Subject: [PATCH 4/7] ipalloc implementation --- counter.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/counter.go b/counter.go index b42960d..ec0d0b4 100644 --- a/counter.go +++ b/counter.go @@ -9,49 +9,49 @@ import ( // Counter - context container type Counter struct { - begin int - end int - start int - len int - cap int - counters []int + begin uint64 + end uint64 + start uint64 + len uint64 + cap uint64 + counters []uint64 } // NewCounter - Allocate a set of counters -func NewCounter(begin int, length int) *Counter { +func NewCounter(begin uint64, length uint64) *Counter { counter := new(Counter) - counter.counters = make([]int, length) + counter.counters = make([]uint64, length) counter.begin = begin counter.start = 0 counter.end = length - 1 counter.len = length counter.cap = length - for i := 0; i < length; i++ { + for i := uint64(0); i < length; i++ { counter.counters[i] = i + 1 } - counter.counters[length-1] = -1 + counter.counters[length-1] = ^uint64(0) return counter } // GetCounter - Get next available counter -func (C *Counter) GetCounter() (int, error) { - if C.cap <= 0 || C.start == -1 { - return -1, errors.New("Overflow") +func (C *Counter) GetCounter() (uint64, error) { + if C.cap <= 0 || C.start == ^uint64(0) { + return ^uint64(0), errors.New("Overflow") } C.cap-- var rid = C.start if C.start == C.end { - C.start = -1 + C.start = ^uint64(0) } else { C.start = C.counters[rid] - C.counters[rid] = -1 + C.counters[rid] = ^uint64(0) } return rid + C.begin, nil } // PutCounter - Return a counter to the available list -func (C *Counter) PutCounter(id int) error { +func (C *Counter) PutCounter(id uint64) error { if id < C.begin || id >= C.begin+C.len { return errors.New("Range") } @@ -60,7 +60,7 @@ func (C *Counter) PutCounter(id int) error { C.end = rid C.counters[tmp] = rid C.cap++ - if C.start == -1 { + if C.start == ^uint64(0) { C.start = C.end } return nil From 6d6c165b6e9403ef8394de088a83a31271a881b6 Mon Sep 17 00:00:00 2001 From: dj-nlx Date: Tue, 17 Jan 2023 01:04:33 +0900 Subject: [PATCH 5/7] ipalloc implementation --- ipalloc.go | 31 ++++++++++++++++++++++--------- lib_test.go | 7 +++++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/ipalloc.go b/ipalloc.go index d1fdcd7..bb35157 100644 --- a/ipalloc.go +++ b/ipalloc.go @@ -15,6 +15,7 @@ const ( type IpRange struct { ipNet net.IPNet freeID *Counter + first uint64 ident map[uint32]struct{} } @@ -74,6 +75,7 @@ func diffIPIndex(baseIP net.IP, IP net.IP) uint64 { func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (net.IP, error) { var ipCPool *IpClusterPool var ipr *IpRange + var newIndex uint64 _, ipn, err := net.ParseCIDR(cidr) if err != nil { @@ -94,12 +96,21 @@ func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (n } if _, ok := ipr.ident[id]; ok { - return net.IP{0, 0, 0, 0}, errors.New("IP Range,Ident exists") + if id != 0 { + return net.IP{0, 0, 0, 0}, errors.New("IP Range,Ident exists") + } } - newIndex, err := ipr.freeID.GetCounter() - if err != nil { - return net.IP{0, 0, 0, 0}, errors.New("IP Alloc counter failure") + if id == 0 || ipr.first == 0 { + newIndex, err = ipr.freeID.GetCounter() + if err != nil { + return net.IP{0, 0, 0, 0}, errors.New("IP Alloc counter failure") + } + if ipr.first == 0 { + ipr.first = newIndex + } + } else { + newIndex = ipr.first } ipr.ident[id] = struct{}{} @@ -139,13 +150,15 @@ func (ipa *IpAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPS return errors.New("IP return index not found") } - err = ipr.freeID.PutCounter(retIndex) - if err != nil { - return errors.New("IP Range counter failure") - } - delete(ipr.ident, id) + if len(ipr.ident) == 0 { + err = ipr.freeID.PutCounter(retIndex) + if err != nil { + return errors.New("IP Range counter failure") + } + } + return nil } diff --git a/lib_test.go b/lib_test.go index 1094292..2a04aa8 100644 --- a/lib_test.go +++ b/lib_test.go @@ -289,7 +289,7 @@ func TestIPAlloc(t *testing.T) { ipa.AddIPRange(IPClusterDefault, "123.123.123.0/24") for i := 0; i < 255; i++ { - ip, err := ipa.AllocateNewIP(IPClusterDefault, "123.123.123.0/24", uint32(i)) + ip, err := ipa.AllocateNewIP(IPClusterDefault, "123.123.123.0/24", uint32(0)) if i >= 254 && err == nil { t.Fatal("Failed IP Alloc for 123.123.123.0/24 - Check Alloc Algo") } else if i < 254 && err != nil { @@ -342,10 +342,13 @@ func TestIPAlloc(t *testing.T) { t.Fatalf("IP Alloc failed for 12.12.0.0/16:1:%s", err) } - _, err = ipa.AllocateNewIP(IPClusterDefault, "12.12.0.0/16", 1) + ip2, err := ipa.AllocateNewIP(IPClusterDefault, "12.12.0.0/16", 1) if err != nil { t.Fatalf("IP Alloc failed for 12.12.0.0/16:2:%s", err) } + if ip2.String() != "12.12.0.1" { + t.Fatalf("Shared IP Alloc failed for 12.12.0.0/16:2:%s", err) + } err = ipa.DeAllocateIP(IPClusterDefault, "12.12.0.0/16", 0, ip1.String()) if err != nil { From c8e22a5d1b95f3b558f674697b06f6706ced7be6 Mon Sep 17 00:00:00 2001 From: Dip J Date: Tue, 17 Jan 2023 13:05:24 +0900 Subject: [PATCH 6/7] ipalloc implementation --- ipalloc.go | 81 +++++++++++++++++++++++++++++++++-------------------- lib_test.go | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 31 deletions(-) diff --git a/ipalloc.go b/ipalloc.go index bb35157..ad00e33 100644 --- a/ipalloc.go +++ b/ipalloc.go @@ -8,24 +8,28 @@ import ( "net" ) +// Constants const ( IPClusterDefault = "default" ) -type IpRange struct { +// IPRange - Defines an IPRange +type IPRange struct { ipNet net.IPNet freeID *Counter first uint64 ident map[uint32]struct{} } -type IpClusterPool struct { +// IPClusterPool - Holds IP ranges for a cluster +type IPClusterPool struct { name string - pool map[string]*IpRange + pool map[string]*IPRange } -type IpAllocator struct { - ipBlocks map[string]*IpClusterPool +// IPAllocator - Main IP allocator context +type IPAllocator struct { + ipBlocks map[string]*IPClusterPool } func addIPIndex(ip net.IP, index uint64) net.IP { @@ -72,9 +76,12 @@ func diffIPIndex(baseIP net.IP, IP net.IP) uint64 { return index } -func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (net.IP, error) { - var ipCPool *IpClusterPool - var ipr *IpRange +// AllocateNewIP - Allocate a New IP address from the given cluster and CIDR range +// If id is 0, a new IP address will be allocated else IP addresses will be shared and +// it will be same as the first IP address allocted for this range +func (ipa *IPAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (net.IP, error) { + var ipCPool *IPClusterPool + var ipr *IPRange var newIndex uint64 _, ipn, err := net.ParseCIDR(cidr) @@ -120,9 +127,10 @@ func (ipa *IpAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (n return retIP, nil } -func (ipa *IpAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPString string) error { - var ipCPool *IpClusterPool - var ipr *IpRange +// DeAllocateIP - Deallocate the IP address from the given cluster and CIDR range +func (ipa *IPAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPString string) error { + var ipCPool *IPClusterPool + var ipr *IPRange _, _, err := net.ParseCIDR(cidr) if err != nil { return errors.New("Invalid CIDR") @@ -147,7 +155,9 @@ func (ipa *IpAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPS retIndex := diffIPIndex(ipr.ipNet.IP, IP) if retIndex <= 0 { - return errors.New("IP return index not found") + if retIndex != 0 || (retIndex == 0 && ipr.first != 0) { + return errors.New("IP return index not found") + } } delete(ipr.ident, id) @@ -162,10 +172,11 @@ func (ipa *IpAllocator) DeAllocateIP(cluster string, cidr string, id uint32, IPS return nil } -func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { - var ipCPool *IpClusterPool - ip, ipn, err := net.ParseCIDR(cidr) +// AddIPRange - Add a new IP Range for allocation in a cluster +func (ipa *IPAllocator) AddIPRange(cluster string, cidr string) error { + var ipCPool *IPClusterPool + ip, ipn, err := net.ParseCIDR(cidr) if err != nil { return errors.New("Invalid CIDR") } @@ -174,7 +185,7 @@ func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { if cluster != IPClusterDefault { if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil { - ipCPool = new(IpClusterPool) + ipCPool = new(IPClusterPool) ipCPool.name = cluster ipa.ipBlocks[cluster] = ipCPool } @@ -190,22 +201,28 @@ func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { } } - ipr := new(IpRange) + ipr := new(IPRange) ipr.ipNet = *ipn iprSz := uint64(0) sz, _ := ipn.Mask.Size() + start := uint64(1) if IsNetIPv4(ip.String()) { - if sz == 31 { - iprSz = 1 + ignore := uint64(0) + if sz != 32 && sz%8 == 0 { + ignore = 2 } else { - iprSz = (1 << (32 - sz)) - 2 + start = 0 } + + iprSz = (1 << (32 - sz)) - ignore } else { - if sz == 127 { - iprSz = 1 + ignore := uint64(0) + if sz != 128 && sz%8 == 0 { + ignore = 2 } else { - iprSz = (1 << (128 - sz)) - 2 + start = 0 } + iprSz = (1 << (128 - sz)) - ignore } if iprSz < 1 { @@ -218,7 +235,7 @@ func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { // If it is a x.x.x.0/24, then we will allocate // from x.x.x.1 to x.x.x.254 - ipr.freeID = NewCounter(1, iprSz) + ipr.freeID = NewCounter(start, iprSz) if ipr.freeID == nil { return errors.New("IP Pool alloc failed") @@ -230,8 +247,9 @@ func (ipa *IpAllocator) AddIPRange(cluster string, cidr string) error { return nil } -func (ipa *IpAllocator) DeleteIPRange(cluster string, cidr string) error { - var ipCPool *IpClusterPool +// DeleteIPRange - Delete a IP Range from allocation in a cluster +func (ipa *IPAllocator) DeleteIPRange(cluster string, cidr string) error { + var ipCPool *IPClusterPool _, _, err := net.ParseCIDR(cidr) if err != nil { @@ -251,12 +269,13 @@ func (ipa *IpAllocator) DeleteIPRange(cluster string, cidr string) error { return nil } -func IpAllocatorNew() *IpAllocator { - ipa := new(IpAllocator) - ipa.ipBlocks = make(map[string]*IpClusterPool) +// IpAllocatorNew - Create a new allocator +func IpAllocatorNew() *IPAllocator { + ipa := new(IPAllocator) + ipa.ipBlocks = make(map[string]*IPClusterPool) - ipCPool := new(IpClusterPool) - ipCPool.pool = make(map[string]*IpRange) + ipCPool := new(IPClusterPool) + ipCPool.pool = make(map[string]*IPRange) ipa.ipBlocks[IPClusterDefault] = ipCPool return ipa diff --git a/lib_test.go b/lib_test.go index 2a04aa8..7ea7212 100644 --- a/lib_test.go +++ b/lib_test.go @@ -318,6 +318,15 @@ func TestIPAlloc(t *testing.T) { t.Fatal("Failed IP Alloc for 11.11.11.0/31 - Check Alloc Algo") } + if ip.String() != "11.11.11.0" { + t.Fatalf("Failed IP Alloc for 11.11.11.0/31: %s:%s", ip.String(), "11.11.11.0") + } + + ip, err = ipa.AllocateNewIP(IPClusterDefault, "11.11.11.0/31", 0) + if err != nil { + t.Fatal("Failed IP Alloc for 11.11.11.0/31 - Check Alloc Algo") + } + if ip.String() != "11.11.11.1" { t.Fatalf("Failed IP Alloc for 11.11.11.0/31: %s:%s", ip.String(), "11.11.11.1") } @@ -373,4 +382,48 @@ func TestIPAlloc(t *testing.T) { if ip1.String() != "3ffe::1" { t.Fatalf("IP Alloc failed - 3ffe::1:%s", ip1.String()) } + + err = ipa.AddIPRange(IPClusterDefault, "100.100.100.1/32") + if err != nil { + t.Fatal("Failed to Add IP Range for 100.100.100.1/32") + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "100.100.100.1/32", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 100.100.100.1/32:%s", err) + } + + if ip1.String() != "100.100.100.1" { + t.Fatalf("IP Alloc failed - 100.100.100.1:%s", ip1.String()) + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "100.100.100.1/32", 0) + if err == nil { + t.Fatalf("IP Alloc should fail for 100.100.100.1/32:%s", err) + } + + err = ipa.DeAllocateIP(IPClusterDefault, "100.100.100.1/32", 0, "100.100.100.1") + if err != nil { + t.Fatalf("IP DeAlloc failed for %s:%s", "100.100.100.1", err) + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "100.100.100.1/32", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 100.100.100.1/32:%s", err) + } + + err = ipa.AddIPRange(IPClusterDefault, "74.125.227.24/29") + if err != nil { + t.Fatalf("IP Alloc failed for 74.125.227.24/29:%s", err) + } + + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "74.125.227.24/29", 0) + if err != nil { + t.Fatalf("IP Alloc failed for 100.100.100.1/32:%s", err) + } + + if ip1.String() != "74.125.227.24" { + t.Fatalf("IP Alloc failed for 74.125.227.24:%s", ip1.String()) + } + } From 1a012a436f36664d66b9364110b7ed0d20696439 Mon Sep 17 00:00:00 2001 From: Dip J Date: Tue, 17 Jan 2023 14:19:46 +0900 Subject: [PATCH 7/7] ipalloc implementation --- ipalloc.go | 6 ++++-- lib_test.go | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ipalloc.go b/ipalloc.go index ad00e33..32337b9 100644 --- a/ipalloc.go +++ b/ipalloc.go @@ -17,6 +17,7 @@ const ( type IPRange struct { ipNet net.IPNet freeID *Counter + fOK bool first uint64 ident map[uint32]struct{} } @@ -108,13 +109,14 @@ func (ipa *IPAllocator) AllocateNewIP(cluster string, cidr string, id uint32) (n } } - if id == 0 || ipr.first == 0 { + if id == 0 || !ipr.fOK { newIndex, err = ipr.freeID.GetCounter() if err != nil { return net.IP{0, 0, 0, 0}, errors.New("IP Alloc counter failure") } - if ipr.first == 0 { + if !ipr.fOK { ipr.first = newIndex + ipr.fOK = true } } else { newIndex = ipr.first diff --git a/lib_test.go b/lib_test.go index 7ea7212..35e1118 100644 --- a/lib_test.go +++ b/lib_test.go @@ -426,4 +426,13 @@ func TestIPAlloc(t *testing.T) { t.Fatalf("IP Alloc failed for 74.125.227.24:%s", ip1.String()) } + ip1, err = ipa.AllocateNewIP(IPClusterDefault, "74.125.227.24/29", 1) + if err != nil { + t.Fatalf("IP Alloc failed for 100.100.100.1/32:%s", err) + } + + if ip1.String() != "74.125.227.24" { + t.Fatalf("IP Alloc failed for 74.125.227.24:%s", ip1.String()) + } + }