Files
lneto/ipv4/prefix_test.go
T
Pat Whittingslow a2970b923d add ipv6 to xnet.StackAsync (#107)
* add ipv6 to xnet.StackAsync

* dns improvements

* improve DNS workings of StackAsync

* add tentative ICMPv6

* work on prefixes and fix some small bugs, plan UDP/TCP6

* fix bugs in StackAsync and ipv4.Prefix.Contains

* update arpsubtable

* completely remove legacy internet.StackIP for StackIPv4/v6

* ipv4/ipv6 tcp/udp

* add TCP6/UDP6 dialing APIs

* add xnet.Stack6 interface

* more ipv6 integration into StackAsync; various tweaks to lneto and documentation+TODOs

* add stack6 tests

* replace netip.Prefix with ipv4.Prefix where it makes sense
2026-05-13 15:31:18 -03:00

229 lines
6.5 KiB
Go

package ipv4
import (
"net/netip"
"testing"
)
func TestPrefixFrom(t *testing.T) {
tests := []struct {
addr [4]byte
bits uint8
wantValid bool
wantBits uint8
wantAddr [4]byte
}{
{[4]byte{192, 168, 1, 0}, 24, true, 24, [4]byte{192, 168, 1, 0}},
{[4]byte{10, 0, 0, 0}, 8, true, 8, [4]byte{10, 0, 0, 0}},
{[4]byte{0, 0, 0, 0}, 0, true, 0, [4]byte{0, 0, 0, 0}},
{[4]byte{1, 2, 3, 4}, 32, true, 32, [4]byte{1, 2, 3, 4}},
{[4]byte{1, 2, 3, 4}, 33, true, 0, [4]byte{1, 2, 3, 4}}, // >32 clamped to 0
}
for _, tc := range tests {
p := PrefixFrom(tc.addr, tc.bits)
if p.IsValid() != tc.wantValid {
t.Errorf("PrefixFrom(%v, %d).IsValid() = %v, want %v", tc.addr, tc.bits, p.IsValid(), tc.wantValid)
}
if p.Bits() != tc.wantBits {
t.Errorf("PrefixFrom(%v, %d).Bits() = %d, want %d", tc.addr, tc.bits, p.Bits(), tc.wantBits)
}
if p.Addr() != tc.wantAddr {
t.Errorf("PrefixFrom(%v, %d).Addr() = %v, want %v", tc.addr, tc.bits, p.Addr(), tc.wantAddr)
}
}
}
func TestPrefixZeroValue(t *testing.T) {
var p Prefix
if p.IsValid() {
t.Error("zero Prefix should be invalid")
}
}
func TestPrefixFromNetip(t *testing.T) {
tests := []struct {
in string
wantValid bool
}{
{"192.168.1.0/24", true},
{"10.0.0.0/8", true},
{"0.0.0.0/0", true},
{"1.2.3.4/32", true},
{"::1/128", false}, // IPv6 should yield invalid
}
for _, tc := range tests {
npfx, err := netip.ParsePrefix(tc.in)
if err != nil {
t.Fatalf("ParsePrefix(%q): %v", tc.in, err)
}
p := PrefixFromNetip(npfx)
if p.IsValid() != tc.wantValid {
t.Errorf("PrefixFromNetip(%q).IsValid() = %v, want %v", tc.in, p.IsValid(), tc.wantValid)
}
if !tc.wantValid {
continue
}
if p.NetipPrefix() != npfx {
t.Errorf("PrefixFromNetip(%q).NetipPrefix() = %v, want %v", tc.in, p.NetipPrefix(), npfx)
}
}
}
func TestPrefixNetipRoundtrip(t *testing.T) {
inputs := []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "0.0.0.0/0", "1.2.3.4/32"}
for _, s := range inputs {
npfx := netip.MustParsePrefix(s)
p := PrefixFromNetip(npfx)
if got := p.NetipPrefix(); got != npfx {
t.Errorf("roundtrip %q: got %v", s, got)
}
}
}
func TestPrefixContains(t *testing.T) {
p := PrefixFrom([4]byte{192, 168, 1, 0}, 24)
tests := []struct {
addr [4]byte
want bool
}{
{[4]byte{192, 168, 1, 0}, true},
{[4]byte{192, 168, 1, 1}, true},
{[4]byte{192, 168, 1, 255}, true},
{[4]byte{192, 168, 2, 0}, false},
{[4]byte{10, 0, 0, 1}, false},
}
for _, tc := range tests {
if got := p.Contains(tc.addr); got != tc.want {
t.Errorf("%v.Contains(%v) = %v, want %v", p.NetipPrefix(), tc.addr, got, tc.want)
}
}
var invalid Prefix
if invalid.Contains([4]byte{0, 0, 0, 0}) {
t.Error("invalid Prefix.Contains should return false")
}
}
func TestPrefixMasked(t *testing.T) {
// Address with host bits set.
p := PrefixFrom([4]byte{192, 168, 1, 5}, 24)
m := p.Masked()
want := [4]byte{192, 168, 1, 0}
if m.Addr() != want {
t.Errorf("Masked().Addr() = %v, want %v", m.Addr(), want)
}
if m.Bits() != 24 {
t.Errorf("Masked().Bits() = %d, want 24", m.Bits())
}
}
func TestPrefixIsSingleIP(t *testing.T) {
if !PrefixFrom([4]byte{1, 2, 3, 4}, 32).IsSingleIP() {
t.Error("/32 should be single IP")
}
if PrefixFrom([4]byte{1, 2, 3, 4}, 31).IsSingleIP() {
t.Error("/31 should not be single IP")
}
var invalid Prefix
if invalid.IsSingleIP() {
t.Error("invalid Prefix.IsSingleIP should return false")
}
}
func TestPrefixOverlaps(t *testing.T) {
tests := []struct {
a, b string
want bool
}{
{"192.168.0.0/16", "192.168.1.0/24", true},
{"10.0.0.0/8", "10.1.2.0/24", true},
{"10.0.0.0/8", "192.168.0.0/16", false},
{"0.0.0.0/0", "1.2.3.4/32", true},
{"1.2.3.4/32", "1.2.3.4/32", true},
{"1.2.3.4/32", "1.2.3.5/32", false},
}
for _, tc := range tests {
a := PrefixFromNetip(netip.MustParsePrefix(tc.a))
b := PrefixFromNetip(netip.MustParsePrefix(tc.b))
if got := a.Overlaps(b); got != tc.want {
t.Errorf("%s.Overlaps(%s) = %v, want %v", tc.a, tc.b, got, tc.want)
}
// Symmetry.
if got := b.Overlaps(a); got != tc.want {
t.Errorf("%s.Overlaps(%s) [symmetric] = %v, want %v", tc.b, tc.a, got, tc.want)
}
}
var invalid Prefix
valid := PrefixFrom([4]byte{10, 0, 0, 0}, 8)
if invalid.Overlaps(valid) || valid.Overlaps(invalid) {
t.Error("invalid Prefix.Overlaps should return false")
}
}
func TestPrefixNext(t *testing.T) {
tests := []struct {
prefix string
addr [4]byte
want [4]byte
}{
// Normal increment within /24.
{"192.168.1.0/24", [4]byte{192, 168, 1, 0}, [4]byte{192, 168, 1, 1}},
{"192.168.1.0/24", [4]byte{192, 168, 1, 1}, [4]byte{192, 168, 1, 2}},
{"192.168.1.0/24", [4]byte{192, 168, 1, 254}, [4]byte{192, 168, 1, 255}},
// Wrap-around: last host addr in /24 wraps to first.
{"192.168.1.0/24", [4]byte{192, 168, 1, 255}, [4]byte{192, 168, 1, 0}},
// /32: only one host, wraps to itself.
{"1.2.3.4/32", [4]byte{1, 2, 3, 4}, [4]byte{1, 2, 3, 4}},
// /31: two hosts, wraps.
{"10.0.0.0/31", [4]byte{10, 0, 0, 0}, [4]byte{10, 0, 0, 1}},
{"10.0.0.0/31", [4]byte{10, 0, 0, 1}, [4]byte{10, 0, 0, 0}},
// /8: increment and wrap within the network.
{"10.0.0.0/8", [4]byte{10, 0, 0, 255}, [4]byte{10, 0, 1, 0}},
{"10.0.0.0/8", [4]byte{10, 255, 255, 255}, [4]byte{10, 0, 0, 0}},
}
for _, tc := range tests {
p := PrefixFromNetip(netip.MustParsePrefix(tc.prefix))
got := p.Next(tc.addr)
if got != tc.want {
t.Errorf("%s.Next(%v) = %v, want %v", tc.prefix, tc.addr, got, tc.want)
}
}
}
func TestPrefixCompare(t *testing.T) {
var invalid Prefix
a := PrefixFromNetip(netip.MustParsePrefix("10.0.0.0/8"))
b := PrefixFromNetip(netip.MustParsePrefix("192.168.0.0/16"))
// invalid < valid
if invalid.Compare(a) != -1 {
t.Error("invalid.Compare(valid) should be -1")
}
if a.Compare(invalid) != 1 {
t.Error("valid.Compare(invalid) should be 1")
}
// two invalids are equal
if invalid.Compare(Prefix{}) != 0 {
t.Error("invalid.Compare(invalid) should be 0")
}
// reflexive
if a.Compare(a) != 0 {
t.Error("a.Compare(a) should be 0")
}
// ordering
if got := a.Compare(b); got >= 0 {
t.Errorf("10/8.Compare(192.168/16) should be negative, got %d", got)
}
if got := b.Compare(a); got <= 0 {
t.Errorf("192.168/16.Compare(10/8) should be positive, got %d", got)
}
// shorter prefix < longer prefix when masked addr is equal
a8 := PrefixFromNetip(netip.MustParsePrefix("10.0.0.0/8"))
a16 := PrefixFromNetip(netip.MustParsePrefix("10.0.0.0/16"))
if a8.Compare(a16) >= 0 {
t.Error("10/8 should sort before 10/16")
}
}