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