cmd/natc: add test and fix for ip exhaustion
This is a very dumb fix as it has an unbounded worst case runtime. IP allocation needs to be done in a more sane way in a follow-up. Updates #15367 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
committed by
James Tucker
parent
2685484f26
commit
d0e7af3830
+34
-12
@@ -94,18 +94,24 @@ func main() {
|
||||
}
|
||||
ignoreDstTable.Insert(pfx, true)
|
||||
}
|
||||
var v4Prefixes []netip.Prefix
|
||||
var (
|
||||
v4Prefixes []netip.Prefix
|
||||
numV4DNSAddrs int
|
||||
)
|
||||
for _, s := range strings.Split(*v4PfxStr, ",") {
|
||||
p := netip.MustParsePrefix(strings.TrimSpace(s))
|
||||
if p.Masked() != p {
|
||||
log.Fatalf("v4 prefix %v is not a masked prefix", p)
|
||||
}
|
||||
v4Prefixes = append(v4Prefixes, p)
|
||||
numIPs := 1 << (32 - p.Bits())
|
||||
numV4DNSAddrs += numIPs
|
||||
}
|
||||
if len(v4Prefixes) == 0 {
|
||||
log.Fatalf("no v4 prefixes specified")
|
||||
}
|
||||
dnsAddr := v4Prefixes[0].Addr()
|
||||
numV4DNSAddrs -= 1 // Subtract the dnsAddr allocated above.
|
||||
ts := &tsnet.Server{
|
||||
Hostname: *hostname,
|
||||
}
|
||||
@@ -153,12 +159,13 @@ func main() {
|
||||
}
|
||||
|
||||
c := &connector{
|
||||
ts: ts,
|
||||
lc: lc,
|
||||
dnsAddr: dnsAddr,
|
||||
v4Ranges: v4Prefixes,
|
||||
v6ULA: ula(uint16(*siteID)),
|
||||
ignoreDsts: ignoreDstTable,
|
||||
ts: ts,
|
||||
lc: lc,
|
||||
dnsAddr: dnsAddr,
|
||||
v4Ranges: v4Prefixes,
|
||||
numV4DNSAddrs: numV4DNSAddrs,
|
||||
v6ULA: ula(uint16(*siteID)),
|
||||
ignoreDsts: ignoreDstTable,
|
||||
}
|
||||
c.run(ctx)
|
||||
}
|
||||
@@ -177,6 +184,11 @@ type connector struct {
|
||||
// v4Ranges is the list of IPv4 ranges to advertise and assign addresses from.
|
||||
// These are masked prefixes.
|
||||
v4Ranges []netip.Prefix
|
||||
|
||||
// numV4DNSAddrs is the total size of the IPv4 ranges in addresses, minus the
|
||||
// dnsAddr allocation.
|
||||
numV4DNSAddrs int
|
||||
|
||||
// v6ULA is the ULA prefix used by the app connector to assign IPv6 addresses.
|
||||
v6ULA netip.Prefix
|
||||
|
||||
@@ -502,6 +514,7 @@ type perPeerState struct {
|
||||
mu sync.Mutex
|
||||
domainToAddr map[string][]netip.Addr
|
||||
addrToDomain *bart.Table[string]
|
||||
numV4Allocs int
|
||||
}
|
||||
|
||||
// domainForIP returns the domain name assigned to the given IP address and
|
||||
@@ -547,17 +560,25 @@ func (ps *perPeerState) isIPUsedLocked(ip netip.Addr) bool {
|
||||
|
||||
// unusedIPv4Locked returns an unused IPv4 address from the available ranges.
|
||||
func (ps *perPeerState) unusedIPv4Locked() netip.Addr {
|
||||
// All addresses have been allocated.
|
||||
if ps.numV4Allocs >= ps.c.numV4DNSAddrs {
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// TODO: skip ranges that have been exhausted
|
||||
for _, r := range ps.c.v4Ranges {
|
||||
ip := randV4(r)
|
||||
for r.Contains(ip) {
|
||||
// TODO: implement a much more efficient algorithm for finding unused IPs,
|
||||
// this is fairly crazy.
|
||||
for {
|
||||
for _, r := range ps.c.v4Ranges {
|
||||
ip := randV4(r)
|
||||
if !r.Contains(ip) {
|
||||
panic("error: randV4 returned invalid address")
|
||||
}
|
||||
if !ps.isIPUsedLocked(ip) && ip != ps.c.dnsAddr {
|
||||
return ip
|
||||
}
|
||||
ip = ip.Next()
|
||||
}
|
||||
}
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// randV4 returns a random IPv4 address within the given prefix.
|
||||
@@ -583,6 +604,7 @@ func (ps *perPeerState) assignAddrsLocked(domain string) []netip.Addr {
|
||||
if !v4.IsValid() {
|
||||
return nil
|
||||
}
|
||||
ps.numV4Allocs++
|
||||
as16 := ps.c.v6ULA.Addr().As16()
|
||||
as4 := v4.As4()
|
||||
copy(as16[12:], as4[:])
|
||||
|
||||
Reference in New Issue
Block a user