istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/util/network/ip_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package network
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net"
    21  	"net/netip"
    22  	"strings"
    23  	"testing"
    24  )
    25  
    26  // The test may run on a system with localhost = 127.0.0.1 or ::1, so we
    27  // determine that value and use it in the "expected" results for the test
    28  // cases in TestResolveAddr(). Need to wrap IPv6 addresses in square
    29  // brackets.
    30  func determineLocalHostIPString(t *testing.T) string {
    31  	ips, err := net.DefaultResolver.LookupNetIP(context.Background(), "ip", "localhost")
    32  	if err != nil || len(ips) == 0 {
    33  		t.Fatalf("Test setup failure - unable to determine IP of localhost: %v", err)
    34  	}
    35  	var ret string
    36  	for _, ip := range ips {
    37  		// unwrap the IPv4-mapped IPv6 address
    38  		unwrapIP := ip.Unmap()
    39  		if unwrapIP.Is6() {
    40  			ret = fmt.Sprintf("[%s]", unwrapIP.String())
    41  		} else {
    42  			return unwrapIP.String()
    43  		}
    44  	}
    45  	return ret
    46  }
    47  
    48  func MockLookupIPAddr(_ context.Context, _ string) ([]netip.Addr, error) {
    49  	ret := []netip.Addr{
    50  		netip.MustParseAddr("2001:db8::68"),
    51  		netip.MustParseAddr("1.2.3.4"),
    52  		netip.MustParseAddr("1.2.3.5"),
    53  	}
    54  	return ret, nil
    55  }
    56  
    57  func MockLookupIPAddrIPv6(_ context.Context, _ string) ([]netip.Addr, error) {
    58  	ret := []netip.Addr{
    59  		netip.MustParseAddr("2001:db8::68"),
    60  	}
    61  	return ret, nil
    62  }
    63  
    64  func TestResolveAddr(t *testing.T) {
    65  	localIP := determineLocalHostIPString(t)
    66  
    67  	testCases := []struct {
    68  		name     string
    69  		input    string
    70  		expected string
    71  		errStr   string
    72  		lookup   func(ctx context.Context, addr string) ([]netip.Addr, error)
    73  	}{
    74  		{
    75  			name:     "Host by name",
    76  			input:    "localhost:9080",
    77  			expected: fmt.Sprintf("%s:9080", localIP),
    78  			errStr:   "",
    79  			lookup:   nil,
    80  		},
    81  		{
    82  			name:     "Host by name w/brackets",
    83  			input:    "[localhost]:9080",
    84  			expected: fmt.Sprintf("%s:9080", localIP),
    85  			errStr:   "",
    86  			lookup:   nil,
    87  		},
    88  		{
    89  			name:     "Host by IPv4",
    90  			input:    "127.0.0.1:9080",
    91  			expected: "127.0.0.1:9080",
    92  			errStr:   "",
    93  			lookup:   nil,
    94  		},
    95  		{
    96  			name:     "Host by IPv6",
    97  			input:    "[::1]:9080",
    98  			expected: "[::1]:9080",
    99  			errStr:   "",
   100  			lookup:   nil,
   101  		},
   102  		{
   103  			name:     "Bad IPv4",
   104  			input:    "127.0.0.1.1:9080",
   105  			expected: "",
   106  			errStr:   "lookup failed for IP address: lookup 127.0.0.1.1: no such host",
   107  			lookup:   nil,
   108  		},
   109  		{
   110  			name:     "Bad IPv6",
   111  			input:    "[2001:db8::bad::1]:9080",
   112  			expected: "",
   113  			errStr:   "lookup failed for IP address: lookup 2001:db8::bad::1: no such host",
   114  			lookup:   nil,
   115  		},
   116  		{
   117  			name:     "Empty host",
   118  			input:    "",
   119  			expected: "",
   120  			errStr:   ErrResolveNoAddress.Error(),
   121  			lookup:   nil,
   122  		},
   123  		{
   124  			name:     "IPv6 missing brackets",
   125  			input:    "2001:db8::20:9080",
   126  			expected: "",
   127  			errStr:   "address 2001:db8::20:9080: too many colons in address",
   128  			lookup:   nil,
   129  		},
   130  		{
   131  			name:     "Colon, but no port",
   132  			input:    "localhost:",
   133  			expected: "",
   134  			errStr:   "",
   135  			lookup:   nil,
   136  		},
   137  		{
   138  			name:     "Missing port",
   139  			input:    "localhost",
   140  			expected: "",
   141  			errStr:   "address localhost: missing port in address",
   142  			lookup:   nil,
   143  		},
   144  		{
   145  			name:     "Missing host",
   146  			input:    ":9080",
   147  			expected: "",
   148  			errStr:   "lookup failed for IP address",
   149  			lookup:   nil,
   150  		},
   151  		{
   152  			name:     "Host by name - non local",
   153  			input:    "www.foo.com:9080",
   154  			expected: "1.2.3.4:9080",
   155  			errStr:   "",
   156  			lookup:   MockLookupIPAddr,
   157  		},
   158  		{
   159  			name:     "Host by name - non local 0 IPv6 only address",
   160  			input:    "www.foo.com:9080",
   161  			expected: "[2001:db8::68]:9080",
   162  			errStr:   "",
   163  			lookup:   MockLookupIPAddrIPv6,
   164  		},
   165  	}
   166  
   167  	for _, tc := range testCases {
   168  		actual, err := ResolveAddr(tc.input, tc.lookup)
   169  		if err != nil {
   170  			if tc.errStr == "" {
   171  				t.Errorf("[%s] expected success, but saw error: %v", tc.name, err)
   172  			} else if !strings.Contains(err.Error(), tc.errStr) {
   173  				t.Errorf("[%s] expected error %q, got %q", tc.name, tc.errStr, err.Error())
   174  			}
   175  		} else {
   176  			if tc.errStr != "" {
   177  				t.Errorf("[%s] no error seen, but expected failure: %s", tc.name, tc.errStr)
   178  			} else if actual != tc.expected {
   179  				t.Errorf("[%s] expected address %q, got %q", tc.name, tc.expected, actual)
   180  			}
   181  		}
   182  	}
   183  }
   184  
   185  func TestAllIPv6(t *testing.T) {
   186  	tests := []struct {
   187  		name     string
   188  		addrs    []string
   189  		expected bool
   190  	}{
   191  		{
   192  			name:     "ipv4 only",
   193  			addrs:    []string{"1.1.1.1", "127.0.0.1", "2.2.2.2"},
   194  			expected: false,
   195  		},
   196  		{
   197  			name:     "ipv6 only",
   198  			addrs:    []string{"1111:2222::1", "::1", "2222:3333::1"},
   199  			expected: true,
   200  		},
   201  		{
   202  			name:     "mixed ipv4 and ipv6",
   203  			addrs:    []string{"1111:2222::1", "::1", "127.0.0.1", "2.2.2.2", "2222:3333::1"},
   204  			expected: false,
   205  		},
   206  		{
   207  			name:     "test for invalid ip address",
   208  			addrs:    []string{"invalidip"},
   209  			expected: true,
   210  		},
   211  	}
   212  	for _, tt := range tests {
   213  		result := AllIPv6(tt.addrs)
   214  		if result != tt.expected {
   215  			t.Errorf("Test %s failed, expected: %t got: %t", tt.name, tt.expected, result)
   216  		}
   217  	}
   218  }
   219  
   220  func TestAllIPv4(t *testing.T) {
   221  	tests := []struct {
   222  		name     string
   223  		addrs    []string
   224  		expected bool
   225  	}{
   226  		{
   227  			name:     "ipv4 only",
   228  			addrs:    []string{"1.1.1.1", "127.0.0.1", "2.2.2.2"},
   229  			expected: true,
   230  		},
   231  		{
   232  			name:     "ipv6 only",
   233  			addrs:    []string{"1111:2222::1", "::1", "2222:3333::1"},
   234  			expected: false,
   235  		},
   236  		{
   237  			name:     "mixed ipv4 and ipv6",
   238  			addrs:    []string{"1111:2222::1", "::1", "127.0.0.1", "2.2.2.2", "2222:3333::1"},
   239  			expected: false,
   240  		},
   241  		{
   242  			name:     "test for invalid ip address",
   243  			addrs:    []string{"invalidip"},
   244  			expected: true,
   245  		},
   246  	}
   247  	for _, tt := range tests {
   248  		result := AllIPv4(tt.addrs)
   249  		if result != tt.expected {
   250  			t.Errorf("Test %s failed, expected: %t got: %t", tt.name, tt.expected, result)
   251  		}
   252  	}
   253  }
   254  
   255  func TestGlobalUnicastIP(t *testing.T) {
   256  	tests := []struct {
   257  		name     string
   258  		addrs    []string
   259  		expected string
   260  	}{
   261  		{
   262  			name:     "test for globalunicastip",
   263  			addrs:    []string{"127.0.0.1", "1.1.1.1"},
   264  			expected: "1.1.1.1",
   265  		},
   266  		{
   267  			name:     "test for empty value",
   268  			addrs:    []string{},
   269  			expected: "",
   270  		},
   271  		{
   272  			name:     "test for invalid ip address",
   273  			addrs:    []string{"invalidip"},
   274  			expected: "",
   275  		},
   276  	}
   277  	for _, tt := range tests {
   278  		result := GlobalUnicastIP(tt.addrs)
   279  		if result != tt.expected {
   280  			t.Errorf("Test %s failed, expected: %v got: %v", tt.name, tt.expected, result)
   281  		}
   282  	}
   283  }