mirror of
https://github.com/danderson/netboot.git
synced 2025-10-16 10:01:20 +02:00
143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
package dhcp6
|
|
|
|
import (
|
|
"net"
|
|
"math/rand"
|
|
"time"
|
|
"math/big"
|
|
"hash/fnv"
|
|
"sync"
|
|
)
|
|
|
|
type AssociationExpiration struct {
|
|
expiresAt time.Time
|
|
ia *IdentityAssociation
|
|
}
|
|
|
|
type Fifo struct {q []interface{}}
|
|
|
|
func newFifo() Fifo {
|
|
return Fifo{q: make([]interface{}, 0, 1000)}
|
|
}
|
|
|
|
func (f *Fifo) Push(v interface{}) {
|
|
f.q = append(f.q, v)
|
|
}
|
|
|
|
func (f *Fifo) Shift() interface{} {
|
|
var to_ret interface{}
|
|
to_ret, f.q = f.q[0], f.q[1:]
|
|
return to_ret
|
|
}
|
|
|
|
func (f *Fifo) Size() int {
|
|
return len(f.q)
|
|
}
|
|
|
|
func (f *Fifo) Peek() interface{} {
|
|
if len(f.q) == 0 {
|
|
return nil
|
|
}
|
|
return f.q[0]
|
|
}
|
|
|
|
type RandomAddressPool struct {
|
|
poolStartAddress *big.Int
|
|
poolEndAddress *big.Int
|
|
identityAssociations map[uint64]*IdentityAssociation
|
|
usedIps map[uint64]struct{}
|
|
identityAssociationExpirations Fifo
|
|
validLifetime uint32 // in seconds
|
|
timeNow func() time.Time
|
|
lock sync.Mutex
|
|
}
|
|
|
|
func NewRandomAddressPool(poolStartAddress, poolEndAddress net.IP, validLifetime uint32) *RandomAddressPool {
|
|
to_ret := &RandomAddressPool{}
|
|
to_ret.validLifetime = validLifetime
|
|
to_ret.poolStartAddress = big.NewInt(0)
|
|
to_ret.poolStartAddress.SetBytes(poolStartAddress)
|
|
to_ret.poolEndAddress = big.NewInt(0)
|
|
to_ret.poolEndAddress.SetBytes(poolEndAddress)
|
|
to_ret.identityAssociations = make(map[uint64]*IdentityAssociation)
|
|
to_ret.usedIps = make(map[uint64]struct{})
|
|
to_ret.identityAssociationExpirations = newFifo()
|
|
to_ret.timeNow = func() time.Time { return time.Now() }
|
|
|
|
ticker := time.NewTicker(time.Second * 10).C
|
|
go func() {
|
|
for {
|
|
<- ticker
|
|
to_ret.ExpireIdentityAssociations()
|
|
}
|
|
}()
|
|
|
|
return to_ret
|
|
}
|
|
|
|
func (p *RandomAddressPool) ReserveAddress(clientId, interfaceId []byte) *IdentityAssociation {
|
|
p.lock.Lock()
|
|
defer p.lock.Unlock()
|
|
|
|
clientIdHash := p.calculateIaIdHash(clientId, interfaceId)
|
|
association, exists := p.identityAssociations[clientIdHash]; if exists {
|
|
return association
|
|
}
|
|
|
|
for {
|
|
rng := rand.New(rand.NewSource(p.timeNow().UnixNano()))
|
|
// we assume that ip addresses adhere to high 64 bits for net and subnet ids, low 64 bits are for host id rule
|
|
hostOffset := rng.Uint64()%(p.poolEndAddress.Uint64() - p.poolStartAddress.Uint64() + 1)
|
|
newIp := big.NewInt(0).Add(p.poolStartAddress, big.NewInt(0).SetUint64(hostOffset))
|
|
_, exists := p.usedIps[newIp.Uint64()]; if !exists {
|
|
timeNow := p.timeNow()
|
|
to_ret := &IdentityAssociation{ClientId: clientId,
|
|
InterfaceId: interfaceId,
|
|
IpAddress: newIp.Bytes(),
|
|
CreatedAt: timeNow }
|
|
p.identityAssociations[clientIdHash] = to_ret
|
|
p.usedIps[newIp.Uint64()] = struct{}{}
|
|
p.identityAssociationExpirations.Push(&AssociationExpiration{expiresAt: p.calculateAssociationExpiration(timeNow), ia: to_ret})
|
|
return to_ret
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *RandomAddressPool) ReleaseAddress(clientId, interfaceId []byte) {
|
|
p.lock.Lock()
|
|
defer p.lock.Unlock()
|
|
|
|
association, exists := p.identityAssociations[p.calculateIaIdHash(clientId, interfaceId)]
|
|
if !exists {
|
|
return
|
|
}
|
|
delete(p.usedIps, big.NewInt(0).SetBytes(association.IpAddress).Uint64())
|
|
delete(p.identityAssociations, p.calculateIaIdHash(clientId, interfaceId))
|
|
}
|
|
|
|
func (p *RandomAddressPool) ExpireIdentityAssociations() {
|
|
p.lock.Lock()
|
|
defer p.lock.Unlock()
|
|
|
|
for {
|
|
if p.identityAssociationExpirations.Size() < 1 { break }
|
|
expiration := p.identityAssociationExpirations.Peek().(*AssociationExpiration)
|
|
if p.timeNow().Before(expiration.expiresAt) { break }
|
|
p.identityAssociationExpirations.Shift()
|
|
delete(p.identityAssociations, p.calculateIaIdHash(expiration.ia.ClientId, expiration.ia.InterfaceId))
|
|
delete(p.usedIps, big.NewInt(0).SetBytes(expiration.ia.IpAddress).Uint64())
|
|
}
|
|
}
|
|
|
|
func (p *RandomAddressPool) calculateAssociationExpiration(now time.Time) time.Time {
|
|
return now.Add(time.Duration(p.validLifetime)*time.Second)
|
|
}
|
|
|
|
func (p *RandomAddressPool) calculateIaIdHash(clientId, interfaceId []byte) uint64 {
|
|
h := fnv.New64a()
|
|
h.Write(clientId)
|
|
h.Write(interfaceId)
|
|
return h.Sum64()
|
|
}
|