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 for _, tt := range tests {
1128 got := tt.a.Compare(tt.b)
1129 if got != tt.want {
1130 t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
1131 }
1132
1133
1134 if got == tt.want {
1135 got2 := tt.b.Compare(tt.a)
1136 if want2 := -1 * tt.want; got2 != want2 {
1137 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
1138 }
1139 }
1140 }
1141
1142
1143 values := []Prefix{
1144 mustPrefix("1.2.3.0/24"),
1145 mustPrefix("fe90::/64"),
1146 mustPrefix("fe80::/64"),
1147 mustPrefix("1.2.0.0/16"),
1148 Prefix{},
1149 mustPrefix("fe80::/48"),
1150 mustPrefix("1.2.0.0/24"),
1151 }
1152 slices.SortFunc(values, Prefix.Compare)
1153 got := fmt.Sprintf("%s", values)
1154 want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
1155 if got != want {
1156 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
1157 }
1158 }
1159
1160 func TestIPStringExpanded(t *testing.T) {
1161 tests := []struct {
1162 ip Addr
1163 s string
1164 }{
1165 {
1166 ip: Addr{},
1167 s: "invalid IP",
1168 },
1169 {
1170 ip: mustIP("192.0.2.1"),
1171 s: "192.0.2.1",
1172 },
1173 {
1174 ip: mustIP("::ffff:192.0.2.1"),
1175 s: "0000:0000:0000:0000:0000:ffff:c000:0201",
1176 },
1177 {
1178 ip: mustIP("2001:db8::1"),
1179 s: "2001:0db8:0000:0000:0000:0000:0000:0001",
1180 },
1181 {
1182 ip: mustIP("2001:db8::1%eth0"),
1183 s: "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
1184 },
1185 }
1186
1187 for _, tt := range tests {
1188 t.Run(tt.ip.String(), func(t *testing.T) {
1189 want := tt.s
1190 got := tt.ip.StringExpanded()
1191
1192 if got != want {
1193 t.Fatalf("got %s, want %s", got, want)
1194 }
1195 })
1196 }
1197 }
1198
1199 func TestPrefixMasking(t *testing.T) {
1200 type subtest struct {
1201 ip Addr
1202 bits uint8
1203 p Prefix
1204 ok bool
1205 }
1206
1207
1208 makeIPv6 := func(zone string) []subtest {
1209 if zone != "" {
1210 zone = "%" + zone
1211 }
1212
1213 return []subtest{
1214 {
1215 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1216 bits: 255,
1217 },
1218 {
1219 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1220 bits: 32,
1221 p: mustPrefix("2001:db8::/32"),
1222 ok: true,
1223 },
1224 {
1225 ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
1226 bits: 96,
1227 p: mustPrefix("fe80::dead:beef:0:0/96"),
1228 ok: true,
1229 },
1230 {
1231 ip: mustIP(fmt.Sprintf("aaaa::%s", zone)),
1232 bits: 4,
1233 p: mustPrefix("a000::/4"),
1234 ok: true,
1235 },
1236 {
1237 ip: mustIP(fmt.Sprintf("::%s", zone)),
1238 bits: 63,
1239 p: mustPrefix("::/63"),
1240 ok: true,
1241 },
1242 }
1243 }
1244
1245 tests := []struct {
1246 family string
1247 subtests []subtest
1248 }{
1249 {
1250 family: "nil",
1251 subtests: []subtest{
1252 {
1253 bits: 255,
1254 ok: true,
1255 },
1256 {
1257 bits: 16,
1258 ok: true,
1259 },
1260 },
1261 },
1262 {
1263 family: "IPv4",
1264 subtests: []subtest{
1265 {
1266 ip: mustIP("192.0.2.0"),
1267 bits: 255,
1268 },
1269 {
1270 ip: mustIP("192.0.2.0"),
1271 bits: 16,
1272 p: mustPrefix("192.0.0.0/16"),
1273 ok: true,
1274 },
1275 {
1276 ip: mustIP("255.255.255.255"),
1277 bits: 20,
1278 p: mustPrefix("255.255.240.0/20"),
1279 ok: true,
1280 },
1281 {
1282
1283
1284 ip: mustIP("100.98.156.66"),
1285 bits: 10,
1286 p: mustPrefix("100.64.0.0/10"),
1287 ok: true,
1288 },
1289 },
1290 },
1291 {
1292 family: "IPv6",
1293 subtests: makeIPv6(""),
1294 },
1295 {
1296 family: "IPv6 zone",
1297 subtests: makeIPv6("eth0"),
1298 },
1299 }
1300
1301 for _, tt := range tests {
1302 t.Run(tt.family, func(t *testing.T) {
1303 for _, st := range tt.subtests {
1304 t.Run(st.p.String(), func(t *testing.T) {
1305
1306 orig := st.ip.String()
1307
1308 p, err := st.ip.Prefix(int(st.bits))
1309 if st.ok && err != nil {
1310 t.Fatalf("failed to produce prefix: %v", err)
1311 }
1312 if !st.ok && err == nil {
1313 t.Fatal("expected an error, but none occurred")
1314 }
1315 if err != nil {
1316 t.Logf("err: %v", err)
1317 return
1318 }
1319
1320 if !reflect.DeepEqual(p, st.p) {
1321 t.Errorf("prefix = %q, want %q", p, st.p)
1322 }
1323
1324 if got := st.ip.String(); got != orig {
1325 t.Errorf("IP was mutated: %q, want %q", got, orig)
1326 }
1327 })
1328 }
1329 })
1330 }
1331 }
1332
1333 func TestPrefixMarshalUnmarshal(t *testing.T) {
1334 tests := []string{
1335 "",
1336 "1.2.3.4/32",
1337 "0.0.0.0/0",
1338 "::/0",
1339 "::1/128",
1340 "2001:db8::/32",
1341 }
1342
1343 for _, s := range tests {
1344 t.Run(s, func(t *testing.T) {
1345
1346
1347 orig := `"` + s + `"`
1348
1349 var p Prefix
1350 if err := json.Unmarshal([]byte(orig), &p); err != nil {
1351 t.Fatalf("failed to unmarshal: %v", err)
1352 }
1353
1354 pb, err := json.Marshal(p)
1355 if err != nil {
1356 t.Fatalf("failed to marshal: %v", err)
1357 }
1358
1359 back := string(pb)
1360 if orig != back {
1361 t.Errorf("Marshal = %q; want %q", back, orig)
1362 }
1363 })
1364 }
1365 }
1366
1367 func TestPrefixUnmarshalTextNonZero(t *testing.T) {
1368 ip := mustPrefix("fe80::/64")
1369 if err := ip.UnmarshalText([]byte("xxx")); err == nil {
1370 t.Fatal("unmarshaled into non-empty Prefix")
1371 }
1372 }
1373
1374 func TestIs4AndIs6(t *testing.T) {
1375 tests := []struct {
1376 ip Addr
1377 is4 bool
1378 is6 bool
1379 }{
1380 {Addr{}, false, false},
1381 {mustIP("1.2.3.4"), true, false},
1382 {mustIP("127.0.0.2"), true, false},
1383 {mustIP("::1"), false, true},
1384 {mustIP("::ffff:192.0.2.128"), false, true},
1385 {mustIP("::fffe:c000:0280"), false, true},
1386 {mustIP("::1%eth0"), false, true},
1387 }
1388 for _, tt := range tests {
1389 got4 := tt.ip.Is4()
1390 if got4 != tt.is4 {
1391 t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
1392 }
1393
1394 got6 := tt.ip.Is6()
1395 if got6 != tt.is6 {
1396 t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
1397 }
1398 }
1399 }
1400
1401 func TestIs4In6(t *testing.T) {
1402 tests := []struct {
1403 ip Addr
1404 want bool
1405 wantUnmap Addr
1406 }{
1407 {Addr{}, false, Addr{}},
1408 {mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
1409 {mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
1410 {mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
1411 {mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
1412 {mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1413 {mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
1414 {mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1415 {mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1416 {mustIP("::1"), false, mustIP("::1")},
1417 {mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
1418 }
1419 for _, tt := range tests {
1420 got := tt.ip.Is4In6()
1421 if got != tt.want {
1422 t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
1423 }
1424 u := tt.ip.Unmap()
1425 if u != tt.wantUnmap {
1426 t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
1427 }
1428 }
1429 }
1430
1431 func TestPrefixMasked(t *testing.T) {
1432 tests := []struct {
1433 prefix Prefix
1434 masked Prefix
1435 }{
1436 {
1437 prefix: mustPrefix("192.168.0.255/24"),
1438 masked: mustPrefix("192.168.0.0/24"),
1439 },
1440 {
1441 prefix: mustPrefix("2100::/3"),
1442 masked: mustPrefix("2000::/3"),
1443 },
1444 {
1445 prefix: PrefixFrom(mustIP("2000::"), 129),
1446 masked: Prefix{},
1447 },
1448 {
1449 prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
1450 masked: Prefix{},
1451 },
1452 }
1453 for _, test := range tests {
1454 t.Run(test.prefix.String(), func(t *testing.T) {
1455 got := test.prefix.Masked()
1456 if got != test.masked {
1457 t.Errorf("Masked=%s, want %s", got, test.masked)
1458 }
1459 })
1460 }
1461 }
1462
1463 func TestPrefix(t *testing.T) {
1464 tests := []struct {
1465 prefix string
1466 ip Addr
1467 bits int
1468 str string
1469 contains []Addr
1470 notContains []Addr
1471 }{
1472 {
1473 prefix: "192.168.0.0/24",
1474 ip: mustIP("192.168.0.0"),
1475 bits: 24,
1476 contains: mustIPs("192.168.0.1", "192.168.0.55"),
1477 notContains: mustIPs("192.168.1.1", "1.1.1.1"),
1478 },
1479 {
1480 prefix: "192.168.1.1/32",
1481 ip: mustIP("192.168.1.1"),
1482 bits: 32,
1483 contains: mustIPs("192.168.1.1"),
1484 notContains: mustIPs("192.168.1.2"),
1485 },
1486 {
1487 prefix: "100.64.0.0/10",
1488 ip: mustIP("100.64.0.0"),
1489 bits: 10,
1490 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"),
1491 notContains: mustIPs("100.63.255.255", "100.128.0.0"),
1492 },
1493 {
1494 prefix: "2001:db8::/96",
1495 ip: mustIP("2001:db8::"),
1496 bits: 96,
1497 contains: mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
1498 notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
1499 },
1500 {
1501 prefix: "0.0.0.0/0",
1502 ip: mustIP("0.0.0.0"),
1503 bits: 0,
1504 contains: mustIPs("192.168.0.1", "1.1.1.1"),
1505 notContains: append(mustIPs("2001:db8::1"), Addr{}),
1506 },
1507 {
1508 prefix: "::/0",
1509 ip: mustIP("::"),
1510 bits: 0,
1511 contains: mustIPs("::1", "2001:db8::1"),
1512 notContains: mustIPs("192.0.2.1"),
1513 },
1514 {
1515 prefix: "2000::/3",
1516 ip: mustIP("2000::"),
1517 bits: 3,
1518 contains: mustIPs("2001:db8::1"),
1519 notContains: mustIPs("fe80::1"),
1520 },
1521 }
1522 for _, test := range tests {
1523 t.Run(test.prefix, func(t *testing.T) {
1524 prefix, err := ParsePrefix(test.prefix)
1525 if err != nil {
1526 t.Fatal(err)
1527 }
1528 if prefix.Addr() != test.ip {
1529 t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
1530 }
1531 if prefix.Bits() != test.bits {
1532 t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
1533 }
1534 for _, ip := range test.contains {
1535 if !prefix.Contains(ip) {
1536 t.Errorf("does not contain %s", ip)
1537 }
1538 }
1539 for _, ip := range test.notContains {
1540 if prefix.Contains(ip) {
1541 t.Errorf("contains %s", ip)
1542 }
1543 }
1544 want := test.str
1545 if want == "" {
1546 want = test.prefix
1547 }
1548 if got := prefix.String(); got != want {
1549 t.Errorf("prefix.String()=%q, want %q", got, want)
1550 }
1551
1552 TestAppendToMarshal(t, prefix)
1553 })
1554 }
1555 }
1556
1557 func TestPrefixFromInvalidBits(t *testing.T) {
1558 v4 := MustParseAddr("1.2.3.4")
1559 v6 := MustParseAddr("66::66")
1560 tests := []struct {
1561 ip Addr
1562 in, want int
1563 }{
1564 {v4, 0, 0},
1565 {v6, 0, 0},
1566 {v4, 1, 1},
1567 {v4, 33, -1},
1568 {v6, 33, 33},
1569 {v6, 127, 127},
1570 {v6, 128, 128},
1571 {v4, 254, -1},
1572 {v4, 255, -1},
1573 {v4, -1, -1},
1574 {v6, -1, -1},
1575 {v4, -5, -1},
1576 {v6, -5, -1},
1577 }
1578 for _, tt := range tests {
1579 p := PrefixFrom(tt.ip, tt.in)
1580 if got := p.Bits(); got != tt.want {
1581 t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
1582 }
1583 }
1584 }
1585
1586 func TestParsePrefixAllocs(t *testing.T) {
1587 tests := []struct {
1588 ip string
1589 slash string
1590 }{
1591 {"192.168.1.0", "/24"},
1592 {"aaaa:bbbb:cccc::", "/24"},
1593 }
1594 for _, test := range tests {
1595 prefix := test.ip + test.slash
1596 t.Run(prefix, func(t *testing.T) {
1597 ipAllocs := int(testing.AllocsPerRun(5, func() {
1598 ParseAddr(test.ip)
1599 }))
1600 prefixAllocs := int(testing.AllocsPerRun(5, func() {
1601 ParsePrefix(prefix)
1602 }))
1603 if got := prefixAllocs - ipAllocs; got != 0 {
1604 t.Errorf("allocs=%d, want 0", got)
1605 }
1606 })
1607 }
1608 }
1609
1610 func TestParsePrefixError(t *testing.T) {
1611 tests := []struct {
1612 prefix string
1613 errstr string
1614 }{
1615 {
1616 prefix: "192.168.0.0",
1617 errstr: "no '/'",
1618 },
1619 {
1620 prefix: "1.257.1.1/24",
1621 errstr: "value >255",
1622 },
1623 {
1624 prefix: "1.1.1.0/q",
1625 errstr: "bad bits",
1626 },
1627 {
1628 prefix: "1.1.1.0/-1",
1629 errstr: "bad bits",
1630 },
1631 {
1632 prefix: "1.1.1.0/33",
1633 errstr: "out of range",
1634 },
1635 {
1636 prefix: "2001::/129",
1637 errstr: "out of range",
1638 },
1639
1640 {
1641 prefix: "1.1.1.0%a/24",
1642 errstr: "unexpected character",
1643 },
1644 {
1645 prefix: "2001:db8::%a/32",
1646 errstr: "zones cannot be present",
1647 },
1648 {
1649 prefix: "1.1.1.0/+32",
1650 errstr: "bad bits",
1651 },
1652 {
1653 prefix: "1.1.1.0/-32",
1654 errstr: "bad bits",
1655 },
1656 {
1657 prefix: "1.1.1.0/032",
1658 errstr: "bad bits",
1659 },
1660 {
1661 prefix: "1.1.1.0/0032",
1662 errstr: "bad bits",
1663 },
1664 }
1665 for _, test := range tests {
1666 t.Run(test.prefix, func(t *testing.T) {
1667 _, err := ParsePrefix(test.prefix)
1668 if err == nil {
1669 t.Fatal("no error")
1670 }
1671 if got := err.Error(); !strings.Contains(got, test.errstr) {
1672 t.Errorf("error is missing substring %q: %s", test.errstr, got)
1673 }
1674 })
1675 }
1676 }
1677
1678 func TestPrefixIsSingleIP(t *testing.T) {
1679 tests := []struct {
1680 ipp Prefix
1681 want bool
1682 }{
1683 {ipp: mustPrefix("127.0.0.1/32"), want: true},
1684 {ipp: mustPrefix("127.0.0.1/31"), want: false},
1685 {ipp: mustPrefix("127.0.0.1/0"), want: false},
1686 {ipp: mustPrefix("::1/128"), want: true},
1687 {ipp: mustPrefix("::1/127"), want: false},
1688 {ipp: mustPrefix("::1/0"), want: false},
1689 {ipp: Prefix{}, want: false},
1690 }
1691 for _, tt := range tests {
1692 got := tt.ipp.IsSingleIP()
1693 if got != tt.want {
1694 t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
1695 }
1696 }
1697 }
1698
1699 func mustIPs(strs ...string) []Addr {
1700 var res []Addr
1701 for _, s := range strs {
1702 res = append(res, mustIP(s))
1703 }
1704 return res
1705 }
1706
1707 func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
1708 b.ReportAllocs()
1709 tests := []struct {
1710 name string
1711 ip string
1712 }{
1713 {"ipv4", "1.2.3.4"},
1714 {"ipv6", "2001:db8::1"},
1715 {"ipv6+zone", "2001:db8::1%eth0"},
1716 }
1717 for _, tc := range tests {
1718 b.Run(tc.name, func(b *testing.B) {
1719 ip := mustIP(tc.ip)
1720 for i := 0; i < b.N; i++ {
1721 bt, err := ip.MarshalBinary()
1722 if err != nil {
1723 b.Fatal(err)
1724 }
1725 var ip2 Addr
1726 if err := ip2.UnmarshalBinary(bt); err != nil {
1727 b.Fatal(err)
1728 }
1729 }
1730 })
1731 }
1732 }
1733
1734 func BenchmarkStdIPv4(b *testing.B) {
1735 b.ReportAllocs()
1736 ips := []net.IP{}
1737 for i := 0; i < b.N; i++ {
1738 ip := net.IPv4(8, 8, 8, 8)
1739 ips = ips[:0]
1740 for i := 0; i < 100; i++ {
1741 ips = append(ips, ip)
1742 }
1743 }
1744 }
1745
1746 func BenchmarkIPv4(b *testing.B) {
1747 b.ReportAllocs()
1748 ips := []Addr{}
1749 for i := 0; i < b.N; i++ {
1750 ip := IPv4(8, 8, 8, 8)
1751 ips = ips[:0]
1752 for i := 0; i < 100; i++ {
1753 ips = append(ips, ip)
1754 }
1755 }
1756 }
1757
1758
1759
1760
1761 type ip4i struct {
1762 ip4 [4]byte
1763 flags1 byte
1764 flags2 byte
1765 flags3 byte
1766 flags4 byte
1767 ipv6 any
1768 }
1769
1770 func newip4i_v4(a, b, c, d byte) ip4i {
1771 return ip4i{ip4: [4]byte{a, b, c, d}}
1772 }
1773
1774
1775 func BenchmarkIPv4_inline(b *testing.B) {
1776 b.ReportAllocs()
1777 ips := []ip4i{}
1778 for i := 0; i < b.N; i++ {
1779 ip := newip4i_v4(8, 8, 8, 8)
1780 ips = ips[:0]
1781 for i := 0; i < 100; i++ {
1782 ips = append(ips, ip)
1783 }
1784 }
1785 }
1786
1787 func BenchmarkStdIPv6(b *testing.B) {
1788 b.ReportAllocs()
1789 ips := []net.IP{}
1790 for i := 0; i < b.N; i++ {
1791 ip := net.ParseIP("2001:db8::1")
1792 ips = ips[:0]
1793 for i := 0; i < 100; i++ {
1794 ips = append(ips, ip)
1795 }
1796 }
1797 }
1798
1799 func BenchmarkIPv6(b *testing.B) {
1800 b.ReportAllocs()
1801 ips := []Addr{}
1802 for i := 0; i < b.N; i++ {
1803 ip := mustIP("2001:db8::1")
1804 ips = ips[:0]
1805 for i := 0; i < 100; i++ {
1806 ips = append(ips, ip)
1807 }
1808 }
1809 }
1810
1811 func BenchmarkIPv4Contains(b *testing.B) {
1812 b.ReportAllocs()
1813 prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
1814 ip := IPv4(192, 168, 1, 1)
1815 for i := 0; i < b.N; i++ {
1816 prefix.Contains(ip)
1817 }
1818 }
1819
1820 func BenchmarkIPv6Contains(b *testing.B) {
1821 b.ReportAllocs()
1822 prefix := MustParsePrefix("::1/128")
1823 ip := MustParseAddr("::1")
1824 for i := 0; i < b.N; i++ {
1825 prefix.Contains(ip)
1826 }
1827 }
1828
1829 var parseBenchInputs = []struct {
1830 name string
1831 ip string
1832 }{
1833 {"v4", "192.168.1.1"},
1834 {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
1835 {"v6_ellipsis", "fd7a:115c::626b:430b"},
1836 {"v6_v4", "::ffff:192.168.140.255"},
1837 {"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
1838 }
1839
1840 func BenchmarkParseAddr(b *testing.B) {
1841 sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1"))
1842 for _, test := range parseBenchInputs {
1843 b.Run(test.name, func(b *testing.B) {
1844 b.ReportAllocs()
1845 for i := 0; i < b.N; i++ {
1846 sinkIP, _ = ParseAddr(test.ip)
1847 }
1848 })
1849 }
1850 }
1851
1852 func BenchmarkStdParseIP(b *testing.B) {
1853 for _, test := range parseBenchInputs {
1854 b.Run(test.name, func(b *testing.B) {
1855 b.ReportAllocs()
1856 for i := 0; i < b.N; i++ {
1857 sinkStdIP = net.ParseIP(test.ip)
1858 }
1859 })
1860 }
1861 }
1862
1863 func BenchmarkAddrString(b *testing.B) {
1864 for _, test := range parseBenchInputs {
1865 ip := MustParseAddr(test.ip)
1866 b.Run(test.name, func(b *testing.B) {
1867 b.ReportAllocs()
1868 for i := 0; i < b.N; i++ {
1869 sinkString = ip.String()
1870 }
1871 })
1872 }
1873 }
1874
1875 func BenchmarkIPStringExpanded(b *testing.B) {
1876 for _, test := range parseBenchInputs {
1877 ip := MustParseAddr(test.ip)
1878 b.Run(test.name, func(b *testing.B) {
1879 b.ReportAllocs()
1880 for i := 0; i < b.N; i++ {
1881 sinkString = ip.StringExpanded()
1882 }
1883 })
1884 }
1885 }
1886
1887 func BenchmarkAddrMarshalText(b *testing.B) {
1888 for _, test := range parseBenchInputs {
1889 ip := MustParseAddr(test.ip)
1890 b.Run(test.name, func(b *testing.B) {
1891 b.ReportAllocs()
1892 for i := 0; i < b.N; i++ {
1893 sinkBytes, _ = ip.MarshalText()
1894 }
1895 })
1896 }
1897 }
1898
1899 func BenchmarkAddrPortString(b *testing.B) {
1900 for _, test := range parseBenchInputs {
1901 ip := MustParseAddr(test.ip)
1902 ipp := AddrPortFrom(ip, 60000)
1903 b.Run(test.name, func(b *testing.B) {
1904 b.ReportAllocs()
1905 for i := 0; i < b.N; i++ {
1906 sinkString = ipp.String()
1907 }
1908 })
1909 }
1910 }
1911
1912 func BenchmarkAddrPortMarshalText(b *testing.B) {
1913 for _, test := range parseBenchInputs {
1914 ip := MustParseAddr(test.ip)
1915 ipp := AddrPortFrom(ip, 60000)
1916 b.Run(test.name, func(b *testing.B) {
1917 b.ReportAllocs()
1918 for i := 0; i < b.N; i++ {
1919 sinkBytes, _ = ipp.MarshalText()
1920 }
1921 })
1922 }
1923 }
1924
1925 func BenchmarkPrefixMasking(b *testing.B) {
1926 tests := []struct {
1927 name string
1928 ip Addr
1929 bits int
1930 }{
1931 {
1932 name: "IPv4 /32",
1933 ip: IPv4(192, 0, 2, 0),
1934 bits: 32,
1935 },
1936 {
1937 name: "IPv4 /17",
1938 ip: IPv4(192, 0, 2, 0),
1939 bits: 17,
1940 },
1941 {
1942 name: "IPv4 /0",
1943 ip: IPv4(192, 0, 2, 0),
1944 bits: 0,
1945 },
1946 {
1947 name: "IPv6 /128",
1948 ip: mustIP("2001:db8::1"),
1949 bits: 128,
1950 },
1951 {
1952 name: "IPv6 /65",
1953 ip: mustIP("2001:db8::1"),
1954 bits: 65,
1955 },
1956 {
1957 name: "IPv6 /0",
1958 ip: mustIP("2001:db8::1"),
1959 bits: 0,
1960 },
1961 {
1962 name: "IPv6 zone /128",
1963 ip: mustIP("2001:db8::1%eth0"),
1964 bits: 128,
1965 },
1966 {
1967 name: "IPv6 zone /65",
1968 ip: mustIP("2001:db8::1%eth0"),
1969 bits: 65,
1970 },
1971 {
1972 name: "IPv6 zone /0",
1973 ip: mustIP("2001:db8::1%eth0"),
1974 bits: 0,
1975 },
1976 }
1977
1978 for _, tt := range tests {
1979 b.Run(tt.name, func(b *testing.B) {
1980 b.ReportAllocs()
1981
1982 for i := 0; i < b.N; i++ {
1983 sinkPrefix, _ = tt.ip.Prefix(tt.bits)
1984 }
1985 })
1986 }
1987 }
1988
1989 func BenchmarkPrefixMarshalText(b *testing.B) {
1990 b.ReportAllocs()
1991 ipp := MustParsePrefix("66.55.44.33/22")
1992 for i := 0; i < b.N; i++ {
1993 sinkBytes, _ = ipp.MarshalText()
1994 }
1995 }
1996
1997 func BenchmarkParseAddrPort(b *testing.B) {
1998 for _, test := range parseBenchInputs {
1999 var ipp string
2000 if strings.HasPrefix(test.name, "v6") {
2001 ipp = fmt.Sprintf("[%s]:1234", test.ip)
2002 } else {
2003 ipp = fmt.Sprintf("%s:1234", test.ip)
2004 }
2005 b.Run(test.name, func(b *testing.B) {
2006 b.ReportAllocs()
2007
2008 for i := 0; i < b.N; i++ {
2009 sinkAddrPort, _ = ParseAddrPort(ipp)
2010 }
2011 })
2012 }
2013 }
2014
2015 func TestAs4(t *testing.T) {
2016 tests := []struct {
2017 ip Addr
2018 want [4]byte
2019 wantPanic bool
2020 }{
2021 {
2022 ip: mustIP("1.2.3.4"),
2023 want: [4]byte{1, 2, 3, 4},
2024 },
2025 {
2026 ip: AddrFrom16(mustIP("1.2.3.4").As16()),
2027 want: [4]byte{1, 2, 3, 4},
2028 },
2029 {
2030 ip: mustIP("0.0.0.0"),
2031 want: [4]byte{0, 0, 0, 0},
2032 },
2033 {
2034 ip: Addr{},
2035 wantPanic: true,
2036 },
2037 {
2038 ip: mustIP("::1"),
2039 wantPanic: true,
2040 },
2041 }
2042 as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
2043 defer func() {
2044 if recover() != nil {
2045 gotPanic = true
2046 return
2047 }
2048 }()
2049 v = ip.As4()
2050 return
2051 }
2052 for i, tt := range tests {
2053 got, gotPanic := as4(tt.ip)
2054 if gotPanic != tt.wantPanic {
2055 t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
2056 continue
2057 }
2058 if got != tt.want {
2059 t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
2060 }
2061 }
2062 }
2063
2064 func TestPrefixOverlaps(t *testing.T) {
2065 pfx := mustPrefix
2066 tests := []struct {
2067 a, b Prefix
2068 want bool
2069 }{
2070 {Prefix{}, pfx("1.2.0.0/16"), false},
2071 {pfx("1.2.0.0/16"), Prefix{}, false},
2072 {pfx("::0/3"), pfx("0.0.0.0/3"), false},
2073
2074 {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true},
2075
2076 {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
2077 {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
2078
2079 {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
2080 {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
2081
2082
2083 {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
2084 {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
2085
2086 {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true},
2087
2088
2089 {pfx("5::1/128"), pfx("5::0/8"), true},
2090 {pfx("5::0/8"), pfx("5::1/128"), true},
2091
2092
2093 {pfx("1::1/128"), pfx("2::2/128"), false},
2094 {pfx("0100::0/8"), pfx("::1/128"), false},
2095
2096
2097 {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
2098
2099
2100 {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
2101 {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
2102 }
2103 for i, tt := range tests {
2104 if got := tt.a.Overlaps(tt.b); got != tt.want {
2105 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
2106 }
2107
2108 if got := tt.b.Overlaps(tt.a); got != tt.want {
2109 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
2110 }
2111 }
2112 }
2113
2114
2115
2116
2117
2118
2119
2120 var (
2121 sinkIP Addr
2122 sinkStdIP net.IP
2123 sinkAddrPort AddrPort
2124 sinkPrefix Prefix
2125 sinkPrefixSlice []Prefix
2126 sinkInternValue unique.Handle[AddrDetail]
2127 sinkIP16 [16]byte
2128 sinkIP4 [4]byte
2129 sinkBool bool
2130 sinkString string
2131 sinkBytes []byte
2132 sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)}
2133 )
2134
2135 func TestNoAllocs(t *testing.T) {
2136 if asan.Enabled {
2137 t.Skip("test allocates more with -asan; see #70079")
2138 }
2139
2140
2141
2142 panicIP := func(ip Addr, err error) Addr {
2143 if err != nil {
2144 panic(err)
2145 }
2146 return ip
2147 }
2148 panicPfx := func(pfx Prefix, err error) Prefix {
2149 if err != nil {
2150 panic(err)
2151 }
2152 return pfx
2153 }
2154 panicIPP := func(ipp AddrPort, err error) AddrPort {
2155 if err != nil {
2156 panic(err)
2157 }
2158 return ipp
2159 }
2160 test := func(name string, f func()) {
2161 t.Run(name, func(t *testing.T) {
2162 n := testing.AllocsPerRun(1000, f)
2163 if n != 0 {
2164 t.Fatalf("allocs = %d; want 0", int(n))
2165 }
2166 })
2167 }
2168
2169
2170 test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
2171 test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
2172 test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
2173 test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
2174 test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
2175 test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
2176 test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
2177 test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2178 test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2179 test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2180
2181
2182 test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2183 test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2184 test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2185 test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2186 test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2187 test("Addr.Compare", func() {
2188 a := MustParseAddr("1.2.3.4")
2189 b := MustParseAddr("2.3.4.5")
2190 sinkBool = a.Compare(b) == 0
2191 })
2192 test("Addr.Less", func() {
2193 a := MustParseAddr("1.2.3.4")
2194 b := MustParseAddr("2.3.4.5")
2195 sinkBool = a.Less(b)
2196 })
2197 test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2198 test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2199 test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2200 test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2201 test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2202 test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2203 test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2204 test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2205 test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2206 test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2207 test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2208 test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2209 test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2210 test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2211 test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2212 test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2213 test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2214 test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2215 test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2216
2217
2218 test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2219 test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2220 test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2221
2222
2223 test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2224 test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2225 test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2226 test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2227
2228
2229 test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2230 test("Prefix.Overlaps", func() {
2231 a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2232 sinkBool = a.Overlaps(b)
2233 })
2234 test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2235 test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2236 test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2237 }
2238
2239 func TestAddrStringAllocs(t *testing.T) {
2240 tests := []struct {
2241 name string
2242 ip Addr
2243 wantAllocs int
2244 }{
2245 {"zero", Addr{}, 0},
2246 {"ipv4", MustParseAddr("192.168.1.1"), 1},
2247 {"ipv6", MustParseAddr("2001:db8::1"), 1},
2248 {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2249 {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2250 {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2251 }
2252 optimizationOff := testenv.OptimizationOff()
2253 for _, tc := range tests {
2254 t.Run(tc.name, func(t *testing.T) {
2255 if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2256
2257 t.Skipf("skipping on %v", testenv.Builder())
2258 }
2259 allocs := int(testing.AllocsPerRun(1000, func() {
2260 sinkString = tc.ip.String()
2261 }))
2262 if allocs != tc.wantAllocs {
2263 t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2264 }
2265 })
2266 }
2267 }
2268
2269 func TestPrefixString(t *testing.T) {
2270 tests := []struct {
2271 ipp Prefix
2272 want string
2273 }{
2274 {Prefix{}, "invalid Prefix"},
2275 {PrefixFrom(Addr{}, 8), "invalid Prefix"},
2276 {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2277 }
2278
2279 for _, tt := range tests {
2280 if got := tt.ipp.String(); got != tt.want {
2281 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2282 }
2283 }
2284 }
2285
2286 func TestAddrPortString(t *testing.T) {
2287 tests := []struct {
2288 ipp AddrPort
2289 want string
2290 }{
2291 {MustParseAddrPort("127.0.0.1:80"), "127.0.0.1:80"},
2292 {MustParseAddrPort("[0000::0]:8080"), "[::]:8080"},
2293 {MustParseAddrPort("[FFFF::1]:8080"), "[ffff::1]:8080"},
2294 {AddrPort{}, "invalid AddrPort"},
2295 {AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2296 }
2297
2298 for _, tt := range tests {
2299 if got := tt.ipp.String(); got != tt.want {
2300 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2301 }
2302 }
2303 }
2304
2305 func TestAsSlice(t *testing.T) {
2306 tests := []struct {
2307 in Addr
2308 want []byte
2309 }{
2310 {in: Addr{}, want: nil},
2311 {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2312 {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2313 }
2314
2315 for _, test := range tests {
2316 got := test.in.AsSlice()
2317 if !bytes.Equal(got, test.want) {
2318 t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2319 }
2320 }
2321 }
2322
2323 var sink16 [16]byte
2324
2325 func BenchmarkAs16(b *testing.B) {
2326 addr := MustParseAddr("1::10")
2327 for i := 0; i < b.N; i++ {
2328 sink16 = addr.As16()
2329 }
2330 }
2331
View as plain text