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

View as plain text