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