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 BenchmarkPrefixString(b *testing.B) {
2053 b.ReportAllocs()
2054 ipp := MustParsePrefix("66.55.44.33/22")
2055 for i := 0; i < b.N; i++ {
2056 sinkString = ipp.String()
2057 }
2058 }
2059
2060 func BenchmarkPrefixMarshalText(b *testing.B) {
2061 b.ReportAllocs()
2062 ipp := MustParsePrefix("66.55.44.33/22")
2063 for i := 0; i < b.N; i++ {
2064 sinkBytes, _ = ipp.MarshalText()
2065 }
2066 }
2067
2068 func BenchmarkParseAddrPort(b *testing.B) {
2069 for _, test := range parseBenchInputs {
2070 var ipp string
2071 if strings.HasPrefix(test.name, "v6") {
2072 ipp = fmt.Sprintf("[%s]:1234", test.ip)
2073 } else {
2074 ipp = fmt.Sprintf("%s:1234", test.ip)
2075 }
2076 b.Run(test.name, func(b *testing.B) {
2077 b.ReportAllocs()
2078
2079 for i := 0; i < b.N; i++ {
2080 sinkAddrPort, _ = ParseAddrPort(ipp)
2081 }
2082 })
2083 }
2084 }
2085
2086 func TestAs4(t *testing.T) {
2087 tests := []struct {
2088 ip Addr
2089 want [4]byte
2090 wantPanic bool
2091 }{
2092 {
2093 ip: mustIP("1.2.3.4"),
2094 want: [4]byte{1, 2, 3, 4},
2095 },
2096 {
2097 ip: AddrFrom16(mustIP("1.2.3.4").As16()),
2098 want: [4]byte{1, 2, 3, 4},
2099 },
2100 {
2101 ip: mustIP("0.0.0.0"),
2102 want: [4]byte{0, 0, 0, 0},
2103 },
2104 {
2105 ip: Addr{},
2106 wantPanic: true,
2107 },
2108 {
2109 ip: mustIP("::1"),
2110 wantPanic: true,
2111 },
2112 }
2113 as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
2114 defer func() {
2115 if recover() != nil {
2116 gotPanic = true
2117 return
2118 }
2119 }()
2120 v = ip.As4()
2121 return
2122 }
2123 for i, tt := range tests {
2124 got, gotPanic := as4(tt.ip)
2125 if gotPanic != tt.wantPanic {
2126 t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
2127 continue
2128 }
2129 if got != tt.want {
2130 t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
2131 }
2132 }
2133 }
2134
2135 func TestPrefixOverlaps(t *testing.T) {
2136 pfx := mustPrefix
2137 tests := []struct {
2138 a, b Prefix
2139 want bool
2140 }{
2141 {Prefix{}, pfx("1.2.0.0/16"), false},
2142 {pfx("1.2.0.0/16"), Prefix{}, false},
2143 {pfx("::0/3"), pfx("0.0.0.0/3"), false},
2144
2145 {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true},
2146
2147 {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
2148 {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
2149
2150 {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
2151 {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
2152
2153
2154 {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
2155 {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
2156
2157 {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true},
2158
2159
2160 {pfx("5::1/128"), pfx("5::0/8"), true},
2161 {pfx("5::0/8"), pfx("5::1/128"), true},
2162
2163
2164 {pfx("1::1/128"), pfx("2::2/128"), false},
2165 {pfx("0100::0/8"), pfx("::1/128"), false},
2166
2167
2168 {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
2169
2170
2171 {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
2172 {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
2173 }
2174 for i, tt := range tests {
2175 if got := tt.a.Overlaps(tt.b); got != tt.want {
2176 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
2177 }
2178
2179 if got := tt.b.Overlaps(tt.a); got != tt.want {
2180 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
2181 }
2182 }
2183 }
2184
2185
2186
2187
2188
2189
2190
2191 var (
2192 sinkIP Addr
2193 sinkStdIP net.IP
2194 sinkAddrPort AddrPort
2195 sinkPrefix Prefix
2196 sinkPrefixSlice []Prefix
2197 sinkInternValue unique.Handle[AddrDetail]
2198 sinkIP16 [16]byte
2199 sinkIP4 [4]byte
2200 sinkBool bool
2201 sinkString string
2202 sinkBytes []byte
2203 sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)}
2204 )
2205
2206 func TestNoAllocs(t *testing.T) {
2207 if asan.Enabled {
2208 t.Skip("test allocates more with -asan; see #70079")
2209 }
2210
2211
2212
2213 panicIP := func(ip Addr, err error) Addr {
2214 if err != nil {
2215 panic(err)
2216 }
2217 return ip
2218 }
2219 panicPfx := func(pfx Prefix, err error) Prefix {
2220 if err != nil {
2221 panic(err)
2222 }
2223 return pfx
2224 }
2225 panicIPP := func(ipp AddrPort, err error) AddrPort {
2226 if err != nil {
2227 panic(err)
2228 }
2229 return ipp
2230 }
2231 test := func(name string, f func()) {
2232 t.Run(name, func(t *testing.T) {
2233 n := testing.AllocsPerRun(1000, f)
2234 if n != 0 {
2235 t.Fatalf("allocs = %d; want 0", int(n))
2236 }
2237 })
2238 }
2239
2240
2241 test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
2242 test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
2243 test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
2244 test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
2245 test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
2246 test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
2247 test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
2248 test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2249 test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2250 test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2251
2252
2253 test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2254 test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2255 test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2256 test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2257 test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2258 test("Addr.Compare", func() {
2259 a := MustParseAddr("1.2.3.4")
2260 b := MustParseAddr("2.3.4.5")
2261 sinkBool = a.Compare(b) == 0
2262 })
2263 test("Addr.Less", func() {
2264 a := MustParseAddr("1.2.3.4")
2265 b := MustParseAddr("2.3.4.5")
2266 sinkBool = a.Less(b)
2267 })
2268 test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2269 test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2270 test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2271 test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2272 test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2273 test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2274 test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2275 test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2276 test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2277 test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2278 test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2279 test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2280 test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2281 test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2282 test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2283 test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2284 test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2285 test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2286 test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2287
2288
2289 test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2290 test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2291 test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2292
2293
2294 test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2295 test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2296 test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2297 test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2298
2299
2300 test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2301 test("Prefix.Overlaps", func() {
2302 a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2303 sinkBool = a.Overlaps(b)
2304 })
2305 test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2306 test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2307 test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2308 }
2309
2310 func TestAddrStringAllocs(t *testing.T) {
2311 tests := []struct {
2312 name string
2313 ip Addr
2314 wantAllocs int
2315 }{
2316 {"zero", Addr{}, 0},
2317 {"ipv4", MustParseAddr("192.168.1.1"), 1},
2318 {"ipv6", MustParseAddr("2001:db8::1"), 1},
2319 {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2320 {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2321 {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2322 }
2323 optimizationOff := testenv.OptimizationOff()
2324 for _, tc := range tests {
2325 t.Run(tc.name, func(t *testing.T) {
2326 if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2327
2328 t.Skipf("skipping on %v", testenv.Builder())
2329 }
2330 allocs := int(testing.AllocsPerRun(1000, func() {
2331 sinkString = tc.ip.String()
2332 }))
2333 if allocs != tc.wantAllocs {
2334 t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2335 }
2336 })
2337 }
2338 }
2339
2340 func TestPrefixString(t *testing.T) {
2341 tests := []struct {
2342 ipp Prefix
2343 want string
2344 }{
2345 {Prefix{}, "invalid Prefix"},
2346 {PrefixFrom(Addr{}, 8), "invalid Prefix"},
2347 {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2348 }
2349
2350 for _, tt := range tests {
2351 if got := tt.ipp.String(); got != tt.want {
2352 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2353 }
2354 }
2355 }
2356
2357 func TestAddrPortString(t *testing.T) {
2358 tests := []struct {
2359 ipp AddrPort
2360 want string
2361 }{
2362 {MustParseAddrPort("127.0.0.1:80"), "127.0.0.1:80"},
2363 {MustParseAddrPort("[0000::0]:8080"), "[::]:8080"},
2364 {MustParseAddrPort("[FFFF::1]:8080"), "[ffff::1]:8080"},
2365 {AddrPort{}, "invalid AddrPort"},
2366 {AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2367 }
2368
2369 for _, tt := range tests {
2370 if got := tt.ipp.String(); got != tt.want {
2371 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2372 }
2373 }
2374 }
2375
2376 func TestAsSlice(t *testing.T) {
2377 tests := []struct {
2378 in Addr
2379 want []byte
2380 }{
2381 {in: Addr{}, want: nil},
2382 {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2383 {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2384 }
2385
2386 for _, test := range tests {
2387 got := test.in.AsSlice()
2388 if !bytes.Equal(got, test.want) {
2389 t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2390 }
2391 }
2392 }
2393
2394 var sink16 [16]byte
2395
2396 func BenchmarkAs16(b *testing.B) {
2397 addr := MustParseAddr("1::10")
2398 for i := 0; i < b.N; i++ {
2399 sink16 = addr.As16()
2400 }
2401 }
2402
View as plain text