github.com/noisysockets/noisysockets@v0.21.2-0.20240515114641-7f467e651c90/internal/dns/addrselect/addrselect_test.go (about)

     1  // SPDX-License-Identifier: MPL-2.0
     2  /*
     3   * Copyright (C) 2024 The Noisy Sockets Authors.
     4   *
     5   * This Source Code Form is subject to the terms of the Mozilla Public
     6   * License, v. 2.0. If a copy of the MPL was not distributed with this
     7   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     8   *
     9   * Portions of this file are based on code originally from the Go project,
    10   *
    11   * Copyright (c) 2015 The Go Authors. All rights reserved.
    12   *
    13   * Redistribution and use in source and binary forms, with or without
    14   * modification, are permitted provided that the following conditions are
    15   * met:
    16   *
    17   *   * Redistributions of source code must retain the above copyright
    18   *     notice, this list of conditions and the following disclaimer.
    19   *   * Redistributions in binary form must reproduce the above
    20   *     copyright notice, this list of conditions and the following disclaimer
    21   *     in the documentation and/or other materials provided with the
    22   *     distribution.
    23   *   * Neither the name of Google Inc. nor the names of its
    24   *     contributors may be used to endorse or promote products derived from
    25   *     this software without specific prior written permission.
    26   *
    27   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    28   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    29   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    30   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    31   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    32   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    33   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    34   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    35   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    36   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    37   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    38   */
    39  
    40  package addrselect
    41  
    42  import (
    43  	"net/netip"
    44  	"reflect"
    45  	"testing"
    46  
    47  	"github.com/noisysockets/noisysockets/network"
    48  )
    49  
    50  func TestSortByRFC6724(t *testing.T) {
    51  	tests := []struct {
    52  		in      []netip.Addr
    53  		srcs    []netip.Addr
    54  		want    []netip.Addr
    55  		reverse bool // also test it starting backwards
    56  	}{
    57  		// Examples from RFC 6724 section 10.2:
    58  
    59  		// Prefer matching scope.
    60  		{
    61  			in: []netip.Addr{
    62  				netip.MustParseAddr("2001:db8:1::1"),
    63  				netip.MustParseAddr("198.51.100.121"),
    64  			},
    65  			srcs: []netip.Addr{
    66  				netip.MustParseAddr("2001:db8:1::2"),
    67  				netip.MustParseAddr("169.254.13.78"),
    68  			},
    69  			want: []netip.Addr{
    70  				netip.MustParseAddr("2001:db8:1::1"),
    71  				netip.MustParseAddr("198.51.100.121"),
    72  			},
    73  			reverse: true,
    74  		},
    75  
    76  		// Prefer matching scope.
    77  		{
    78  			in: []netip.Addr{
    79  				netip.MustParseAddr("2001:db8:1::1"),
    80  				netip.MustParseAddr("198.51.100.121"),
    81  			},
    82  			srcs: []netip.Addr{
    83  				netip.MustParseAddr("fe80::1"),
    84  				netip.MustParseAddr("198.51.100.117"),
    85  			},
    86  			want: []netip.Addr{
    87  				netip.MustParseAddr("198.51.100.121"),
    88  				netip.MustParseAddr("2001:db8:1::1"),
    89  			},
    90  			reverse: true,
    91  		},
    92  
    93  		// Prefer higher precedence.
    94  		{
    95  			in: []netip.Addr{
    96  				netip.MustParseAddr("2001:db8:1::1"),
    97  				netip.MustParseAddr("10.1.2.3"),
    98  			},
    99  			srcs: []netip.Addr{
   100  				netip.MustParseAddr("2001:db8:1::2"),
   101  				netip.MustParseAddr("10.1.2.4"),
   102  			},
   103  			want: []netip.Addr{
   104  				netip.MustParseAddr("2001:db8:1::1"),
   105  				netip.MustParseAddr("10.1.2.3"),
   106  			},
   107  			reverse: true,
   108  		},
   109  
   110  		// Prefer smaller scope.
   111  		{
   112  			in: []netip.Addr{
   113  				netip.MustParseAddr("2001:db8:1::1"),
   114  				netip.MustParseAddr("fe80::1"),
   115  			},
   116  			srcs: []netip.Addr{
   117  				netip.MustParseAddr("2001:db8:1::2"),
   118  				netip.MustParseAddr("fe80::2"),
   119  			},
   120  			want: []netip.Addr{
   121  				netip.MustParseAddr("fe80::1"),
   122  				netip.MustParseAddr("2001:db8:1::1"),
   123  			},
   124  			reverse: true,
   125  		},
   126  
   127  		// Issue 13283.  Having a 10/8 source address does not
   128  		// mean we should prefer 23/8 destination addresses.
   129  		{
   130  			in: []netip.Addr{
   131  				netip.MustParseAddr("54.83.193.112"),
   132  				netip.MustParseAddr("184.72.238.214"),
   133  				netip.MustParseAddr("23.23.172.185"),
   134  				netip.MustParseAddr("75.101.148.21"),
   135  				netip.MustParseAddr("23.23.134.56"),
   136  				netip.MustParseAddr("23.21.50.150"),
   137  			},
   138  			srcs: []netip.Addr{
   139  				netip.MustParseAddr("10.2.3.4"),
   140  				netip.MustParseAddr("10.2.3.4"),
   141  				netip.MustParseAddr("10.2.3.4"),
   142  				netip.MustParseAddr("10.2.3.4"),
   143  				netip.MustParseAddr("10.2.3.4"),
   144  				netip.MustParseAddr("10.2.3.4"),
   145  			},
   146  			want: []netip.Addr{
   147  				netip.MustParseAddr("54.83.193.112"),
   148  				netip.MustParseAddr("184.72.238.214"),
   149  				netip.MustParseAddr("23.23.172.185"),
   150  				netip.MustParseAddr("75.101.148.21"),
   151  				netip.MustParseAddr("23.23.134.56"),
   152  				netip.MustParseAddr("23.21.50.150"),
   153  			},
   154  			reverse: false,
   155  		},
   156  	}
   157  	for i, tt := range tests {
   158  		inCopy := make([]netip.Addr, len(tt.in))
   159  		copy(inCopy, tt.in)
   160  		srcCopy := make([]netip.Addr, len(tt.in))
   161  		copy(srcCopy, tt.srcs)
   162  		SortByRFC6724withSrcs(network.Host(), inCopy, srcCopy)
   163  		if !reflect.DeepEqual(inCopy, tt.want) {
   164  			t.Errorf("test %d:\nin = %s\ngot: %s\nwant: %s\n", i, tt.in, inCopy, tt.want)
   165  		}
   166  		if tt.reverse {
   167  			copy(inCopy, tt.in)
   168  			copy(srcCopy, tt.srcs)
   169  			for j := 0; j < len(inCopy)/2; j++ {
   170  				k := len(inCopy) - j - 1
   171  				inCopy[j], inCopy[k] = inCopy[k], inCopy[j]
   172  				srcCopy[j], srcCopy[k] = srcCopy[k], srcCopy[j]
   173  			}
   174  			SortByRFC6724withSrcs(network.Host(), inCopy, srcCopy)
   175  			if !reflect.DeepEqual(inCopy, tt.want) {
   176  				t.Errorf("test %d, starting backwards:\nin = %s\ngot: %s\nwant: %s\n", i, tt.in, inCopy, tt.want)
   177  			}
   178  		}
   179  
   180  	}
   181  
   182  }
   183  
   184  func TestRFC6724PolicyTableOrder(t *testing.T) {
   185  	for i := 0; i < len(rfc6724policyTable)-1; i++ {
   186  		if !(rfc6724policyTable[i].Prefix.Bits() >= rfc6724policyTable[i+1].Prefix.Bits()) {
   187  			t.Errorf("rfc6724policyTable item number %d sorted in wrong order = %d bits, next item = %d bits;", i, rfc6724policyTable[i].Prefix.Bits(), rfc6724policyTable[i+1].Prefix.Bits())
   188  		}
   189  	}
   190  }
   191  
   192  func TestRFC6724PolicyTableContent(t *testing.T) {
   193  	expectedRfc6724policyTable := policyTable{
   194  		{
   195  			Prefix:     netip.MustParsePrefix("::1/128"),
   196  			Precedence: 50,
   197  			Label:      0,
   198  		},
   199  		{
   200  			Prefix:     netip.MustParsePrefix("::ffff:0:0/96"),
   201  			Precedence: 35,
   202  			Label:      4,
   203  		},
   204  		{
   205  			Prefix:     netip.MustParsePrefix("::/96"),
   206  			Precedence: 1,
   207  			Label:      3,
   208  		},
   209  		{
   210  			Prefix:     netip.MustParsePrefix("2001::/32"),
   211  			Precedence: 5,
   212  			Label:      5,
   213  		},
   214  		{
   215  			Prefix:     netip.MustParsePrefix("2002::/16"),
   216  			Precedence: 30,
   217  			Label:      2,
   218  		},
   219  		{
   220  			Prefix:     netip.MustParsePrefix("3ffe::/16"),
   221  			Precedence: 1,
   222  			Label:      12,
   223  		},
   224  		{
   225  			Prefix:     netip.MustParsePrefix("fec0::/10"),
   226  			Precedence: 1,
   227  			Label:      11,
   228  		},
   229  		{
   230  			Prefix:     netip.MustParsePrefix("fc00::/7"),
   231  			Precedence: 3,
   232  			Label:      13,
   233  		},
   234  		{
   235  			Prefix:     netip.MustParsePrefix("::/0"),
   236  			Precedence: 40,
   237  			Label:      1,
   238  		},
   239  	}
   240  	if !reflect.DeepEqual(rfc6724policyTable, expectedRfc6724policyTable) {
   241  		t.Errorf("rfc6724policyTable has wrong contend = %v; want %v", rfc6724policyTable, expectedRfc6724policyTable)
   242  	}
   243  }
   244  
   245  func TestRFC6724PolicyTableClassify(t *testing.T) {
   246  	tests := []struct {
   247  		ip   netip.Addr
   248  		want policyTableEntry
   249  	}{
   250  		{
   251  			ip: netip.MustParseAddr("127.0.0.1"),
   252  			want: policyTableEntry{
   253  				Prefix:     netip.MustParsePrefix("::ffff:0:0/96"),
   254  				Precedence: 35,
   255  				Label:      4,
   256  			},
   257  		},
   258  		{
   259  			ip: netip.MustParseAddr("2601:645:8002:a500:986f:1db8:c836:bd65"),
   260  			want: policyTableEntry{
   261  				Prefix:     netip.MustParsePrefix("::/0"),
   262  				Precedence: 40,
   263  				Label:      1,
   264  			},
   265  		},
   266  		{
   267  			ip: netip.MustParseAddr("::1"),
   268  			want: policyTableEntry{
   269  				Prefix:     netip.MustParsePrefix("::1/128"),
   270  				Precedence: 50,
   271  				Label:      0,
   272  			},
   273  		},
   274  		{
   275  			ip: netip.MustParseAddr("2002::ab12"),
   276  			want: policyTableEntry{
   277  				Prefix:     netip.MustParsePrefix("2002::/16"),
   278  				Precedence: 30,
   279  				Label:      2,
   280  			},
   281  		},
   282  	}
   283  	for i, tt := range tests {
   284  		got := rfc6724policyTable.Classify(tt.ip)
   285  		if !reflect.DeepEqual(got, tt.want) {
   286  			t.Errorf("%d. Classify(%s) = %v; want %v", i, tt.ip, got, tt.want)
   287  		}
   288  	}
   289  }
   290  
   291  func TestRFC6724ClassifyScope(t *testing.T) {
   292  	tests := []struct {
   293  		ip   netip.Addr
   294  		want scope
   295  	}{
   296  		{netip.MustParseAddr("127.0.0.1"), scopeLinkLocal},   // rfc6724#section-3.2
   297  		{netip.MustParseAddr("::1"), scopeLinkLocal},         // rfc4007#section-4
   298  		{netip.MustParseAddr("169.254.1.2"), scopeLinkLocal}, // rfc6724#section-3.2
   299  		{netip.MustParseAddr("fec0::1"), scopeSiteLocal},
   300  		{netip.MustParseAddr("8.8.8.8"), scopeGlobal},
   301  
   302  		{netip.MustParseAddr("ff02::"), scopeLinkLocal},  // IPv6 multicast
   303  		{netip.MustParseAddr("ff05::"), scopeSiteLocal},  // IPv6 multicast
   304  		{netip.MustParseAddr("ff04::"), scopeAdminLocal}, // IPv6 multicast
   305  		{netip.MustParseAddr("ff0e::"), scopeGlobal},     // IPv6 multicast
   306  
   307  		{netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 0, 0, 0}), scopeGlobal}, // IPv4 link-local multicast as 16 bytes
   308  		{netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 2, 2, 2}), scopeGlobal}, // IPv4 global multicast as 16 bytes
   309  		{netip.AddrFrom4([4]byte{0xe0, 0, 0, 0}), scopeGlobal},                                       // IPv4 link-local multicast as 4 bytes
   310  		{netip.AddrFrom4([4]byte{0xe0, 2, 2, 2}), scopeGlobal},                                       // IPv4 global multicast as 4 bytes
   311  	}
   312  	for i, tt := range tests {
   313  		got := classifyScope(tt.ip)
   314  		if got != tt.want {
   315  			t.Errorf("%d. classifyScope(%s) = %x; want %x", i, tt.ip, got, tt.want)
   316  		}
   317  	}
   318  }
   319  
   320  func TestRFC6724CommonPrefixLength(t *testing.T) {
   321  	tests := []struct {
   322  		a    netip.Addr
   323  		b    netip.Addr
   324  		want int
   325  	}{
   326  		{netip.MustParseAddr("fe80::1"), netip.MustParseAddr("fe80::2"), 64},
   327  		{netip.MustParseAddr("fe81::1"), netip.MustParseAddr("fe80::2"), 15},
   328  		{netip.MustParseAddr("127.0.0.1"), netip.MustParseAddr("fe80::1"), 0}, // diff size
   329  		{netip.AddrFrom4([4]byte{1, 2, 3, 4}), netip.AddrFrom4([4]byte{1, 2, 3, 4}), 32},
   330  		{netip.AddrFrom4([4]byte{1, 2, 255, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 16},
   331  		{netip.AddrFrom4([4]byte{1, 2, 127, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 17},
   332  		{netip.AddrFrom4([4]byte{1, 2, 63, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 18},
   333  		{netip.AddrFrom4([4]byte{1, 2, 31, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 19},
   334  		{netip.AddrFrom4([4]byte{1, 2, 15, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 20},
   335  		{netip.AddrFrom4([4]byte{1, 2, 7, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 21},
   336  		{netip.AddrFrom4([4]byte{1, 2, 3, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 22},
   337  		{netip.AddrFrom4([4]byte{1, 2, 1, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 23},
   338  		{netip.AddrFrom4([4]byte{1, 2, 0, 255}), netip.AddrFrom4([4]byte{1, 2, 0, 0}), 24},
   339  	}
   340  	for i, tt := range tests {
   341  		got := commonPrefixLen(tt.a, tt.b)
   342  		if got != tt.want {
   343  			t.Errorf("%d. commonPrefixLen(%s, %s) = %d; want %d", i, tt.a, tt.b, got, tt.want)
   344  		}
   345  	}
   346  }