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