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