mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 07:16:38 +00:00
146 lines
3.7 KiB
Go
146 lines
3.7 KiB
Go
package fakeip
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/netip"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
// 240.0.0.1 - 240.255.255.254, block 240.0.0.0/8 (reserved, RFC 1112)
|
|
v4Base = netip.AddrFrom4([4]byte{240, 0, 0, 1})
|
|
v4Max = netip.AddrFrom4([4]byte{240, 255, 255, 254})
|
|
v4Block = netip.PrefixFrom(netip.AddrFrom4([4]byte{240, 0, 0, 0}), 8)
|
|
|
|
// 0100::1 - 0100::ffff:ffff:ffff:fffe, block 0100::/64 (discard, RFC 6666)
|
|
v6Base = netip.AddrFrom16([16]byte{0x01, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01})
|
|
v6Max = netip.AddrFrom16([16]byte{0x01, 0x00, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe})
|
|
v6Block = netip.PrefixFrom(netip.AddrFrom16([16]byte{0x01, 0x00}), 64)
|
|
)
|
|
|
|
// fakeIPPool holds the allocation state for a single address family.
|
|
type fakeIPPool struct {
|
|
nextIP netip.Addr
|
|
baseIP netip.Addr
|
|
maxIP netip.Addr
|
|
block netip.Prefix
|
|
allocated map[netip.Addr]netip.Addr // real IP -> fake IP
|
|
fakeToReal map[netip.Addr]netip.Addr // fake IP -> real IP
|
|
}
|
|
|
|
func newPool(base, maxAddr netip.Addr, block netip.Prefix) *fakeIPPool {
|
|
return &fakeIPPool{
|
|
nextIP: base,
|
|
baseIP: base,
|
|
maxIP: maxAddr,
|
|
block: block,
|
|
allocated: make(map[netip.Addr]netip.Addr),
|
|
fakeToReal: make(map[netip.Addr]netip.Addr),
|
|
}
|
|
}
|
|
|
|
// allocate allocates a fake IP for the given real IP.
|
|
// Returns the existing fake IP if already allocated.
|
|
func (p *fakeIPPool) allocate(realIP netip.Addr) (netip.Addr, error) {
|
|
if fakeIP, exists := p.allocated[realIP]; exists {
|
|
return fakeIP, nil
|
|
}
|
|
|
|
startIP := p.nextIP
|
|
for {
|
|
currentIP := p.nextIP
|
|
|
|
// Advance to next IP, wrapping at boundary
|
|
if p.nextIP.Compare(p.maxIP) >= 0 {
|
|
p.nextIP = p.baseIP
|
|
} else {
|
|
p.nextIP = p.nextIP.Next()
|
|
}
|
|
|
|
if _, inUse := p.fakeToReal[currentIP]; !inUse {
|
|
p.allocated[realIP] = currentIP
|
|
p.fakeToReal[currentIP] = realIP
|
|
return currentIP, nil
|
|
}
|
|
|
|
if p.nextIP.Compare(startIP) == 0 {
|
|
return netip.Addr{}, fmt.Errorf("no more fake IPs available in %s block", p.block)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Manager manages allocation of fake IPs for dynamic DNS routes.
|
|
// IPv4 uses 240.0.0.0/8 (reserved), IPv6 uses 0100::/64 (discard, RFC 6666).
|
|
type Manager struct {
|
|
mu sync.Mutex
|
|
v4 *fakeIPPool
|
|
v6 *fakeIPPool
|
|
}
|
|
|
|
// NewManager creates a new fake IP manager.
|
|
func NewManager() *Manager {
|
|
return &Manager{
|
|
v4: newPool(v4Base, v4Max, v4Block),
|
|
v6: newPool(v6Base, v6Max, v6Block),
|
|
}
|
|
}
|
|
|
|
func (m *Manager) pool(ip netip.Addr) *fakeIPPool {
|
|
if ip.Is6() {
|
|
return m.v6
|
|
}
|
|
return m.v4
|
|
}
|
|
|
|
// AllocateFakeIP allocates a fake IP for the given real IP.
|
|
func (m *Manager) AllocateFakeIP(realIP netip.Addr) (netip.Addr, error) {
|
|
realIP = realIP.Unmap()
|
|
if !realIP.IsValid() {
|
|
return netip.Addr{}, errors.New("invalid IP address")
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
return m.pool(realIP).allocate(realIP)
|
|
}
|
|
|
|
// GetFakeIP returns the fake IP for a real IP if it exists.
|
|
func (m *Manager) GetFakeIP(realIP netip.Addr) (netip.Addr, bool) {
|
|
realIP = realIP.Unmap()
|
|
if !realIP.IsValid() {
|
|
return netip.Addr{}, false
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
fakeIP, ok := m.pool(realIP).allocated[realIP]
|
|
return fakeIP, ok
|
|
}
|
|
|
|
// GetRealIP returns the real IP for a fake IP if it exists.
|
|
func (m *Manager) GetRealIP(fakeIP netip.Addr) (netip.Addr, bool) {
|
|
fakeIP = fakeIP.Unmap()
|
|
if !fakeIP.IsValid() {
|
|
return netip.Addr{}, false
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
realIP, ok := m.pool(fakeIP).fakeToReal[fakeIP]
|
|
return realIP, ok
|
|
}
|
|
|
|
// GetFakeIPBlock returns the v4 fake IP block used by this manager.
|
|
func (m *Manager) GetFakeIPBlock() netip.Prefix {
|
|
return m.v4.block
|
|
}
|
|
|
|
// GetFakeIPv6Block returns the v6 fake IP block used by this manager.
|
|
func (m *Manager) GetFakeIPv6Block() netip.Prefix {
|
|
return m.v6.block
|
|
}
|