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