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 }