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