Source file src/net/netip/netip_test.go

     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/testenv"
    13  	"net"
    14  	. "net/netip"
    15  	"reflect"
    16  	"slices"
    17  	"strings"
    18  	"testing"
    19  	"unique"
    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"): 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  
   313  func 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  
   348  func 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  
   354  func 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  
   395  func 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  
   422  func 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  
   462  func 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  
   488  func 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  
   529  func 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  
   552  func 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  
   579  func 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  
   827  func 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  
   872  func 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  
   957  func 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  
  1008  func 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  
  1060  func 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  
  1099  func 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  
  1233  func 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  
  1267  func 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  
  1274  func 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  
  1301  func 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  
  1331  func 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  
  1363  func 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  
  1457  func 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  
  1486  func 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  
  1510  func 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  
  1578  func 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  
  1599  func 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  
  1607  func 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  
  1634  func 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  
  1646  func 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.
  1661  type ip4i struct {
  1662  	ip4    [4]byte
  1663  	flags1 byte
  1664  	flags2 byte
  1665  	flags3 byte
  1666  	flags4 byte
  1667  	ipv6   any
  1668  }
  1669  
  1670  func 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.
  1675  func 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  
  1687  func 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  
  1699  func 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  
  1711  func 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  
  1720  func 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  
  1729  var 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  
  1740  func 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  
  1752  func 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  
  1763  func 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  
  1775  func 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  
  1787  func 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  
  1799  func 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  
  1812  func 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  
  1825  func 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  
  1889  func 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  
  1897  func 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  
  1915  func 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  
  1964  func 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.
  2020  var (
  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  
  2035  func 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  
  2135  func 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  
  2165  func 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  
  2182  func 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  
  2198  func 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  
  2216  var sink16 [16]byte
  2217  
  2218  func BenchmarkAs16(b *testing.B) {
  2219  	addr := MustParseAddr("1::10")
  2220  	for i := 0; i < b.N; i++ {
  2221  		sink16 = addr.As16()
  2222  	}
  2223  }
  2224  

View as plain text