Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR - Shared or Unique IP Allocation for given range #1

Merged
merged 7 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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
Expand Down
284 changes: 284 additions & 0 deletions ipalloc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright (c) 2023 NetLOX Inc

package loxilib

import (
"errors"
"net"
)

// Constants
const (
IPClusterDefault = "default"
)

// IPRange - Defines an IPRange
type IPRange struct {
ipNet net.IPNet
freeID *Counter
fOK bool
first uint64
ident map[uint32]struct{}
}

// IPClusterPool - Holds IP ranges for a cluster
type IPClusterPool struct {
name string
pool map[string]*IPRange
}

// IPAllocator - Main IP allocator context
type IPAllocator struct {
ipBlocks map[string]*IPClusterPool
}

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
}

// 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)

if err != nil {
return net.IP{0, 0, 0, 0}, errors.New("Invalid CIDR")
}

if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil {
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 {
return net.IP{0, 0, 0, 0}, errors.New("No such IP Range")
}

if _, ok := ipr.ident[id]; ok {
if id != 0 {
return net.IP{0, 0, 0, 0}, errors.New("IP Range,Ident exists")
}
}

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.fOK {
ipr.first = newIndex
ipr.fOK = true
}
} else {
newIndex = ipr.first
}

ipr.ident[id] = struct{}{}

retIP := addIPIndex(ipn.IP, uint64(newIndex))

return retIP, nil
}

// 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")
}

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 {
if retIndex != 0 || (retIndex == 0 && ipr.first != 0) {
return errors.New("IP return index not found")
}
}

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
}

// 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")
}

ipCPool = ipa.ipBlocks[IPClusterDefault]

if cluster != IPClusterDefault {
if ipCPool = ipa.ipBlocks[cluster]; ipCPool == nil {
ipCPool = new(IPClusterPool)
ipCPool.name = cluster
ipa.ipBlocks[cluster] = ipCPool
}
}

if ipCPool == nil {
return errors.New("Can't find IP Cluster Pool")
}

for _, ipr := range ipCPool.pool {
if ipr.ipNet.Contains(ip) {
return errors.New("Existing IP Pool")
}
}

ipr := new(IPRange)
ipr.ipNet = *ipn
iprSz := uint64(0)
sz, _ := ipn.Mask.Size()
start := uint64(1)
if IsNetIPv4(ip.String()) {
ignore := uint64(0)
if sz != 32 && sz%8 == 0 {
ignore = 2
} else {
start = 0
}

iprSz = (1 << (32 - sz)) - ignore
} else {
ignore := uint64(0)
if sz != 128 && sz%8 == 0 {
ignore = 2
} else {
start = 0
}
iprSz = (1 << (128 - sz)) - ignore
}

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/24, then we will allocate
// from x.x.x.1 to x.x.x.254
ipr.freeID = NewCounter(start, iprSz)

if ipr.freeID == nil {
return errors.New("IP Pool alloc failed")
}

ipr.ident = make(map[uint32]struct{})
ipCPool.pool[cidr] = ipr

return nil
}

// 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 {
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
}

// 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)
ipa.ipBlocks[IPClusterDefault] = ipCPool

return ipa
}
Loading