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