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