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.
23 var long = flag.Bool("long", false, "run long tests")
25 type uint128 = Uint128
28 mustPrefix = MustParsePrefix
29 mustIP = MustParseAddr
30 mustIPPort = MustParseAddrPort
33 func TestParseAddr(t *testing.T) {
34 var validIPs = []struct {
36 ip Addr // output of ParseAddr()
37 str string // output of String(). If "", use in.
40 // Basic zero IPv4 address.
43 ip: MkAddr(Mk128(0, 0xffff00000000), Z4),
45 // Basic non-zero IPv4 address.
47 in: "192.168.140.255",
48 ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4),
50 // IPv4 address in windows-style "print all the digits" form.
52 in: "010.000.015.001",
53 wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`,
55 // IPv4 address with a silly amount of leading zeros.
57 in: "000001.00000002.00000003.000000004",
58 wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`,
60 // 4-in-6 with octet with leading zero
62 in: "::ffff:1.2.03.4",
63 wantErr: `ParseAddr("::ffff:1.2.03.4"): ParseAddr("1.2.03.4"): IPv4 field has octet with leading zero (at "1.2.03.4")`,
65 // Basic zero IPv6 address.
68 ip: MkAddr(Mk128(0, 0), Z6noz),
73 ip: MkAddr(Mk128(0, 1), Z6noz),
75 // Fully expanded IPv6 address.
77 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
78 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz),
80 // IPv6 with elided fields in the middle.
82 in: "fd7a:115c::626b:430b",
83 ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz),
85 // IPv6 with elided fields at the end.
87 in: "fd7a:115c:a1e0:ab12:4843:cd96::",
88 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz),
90 // IPv6 with single elided field at the end.
92 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b::",
93 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz),
94 str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
96 // IPv6 with single elided field in the middle.
98 in: "fd7a:115c:a1e0::4843:cd96:626b:430b",
99 ip: MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz),
100 str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b",
102 // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6)
104 in: "::ffff:192.168.140.255",
105 ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz),
106 str: "::ffff:192.168.140.255",
108 // IPv6 with a zone specifier.
110 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
111 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), intern.Get("eth0")),
113 // IPv6 with dotted decimal and zone specifier.
115 in: "1:2::ffff:192.168.140.255%eth1",
116 ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), intern.Get("eth1")),
117 str: "1:2::ffff:c0a8:8cff%eth1",
121 in: "::ffff:192.168.140.255%eth1",
122 ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), intern.Get("eth1")),
123 str: "::ffff:192.168.140.255%eth1",
125 // IPv6 with capital letters.
127 in: "FD9E:1A04:F01D::1",
128 ip: MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz),
129 str: "fd9e:1a04:f01d::1",
133 for _, test := range validIPs {
134 t.Run(test.in, func(t *testing.T) {
135 got, err := ParseAddr(test.in)
137 if err.Error() == test.wantErr {
142 if test.wantErr != "" {
143 t.Fatalf("wanted error %q; got none", test.wantErr)
146 t.Errorf("got %#v, want %#v", got, test.ip)
149 // Check that ParseAddr is a pure function.
150 got2, err := ParseAddr(test.in)
155 t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2)
158 // Check that ParseAddr(ip.String()) is the identity function.
160 got3, err := ParseAddr(s)
165 t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got)
168 // Check that the slow-but-readable parser produces the same result.
169 slow, err := parseIPSlow(test.in)
174 t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow)
177 // Check that the parsed IP formats as expected.
184 t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants)
187 // Check that AppendTo matches MarshalText.
188 TestAppendToMarshal(t, got)
190 // Check that MarshalText/UnmarshalText work similarly to
191 // ParseAddr/String (see TestIPMarshalUnmarshal for
192 // marshal-specific behavior that's not common with
193 // ParseAddr/String).
194 js := `"` + test.in + `"`
196 if err := json.Unmarshal([]byte(js), &jsgot); err != nil {
200 t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got)
202 jsb, err := json.Marshal(jsgot)
206 jswant := `"` + wants + `"`
207 jsback := string(jsb)
208 if jsback != jswant {
209 t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant)
214 var invalidIPs = []string{
219 // Single number. Some parsers accept this as an IPv4 address in
220 // big-endian uint32 form, but we don't.
222 // IPv4 with a zone specifier
224 // IPv4 field must have at least one digit
228 // IPv4 address too long
230 // IPv4 in dotted octal form
231 "0300.0250.0214.0377",
232 // IPv4 in dotted hex form
233 "0xc0.0xa8.0x8c.0xff",
234 // IPv4 in class B form
236 // IPv4 in class B form, with a small enough number to be
237 // parseable as a regular dotted decimal field.
239 // IPv4 in class A form
241 // IPv4 in class A form, with a small enough number to be
242 // parseable as a regular dotted decimal field.
244 // IPv4 field has value >255
246 // IPv4 with too many fields
248 // IPv6 with not enough fields
250 // IPv6 with too many fields
252 // IPv6 with 8 fields and a :: expander
254 // IPv6 with a field bigger than 2b
256 // IPv6 with non-hex values in field
257 "fe80:tail:scal:e::",
258 // IPv6 with a zone delimiter but no zone.
260 // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4.
261 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
262 // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4.
263 "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
264 // IPv6 with invalid embedded IPv4.
265 "::ffff:192.168.140.bad",
266 // IPv6 with multiple ellipsis ::.
268 // IPv6 with invalid non hex/colon character.
270 // IPv6 with truncated bytes after single colon.
274 for _, s := range invalidIPs {
275 t.Run(s, func(t *testing.T) {
276 got, err := ParseAddr(s)
278 t.Errorf("ParseAddr(%q) = %#v, want error", s, got)
281 slow, err := parseIPSlow(s)
283 t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow)
286 std := net.ParseIP(s)
288 t.Errorf("net.ParseIP(%q) = %#v, want error", s, std)
292 // Don't test unmarshaling of "" here, do it in
293 // IPMarshalUnmarshal.
297 js := []byte(`"` + s + `"`)
298 if err := json.Unmarshal(js, &jsgot); err == nil {
299 t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot)
305 func TestAddrFromSlice(t *testing.T) {
312 ip: []byte{10, 0, 0, 1},
313 wantAddr: mustIP("10.0.0.1"),
317 ip: []byte{0xfe, 0x80, 15: 0x01},
318 wantAddr: mustIP("fe80::01"),
332 for _, tt := range tests {
333 addr, ok := AddrFromSlice(tt.ip)
334 if ok != tt.wantOK || addr != tt.wantAddr {
335 t.Errorf("AddrFromSlice(%#v) = %#v, %v, want %#v, %v", tt.ip, addr, ok, tt.wantAddr, tt.wantOK)
340 func TestIPv4Constructors(t *testing.T) {
341 if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") {
342 t.Errorf("don't match")
346 func TestAddrMarshalUnmarshalBinary(t *testing.T) {
353 {"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16},
354 {"::ffff:c000:0280", 16},
355 {"::ffff:c000:0280%eth0", 20},
357 for _, tc := range tests {
362 b, err := ip.MarshalBinary()
366 if len(b) != tc.wantSize {
367 t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize)
370 if err := ip2.UnmarshalBinary(b); err != nil {
374 t.Fatalf("got %v; want %v", ip2, ip)
378 // Cannot unmarshal from unexpected IP length.
379 for _, n := range []int{3, 5} {
381 if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
382 t.Fatalf("unmarshaled from unexpected IP length %d", n)
387 func TestAddrPortMarshalTextString(t *testing.T) {
392 {mustIPPort("1.2.3.4:80"), "1.2.3.4:80"},
393 {mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"},
394 {mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"},
395 {mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"},
396 {mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"},
398 for i, tt := range tests {
399 if got := tt.in.String(); got != tt.want {
400 t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want)
402 mt, err := tt.in.MarshalText()
404 t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
407 if string(mt) != tt.want {
408 t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want)
413 func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
418 {"1.2.3.4:51820", 4 + 2},
419 {"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
420 {"[::ffff:c000:0280]:65535", 16 + 2},
421 {"[::ffff:c000:0280%eth0]:1", 20 + 2},
423 for _, tc := range tests {
425 if len(tc.ipport) > 0 {
426 ipport = mustIPPort(tc.ipport)
428 b, err := ipport.MarshalBinary()
432 if len(b) != tc.wantSize {
433 t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
436 if err := ipport2.UnmarshalBinary(b); err != nil {
439 if ipport != ipport2 {
440 t.Fatalf("got %v; want %v", ipport2, ipport)
444 // Cannot unmarshal from unexpected lengths.
445 for _, n := range []int{3, 7} {
447 if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
448 t.Fatalf("unmarshaled from unexpected length %d", n)
453 func TestPrefixMarshalTextString(t *testing.T) {
458 {mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
459 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
460 {mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
461 {mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
462 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped
464 for i, tt := range tests {
465 if got := tt.in.String(); got != tt.want {
466 t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want)
468 mt, err := tt.in.MarshalText()
470 t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err)
473 if string(mt) != tt.want {
474 t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want)
479 func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
480 type testCase struct {
485 {mustPrefix("1.2.3.4/24"), 4 + 1},
486 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
487 {mustPrefix("::ffff:c000:0280/96"), 16 + 1},
488 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped
490 tests = append(tests,
491 testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
492 testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
493 for _, tc := range tests {
495 b, err := prefix.MarshalBinary()
499 if len(b) != tc.wantSize {
500 t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
503 if err := prefix2.UnmarshalBinary(b); err != nil {
506 if prefix != prefix2 {
507 t.Fatalf("got %v; want %v", prefix2, prefix)
511 // Cannot unmarshal from unexpected lengths.
512 for _, n := range []int{3, 6} {
514 if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
515 t.Fatalf("unmarshaled from unexpected length %d", n)
520 func TestAddrMarshalUnmarshal(t *testing.T) {
521 // This only tests the cases where Marshal/Unmarshal diverges from
522 // the behavior of ParseAddr/String. For the rest of the test cases,
523 // see TestParseAddr above.
526 if err := json.Unmarshal([]byte(orig), &ip); err != nil {
527 t.Fatalf("Unmarshal(%q) got error %v", orig, err)
530 t.Errorf("Unmarshal(%q) is not the zero Addr", orig)
533 jsb, err := json.Marshal(ip)
535 t.Fatalf("Marshal(%v) got error %v", ip, err)
539 t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig)
543 func TestAddrFrom16(t *testing.T) {
551 in: [...]byte{15: 1},
552 want: MkAddr(Mk128(0, 1), Z6noz),
556 in: [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4},
557 want: MkAddr(Mk128(0, 0xffff01020304), Z6noz),
560 for _, tt := range tests {
561 t.Run(tt.name, func(t *testing.T) {
562 got := AddrFrom16(tt.in)
564 t.Errorf("got %#v; want %#v", got, tt.want)
570 func TestIPProperties(t *testing.T) {
574 unicast4 = mustIP("192.0.2.1")
575 unicast6 = mustIP("2001:db8::1")
576 unicastZone6 = mustIP("2001:db8::1%eth0")
577 unicast6Unassigned = mustIP("4000::1") // not in 2000::/3.
579 multicast4 = mustIP("224.0.0.1")
580 multicast6 = mustIP("ff02::1")
581 multicastZone6 = mustIP("ff02::1%eth0")
583 llu4 = mustIP("169.254.0.1")
584 llu6 = mustIP("fe80::1")
585 llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
586 lluZone6 = mustIP("fe80::1%eth0")
588 loopback4 = mustIP("127.0.0.1")
590 ilm6 = mustIP("ff01::1")
591 ilmZone6 = mustIP("ff01::1%eth0")
593 private4a = mustIP("10.0.0.1")
594 private4b = mustIP("172.16.0.1")
595 private4c = mustIP("192.168.1.1")
596 private6 = mustIP("fd00::1")
603 interfaceLocalMulticast bool
604 linkLocalMulticast bool
605 linkLocalUnicast bool
616 name: "unicast v4Addr",
621 name: "unicast v6Addr",
626 name: "unicast v6AddrZone",
631 name: "unicast v6Addr unassigned",
632 ip: unicast6Unassigned,
636 name: "multicast v4Addr",
638 linkLocalMulticast: true,
642 name: "multicast v6Addr",
644 linkLocalMulticast: true,
648 name: "multicast v6AddrZone",
650 linkLocalMulticast: true,
654 name: "link-local unicast v4Addr",
656 linkLocalUnicast: true,
659 name: "link-local unicast v6Addr",
661 linkLocalUnicast: true,
664 name: "link-local unicast v6Addr upper bound",
666 linkLocalUnicast: true,
669 name: "link-local unicast v6AddrZone",
671 linkLocalUnicast: true,
674 name: "loopback v4Addr",
679 name: "loopback v6Addr",
684 name: "interface-local multicast v6Addr",
686 interfaceLocalMulticast: true,
690 name: "interface-local multicast v6AddrZone",
692 interfaceLocalMulticast: true,
696 name: "private v4Addr 10/8",
702 name: "private v4Addr 172.16/12",
708 name: "private v4Addr 192.168/16",
714 name: "private v6Addr",
720 name: "unspecified v4Addr",
721 ip: IPv4Unspecified(),
725 name: "unspecified v6Addr",
726 ip: IPv6Unspecified(),
731 for _, tt := range tests {
732 t.Run(tt.name, func(t *testing.T) {
733 gu := tt.ip.IsGlobalUnicast()
734 if gu != tt.globalUnicast {
735 t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast)
738 ilm := tt.ip.IsInterfaceLocalMulticast()
739 if ilm != tt.interfaceLocalMulticast {
740 t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast)
743 llu := tt.ip.IsLinkLocalUnicast()
744 if llu != tt.linkLocalUnicast {
745 t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast)
748 llm := tt.ip.IsLinkLocalMulticast()
749 if llm != tt.linkLocalMulticast {
750 t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast)
753 lo := tt.ip.IsLoopback()
754 if lo != tt.loopback {
755 t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback)
758 multicast := tt.ip.IsMulticast()
759 if multicast != tt.multicast {
760 t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast)
763 private := tt.ip.IsPrivate()
764 if private != tt.private {
765 t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private)
768 unspecified := tt.ip.IsUnspecified()
769 if unspecified != tt.unspecified {
770 t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified)
776 func TestAddrWellKnown(t *testing.T) {
783 name: "IPv6 link-local all nodes",
784 ip: IPv6LinkLocalAllNodes(),
785 std: net.IPv6linklocalallnodes,
788 name: "IPv6 link-local all routers",
789 ip: IPv6LinkLocalAllRouters(),
790 std: net.IPv6linklocalallrouters,
793 name: "IPv6 loopback",
795 std: net.IPv6loopback,
798 name: "IPv6 unspecified",
799 ip: IPv6Unspecified(),
800 std: net.IPv6unspecified,
804 for _, tt := range tests {
805 t.Run(tt.name, func(t *testing.T) {
806 want := tt.std.String()
807 got := tt.ip.String()
810 t.Fatalf("got %s, want %s", got, want)
816 func TestAddrLessCompare(t *testing.T) {
821 {Addr{}, Addr{}, false},
822 {Addr{}, mustIP("1.2.3.4"), true},
823 {mustIP("1.2.3.4"), Addr{}, false},
825 {mustIP("1.2.3.4"), mustIP("0102:0304::0"), true},
826 {mustIP("0102:0304::0"), mustIP("1.2.3.4"), false},
827 {mustIP("1.2.3.4"), mustIP("1.2.3.4"), false},
829 {mustIP("::1"), mustIP("::2"), true},
830 {mustIP("::1"), mustIP("::1%foo"), true},
831 {mustIP("::1%foo"), mustIP("::2"), true},
832 {mustIP("::2"), mustIP("::3"), true},
834 {mustIP("::"), mustIP("0.0.0.0"), false},
835 {mustIP("0.0.0.0"), mustIP("::"), true},
837 {mustIP("::1%a"), mustIP("::1%b"), true},
838 {mustIP("::1%a"), mustIP("::1%a"), false},
839 {mustIP("::1%b"), mustIP("::1%a"), false},
841 for _, tt := range tests {
842 got := tt.a.Less(tt.b)
844 t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
846 cmp := tt.a.Compare(tt.b)
847 if got && cmp != -1 {
848 t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp)
850 if cmp < -1 || cmp > 1 {
851 t.Errorf("bogus Compare return value %v", cmp)
853 if cmp == 0 && tt.a != tt.b {
854 t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b)
856 if cmp == 1 && !tt.b.Less(tt.a) {
857 t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b)
860 // Also check inverse.
861 if got == tt.want && got {
862 got2 := tt.b.Less(tt.a)
864 t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
878 sort.Slice(values, func(i, j int) bool { return values[i].Less(values[j]) })
879 got := fmt.Sprintf("%s", values)
880 want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]`
882 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
886 func TestAddrPortCompare(t *testing.T) {
891 {AddrPort{}, AddrPort{}, 0},
892 {AddrPort{}, mustIPPort("1.2.3.4:80"), -1},
894 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:80"), 0},
895 {mustIPPort("[::1]:80"), mustIPPort("[::1]:80"), 0},
897 {mustIPPort("1.2.3.4:80"), mustIPPort("2.3.4.5:22"), -1},
898 {mustIPPort("[::1]:80"), mustIPPort("[::2]:22"), -1},
900 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:443"), -1},
901 {mustIPPort("[::1]:80"), mustIPPort("[::1]:443"), -1},
903 {mustIPPort("1.2.3.4:80"), mustIPPort("[0102:0304::0]:80"), -1},
905 for _, tt := range tests {
906 got := tt.a.Compare(tt.b)
908 t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
911 // Also check inverse.
913 got2 := tt.b.Compare(tt.a)
914 if want2 := -1 * tt.want; got2 != want2 {
915 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
921 values := []AddrPort{
922 mustIPPort("[::1]:80"),
923 mustIPPort("[::2]:80"),
925 mustIPPort("1.2.3.4:443"),
926 mustIPPort("8.8.8.8:8080"),
927 mustIPPort("[::1%foo]:1024"),
929 slices.SortFunc(values, func(a, b AddrPort) int { return a.Compare(b) })
930 got := fmt.Sprintf("%s", values)
931 want := `[invalid AddrPort 1.2.3.4:443 8.8.8.8:8080 [::1]:80 [::1%foo]:1024 [::2]:80]`
933 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
937 func TestPrefixCompare(t *testing.T) {
942 {Prefix{}, Prefix{}, 0},
943 {Prefix{}, mustPrefix("1.2.3.0/24"), -1},
945 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/24"), 0},
946 {mustPrefix("fe80::/64"), mustPrefix("fe80::/64"), 0},
948 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.4.0/24"), -1},
949 {mustPrefix("fe80::/64"), mustPrefix("fe90::/64"), -1},
951 {mustPrefix("1.2.0.0/16"), mustPrefix("1.2.0.0/24"), -1},
952 {mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
954 {mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
956 for _, tt := range tests {
957 got := tt.a.Compare(tt.b)
959 t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
962 // Also check inverse.
964 got2 := tt.b.Compare(tt.a)
965 if want2 := -1 * tt.want; got2 != want2 {
966 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
973 mustPrefix("1.2.3.0/24"),
974 mustPrefix("fe90::/64"),
975 mustPrefix("fe80::/64"),
976 mustPrefix("1.2.0.0/16"),
978 mustPrefix("fe80::/48"),
979 mustPrefix("1.2.0.0/24"),
981 slices.SortFunc(values, func(a, b Prefix) int { return a.Compare(b) })
982 got := fmt.Sprintf("%s", values)
983 want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
985 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
989 func TestIPStringExpanded(t *testing.T) {
999 ip: mustIP("192.0.2.1"),
1003 ip: mustIP("::ffff:192.0.2.1"),
1004 s: "0000:0000:0000:0000:0000:ffff:c000:0201",
1007 ip: mustIP("2001:db8::1"),
1008 s: "2001:0db8:0000:0000:0000:0000:0000:0001",
1011 ip: mustIP("2001:db8::1%eth0"),
1012 s: "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
1016 for _, tt := range tests {
1017 t.Run(tt.ip.String(), func(t *testing.T) {
1019 got := tt.ip.StringExpanded()
1022 t.Fatalf("got %s, want %s", got, want)
1028 func TestPrefixMasking(t *testing.T) {
1029 type subtest struct {
1036 // makeIPv6 produces a set of IPv6 subtests with an optional zone identifier.
1037 makeIPv6 := func(zone string) []subtest {
1044 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1048 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1050 p: mustPrefix("2001:db8::/32"),
1054 ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
1056 p: mustPrefix("fe80::dead:beef:0:0/96"),
1060 ip: mustIP(fmt.Sprintf("aaaa::%s", zone)),
1062 p: mustPrefix("a000::/4"),
1066 ip: mustIP(fmt.Sprintf("::%s", zone)),
1068 p: mustPrefix("::/63"),
1080 subtests: []subtest{
1093 subtests: []subtest{
1095 ip: mustIP("192.0.2.0"),
1099 ip: mustIP("192.0.2.0"),
1101 p: mustPrefix("192.0.0.0/16"),
1105 ip: mustIP("255.255.255.255"),
1107 p: mustPrefix("255.255.240.0/20"),
1111 // Partially masking one byte that contains both
1112 // 1s and 0s on either side of the mask limit.
1113 ip: mustIP("100.98.156.66"),
1115 p: mustPrefix("100.64.0.0/10"),
1122 subtests: makeIPv6(""),
1125 family: "IPv6 zone",
1126 subtests: makeIPv6("eth0"),
1130 for _, tt := range tests {
1131 t.Run(tt.family, func(t *testing.T) {
1132 for _, st := range tt.subtests {
1133 t.Run(st.p.String(), func(t *testing.T) {
1134 // Ensure st.ip is not mutated.
1135 orig := st.ip.String()
1137 p, err := st.ip.Prefix(int(st.bits))
1138 if st.ok && err != nil {
1139 t.Fatalf("failed to produce prefix: %v", err)
1141 if !st.ok && err == nil {
1142 t.Fatal("expected an error, but none occurred")
1145 t.Logf("err: %v", err)
1149 if !reflect.DeepEqual(p, st.p) {
1150 t.Errorf("prefix = %q, want %q", p, st.p)
1153 if got := st.ip.String(); got != orig {
1154 t.Errorf("IP was mutated: %q, want %q", got, orig)
1162 func TestPrefixMarshalUnmarshal(t *testing.T) {
1172 for _, s := range tests {
1173 t.Run(s, func(t *testing.T) {
1174 // Ensure that JSON (and by extension, text) marshaling is
1175 // sane by entering quoted input.
1176 orig := `"` + s + `"`
1179 if err := json.Unmarshal([]byte(orig), &p); err != nil {
1180 t.Fatalf("failed to unmarshal: %v", err)
1183 pb, err := json.Marshal(p)
1185 t.Fatalf("failed to marshal: %v", err)
1190 t.Errorf("Marshal = %q; want %q", back, orig)
1196 func TestPrefixUnmarshalTextNonZero(t *testing.T) {
1197 ip := mustPrefix("fe80::/64")
1198 if err := ip.UnmarshalText([]byte("xxx")); err == nil {
1199 t.Fatal("unmarshaled into non-empty Prefix")
1203 func TestIs4AndIs6(t *testing.T) {
1209 {Addr{}, false, false},
1210 {mustIP("1.2.3.4"), true, false},
1211 {mustIP("127.0.0.2"), true, false},
1212 {mustIP("::1"), false, true},
1213 {mustIP("::ffff:192.0.2.128"), false, true},
1214 {mustIP("::fffe:c000:0280"), false, true},
1215 {mustIP("::1%eth0"), false, true},
1217 for _, tt := range tests {
1220 t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
1225 t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
1230 func TestIs4In6(t *testing.T) {
1236 {Addr{}, false, Addr{}},
1237 {mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
1238 {mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
1239 {mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
1240 {mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
1241 {mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1242 {mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
1243 {mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1244 {mustIP("0:0:0:0:000000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1245 {mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1246 {mustIP("::1"), false, mustIP("::1")},
1247 {mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
1249 for _, tt := range tests {
1250 got := tt.ip.Is4In6()
1252 t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
1255 if u != tt.wantUnmap {
1256 t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
1261 func TestPrefixMasked(t *testing.T) {
1267 prefix: mustPrefix("192.168.0.255/24"),
1268 masked: mustPrefix("192.168.0.0/24"),
1271 prefix: mustPrefix("2100::/3"),
1272 masked: mustPrefix("2000::/3"),
1275 prefix: PrefixFrom(mustIP("2000::"), 129),
1279 prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
1283 for _, test := range tests {
1284 t.Run(test.prefix.String(), func(t *testing.T) {
1285 got := test.prefix.Masked()
1286 if got != test.masked {
1287 t.Errorf("Masked=%s, want %s", got, test.masked)
1293 func TestPrefix(t *testing.T) {
1303 prefix: "192.168.0.0/24",
1304 ip: mustIP("192.168.0.0"),
1306 contains: mustIPs("192.168.0.1", "192.168.0.55"),
1307 notContains: mustIPs("192.168.1.1", "1.1.1.1"),
1310 prefix: "192.168.1.1/32",
1311 ip: mustIP("192.168.1.1"),
1313 contains: mustIPs("192.168.1.1"),
1314 notContains: mustIPs("192.168.1.2"),
1317 prefix: "100.64.0.0/10", // CGNAT range; prefix not multiple of 8
1318 ip: mustIP("100.64.0.0"),
1320 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"),
1321 notContains: mustIPs("100.63.255.255", "100.128.0.0"),
1324 prefix: "2001:db8::/96",
1325 ip: mustIP("2001:db8::"),
1327 contains: mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
1328 notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
1331 prefix: "0.0.0.0/0",
1332 ip: mustIP("0.0.0.0"),
1334 contains: mustIPs("192.168.0.1", "1.1.1.1"),
1335 notContains: append(mustIPs("2001:db8::1"), Addr{}),
1341 contains: mustIPs("::1", "2001:db8::1"),
1342 notContains: mustIPs("192.0.2.1"),
1346 ip: mustIP("2000::"),
1348 contains: mustIPs("2001:db8::1"),
1349 notContains: mustIPs("fe80::1"),
1352 for _, test := range tests {
1353 t.Run(test.prefix, func(t *testing.T) {
1354 prefix, err := ParsePrefix(test.prefix)
1358 if prefix.Addr() != test.ip {
1359 t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
1361 if prefix.Bits() != test.bits {
1362 t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
1364 for _, ip := range test.contains {
1365 if !prefix.Contains(ip) {
1366 t.Errorf("does not contain %s", ip)
1369 for _, ip := range test.notContains {
1370 if prefix.Contains(ip) {
1371 t.Errorf("contains %s", ip)
1378 if got := prefix.String(); got != want {
1379 t.Errorf("prefix.String()=%q, want %q", got, want)
1382 TestAppendToMarshal(t, prefix)
1387 func TestPrefixFromInvalidBits(t *testing.T) {
1388 v4 := MustParseAddr("1.2.3.4")
1389 v6 := MustParseAddr("66::66")
1408 for _, tt := range tests {
1409 p := PrefixFrom(tt.ip, tt.in)
1410 if got := p.Bits(); got != tt.want {
1411 t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
1416 func TestParsePrefixAllocs(t *testing.T) {
1421 {"192.168.1.0", "/24"},
1422 {"aaaa:bbbb:cccc::", "/24"},
1424 for _, test := range tests {
1425 prefix := test.ip + test.slash
1426 t.Run(prefix, func(t *testing.T) {
1427 ipAllocs := int(testing.AllocsPerRun(5, func() {
1430 prefixAllocs := int(testing.AllocsPerRun(5, func() {
1433 if got := prefixAllocs - ipAllocs; got != 0 {
1434 t.Errorf("allocs=%d, want 0", got)
1440 func TestParsePrefixError(t *testing.T) {
1446 prefix: "192.168.0.0",
1450 prefix: "1.257.1.1/24",
1451 errstr: "value >255",
1454 prefix: "1.1.1.0/q",
1458 prefix: "1.1.1.0/-1",
1462 prefix: "1.1.1.0/33",
1463 errstr: "out of range",
1466 prefix: "2001::/129",
1467 errstr: "out of range",
1469 // Zones are not allowed: https://go.dev/issue/51899
1471 prefix: "1.1.1.0%a/24",
1472 errstr: "unexpected character",
1475 prefix: "2001:db8::%a/32",
1476 errstr: "zones cannot be present",
1479 prefix: "1.1.1.0/+32",
1483 prefix: "1.1.1.0/-32",
1487 prefix: "1.1.1.0/032",
1491 prefix: "1.1.1.0/0032",
1495 for _, test := range tests {
1496 t.Run(test.prefix, func(t *testing.T) {
1497 _, err := ParsePrefix(test.prefix)
1501 if got := err.Error(); !strings.Contains(got, test.errstr) {
1502 t.Errorf("error is missing substring %q: %s", test.errstr, got)
1508 func TestPrefixIsSingleIP(t *testing.T) {
1513 {ipp: mustPrefix("127.0.0.1/32"), want: true},
1514 {ipp: mustPrefix("127.0.0.1/31"), want: false},
1515 {ipp: mustPrefix("127.0.0.1/0"), want: false},
1516 {ipp: mustPrefix("::1/128"), want: true},
1517 {ipp: mustPrefix("::1/127"), want: false},
1518 {ipp: mustPrefix("::1/0"), want: false},
1519 {ipp: Prefix{}, want: false},
1521 for _, tt := range tests {
1522 got := tt.ipp.IsSingleIP()
1524 t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
1529 func mustIPs(strs ...string) []Addr {
1531 for _, s := range strs {
1532 res = append(res, mustIP(s))
1537 func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
1543 {"ipv4", "1.2.3.4"},
1544 {"ipv6", "2001:db8::1"},
1545 {"ipv6+zone", "2001:db8::1%eth0"},
1547 for _, tc := range tests {
1548 b.Run(tc.name, func(b *testing.B) {
1550 for i := 0; i < b.N; i++ {
1551 bt, err := ip.MarshalBinary()
1556 if err := ip2.UnmarshalBinary(bt); err != nil {
1564 func BenchmarkStdIPv4(b *testing.B) {
1567 for i := 0; i < b.N; i++ {
1568 ip := net.IPv4(8, 8, 8, 8)
1570 for i := 0; i < 100; i++ {
1571 ips = append(ips, ip)
1576 func BenchmarkIPv4(b *testing.B) {
1579 for i := 0; i < b.N; i++ {
1580 ip := IPv4(8, 8, 8, 8)
1582 for i := 0; i < 100; i++ {
1583 ips = append(ips, ip)
1588 // ip4i was one of the possible representations of IP that came up in
1589 // discussions, inlining IPv4 addresses, but having an "overflow"
1590 // interface for IPv6 or IPv6 + zone. This is here for benchmarking.
1600 func newip4i_v4(a, b, c, d byte) ip4i {
1601 return ip4i{ip4: [4]byte{a, b, c, d}}
1604 // BenchmarkIPv4_inline benchmarks the candidate representation, ip4i.
1605 func BenchmarkIPv4_inline(b *testing.B) {
1608 for i := 0; i < b.N; i++ {
1609 ip := newip4i_v4(8, 8, 8, 8)
1611 for i := 0; i < 100; i++ {
1612 ips = append(ips, ip)
1617 func BenchmarkStdIPv6(b *testing.B) {
1620 for i := 0; i < b.N; i++ {
1621 ip := net.ParseIP("2001:db8::1")
1623 for i := 0; i < 100; i++ {
1624 ips = append(ips, ip)
1629 func BenchmarkIPv6(b *testing.B) {
1632 for i := 0; i < b.N; i++ {
1633 ip := mustIP("2001:db8::1")
1635 for i := 0; i < 100; i++ {
1636 ips = append(ips, ip)
1641 func BenchmarkIPv4Contains(b *testing.B) {
1643 prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
1644 ip := IPv4(192, 168, 1, 1)
1645 for i := 0; i < b.N; i++ {
1650 func BenchmarkIPv6Contains(b *testing.B) {
1652 prefix := MustParsePrefix("::1/128")
1653 ip := MustParseAddr("::1")
1654 for i := 0; i < b.N; i++ {
1659 var parseBenchInputs = []struct {
1663 {"v4", "192.168.1.1"},
1664 {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
1665 {"v6_ellipsis", "fd7a:115c::626b:430b"},
1666 {"v6_v4", "::ffff:192.168.140.255"},
1667 {"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
1670 func BenchmarkParseAddr(b *testing.B) {
1671 sinkInternValue = intern.Get("eth1") // Pin to not benchmark the intern package
1672 for _, test := range parseBenchInputs {
1673 b.Run(test.name, func(b *testing.B) {
1675 for i := 0; i < b.N; i++ {
1676 sinkIP, _ = ParseAddr(test.ip)
1682 func BenchmarkStdParseIP(b *testing.B) {
1683 for _, test := range parseBenchInputs {
1684 b.Run(test.name, func(b *testing.B) {
1686 for i := 0; i < b.N; i++ {
1687 sinkStdIP = net.ParseIP(test.ip)
1693 func BenchmarkIPString(b *testing.B) {
1694 for _, test := range parseBenchInputs {
1695 ip := MustParseAddr(test.ip)
1696 b.Run(test.name, func(b *testing.B) {
1698 for i := 0; i < b.N; i++ {
1699 sinkString = ip.String()
1705 func BenchmarkIPStringExpanded(b *testing.B) {
1706 for _, test := range parseBenchInputs {
1707 ip := MustParseAddr(test.ip)
1708 b.Run(test.name, func(b *testing.B) {
1710 for i := 0; i < b.N; i++ {
1711 sinkString = ip.StringExpanded()
1717 func BenchmarkIPMarshalText(b *testing.B) {
1719 ip := MustParseAddr("66.55.44.33")
1720 for i := 0; i < b.N; i++ {
1721 sinkBytes, _ = ip.MarshalText()
1725 func BenchmarkAddrPortString(b *testing.B) {
1726 for _, test := range parseBenchInputs {
1727 ip := MustParseAddr(test.ip)
1728 ipp := AddrPortFrom(ip, 60000)
1729 b.Run(test.name, func(b *testing.B) {
1731 for i := 0; i < b.N; i++ {
1732 sinkString = ipp.String()
1738 func BenchmarkAddrPortMarshalText(b *testing.B) {
1739 for _, test := range parseBenchInputs {
1740 ip := MustParseAddr(test.ip)
1741 ipp := AddrPortFrom(ip, 60000)
1742 b.Run(test.name, func(b *testing.B) {
1744 for i := 0; i < b.N; i++ {
1745 sinkBytes, _ = ipp.MarshalText()
1751 func BenchmarkPrefixMasking(b *testing.B) {
1759 ip: IPv4(192, 0, 2, 0),
1764 ip: IPv4(192, 0, 2, 0),
1769 ip: IPv4(192, 0, 2, 0),
1774 ip: mustIP("2001:db8::1"),
1779 ip: mustIP("2001:db8::1"),
1784 ip: mustIP("2001:db8::1"),
1788 name: "IPv6 zone /128",
1789 ip: mustIP("2001:db8::1%eth0"),
1793 name: "IPv6 zone /65",
1794 ip: mustIP("2001:db8::1%eth0"),
1798 name: "IPv6 zone /0",
1799 ip: mustIP("2001:db8::1%eth0"),
1804 for _, tt := range tests {
1805 b.Run(tt.name, func(b *testing.B) {
1808 for i := 0; i < b.N; i++ {
1809 sinkPrefix, _ = tt.ip.Prefix(tt.bits)
1815 func BenchmarkPrefixMarshalText(b *testing.B) {
1817 ipp := MustParsePrefix("66.55.44.33/22")
1818 for i := 0; i < b.N; i++ {
1819 sinkBytes, _ = ipp.MarshalText()
1823 func BenchmarkParseAddrPort(b *testing.B) {
1824 for _, test := range parseBenchInputs {
1826 if strings.HasPrefix(test.name, "v6") {
1827 ipp = fmt.Sprintf("[%s]:1234", test.ip)
1829 ipp = fmt.Sprintf("%s:1234", test.ip)
1831 b.Run(test.name, func(b *testing.B) {
1834 for i := 0; i < b.N; i++ {
1835 sinkAddrPort, _ = ParseAddrPort(ipp)
1841 func TestAs4(t *testing.T) {
1848 ip: mustIP("1.2.3.4"),
1849 want: [4]byte{1, 2, 3, 4},
1852 ip: AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6
1853 want: [4]byte{1, 2, 3, 4},
1856 ip: mustIP("0.0.0.0"),
1857 want: [4]byte{0, 0, 0, 0},
1868 as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
1870 if recover() != nil {
1878 for i, tt := range tests {
1879 got, gotPanic := as4(tt.ip)
1880 if gotPanic != tt.wantPanic {
1881 t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
1885 t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
1890 func TestPrefixOverlaps(t *testing.T) {
1896 {Prefix{}, pfx("1.2.0.0/16"), false}, // first zero
1897 {pfx("1.2.0.0/16"), Prefix{}, false}, // second zero
1898 {pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families
1900 {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal
1902 {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
1903 {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
1905 {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
1906 {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
1908 // Match /0 either order
1909 {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
1910 {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
1912 {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true
1915 {pfx("5::1/128"), pfx("5::0/8"), true},
1916 {pfx("5::0/8"), pfx("5::1/128"), true},
1918 // IPv6 not overlapping
1919 {pfx("1::1/128"), pfx("2::2/128"), false},
1920 {pfx("0100::0/8"), pfx("::1/128"), false},
1922 // IPv4-mapped IPv6 addresses should not overlap with IPv4.
1923 {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
1926 {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
1927 {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
1929 for i, tt := range tests {
1930 if got := tt.a.Overlaps(tt.b); got != tt.want {
1931 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
1933 // Overlaps is commutative
1934 if got := tt.b.Overlaps(tt.a); got != tt.want {
1935 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
1940 // Sink variables are here to force the compiler to not elide
1941 // seemingly useless work in benchmarks and allocation tests. If you
1942 // were to just `_ = foo()` within a test function, the compiler could
1943 // correctly deduce that foo() does nothing and doesn't need to be
1944 // called. By writing results to a global variable, we hide that fact
1945 // from the compiler and force it to keep the code under test.
1949 sinkAddrPort AddrPort
1951 sinkPrefixSlice []Prefix
1952 sinkInternValue *intern.Value
1958 sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)}
1961 func TestNoAllocs(t *testing.T) {
1962 // Wrappers that panic on error, to prove that our alloc-free
1963 // methods are returning successfully.
1964 panicIP := func(ip Addr, err error) Addr {
1970 panicPfx := func(pfx Prefix, err error) Prefix {
1976 panicIPP := func(ipp AddrPort, err error) AddrPort {
1982 test := func(name string, f func()) {
1983 t.Run(name, func(t *testing.T) {
1984 n := testing.AllocsPerRun(1000, f)
1986 t.Fatalf("allocs = %d; want 0", int(n))
1991 // Addr constructors
1992 test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
1993 test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
1994 test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
1995 test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
1996 test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
1997 test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
1998 test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
1999 test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2000 test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2001 test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2004 test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2005 test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2006 test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2007 test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2008 test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2009 test("Addr.Compare", func() {
2010 a := MustParseAddr("1.2.3.4")
2011 b := MustParseAddr("2.3.4.5")
2012 sinkBool = a.Compare(b) == 0
2014 test("Addr.Less", func() {
2015 a := MustParseAddr("1.2.3.4")
2016 b := MustParseAddr("2.3.4.5")
2017 sinkBool = a.Less(b)
2019 test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2020 test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2021 test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2022 test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2023 test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2024 test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2025 test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2026 test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2027 test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2028 test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2029 test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2030 test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2031 test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2032 test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2033 test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2034 test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2035 test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2036 test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2037 test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2039 // AddrPort constructors
2040 test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2041 test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2042 test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2044 // Prefix constructors
2045 test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2046 test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2047 test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2048 test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2051 test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2052 test("Prefix.Overlaps", func() {
2053 a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2054 sinkBool = a.Overlaps(b)
2056 test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2057 test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2058 test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2061 func TestAddrStringAllocs(t *testing.T) {
2067 {"zero", Addr{}, 0},
2068 {"ipv4", MustParseAddr("192.168.1.1"), 1},
2069 {"ipv6", MustParseAddr("2001:db8::1"), 1},
2070 {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2071 {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2072 {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2074 optimizationOff := testenv.OptimizationOff()
2075 for _, tc := range tests {
2076 t.Run(tc.name, func(t *testing.T) {
2077 if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2078 // Optimizations are required to remove some allocs.
2079 t.Skipf("skipping on %v", testenv.Builder())
2081 allocs := int(testing.AllocsPerRun(1000, func() {
2082 sinkString = tc.ip.String()
2084 if allocs != tc.wantAllocs {
2085 t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2091 func TestPrefixString(t *testing.T) {
2096 {Prefix{}, "invalid Prefix"},
2097 {PrefixFrom(Addr{}, 8), "invalid Prefix"},
2098 {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2101 for _, tt := range tests {
2102 if got := tt.ipp.String(); got != tt.want {
2103 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2108 func TestInvalidAddrPortString(t *testing.T) {
2113 {AddrPort{}, "invalid AddrPort"},
2114 {AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2117 for _, tt := range tests {
2118 if got := tt.ipp.String(); got != tt.want {
2119 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2124 func TestAsSlice(t *testing.T) {
2129 {in: Addr{}, want: nil},
2130 {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2131 {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2134 for _, test := range tests {
2135 got := test.in.AsSlice()
2136 if !bytes.Equal(got, test.want) {
2137 t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2144 func BenchmarkAs16(b *testing.B) {
2145 addr := MustParseAddr("1::10")
2146 for i := 0; i < b.N; i++ {
2147 sink16 = addr.As16()