1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package netip_test
6
7import (
8	"bytes"
9	"encoding/json"
10	"flag"
11	"fmt"
12	"internal/testenv"
13	"net"
14	. "net/netip"
15	"reflect"
16	"slices"
17	"strings"
18	"testing"
19	"unique"
20)
21
22var long = flag.Bool("long", false, "run long tests")
23
24type uint128 = Uint128
25
26var (
27	mustPrefix = MustParsePrefix
28	mustIP     = MustParseAddr
29	mustIPPort = MustParseAddrPort
30)
31
32func TestParseAddr(t *testing.T) {
33	var validIPs = []struct {
34		in      string
35		ip      Addr   // output of ParseAddr()
36		str     string // output of String(). If "", use in.
37		wantErr string
38	}{
39		// Basic zero IPv4 address.
40		{
41			in: "0.0.0.0",
42			ip: MkAddr(Mk128(0, 0xffff00000000), Z4),
43		},
44		// Basic non-zero IPv4 address.
45		{
46			in: "192.168.140.255",
47			ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4),
48		},
49		// IPv4 address in windows-style "print all the digits" form.
50		{
51			in:      "010.000.015.001",
52			wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`,
53		},
54		// IPv4 address with a silly amount of leading zeros.
55		{
56			in:      "000001.00000002.00000003.000000004",
57			wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`,
58		},
59		// 4-in-6 with octet with leading zero
60		{
61			in:      "::ffff:1.2.03.4",
62			wantErr: `ParseAddr("::ffff:1.2.03.4"): IPv4 field has octet with leading zero`,
63		},
64		// 4-in-6 with octet with unexpected character
65		{
66			in:      "::ffff:1.2.3.z",
67			wantErr: `ParseAddr("::ffff:1.2.3.z"): unexpected character (at "z")`,
68		},
69		// Basic zero IPv6 address.
70		{
71			in: "::",
72			ip: MkAddr(Mk128(0, 0), Z6noz),
73		},
74		// Localhost IPv6.
75		{
76			in: "::1",
77			ip: MkAddr(Mk128(0, 1), Z6noz),
78		},
79		// Fully expanded IPv6 address.
80		{
81			in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
82			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz),
83		},
84		// IPv6 with elided fields in the middle.
85		{
86			in: "fd7a:115c::626b:430b",
87			ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz),
88		},
89		// IPv6 with elided fields at the end.
90		{
91			in: "fd7a:115c:a1e0:ab12:4843:cd96::",
92			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz),
93		},
94		// IPv6 with single elided field at the end.
95		{
96			in:  "fd7a:115c:a1e0:ab12:4843:cd96:626b::",
97			ip:  MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz),
98			str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
99		},
100		// IPv6 with single elided field in the middle.
101		{
102			in:  "fd7a:115c:a1e0::4843:cd96:626b:430b",
103			ip:  MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz),
104			str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b",
105		},
106		// IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6)
107		{
108			in:  "::ffff:192.168.140.255",
109			ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz),
110			str: "::ffff:192.168.140.255",
111		},
112		// IPv6 with a zone specifier.
113		{
114			in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
115			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(MakeAddrDetail(true, "eth0"))),
116		},
117		// IPv6 with dotted decimal and zone specifier.
118		{
119			in:  "1:2::ffff:192.168.140.255%eth1",
120			ip:  MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
121			str: "1:2::ffff:c0a8:8cff%eth1",
122		},
123		// 4-in-6 with zone
124		{
125			in:  "::ffff:192.168.140.255%eth1",
126			ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
127			str: "::ffff:192.168.140.255%eth1",
128		},
129		// IPv6 with capital letters.
130		{
131			in:  "FD9E:1A04:F01D::1",
132			ip:  MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz),
133			str: "fd9e:1a04:f01d::1",
134		},
135	}
136
137	for _, test := range validIPs {
138		t.Run(test.in, func(t *testing.T) {
139			got, err := ParseAddr(test.in)
140			if err != nil {
141				if err.Error() == test.wantErr {
142					return
143				}
144				t.Fatal(err)
145			}
146			if test.wantErr != "" {
147				t.Fatalf("wanted error %q; got none", test.wantErr)
148			}
149			if got != test.ip {
150				t.Errorf("got %#v, want %#v", got, test.ip)
151			}
152
153			// Check that ParseAddr is a pure function.
154			got2, err := ParseAddr(test.in)
155			if err != nil {
156				t.Fatal(err)
157			}
158			if got != got2 {
159				t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2)
160			}
161
162			// Check that ParseAddr(ip.String()) is the identity function.
163			s := got.String()
164			got3, err := ParseAddr(s)
165			if err != nil {
166				t.Fatal(err)
167			}
168			if got != got3 {
169				t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got)
170			}
171
172			// Check that the slow-but-readable parser produces the same result.
173			slow, err := parseIPSlow(test.in)
174			if err != nil {
175				t.Fatal(err)
176			}
177			if got != slow {
178				t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow)
179			}
180
181			// Check that the parsed IP formats as expected.
182			s = got.String()
183			wants := test.str
184			if wants == "" {
185				wants = test.in
186			}
187			if s != wants {
188				t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants)
189			}
190
191			// Check that AppendTo matches MarshalText.
192			TestAppendToMarshal(t, got)
193
194			// Check that MarshalText/UnmarshalText work similarly to
195			// ParseAddr/String (see TestIPMarshalUnmarshal for
196			// marshal-specific behavior that's not common with
197			// ParseAddr/String).
198			js := `"` + test.in + `"`
199			var jsgot Addr
200			if err := json.Unmarshal([]byte(js), &jsgot); err != nil {
201				t.Fatal(err)
202			}
203			if jsgot != got {
204				t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got)
205			}
206			jsb, err := json.Marshal(jsgot)
207			if err != nil {
208				t.Fatal(err)
209			}
210			jswant := `"` + wants + `"`
211			jsback := string(jsb)
212			if jsback != jswant {
213				t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant)
214			}
215		})
216	}
217
218	var invalidIPs = []string{
219		// Empty string
220		"",
221		// Garbage non-IP
222		"bad",
223		// Single number. Some parsers accept this as an IPv4 address in
224		// big-endian uint32 form, but we don't.
225		"1234",
226		// IPv4 with a zone specifier
227		"1.2.3.4%eth0",
228		// IPv4 field must have at least one digit
229		".1.2.3",
230		"1.2.3.",
231		"1..2.3",
232		// IPv4 address too long
233		"1.2.3.4.5",
234		// IPv4 in dotted octal form
235		"0300.0250.0214.0377",
236		// IPv4 in dotted hex form
237		"0xc0.0xa8.0x8c.0xff",
238		// IPv4 in class B form
239		"192.168.12345",
240		// IPv4 in class B form, with a small enough number to be
241		// parseable as a regular dotted decimal field.
242		"127.0.1",
243		// IPv4 in class A form
244		"192.1234567",
245		// IPv4 in class A form, with a small enough number to be
246		// parseable as a regular dotted decimal field.
247		"127.1",
248		// IPv4 field has value >255
249		"192.168.300.1",
250		// IPv4 with too many fields
251		"192.168.0.1.5.6",
252		// IPv6 with not enough fields
253		"1:2:3:4:5:6:7",
254		// IPv6 with too many fields
255		"1:2:3:4:5:6:7:8:9",
256		// IPv6 with 8 fields and a :: expander
257		"1:2:3:4::5:6:7:8",
258		// IPv6 with a field bigger than 2b
259		"fe801::1",
260		// IPv6 with non-hex values in field
261		"fe80:tail:scal:e::",
262		// IPv6 with a zone delimiter but no zone.
263		"fe80::1%",
264		// IPv6 (without ellipsis) with too many fields for trailing embedded IPv4.
265		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
266		// IPv6 (with ellipsis) with too many fields for trailing embedded IPv4.
267		"ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
268		// IPv6 with invalid embedded IPv4.
269		"::ffff:192.168.140.bad",
270		// IPv6 with multiple ellipsis ::.
271		"fe80::1::1",
272		// IPv6 with invalid non hex/colon character.
273		"fe80:1?:1",
274		// IPv6 with truncated bytes after single colon.
275		"fe80:",
276		// IPv6 with 5 zeros in last group
277		"0:0:0:0:0:ffff:0:00000",
278		// IPv6 with 5 zeros in one group and embedded IPv4
279		"0:0:0:0:00000:ffff:127.1.2.3",
280	}
281
282	for _, s := range invalidIPs {
283		t.Run(s, func(t *testing.T) {
284			got, err := ParseAddr(s)
285			if err == nil {
286				t.Errorf("ParseAddr(%q) = %#v, want error", s, got)
287			}
288
289			slow, err := parseIPSlow(s)
290			if err == nil {
291				t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow)
292			}
293
294			std := net.ParseIP(s)
295			if std != nil {
296				t.Errorf("net.ParseIP(%q) = %#v, want error", s, std)
297			}
298
299			if s == "" {
300				// Don't test unmarshaling of "" here, do it in
301				// IPMarshalUnmarshal.
302				return
303			}
304			var jsgot Addr
305			js := []byte(`"` + s + `"`)
306			if err := json.Unmarshal(js, &jsgot); err == nil {
307				t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot)
308			}
309		})
310	}
311}
312
313func TestAddrFromSlice(t *testing.T) {
314	tests := []struct {
315		ip       []byte
316		wantAddr Addr
317		wantOK   bool
318	}{
319		{
320			ip:       []byte{10, 0, 0, 1},
321			wantAddr: mustIP("10.0.0.1"),
322			wantOK:   true,
323		},
324		{
325			ip:       []byte{0xfe, 0x80, 15: 0x01},
326			wantAddr: mustIP("fe80::01"),
327			wantOK:   true,
328		},
329		{
330			ip:       []byte{0, 1, 2},
331			wantAddr: Addr{},
332			wantOK:   false,
333		},
334		{
335			ip:       nil,
336			wantAddr: Addr{},
337			wantOK:   false,
338		},
339	}
340	for _, tt := range tests {
341		addr, ok := AddrFromSlice(tt.ip)
342		if ok != tt.wantOK || addr != tt.wantAddr {
343			t.Errorf("AddrFromSlice(%#v) = %#v, %v, want %#v, %v", tt.ip, addr, ok, tt.wantAddr, tt.wantOK)
344		}
345	}
346}
347
348func TestIPv4Constructors(t *testing.T) {
349	if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") {
350		t.Errorf("don't match")
351	}
352}
353
354func TestAddrMarshalUnmarshalBinary(t *testing.T) {
355	tests := []struct {
356		ip       string
357		wantSize int
358	}{
359		{"", 0}, // zero IP
360		{"1.2.3.4", 4},
361		{"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16},
362		{"::ffff:c000:0280", 16},
363		{"::ffff:c000:0280%eth0", 20},
364	}
365	for _, tc := range tests {
366		var ip Addr
367		if len(tc.ip) > 0 {
368			ip = mustIP(tc.ip)
369		}
370		b, err := ip.MarshalBinary()
371		if err != nil {
372			t.Fatal(err)
373		}
374		if len(b) != tc.wantSize {
375			t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize)
376		}
377		var ip2 Addr
378		if err := ip2.UnmarshalBinary(b); err != nil {
379			t.Fatal(err)
380		}
381		if ip != ip2 {
382			t.Fatalf("got %v; want %v", ip2, ip)
383		}
384	}
385
386	// Cannot unmarshal from unexpected IP length.
387	for _, n := range []int{3, 5} {
388		var ip2 Addr
389		if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
390			t.Fatalf("unmarshaled from unexpected IP length %d", n)
391		}
392	}
393}
394
395func TestAddrPortMarshalTextString(t *testing.T) {
396	tests := []struct {
397		in   AddrPort
398		want string
399	}{
400		{mustIPPort("1.2.3.4:80"), "1.2.3.4:80"},
401		{mustIPPort("[::]:80"), "[::]:80"},
402		{mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"},
403		{mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"},
404		{mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"},
405		{mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"},
406	}
407	for i, tt := range tests {
408		if got := tt.in.String(); got != tt.want {
409			t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want)
410		}
411		mt, err := tt.in.MarshalText()
412		if err != nil {
413			t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
414			continue
415		}
416		if string(mt) != tt.want {
417			t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want)
418		}
419	}
420}
421
422func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
423	tests := []struct {
424		ipport   string
425		wantSize int
426	}{
427		{"1.2.3.4:51820", 4 + 2},
428		{"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
429		{"[::ffff:c000:0280]:65535", 16 + 2},
430		{"[::ffff:c000:0280%eth0]:1", 20 + 2},
431	}
432	for _, tc := range tests {
433		var ipport AddrPort
434		if len(tc.ipport) > 0 {
435			ipport = mustIPPort(tc.ipport)
436		}
437		b, err := ipport.MarshalBinary()
438		if err != nil {
439			t.Fatal(err)
440		}
441		if len(b) != tc.wantSize {
442			t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
443		}
444		var ipport2 AddrPort
445		if err := ipport2.UnmarshalBinary(b); err != nil {
446			t.Fatal(err)
447		}
448		if ipport != ipport2 {
449			t.Fatalf("got %v; want %v", ipport2, ipport)
450		}
451	}
452
453	// Cannot unmarshal from unexpected lengths.
454	for _, n := range []int{3, 7} {
455		var ipport2 AddrPort
456		if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
457			t.Fatalf("unmarshaled from unexpected length %d", n)
458		}
459	}
460}
461
462func TestPrefixMarshalTextString(t *testing.T) {
463	tests := []struct {
464		in   Prefix
465		want string
466	}{
467		{mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
468		{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
469		{mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
470		{mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
471		{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped
472	}
473	for i, tt := range tests {
474		if got := tt.in.String(); got != tt.want {
475			t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want)
476		}
477		mt, err := tt.in.MarshalText()
478		if err != nil {
479			t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err)
480			continue
481		}
482		if string(mt) != tt.want {
483			t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want)
484		}
485	}
486}
487
488func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
489	type testCase struct {
490		prefix   Prefix
491		wantSize int
492	}
493	tests := []testCase{
494		{mustPrefix("1.2.3.4/24"), 4 + 1},
495		{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
496		{mustPrefix("::ffff:c000:0280/96"), 16 + 1},
497		{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped
498	}
499	tests = append(tests,
500		testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
501		testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
502	for _, tc := range tests {
503		prefix := tc.prefix
504		b, err := prefix.MarshalBinary()
505		if err != nil {
506			t.Fatal(err)
507		}
508		if len(b) != tc.wantSize {
509			t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
510		}
511		var prefix2 Prefix
512		if err := prefix2.UnmarshalBinary(b); err != nil {
513			t.Fatal(err)
514		}
515		if prefix != prefix2 {
516			t.Fatalf("got %v; want %v", prefix2, prefix)
517		}
518	}
519
520	// Cannot unmarshal from unexpected lengths.
521	for _, n := range []int{3, 6} {
522		var prefix2 Prefix
523		if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
524			t.Fatalf("unmarshaled from unexpected length %d", n)
525		}
526	}
527}
528
529func TestAddrMarshalUnmarshal(t *testing.T) {
530	// This only tests the cases where Marshal/Unmarshal diverges from
531	// the behavior of ParseAddr/String. For the rest of the test cases,
532	// see TestParseAddr above.
533	orig := `""`
534	var ip Addr
535	if err := json.Unmarshal([]byte(orig), &ip); err != nil {
536		t.Fatalf("Unmarshal(%q) got error %v", orig, err)
537	}
538	if ip != (Addr{}) {
539		t.Errorf("Unmarshal(%q) is not the zero Addr", orig)
540	}
541
542	jsb, err := json.Marshal(ip)
543	if err != nil {
544		t.Fatalf("Marshal(%v) got error %v", ip, err)
545	}
546	back := string(jsb)
547	if back != orig {
548		t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig)
549	}
550}
551
552func TestAddrFrom16(t *testing.T) {
553	tests := []struct {
554		name string
555		in   [16]byte
556		want Addr
557	}{
558		{
559			name: "v6-raw",
560			in:   [...]byte{15: 1},
561			want: MkAddr(Mk128(0, 1), Z6noz),
562		},
563		{
564			name: "v4-raw",
565			in:   [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4},
566			want: MkAddr(Mk128(0, 0xffff01020304), Z6noz),
567		},
568	}
569	for _, tt := range tests {
570		t.Run(tt.name, func(t *testing.T) {
571			got := AddrFrom16(tt.in)
572			if got != tt.want {
573				t.Errorf("got %#v; want %#v", got, tt.want)
574			}
575		})
576	}
577}
578
579func TestIPProperties(t *testing.T) {
580	var (
581		nilIP Addr
582
583		unicast4           = mustIP("192.0.2.1")
584		unicast6           = mustIP("2001:db8::1")
585		unicastZone6       = mustIP("2001:db8::1%eth0")
586		unicast6Unassigned = mustIP("4000::1") // not in 2000::/3.
587
588		multicast4     = mustIP("224.0.0.1")
589		multicast6     = mustIP("ff02::1")
590		multicastZone6 = mustIP("ff02::1%eth0")
591
592		llu4     = mustIP("169.254.0.1")
593		llu6     = mustIP("fe80::1")
594		llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
595		lluZone6 = mustIP("fe80::1%eth0")
596
597		loopback4 = mustIP("127.0.0.1")
598
599		ilm6     = mustIP("ff01::1")
600		ilmZone6 = mustIP("ff01::1%eth0")
601
602		private4a        = mustIP("10.0.0.1")
603		private4b        = mustIP("172.16.0.1")
604		private4c        = mustIP("192.168.1.1")
605		private6         = mustIP("fd00::1")
606		private6mapped4a = mustIP("::ffff:10.0.0.1")
607		private6mapped4b = mustIP("::ffff:172.16.0.1")
608		private6mapped4c = mustIP("::ffff:192.168.1.1")
609	)
610
611	tests := []struct {
612		name                    string
613		ip                      Addr
614		globalUnicast           bool
615		interfaceLocalMulticast bool
616		linkLocalMulticast      bool
617		linkLocalUnicast        bool
618		loopback                bool
619		multicast               bool
620		private                 bool
621		unspecified             bool
622	}{
623		{
624			name: "nil",
625			ip:   nilIP,
626		},
627		{
628			name:          "unicast v4Addr",
629			ip:            unicast4,
630			globalUnicast: true,
631		},
632		{
633			name:          "unicast v6 mapped v4Addr",
634			ip:            AddrFrom16(unicast4.As16()),
635			globalUnicast: true,
636		},
637		{
638			name:          "unicast v6Addr",
639			ip:            unicast6,
640			globalUnicast: true,
641		},
642		{
643			name:          "unicast v6AddrZone",
644			ip:            unicastZone6,
645			globalUnicast: true,
646		},
647		{
648			name:          "unicast v6Addr unassigned",
649			ip:            unicast6Unassigned,
650			globalUnicast: true,
651		},
652		{
653			name:               "multicast v4Addr",
654			ip:                 multicast4,
655			linkLocalMulticast: true,
656			multicast:          true,
657		},
658		{
659			name:               "multicast v6 mapped v4Addr",
660			ip:                 AddrFrom16(multicast4.As16()),
661			linkLocalMulticast: true,
662			multicast:          true,
663		},
664		{
665			name:               "multicast v6Addr",
666			ip:                 multicast6,
667			linkLocalMulticast: true,
668			multicast:          true,
669		},
670		{
671			name:               "multicast v6AddrZone",
672			ip:                 multicastZone6,
673			linkLocalMulticast: true,
674			multicast:          true,
675		},
676		{
677			name:             "link-local unicast v4Addr",
678			ip:               llu4,
679			linkLocalUnicast: true,
680		},
681		{
682			name:             "link-local unicast v6 mapped v4Addr",
683			ip:               AddrFrom16(llu4.As16()),
684			linkLocalUnicast: true,
685		},
686		{
687			name:             "link-local unicast v6Addr",
688			ip:               llu6,
689			linkLocalUnicast: true,
690		},
691		{
692			name:             "link-local unicast v6Addr upper bound",
693			ip:               llu6Last,
694			linkLocalUnicast: true,
695		},
696		{
697			name:             "link-local unicast v6AddrZone",
698			ip:               lluZone6,
699			linkLocalUnicast: true,
700		},
701		{
702			name:     "loopback v4Addr",
703			ip:       loopback4,
704			loopback: true,
705		},
706		{
707			name:     "loopback v6Addr",
708			ip:       IPv6Loopback(),
709			loopback: true,
710		},
711		{
712			name:     "loopback v6 mapped v4Addr",
713			ip:       AddrFrom16(IPv6Loopback().As16()),
714			loopback: true,
715		},
716		{
717			name:                    "interface-local multicast v6Addr",
718			ip:                      ilm6,
719			interfaceLocalMulticast: true,
720			multicast:               true,
721		},
722		{
723			name:                    "interface-local multicast v6AddrZone",
724			ip:                      ilmZone6,
725			interfaceLocalMulticast: true,
726			multicast:               true,
727		},
728		{
729			name:          "private v4Addr 10/8",
730			ip:            private4a,
731			globalUnicast: true,
732			private:       true,
733		},
734		{
735			name:          "private v4Addr 172.16/12",
736			ip:            private4b,
737			globalUnicast: true,
738			private:       true,
739		},
740		{
741			name:          "private v4Addr 192.168/16",
742			ip:            private4c,
743			globalUnicast: true,
744			private:       true,
745		},
746		{
747			name:          "private v6Addr",
748			ip:            private6,
749			globalUnicast: true,
750			private:       true,
751		},
752		{
753			name:          "private v6 mapped v4Addr 10/8",
754			ip:            private6mapped4a,
755			globalUnicast: true,
756			private:       true,
757		},
758		{
759			name:          "private v6 mapped v4Addr 172.16/12",
760			ip:            private6mapped4b,
761			globalUnicast: true,
762			private:       true,
763		},
764		{
765			name:          "private v6 mapped v4Addr 192.168/16",
766			ip:            private6mapped4c,
767			globalUnicast: true,
768			private:       true,
769		},
770		{
771			name:        "unspecified v4Addr",
772			ip:          IPv4Unspecified(),
773			unspecified: true,
774		},
775		{
776			name:        "unspecified v6Addr",
777			ip:          IPv6Unspecified(),
778			unspecified: true,
779		},
780	}
781
782	for _, tt := range tests {
783		t.Run(tt.name, func(t *testing.T) {
784			gu := tt.ip.IsGlobalUnicast()
785			if gu != tt.globalUnicast {
786				t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast)
787			}
788
789			ilm := tt.ip.IsInterfaceLocalMulticast()
790			if ilm != tt.interfaceLocalMulticast {
791				t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast)
792			}
793
794			llu := tt.ip.IsLinkLocalUnicast()
795			if llu != tt.linkLocalUnicast {
796				t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast)
797			}
798
799			llm := tt.ip.IsLinkLocalMulticast()
800			if llm != tt.linkLocalMulticast {
801				t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast)
802			}
803
804			lo := tt.ip.IsLoopback()
805			if lo != tt.loopback {
806				t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback)
807			}
808
809			multicast := tt.ip.IsMulticast()
810			if multicast != tt.multicast {
811				t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast)
812			}
813
814			private := tt.ip.IsPrivate()
815			if private != tt.private {
816				t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private)
817			}
818
819			unspecified := tt.ip.IsUnspecified()
820			if unspecified != tt.unspecified {
821				t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified)
822			}
823		})
824	}
825}
826
827func TestAddrWellKnown(t *testing.T) {
828	tests := []struct {
829		name string
830		ip   Addr
831		std  net.IP
832	}{
833		{
834			name: "IPv4 unspecified",
835			ip:   IPv4Unspecified(),
836			std:  net.IPv4zero,
837		},
838		{
839			name: "IPv6 link-local all nodes",
840			ip:   IPv6LinkLocalAllNodes(),
841			std:  net.IPv6linklocalallnodes,
842		},
843		{
844			name: "IPv6 link-local all routers",
845			ip:   IPv6LinkLocalAllRouters(),
846			std:  net.IPv6linklocalallrouters,
847		},
848		{
849			name: "IPv6 loopback",
850			ip:   IPv6Loopback(),
851			std:  net.IPv6loopback,
852		},
853		{
854			name: "IPv6 unspecified",
855			ip:   IPv6Unspecified(),
856			std:  net.IPv6unspecified,
857		},
858	}
859
860	for _, tt := range tests {
861		t.Run(tt.name, func(t *testing.T) {
862			want := tt.std.String()
863			got := tt.ip.String()
864
865			if got != want {
866				t.Fatalf("got %s, want %s", got, want)
867			}
868		})
869	}
870}
871
872func TestAddrLessCompare(t *testing.T) {
873	tests := []struct {
874		a, b Addr
875		want bool
876	}{
877		{Addr{}, Addr{}, false},
878		{Addr{}, mustIP("1.2.3.4"), true},
879		{mustIP("1.2.3.4"), Addr{}, false},
880
881		{mustIP("1.2.3.4"), mustIP("0102:0304::0"), true},
882		{mustIP("0102:0304::0"), mustIP("1.2.3.4"), false},
883		{mustIP("1.2.3.4"), mustIP("1.2.3.4"), false},
884
885		{mustIP("::1"), mustIP("::2"), true},
886		{mustIP("::1"), mustIP("::1%foo"), true},
887		{mustIP("::1%foo"), mustIP("::2"), true},
888		{mustIP("::2"), mustIP("::3"), true},
889
890		{mustIP("::"), mustIP("0.0.0.0"), false},
891		{mustIP("0.0.0.0"), mustIP("::"), true},
892
893		{mustIP("::1%a"), mustIP("::1%b"), true},
894		{mustIP("::1%a"), mustIP("::1%a"), false},
895		{mustIP("::1%b"), mustIP("::1%a"), false},
896
897		// For Issue 68113, verify that an IPv4 address and a
898		// v4-mapped-IPv6 address differing only in their zone
899		// pointer are unequal via all three of
900		// ==/Compare/reflect.DeepEqual. In Go 1.22 and
901		// earlier, these were accidentally equal via
902		// DeepEqual due to their zone pointers (z) differing
903		// but pointing to identical structures.
904		{mustIP("::ffff:11.1.1.12"), mustIP("11.1.1.12"), false},
905	}
906	for _, tt := range tests {
907		got := tt.a.Less(tt.b)
908		if got != tt.want {
909			t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
910		}
911		cmp := tt.a.Compare(tt.b)
912		if got && cmp != -1 {
913			t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp)
914		}
915		if cmp < -1 || cmp > 1 {
916			t.Errorf("bogus Compare return value %v", cmp)
917		}
918		if cmp == 0 && tt.a != tt.b {
919			t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b)
920		}
921		if cmp == 1 && !tt.b.Less(tt.a) {
922			t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b)
923		}
924
925		// Also check inverse.
926		if got == tt.want && got {
927			got2 := tt.b.Less(tt.a)
928			if got2 {
929				t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
930			}
931		}
932
933		// Also check reflect.DeepEqual. See issue 68113.
934		deepEq := reflect.DeepEqual(tt.a, tt.b)
935		if (cmp == 0) != deepEq {
936			t.Errorf("%q and %q differ in == (%v) vs reflect.DeepEqual (%v)", tt.a, tt.b, cmp == 0, deepEq)
937		}
938	}
939
940	// And just sort.
941	values := []Addr{
942		mustIP("::1"),
943		mustIP("::2"),
944		Addr{},
945		mustIP("1.2.3.4"),
946		mustIP("8.8.8.8"),
947		mustIP("::1%foo"),
948	}
949	slices.SortFunc(values, Addr.Compare)
950	got := fmt.Sprintf("%s", values)
951	want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]`
952	if got != want {
953		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
954	}
955}
956
957func TestAddrPortCompare(t *testing.T) {
958	tests := []struct {
959		a, b AddrPort
960		want int
961	}{
962		{AddrPort{}, AddrPort{}, 0},
963		{AddrPort{}, mustIPPort("1.2.3.4:80"), -1},
964
965		{mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:80"), 0},
966		{mustIPPort("[::1]:80"), mustIPPort("[::1]:80"), 0},
967
968		{mustIPPort("1.2.3.4:80"), mustIPPort("2.3.4.5:22"), -1},
969		{mustIPPort("[::1]:80"), mustIPPort("[::2]:22"), -1},
970
971		{mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:443"), -1},
972		{mustIPPort("[::1]:80"), mustIPPort("[::1]:443"), -1},
973
974		{mustIPPort("1.2.3.4:80"), mustIPPort("[0102:0304::0]:80"), -1},
975	}
976	for _, tt := range tests {
977		got := tt.a.Compare(tt.b)
978		if got != tt.want {
979			t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
980		}
981
982		// Also check inverse.
983		if got == tt.want {
984			got2 := tt.b.Compare(tt.a)
985			if want2 := -1 * tt.want; got2 != want2 {
986				t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
987			}
988		}
989	}
990
991	// And just sort.
992	values := []AddrPort{
993		mustIPPort("[::1]:80"),
994		mustIPPort("[::2]:80"),
995		AddrPort{},
996		mustIPPort("1.2.3.4:443"),
997		mustIPPort("8.8.8.8:8080"),
998		mustIPPort("[::1%foo]:1024"),
999	}
1000	slices.SortFunc(values, AddrPort.Compare)
1001	got := fmt.Sprintf("%s", values)
1002	want := `[invalid AddrPort 1.2.3.4:443 8.8.8.8:8080 [::1]:80 [::1%foo]:1024 [::2]:80]`
1003	if got != want {
1004		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
1005	}
1006}
1007
1008func TestPrefixCompare(t *testing.T) {
1009	tests := []struct {
1010		a, b Prefix
1011		want int
1012	}{
1013		{Prefix{}, Prefix{}, 0},
1014		{Prefix{}, mustPrefix("1.2.3.0/24"), -1},
1015
1016		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/24"), 0},
1017		{mustPrefix("fe80::/64"), mustPrefix("fe80::/64"), 0},
1018
1019		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.4.0/24"), -1},
1020		{mustPrefix("fe80::/64"), mustPrefix("fe90::/64"), -1},
1021
1022		{mustPrefix("1.2.0.0/16"), mustPrefix("1.2.0.0/24"), -1},
1023		{mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
1024
1025		{mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
1026	}
1027	for _, tt := range tests {
1028		got := tt.a.Compare(tt.b)
1029		if got != tt.want {
1030			t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
1031		}
1032
1033		// Also check inverse.
1034		if got == tt.want {
1035			got2 := tt.b.Compare(tt.a)
1036			if want2 := -1 * tt.want; got2 != want2 {
1037				t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
1038			}
1039		}
1040	}
1041
1042	// And just sort.
1043	values := []Prefix{
1044		mustPrefix("1.2.3.0/24"),
1045		mustPrefix("fe90::/64"),
1046		mustPrefix("fe80::/64"),
1047		mustPrefix("1.2.0.0/16"),
1048		Prefix{},
1049		mustPrefix("fe80::/48"),
1050		mustPrefix("1.2.0.0/24"),
1051	}
1052	slices.SortFunc(values, Prefix.Compare)
1053	got := fmt.Sprintf("%s", values)
1054	want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
1055	if got != want {
1056		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
1057	}
1058}
1059
1060func TestIPStringExpanded(t *testing.T) {
1061	tests := []struct {
1062		ip Addr
1063		s  string
1064	}{
1065		{
1066			ip: Addr{},
1067			s:  "invalid IP",
1068		},
1069		{
1070			ip: mustIP("192.0.2.1"),
1071			s:  "192.0.2.1",
1072		},
1073		{
1074			ip: mustIP("::ffff:192.0.2.1"),
1075			s:  "0000:0000:0000:0000:0000:ffff:c000:0201",
1076		},
1077		{
1078			ip: mustIP("2001:db8::1"),
1079			s:  "2001:0db8:0000:0000:0000:0000:0000:0001",
1080		},
1081		{
1082			ip: mustIP("2001:db8::1%eth0"),
1083			s:  "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
1084		},
1085	}
1086
1087	for _, tt := range tests {
1088		t.Run(tt.ip.String(), func(t *testing.T) {
1089			want := tt.s
1090			got := tt.ip.StringExpanded()
1091
1092			if got != want {
1093				t.Fatalf("got %s, want %s", got, want)
1094			}
1095		})
1096	}
1097}
1098
1099func TestPrefixMasking(t *testing.T) {
1100	type subtest struct {
1101		ip   Addr
1102		bits uint8
1103		p    Prefix
1104		ok   bool
1105	}
1106
1107	// makeIPv6 produces a set of IPv6 subtests with an optional zone identifier.
1108	makeIPv6 := func(zone string) []subtest {
1109		if zone != "" {
1110			zone = "%" + zone
1111		}
1112
1113		return []subtest{
1114			{
1115				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1116				bits: 255,
1117			},
1118			{
1119				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1120				bits: 32,
1121				p:    mustPrefix("2001:db8::/32"),
1122				ok:   true,
1123			},
1124			{
1125				ip:   mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
1126				bits: 96,
1127				p:    mustPrefix("fe80::dead:beef:0:0/96"),
1128				ok:   true,
1129			},
1130			{
1131				ip:   mustIP(fmt.Sprintf("aaaa::%s", zone)),
1132				bits: 4,
1133				p:    mustPrefix("a000::/4"),
1134				ok:   true,
1135			},
1136			{
1137				ip:   mustIP(fmt.Sprintf("::%s", zone)),
1138				bits: 63,
1139				p:    mustPrefix("::/63"),
1140				ok:   true,
1141			},
1142		}
1143	}
1144
1145	tests := []struct {
1146		family   string
1147		subtests []subtest
1148	}{
1149		{
1150			family: "nil",
1151			subtests: []subtest{
1152				{
1153					bits: 255,
1154					ok:   true,
1155				},
1156				{
1157					bits: 16,
1158					ok:   true,
1159				},
1160			},
1161		},
1162		{
1163			family: "IPv4",
1164			subtests: []subtest{
1165				{
1166					ip:   mustIP("192.0.2.0"),
1167					bits: 255,
1168				},
1169				{
1170					ip:   mustIP("192.0.2.0"),
1171					bits: 16,
1172					p:    mustPrefix("192.0.0.0/16"),
1173					ok:   true,
1174				},
1175				{
1176					ip:   mustIP("255.255.255.255"),
1177					bits: 20,
1178					p:    mustPrefix("255.255.240.0/20"),
1179					ok:   true,
1180				},
1181				{
1182					// Partially masking one byte that contains both
1183					// 1s and 0s on either side of the mask limit.
1184					ip:   mustIP("100.98.156.66"),
1185					bits: 10,
1186					p:    mustPrefix("100.64.0.0/10"),
1187					ok:   true,
1188				},
1189			},
1190		},
1191		{
1192			family:   "IPv6",
1193			subtests: makeIPv6(""),
1194		},
1195		{
1196			family:   "IPv6 zone",
1197			subtests: makeIPv6("eth0"),
1198		},
1199	}
1200
1201	for _, tt := range tests {
1202		t.Run(tt.family, func(t *testing.T) {
1203			for _, st := range tt.subtests {
1204				t.Run(st.p.String(), func(t *testing.T) {
1205					// Ensure st.ip is not mutated.
1206					orig := st.ip.String()
1207
1208					p, err := st.ip.Prefix(int(st.bits))
1209					if st.ok && err != nil {
1210						t.Fatalf("failed to produce prefix: %v", err)
1211					}
1212					if !st.ok && err == nil {
1213						t.Fatal("expected an error, but none occurred")
1214					}
1215					if err != nil {
1216						t.Logf("err: %v", err)
1217						return
1218					}
1219
1220					if !reflect.DeepEqual(p, st.p) {
1221						t.Errorf("prefix = %q, want %q", p, st.p)
1222					}
1223
1224					if got := st.ip.String(); got != orig {
1225						t.Errorf("IP was mutated: %q, want %q", got, orig)
1226					}
1227				})
1228			}
1229		})
1230	}
1231}
1232
1233func TestPrefixMarshalUnmarshal(t *testing.T) {
1234	tests := []string{
1235		"",
1236		"1.2.3.4/32",
1237		"0.0.0.0/0",
1238		"::/0",
1239		"::1/128",
1240		"2001:db8::/32",
1241	}
1242
1243	for _, s := range tests {
1244		t.Run(s, func(t *testing.T) {
1245			// Ensure that JSON  (and by extension, text) marshaling is
1246			// sane by entering quoted input.
1247			orig := `"` + s + `"`
1248
1249			var p Prefix
1250			if err := json.Unmarshal([]byte(orig), &p); err != nil {
1251				t.Fatalf("failed to unmarshal: %v", err)
1252			}
1253
1254			pb, err := json.Marshal(p)
1255			if err != nil {
1256				t.Fatalf("failed to marshal: %v", err)
1257			}
1258
1259			back := string(pb)
1260			if orig != back {
1261				t.Errorf("Marshal = %q; want %q", back, orig)
1262			}
1263		})
1264	}
1265}
1266
1267func TestPrefixUnmarshalTextNonZero(t *testing.T) {
1268	ip := mustPrefix("fe80::/64")
1269	if err := ip.UnmarshalText([]byte("xxx")); err == nil {
1270		t.Fatal("unmarshaled into non-empty Prefix")
1271	}
1272}
1273
1274func TestIs4AndIs6(t *testing.T) {
1275	tests := []struct {
1276		ip  Addr
1277		is4 bool
1278		is6 bool
1279	}{
1280		{Addr{}, false, false},
1281		{mustIP("1.2.3.4"), true, false},
1282		{mustIP("127.0.0.2"), true, false},
1283		{mustIP("::1"), false, true},
1284		{mustIP("::ffff:192.0.2.128"), false, true},
1285		{mustIP("::fffe:c000:0280"), false, true},
1286		{mustIP("::1%eth0"), false, true},
1287	}
1288	for _, tt := range tests {
1289		got4 := tt.ip.Is4()
1290		if got4 != tt.is4 {
1291			t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
1292		}
1293
1294		got6 := tt.ip.Is6()
1295		if got6 != tt.is6 {
1296			t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
1297		}
1298	}
1299}
1300
1301func TestIs4In6(t *testing.T) {
1302	tests := []struct {
1303		ip        Addr
1304		want      bool
1305		wantUnmap Addr
1306	}{
1307		{Addr{}, false, Addr{}},
1308		{mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
1309		{mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
1310		{mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
1311		{mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
1312		{mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1313		{mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
1314		{mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1315		{mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1316		{mustIP("::1"), false, mustIP("::1")},
1317		{mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
1318	}
1319	for _, tt := range tests {
1320		got := tt.ip.Is4In6()
1321		if got != tt.want {
1322			t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
1323		}
1324		u := tt.ip.Unmap()
1325		if u != tt.wantUnmap {
1326			t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
1327		}
1328	}
1329}
1330
1331func TestPrefixMasked(t *testing.T) {
1332	tests := []struct {
1333		prefix Prefix
1334		masked Prefix
1335	}{
1336		{
1337			prefix: mustPrefix("192.168.0.255/24"),
1338			masked: mustPrefix("192.168.0.0/24"),
1339		},
1340		{
1341			prefix: mustPrefix("2100::/3"),
1342			masked: mustPrefix("2000::/3"),
1343		},
1344		{
1345			prefix: PrefixFrom(mustIP("2000::"), 129),
1346			masked: Prefix{},
1347		},
1348		{
1349			prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
1350			masked: Prefix{},
1351		},
1352	}
1353	for _, test := range tests {
1354		t.Run(test.prefix.String(), func(t *testing.T) {
1355			got := test.prefix.Masked()
1356			if got != test.masked {
1357				t.Errorf("Masked=%s, want %s", got, test.masked)
1358			}
1359		})
1360	}
1361}
1362
1363func TestPrefix(t *testing.T) {
1364	tests := []struct {
1365		prefix      string
1366		ip          Addr
1367		bits        int
1368		str         string
1369		contains    []Addr
1370		notContains []Addr
1371	}{
1372		{
1373			prefix:      "192.168.0.0/24",
1374			ip:          mustIP("192.168.0.0"),
1375			bits:        24,
1376			contains:    mustIPs("192.168.0.1", "192.168.0.55"),
1377			notContains: mustIPs("192.168.1.1", "1.1.1.1"),
1378		},
1379		{
1380			prefix:      "192.168.1.1/32",
1381			ip:          mustIP("192.168.1.1"),
1382			bits:        32,
1383			contains:    mustIPs("192.168.1.1"),
1384			notContains: mustIPs("192.168.1.2"),
1385		},
1386		{
1387			prefix:      "100.64.0.0/10", // CGNAT range; prefix not multiple of 8
1388			ip:          mustIP("100.64.0.0"),
1389			bits:        10,
1390			contains:    mustIPs("100.64.0.0", "100.64.0.1", "100.81.251.94", "100.100.100.100", "100.127.255.254", "100.127.255.255"),
1391			notContains: mustIPs("100.63.255.255", "100.128.0.0"),
1392		},
1393		{
1394			prefix:      "2001:db8::/96",
1395			ip:          mustIP("2001:db8::"),
1396			bits:        96,
1397			contains:    mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
1398			notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
1399		},
1400		{
1401			prefix:      "0.0.0.0/0",
1402			ip:          mustIP("0.0.0.0"),
1403			bits:        0,
1404			contains:    mustIPs("192.168.0.1", "1.1.1.1"),
1405			notContains: append(mustIPs("2001:db8::1"), Addr{}),
1406		},
1407		{
1408			prefix:      "::/0",
1409			ip:          mustIP("::"),
1410			bits:        0,
1411			contains:    mustIPs("::1", "2001:db8::1"),
1412			notContains: mustIPs("192.0.2.1"),
1413		},
1414		{
1415			prefix:      "2000::/3",
1416			ip:          mustIP("2000::"),
1417			bits:        3,
1418			contains:    mustIPs("2001:db8::1"),
1419			notContains: mustIPs("fe80::1"),
1420		},
1421	}
1422	for _, test := range tests {
1423		t.Run(test.prefix, func(t *testing.T) {
1424			prefix, err := ParsePrefix(test.prefix)
1425			if err != nil {
1426				t.Fatal(err)
1427			}
1428			if prefix.Addr() != test.ip {
1429				t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
1430			}
1431			if prefix.Bits() != test.bits {
1432				t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
1433			}
1434			for _, ip := range test.contains {
1435				if !prefix.Contains(ip) {
1436					t.Errorf("does not contain %s", ip)
1437				}
1438			}
1439			for _, ip := range test.notContains {
1440				if prefix.Contains(ip) {
1441					t.Errorf("contains %s", ip)
1442				}
1443			}
1444			want := test.str
1445			if want == "" {
1446				want = test.prefix
1447			}
1448			if got := prefix.String(); got != want {
1449				t.Errorf("prefix.String()=%q, want %q", got, want)
1450			}
1451
1452			TestAppendToMarshal(t, prefix)
1453		})
1454	}
1455}
1456
1457func TestPrefixFromInvalidBits(t *testing.T) {
1458	v4 := MustParseAddr("1.2.3.4")
1459	v6 := MustParseAddr("66::66")
1460	tests := []struct {
1461		ip       Addr
1462		in, want int
1463	}{
1464		{v4, 0, 0},
1465		{v6, 0, 0},
1466		{v4, 1, 1},
1467		{v4, 33, -1},
1468		{v6, 33, 33},
1469		{v6, 127, 127},
1470		{v6, 128, 128},
1471		{v4, 254, -1},
1472		{v4, 255, -1},
1473		{v4, -1, -1},
1474		{v6, -1, -1},
1475		{v4, -5, -1},
1476		{v6, -5, -1},
1477	}
1478	for _, tt := range tests {
1479		p := PrefixFrom(tt.ip, tt.in)
1480		if got := p.Bits(); got != tt.want {
1481			t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
1482		}
1483	}
1484}
1485
1486func TestParsePrefixAllocs(t *testing.T) {
1487	tests := []struct {
1488		ip    string
1489		slash string
1490	}{
1491		{"192.168.1.0", "/24"},
1492		{"aaaa:bbbb:cccc::", "/24"},
1493	}
1494	for _, test := range tests {
1495		prefix := test.ip + test.slash
1496		t.Run(prefix, func(t *testing.T) {
1497			ipAllocs := int(testing.AllocsPerRun(5, func() {
1498				ParseAddr(test.ip)
1499			}))
1500			prefixAllocs := int(testing.AllocsPerRun(5, func() {
1501				ParsePrefix(prefix)
1502			}))
1503			if got := prefixAllocs - ipAllocs; got != 0 {
1504				t.Errorf("allocs=%d, want 0", got)
1505			}
1506		})
1507	}
1508}
1509
1510func TestParsePrefixError(t *testing.T) {
1511	tests := []struct {
1512		prefix string
1513		errstr string
1514	}{
1515		{
1516			prefix: "192.168.0.0",
1517			errstr: "no '/'",
1518		},
1519		{
1520			prefix: "1.257.1.1/24",
1521			errstr: "value >255",
1522		},
1523		{
1524			prefix: "1.1.1.0/q",
1525			errstr: "bad bits",
1526		},
1527		{
1528			prefix: "1.1.1.0/-1",
1529			errstr: "bad bits",
1530		},
1531		{
1532			prefix: "1.1.1.0/33",
1533			errstr: "out of range",
1534		},
1535		{
1536			prefix: "2001::/129",
1537			errstr: "out of range",
1538		},
1539		// Zones are not allowed: https://go.dev/issue/51899
1540		{
1541			prefix: "1.1.1.0%a/24",
1542			errstr: "unexpected character",
1543		},
1544		{
1545			prefix: "2001:db8::%a/32",
1546			errstr: "zones cannot be present",
1547		},
1548		{
1549			prefix: "1.1.1.0/+32",
1550			errstr: "bad bits",
1551		},
1552		{
1553			prefix: "1.1.1.0/-32",
1554			errstr: "bad bits",
1555		},
1556		{
1557			prefix: "1.1.1.0/032",
1558			errstr: "bad bits",
1559		},
1560		{
1561			prefix: "1.1.1.0/0032",
1562			errstr: "bad bits",
1563		},
1564	}
1565	for _, test := range tests {
1566		t.Run(test.prefix, func(t *testing.T) {
1567			_, err := ParsePrefix(test.prefix)
1568			if err == nil {
1569				t.Fatal("no error")
1570			}
1571			if got := err.Error(); !strings.Contains(got, test.errstr) {
1572				t.Errorf("error is missing substring %q: %s", test.errstr, got)
1573			}
1574		})
1575	}
1576}
1577
1578func TestPrefixIsSingleIP(t *testing.T) {
1579	tests := []struct {
1580		ipp  Prefix
1581		want bool
1582	}{
1583		{ipp: mustPrefix("127.0.0.1/32"), want: true},
1584		{ipp: mustPrefix("127.0.0.1/31"), want: false},
1585		{ipp: mustPrefix("127.0.0.1/0"), want: false},
1586		{ipp: mustPrefix("::1/128"), want: true},
1587		{ipp: mustPrefix("::1/127"), want: false},
1588		{ipp: mustPrefix("::1/0"), want: false},
1589		{ipp: Prefix{}, want: false},
1590	}
1591	for _, tt := range tests {
1592		got := tt.ipp.IsSingleIP()
1593		if got != tt.want {
1594			t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
1595		}
1596	}
1597}
1598
1599func mustIPs(strs ...string) []Addr {
1600	var res []Addr
1601	for _, s := range strs {
1602		res = append(res, mustIP(s))
1603	}
1604	return res
1605}
1606
1607func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
1608	b.ReportAllocs()
1609	tests := []struct {
1610		name string
1611		ip   string
1612	}{
1613		{"ipv4", "1.2.3.4"},
1614		{"ipv6", "2001:db8::1"},
1615		{"ipv6+zone", "2001:db8::1%eth0"},
1616	}
1617	for _, tc := range tests {
1618		b.Run(tc.name, func(b *testing.B) {
1619			ip := mustIP(tc.ip)
1620			for i := 0; i < b.N; i++ {
1621				bt, err := ip.MarshalBinary()
1622				if err != nil {
1623					b.Fatal(err)
1624				}
1625				var ip2 Addr
1626				if err := ip2.UnmarshalBinary(bt); err != nil {
1627					b.Fatal(err)
1628				}
1629			}
1630		})
1631	}
1632}
1633
1634func BenchmarkStdIPv4(b *testing.B) {
1635	b.ReportAllocs()
1636	ips := []net.IP{}
1637	for i := 0; i < b.N; i++ {
1638		ip := net.IPv4(8, 8, 8, 8)
1639		ips = ips[:0]
1640		for i := 0; i < 100; i++ {
1641			ips = append(ips, ip)
1642		}
1643	}
1644}
1645
1646func BenchmarkIPv4(b *testing.B) {
1647	b.ReportAllocs()
1648	ips := []Addr{}
1649	for i := 0; i < b.N; i++ {
1650		ip := IPv4(8, 8, 8, 8)
1651		ips = ips[:0]
1652		for i := 0; i < 100; i++ {
1653			ips = append(ips, ip)
1654		}
1655	}
1656}
1657
1658// ip4i was one of the possible representations of IP that came up in
1659// discussions, inlining IPv4 addresses, but having an "overflow"
1660// interface for IPv6 or IPv6 + zone. This is here for benchmarking.
1661type ip4i struct {
1662	ip4    [4]byte
1663	flags1 byte
1664	flags2 byte
1665	flags3 byte
1666	flags4 byte
1667	ipv6   any
1668}
1669
1670func newip4i_v4(a, b, c, d byte) ip4i {
1671	return ip4i{ip4: [4]byte{a, b, c, d}}
1672}
1673
1674// BenchmarkIPv4_inline benchmarks the candidate representation, ip4i.
1675func BenchmarkIPv4_inline(b *testing.B) {
1676	b.ReportAllocs()
1677	ips := []ip4i{}
1678	for i := 0; i < b.N; i++ {
1679		ip := newip4i_v4(8, 8, 8, 8)
1680		ips = ips[:0]
1681		for i := 0; i < 100; i++ {
1682			ips = append(ips, ip)
1683		}
1684	}
1685}
1686
1687func BenchmarkStdIPv6(b *testing.B) {
1688	b.ReportAllocs()
1689	ips := []net.IP{}
1690	for i := 0; i < b.N; i++ {
1691		ip := net.ParseIP("2001:db8::1")
1692		ips = ips[:0]
1693		for i := 0; i < 100; i++ {
1694			ips = append(ips, ip)
1695		}
1696	}
1697}
1698
1699func BenchmarkIPv6(b *testing.B) {
1700	b.ReportAllocs()
1701	ips := []Addr{}
1702	for i := 0; i < b.N; i++ {
1703		ip := mustIP("2001:db8::1")
1704		ips = ips[:0]
1705		for i := 0; i < 100; i++ {
1706			ips = append(ips, ip)
1707		}
1708	}
1709}
1710
1711func BenchmarkIPv4Contains(b *testing.B) {
1712	b.ReportAllocs()
1713	prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
1714	ip := IPv4(192, 168, 1, 1)
1715	for i := 0; i < b.N; i++ {
1716		prefix.Contains(ip)
1717	}
1718}
1719
1720func BenchmarkIPv6Contains(b *testing.B) {
1721	b.ReportAllocs()
1722	prefix := MustParsePrefix("::1/128")
1723	ip := MustParseAddr("::1")
1724	for i := 0; i < b.N; i++ {
1725		prefix.Contains(ip)
1726	}
1727}
1728
1729var parseBenchInputs = []struct {
1730	name string
1731	ip   string
1732}{
1733	{"v4", "192.168.1.1"},
1734	{"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
1735	{"v6_ellipsis", "fd7a:115c::626b:430b"},
1736	{"v6_v4", "::ffff:192.168.140.255"},
1737	{"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
1738}
1739
1740func BenchmarkParseAddr(b *testing.B) {
1741	sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1")) // Pin to not benchmark the intern package
1742	for _, test := range parseBenchInputs {
1743		b.Run(test.name, func(b *testing.B) {
1744			b.ReportAllocs()
1745			for i := 0; i < b.N; i++ {
1746				sinkIP, _ = ParseAddr(test.ip)
1747			}
1748		})
1749	}
1750}
1751
1752func BenchmarkStdParseIP(b *testing.B) {
1753	for _, test := range parseBenchInputs {
1754		b.Run(test.name, func(b *testing.B) {
1755			b.ReportAllocs()
1756			for i := 0; i < b.N; i++ {
1757				sinkStdIP = net.ParseIP(test.ip)
1758			}
1759		})
1760	}
1761}
1762
1763func BenchmarkAddrString(b *testing.B) {
1764	for _, test := range parseBenchInputs {
1765		ip := MustParseAddr(test.ip)
1766		b.Run(test.name, func(b *testing.B) {
1767			b.ReportAllocs()
1768			for i := 0; i < b.N; i++ {
1769				sinkString = ip.String()
1770			}
1771		})
1772	}
1773}
1774
1775func BenchmarkIPStringExpanded(b *testing.B) {
1776	for _, test := range parseBenchInputs {
1777		ip := MustParseAddr(test.ip)
1778		b.Run(test.name, func(b *testing.B) {
1779			b.ReportAllocs()
1780			for i := 0; i < b.N; i++ {
1781				sinkString = ip.StringExpanded()
1782			}
1783		})
1784	}
1785}
1786
1787func BenchmarkAddrMarshalText(b *testing.B) {
1788	for _, test := range parseBenchInputs {
1789		ip := MustParseAddr(test.ip)
1790		b.Run(test.name, func(b *testing.B) {
1791			b.ReportAllocs()
1792			for i := 0; i < b.N; i++ {
1793				sinkBytes, _ = ip.MarshalText()
1794			}
1795		})
1796	}
1797}
1798
1799func BenchmarkAddrPortString(b *testing.B) {
1800	for _, test := range parseBenchInputs {
1801		ip := MustParseAddr(test.ip)
1802		ipp := AddrPortFrom(ip, 60000)
1803		b.Run(test.name, func(b *testing.B) {
1804			b.ReportAllocs()
1805			for i := 0; i < b.N; i++ {
1806				sinkString = ipp.String()
1807			}
1808		})
1809	}
1810}
1811
1812func BenchmarkAddrPortMarshalText(b *testing.B) {
1813	for _, test := range parseBenchInputs {
1814		ip := MustParseAddr(test.ip)
1815		ipp := AddrPortFrom(ip, 60000)
1816		b.Run(test.name, func(b *testing.B) {
1817			b.ReportAllocs()
1818			for i := 0; i < b.N; i++ {
1819				sinkBytes, _ = ipp.MarshalText()
1820			}
1821		})
1822	}
1823}
1824
1825func BenchmarkPrefixMasking(b *testing.B) {
1826	tests := []struct {
1827		name string
1828		ip   Addr
1829		bits int
1830	}{
1831		{
1832			name: "IPv4 /32",
1833			ip:   IPv4(192, 0, 2, 0),
1834			bits: 32,
1835		},
1836		{
1837			name: "IPv4 /17",
1838			ip:   IPv4(192, 0, 2, 0),
1839			bits: 17,
1840		},
1841		{
1842			name: "IPv4 /0",
1843			ip:   IPv4(192, 0, 2, 0),
1844			bits: 0,
1845		},
1846		{
1847			name: "IPv6 /128",
1848			ip:   mustIP("2001:db8::1"),
1849			bits: 128,
1850		},
1851		{
1852			name: "IPv6 /65",
1853			ip:   mustIP("2001:db8::1"),
1854			bits: 65,
1855		},
1856		{
1857			name: "IPv6 /0",
1858			ip:   mustIP("2001:db8::1"),
1859			bits: 0,
1860		},
1861		{
1862			name: "IPv6 zone /128",
1863			ip:   mustIP("2001:db8::1%eth0"),
1864			bits: 128,
1865		},
1866		{
1867			name: "IPv6 zone /65",
1868			ip:   mustIP("2001:db8::1%eth0"),
1869			bits: 65,
1870		},
1871		{
1872			name: "IPv6 zone /0",
1873			ip:   mustIP("2001:db8::1%eth0"),
1874			bits: 0,
1875		},
1876	}
1877
1878	for _, tt := range tests {
1879		b.Run(tt.name, func(b *testing.B) {
1880			b.ReportAllocs()
1881
1882			for i := 0; i < b.N; i++ {
1883				sinkPrefix, _ = tt.ip.Prefix(tt.bits)
1884			}
1885		})
1886	}
1887}
1888
1889func BenchmarkPrefixMarshalText(b *testing.B) {
1890	b.ReportAllocs()
1891	ipp := MustParsePrefix("66.55.44.33/22")
1892	for i := 0; i < b.N; i++ {
1893		sinkBytes, _ = ipp.MarshalText()
1894	}
1895}
1896
1897func BenchmarkParseAddrPort(b *testing.B) {
1898	for _, test := range parseBenchInputs {
1899		var ipp string
1900		if strings.HasPrefix(test.name, "v6") {
1901			ipp = fmt.Sprintf("[%s]:1234", test.ip)
1902		} else {
1903			ipp = fmt.Sprintf("%s:1234", test.ip)
1904		}
1905		b.Run(test.name, func(b *testing.B) {
1906			b.ReportAllocs()
1907
1908			for i := 0; i < b.N; i++ {
1909				sinkAddrPort, _ = ParseAddrPort(ipp)
1910			}
1911		})
1912	}
1913}
1914
1915func TestAs4(t *testing.T) {
1916	tests := []struct {
1917		ip        Addr
1918		want      [4]byte
1919		wantPanic bool
1920	}{
1921		{
1922			ip:   mustIP("1.2.3.4"),
1923			want: [4]byte{1, 2, 3, 4},
1924		},
1925		{
1926			ip:   AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6
1927			want: [4]byte{1, 2, 3, 4},
1928		},
1929		{
1930			ip:   mustIP("0.0.0.0"),
1931			want: [4]byte{0, 0, 0, 0},
1932		},
1933		{
1934			ip:        Addr{},
1935			wantPanic: true,
1936		},
1937		{
1938			ip:        mustIP("::1"),
1939			wantPanic: true,
1940		},
1941	}
1942	as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
1943		defer func() {
1944			if recover() != nil {
1945				gotPanic = true
1946				return
1947			}
1948		}()
1949		v = ip.As4()
1950		return
1951	}
1952	for i, tt := range tests {
1953		got, gotPanic := as4(tt.ip)
1954		if gotPanic != tt.wantPanic {
1955			t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
1956			continue
1957		}
1958		if got != tt.want {
1959			t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
1960		}
1961	}
1962}
1963
1964func TestPrefixOverlaps(t *testing.T) {
1965	pfx := mustPrefix
1966	tests := []struct {
1967		a, b Prefix
1968		want bool
1969	}{
1970		{Prefix{}, pfx("1.2.0.0/16"), false},    // first zero
1971		{pfx("1.2.0.0/16"), Prefix{}, false},    // second zero
1972		{pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families
1973
1974		{pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal
1975
1976		{pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
1977		{pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
1978
1979		{pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
1980		{pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
1981
1982		// Match /0 either order
1983		{pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
1984		{pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
1985
1986		{pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true
1987
1988		// IPv6 overlapping
1989		{pfx("5::1/128"), pfx("5::0/8"), true},
1990		{pfx("5::0/8"), pfx("5::1/128"), true},
1991
1992		// IPv6 not overlapping
1993		{pfx("1::1/128"), pfx("2::2/128"), false},
1994		{pfx("0100::0/8"), pfx("::1/128"), false},
1995
1996		// IPv4-mapped IPv6 addresses should not overlap with IPv4.
1997		{PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
1998
1999		// Invalid prefixes
2000		{PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
2001		{PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
2002	}
2003	for i, tt := range tests {
2004		if got := tt.a.Overlaps(tt.b); got != tt.want {
2005			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
2006		}
2007		// Overlaps is commutative
2008		if got := tt.b.Overlaps(tt.a); got != tt.want {
2009			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
2010		}
2011	}
2012}
2013
2014// Sink variables are here to force the compiler to not elide
2015// seemingly useless work in benchmarks and allocation tests. If you
2016// were to just `_ = foo()` within a test function, the compiler could
2017// correctly deduce that foo() does nothing and doesn't need to be
2018// called. By writing results to a global variable, we hide that fact
2019// from the compiler and force it to keep the code under test.
2020var (
2021	sinkIP          Addr
2022	sinkStdIP       net.IP
2023	sinkAddrPort    AddrPort
2024	sinkPrefix      Prefix
2025	sinkPrefixSlice []Prefix
2026	sinkInternValue unique.Handle[AddrDetail]
2027	sinkIP16        [16]byte
2028	sinkIP4         [4]byte
2029	sinkBool        bool
2030	sinkString      string
2031	sinkBytes       []byte
2032	sinkUDPAddr     = &net.UDPAddr{IP: make(net.IP, 0, 16)}
2033)
2034
2035func TestNoAllocs(t *testing.T) {
2036	// Wrappers that panic on error, to prove that our alloc-free
2037	// methods are returning successfully.
2038	panicIP := func(ip Addr, err error) Addr {
2039		if err != nil {
2040			panic(err)
2041		}
2042		return ip
2043	}
2044	panicPfx := func(pfx Prefix, err error) Prefix {
2045		if err != nil {
2046			panic(err)
2047		}
2048		return pfx
2049	}
2050	panicIPP := func(ipp AddrPort, err error) AddrPort {
2051		if err != nil {
2052			panic(err)
2053		}
2054		return ipp
2055	}
2056	test := func(name string, f func()) {
2057		t.Run(name, func(t *testing.T) {
2058			n := testing.AllocsPerRun(1000, f)
2059			if n != 0 {
2060				t.Fatalf("allocs = %d; want 0", int(n))
2061			}
2062		})
2063	}
2064
2065	// Addr constructors
2066	test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
2067	test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
2068	test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
2069	test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
2070	test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
2071	test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
2072	test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
2073	test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2074	test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2075	test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2076
2077	// Addr methods
2078	test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2079	test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2080	test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2081	test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2082	test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2083	test("Addr.Compare", func() {
2084		a := MustParseAddr("1.2.3.4")
2085		b := MustParseAddr("2.3.4.5")
2086		sinkBool = a.Compare(b) == 0
2087	})
2088	test("Addr.Less", func() {
2089		a := MustParseAddr("1.2.3.4")
2090		b := MustParseAddr("2.3.4.5")
2091		sinkBool = a.Less(b)
2092	})
2093	test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2094	test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2095	test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2096	test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2097	test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2098	test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2099	test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2100	test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2101	test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2102	test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2103	test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2104	test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2105	test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2106	test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2107	test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2108	test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2109	test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2110	test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2111	test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2112
2113	// AddrPort constructors
2114	test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2115	test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2116	test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2117
2118	// Prefix constructors
2119	test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2120	test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2121	test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2122	test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2123
2124	// Prefix methods
2125	test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2126	test("Prefix.Overlaps", func() {
2127		a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2128		sinkBool = a.Overlaps(b)
2129	})
2130	test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2131	test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2132	test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2133}
2134
2135func TestAddrStringAllocs(t *testing.T) {
2136	tests := []struct {
2137		name       string
2138		ip         Addr
2139		wantAllocs int
2140	}{
2141		{"zero", Addr{}, 0},
2142		{"ipv4", MustParseAddr("192.168.1.1"), 1},
2143		{"ipv6", MustParseAddr("2001:db8::1"), 1},
2144		{"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2145		{"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2146		{"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2147	}
2148	optimizationOff := testenv.OptimizationOff()
2149	for _, tc := range tests {
2150		t.Run(tc.name, func(t *testing.T) {
2151			if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2152				// Optimizations are required to remove some allocs.
2153				t.Skipf("skipping on %v", testenv.Builder())
2154			}
2155			allocs := int(testing.AllocsPerRun(1000, func() {
2156				sinkString = tc.ip.String()
2157			}))
2158			if allocs != tc.wantAllocs {
2159				t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2160			}
2161		})
2162	}
2163}
2164
2165func TestPrefixString(t *testing.T) {
2166	tests := []struct {
2167		ipp  Prefix
2168		want string
2169	}{
2170		{Prefix{}, "invalid Prefix"},
2171		{PrefixFrom(Addr{}, 8), "invalid Prefix"},
2172		{PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2173	}
2174
2175	for _, tt := range tests {
2176		if got := tt.ipp.String(); got != tt.want {
2177			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2178		}
2179	}
2180}
2181
2182func TestInvalidAddrPortString(t *testing.T) {
2183	tests := []struct {
2184		ipp  AddrPort
2185		want string
2186	}{
2187		{AddrPort{}, "invalid AddrPort"},
2188		{AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2189	}
2190
2191	for _, tt := range tests {
2192		if got := tt.ipp.String(); got != tt.want {
2193			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2194		}
2195	}
2196}
2197
2198func TestAsSlice(t *testing.T) {
2199	tests := []struct {
2200		in   Addr
2201		want []byte
2202	}{
2203		{in: Addr{}, want: nil},
2204		{in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2205		{in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2206	}
2207
2208	for _, test := range tests {
2209		got := test.in.AsSlice()
2210		if !bytes.Equal(got, test.want) {
2211			t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2212		}
2213	}
2214}
2215
2216var sink16 [16]byte
2217
2218func BenchmarkAs16(b *testing.B) {
2219	addr := MustParseAddr("1::10")
2220	for i := 0; i < b.N; i++ {
2221		sink16 = addr.As16()
2222	}
2223}
2224