mirror of
https://github.com/tailscale/tailscale.git
synced 2026-06-11 21:02:39 +08:00
Some checks failed
checklocks / checklocks (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Dockerfile build / deploy (push) Has been cancelled
CI / race-root-integration (1/4) (push) Has been cancelled
CI / race-root-integration (2/4) (push) Has been cancelled
CI / race-root-integration (3/4) (push) Has been cancelled
CI / race-root-integration (4/4) (push) Has been cancelled
CI / test (-race, amd64, 1/3) (push) Has been cancelled
CI / test (-race, amd64, 2/3) (push) Has been cancelled
CI / test (-race, amd64, 3/3) (push) Has been cancelled
CI / test (386) (push) Has been cancelled
CI / test (amd64) (push) Has been cancelled
CI / windows (push) Has been cancelled
CI / privileged (push) Has been cancelled
CI / vm (push) Has been cancelled
CI / race-build (push) Has been cancelled
CI / cross (386, linux) (push) Has been cancelled
CI / cross (amd64, darwin) (push) Has been cancelled
CI / cross (amd64, freebsd) (push) Has been cancelled
CI / cross (amd64, openbsd) (push) Has been cancelled
CI / cross (amd64, windows) (push) Has been cancelled
CI / cross (arm, 5, linux) (push) Has been cancelled
CI / cross (arm, 7, linux) (push) Has been cancelled
CI / cross (arm64, darwin) (push) Has been cancelled
CI / cross (arm64, linux) (push) Has been cancelled
CI / cross (arm64, windows) (push) Has been cancelled
CI / cross (loong64, linux) (push) Has been cancelled
CI / ios (push) Has been cancelled
CI / crossmin (amd64, illumos) (push) Has been cancelled
CI / crossmin (amd64, plan9) (push) Has been cancelled
CI / crossmin (amd64, solaris) (push) Has been cancelled
CI / crossmin (ppc64, aix) (push) Has been cancelled
CI / android (push) Has been cancelled
CI / wasm (push) Has been cancelled
CI / tailscale_go (push) Has been cancelled
CI / fuzz (push) Has been cancelled
CI / depaware (push) Has been cancelled
CI / go_generate (push) Has been cancelled
CI / go_mod_tidy (push) Has been cancelled
CI / licenses (push) Has been cancelled
CI / staticcheck (386, windows) (push) Has been cancelled
CI / staticcheck (amd64, darwin) (push) Has been cancelled
CI / staticcheck (amd64, linux) (push) Has been cancelled
CI / staticcheck (amd64, windows) (push) Has been cancelled
update-flake / update-flake (push) Has been cancelled
CI / notify_slack (push) Has been cancelled
CI / check_mergeability (push) Has been cancelled
Enable nat connector to be run on a cluster of machines for high availability. Updates #14667 Signed-off-by: Fran Bull <fran@tailscale.com>
109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ippool
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
|
|
"go4.org/netipx"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/util/must"
|
|
)
|
|
|
|
func TestIPPoolExhaustion(t *testing.T) {
|
|
smallPrefix := netip.MustParsePrefix("100.64.1.0/30") // Only 4 IPs: .0, .1, .2, .3
|
|
var ipsb netipx.IPSetBuilder
|
|
ipsb.AddPrefix(smallPrefix)
|
|
addrPool := must.Get(ipsb.IPSet())
|
|
pool := SingleMachineIPPool{IPSet: addrPool}
|
|
|
|
assignedIPs := make(map[netip.Addr]string)
|
|
|
|
domains := []string{"a.example.com", "b.example.com", "c.example.com", "d.example.com", "e.example.com"}
|
|
|
|
var errs []error
|
|
|
|
from := tailcfg.NodeID(12345)
|
|
|
|
for i := 0; i < 5; i++ {
|
|
for _, domain := range domains {
|
|
addr, err := pool.IPForDomain(from, domain)
|
|
if err != nil {
|
|
errs = append(errs, fmt.Errorf("failed to get IP for domain %q: %w", domain, err))
|
|
continue
|
|
}
|
|
|
|
if d, ok := assignedIPs[addr]; ok {
|
|
if d != domain {
|
|
t.Errorf("IP %s reused for domain %q, previously assigned to %q", addr, domain, d)
|
|
}
|
|
} else {
|
|
assignedIPs[addr] = domain
|
|
}
|
|
}
|
|
}
|
|
|
|
for addr, domain := range assignedIPs {
|
|
if addr.Is4() && !smallPrefix.Contains(addr) {
|
|
t.Errorf("IP %s for domain %q not in expected range %s", addr, domain, smallPrefix)
|
|
}
|
|
}
|
|
|
|
// expect one error for each iteration with the 5th domain
|
|
if len(errs) != 5 {
|
|
t.Errorf("Expected 5 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
for _, err := range errs {
|
|
if !errors.Is(err, ErrNoIPsAvailable) {
|
|
t.Errorf("generateDNSResponse() error = %v, want ErrNoIPsAvailable", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIPPool(t *testing.T) {
|
|
var ipsb netipx.IPSetBuilder
|
|
ipsb.AddPrefix(netip.MustParsePrefix("100.64.1.0/24"))
|
|
addrPool := must.Get(ipsb.IPSet())
|
|
pool := SingleMachineIPPool{
|
|
IPSet: addrPool,
|
|
}
|
|
from := tailcfg.NodeID(12345)
|
|
addr, err := pool.IPForDomain(from, "example.com")
|
|
if err != nil {
|
|
t.Fatalf("ipForDomain() error = %v", err)
|
|
}
|
|
|
|
if !addr.IsValid() {
|
|
t.Fatal("ipForDomain() returned an invalid address")
|
|
}
|
|
|
|
if !addr.Is4() {
|
|
t.Errorf("Address is not IPv4: %s", addr)
|
|
}
|
|
|
|
if !addrPool.Contains(addr) {
|
|
t.Errorf("IPv4 address %s not in range %s", addr, addrPool)
|
|
}
|
|
|
|
domain, ok := pool.DomainForIP(from, addr, time.Now())
|
|
if !ok {
|
|
t.Errorf("domainForIP(%s) not found", addr)
|
|
} else if domain != "example.com" {
|
|
t.Errorf("domainForIP(%s) = %s, want %s", addr, domain, "example.com")
|
|
}
|
|
|
|
addr2, err := pool.IPForDomain(from, "example.com")
|
|
if err != nil {
|
|
t.Fatalf("ipForDomain() second call error = %v", err)
|
|
}
|
|
|
|
if addr.Compare(addr2) != 0 {
|
|
t.Errorf("ipForDomain() second call = %v, want %v", addr2, addr)
|
|
}
|
|
}
|