1
16
17 package util
18
19 import (
20 "fmt"
21 "net"
22 "testing"
23
24 v1 "k8s.io/api/core/v1"
25 "k8s.io/apimachinery/pkg/util/sets"
26 fake "k8s.io/kubernetes/pkg/proxy/util/testing"
27 netutils "k8s.io/utils/net"
28 )
29
30 type InterfaceAddrsPair struct {
31 itf net.Interface
32 addrs []net.Addr
33 }
34
35 func checkNodeIPs(expected sets.Set[string], actual []net.IP) error {
36 notFound := expected.Clone()
37 extra := sets.New[string]()
38 for _, ip := range actual {
39 str := ip.String()
40 if notFound.Has(str) {
41 notFound.Delete(str)
42 } else {
43 extra.Insert(str)
44 }
45 }
46 if len(notFound) != 0 || len(extra) != 0 {
47 return fmt.Errorf("not found: %v, extra: %v", notFound.UnsortedList(), extra.UnsortedList())
48 }
49 return nil
50 }
51
52 func TestGetNodeIPs(t *testing.T) {
53 type expectation struct {
54 matchAll bool
55 ips sets.Set[string]
56 }
57
58 testCases := []struct {
59 name string
60 cidrs []string
61 itfAddrsPairs []InterfaceAddrsPair
62 expected map[v1.IPFamily]expectation
63
64 nodeIP net.IP
65 }{
66 {
67 name: "IPv4 single",
68 cidrs: []string{"10.20.30.0/24"},
69 itfAddrsPairs: []InterfaceAddrsPair{
70 {
71 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
72 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("10.20.30.51"), Mask: net.CIDRMask(24, 32)}},
73 },
74 {
75 itf: net.Interface{Index: 2, MTU: 0, Name: "eth1", HardwareAddr: nil, Flags: 0},
76 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("100.200.201.1"), Mask: net.CIDRMask(24, 32)}},
77 },
78 },
79 expected: map[v1.IPFamily]expectation{
80 v1.IPv4Protocol: {
81 ips: sets.New[string]("10.20.30.51"),
82 },
83 v1.IPv6Protocol: {
84 matchAll: true,
85 ips: nil,
86 },
87 },
88 },
89 {
90 name: "IPv4 zero CIDR",
91 cidrs: []string{"0.0.0.0/0"},
92 itfAddrsPairs: []InterfaceAddrsPair{
93 {
94 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
95 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("10.20.30.51"), Mask: net.CIDRMask(24, 32)}},
96 },
97 {
98 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
99 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)}},
100 },
101 },
102 expected: map[v1.IPFamily]expectation{
103 v1.IPv4Protocol: {
104 matchAll: true,
105 ips: sets.New[string]("10.20.30.51", "127.0.0.1"),
106 },
107 v1.IPv6Protocol: {
108 matchAll: true,
109 ips: nil,
110 },
111 },
112 },
113 {
114 name: "IPv6 multiple",
115 cidrs: []string{"2001:db8::/64", "::1/128"},
116 itfAddrsPairs: []InterfaceAddrsPair{
117 {
118 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
119 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)}},
120 },
121 {
122 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
123 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)}},
124 },
125 },
126 expected: map[v1.IPFamily]expectation{
127 v1.IPv4Protocol: {
128 matchAll: true,
129 ips: nil,
130 },
131 v1.IPv6Protocol: {
132 ips: sets.New[string]("2001:db8::1", "::1"),
133 },
134 },
135 },
136 {
137 name: "IPv6 zero CIDR",
138 cidrs: []string{"::/0"},
139 itfAddrsPairs: []InterfaceAddrsPair{
140 {
141 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
142 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)}},
143 },
144 {
145 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
146 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)}},
147 },
148 },
149 expected: map[v1.IPFamily]expectation{
150 v1.IPv4Protocol: {
151 matchAll: true,
152 ips: nil,
153 },
154 v1.IPv6Protocol: {
155 matchAll: true,
156 ips: sets.New[string]("2001:db8::1", "::1"),
157 },
158 },
159 },
160 {
161 name: "IPv4 localhost exact",
162 cidrs: []string{"127.0.0.1/32"},
163 itfAddrsPairs: []InterfaceAddrsPair{
164 {
165 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
166 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("10.20.30.51"), Mask: net.CIDRMask(24, 32)}},
167 },
168 {
169 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
170 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)}},
171 },
172 },
173 expected: map[v1.IPFamily]expectation{
174 v1.IPv4Protocol: {
175 ips: sets.New[string]("127.0.0.1"),
176 },
177 v1.IPv6Protocol: {
178 matchAll: true,
179 ips: nil,
180 },
181 },
182 },
183 {
184 name: "IPv4 localhost subnet",
185 cidrs: []string{"127.0.0.0/8"},
186 itfAddrsPairs: []InterfaceAddrsPair{
187 {
188 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
189 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("127.0.1.1"), Mask: net.CIDRMask(8, 32)}},
190 },
191 },
192 expected: map[v1.IPFamily]expectation{
193 v1.IPv4Protocol: {
194 ips: sets.New[string]("127.0.1.1"),
195 },
196 v1.IPv6Protocol: {
197 matchAll: true,
198 ips: nil,
199 },
200 },
201 },
202 {
203 name: "IPv4 multiple",
204 cidrs: []string{"10.20.30.0/24", "100.200.201.0/24"},
205 itfAddrsPairs: []InterfaceAddrsPair{
206 {
207 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
208 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("10.20.30.51"), Mask: net.CIDRMask(24, 32)}},
209 },
210 {
211 itf: net.Interface{Index: 2, MTU: 0, Name: "eth1", HardwareAddr: nil, Flags: 0},
212 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("100.200.201.1"), Mask: net.CIDRMask(24, 32)}},
213 },
214 },
215 expected: map[v1.IPFamily]expectation{
216 v1.IPv4Protocol: {
217 ips: sets.New[string]("10.20.30.51", "100.200.201.1"),
218 },
219 v1.IPv6Protocol: {
220 matchAll: true,
221 ips: nil,
222 },
223 },
224 },
225 {
226 name: "IPv4 multiple, no match",
227 cidrs: []string{"10.20.30.0/24", "100.200.201.0/24"},
228 itfAddrsPairs: []InterfaceAddrsPair{
229 {
230 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
231 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("192.168.1.2"), Mask: net.CIDRMask(24, 32)}},
232 },
233 {
234 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
235 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)}},
236 },
237 },
238 expected: map[v1.IPFamily]expectation{
239 v1.IPv4Protocol: {
240 ips: nil,
241 },
242 v1.IPv6Protocol: {
243 matchAll: true,
244 ips: nil,
245 },
246 },
247 },
248 {
249 name: "empty list, IPv4 addrs",
250 cidrs: []string{},
251 itfAddrsPairs: []InterfaceAddrsPair{
252 {
253 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
254 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("192.168.1.2"), Mask: net.CIDRMask(24, 32)}},
255 },
256 {
257 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
258 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)}},
259 },
260 },
261 expected: map[v1.IPFamily]expectation{
262 v1.IPv4Protocol: {
263 matchAll: true,
264 ips: sets.New[string]("192.168.1.2", "127.0.0.1"),
265 },
266 v1.IPv6Protocol: {
267 matchAll: true,
268 ips: nil,
269 },
270 },
271 },
272 {
273 name: "empty list, IPv6 addrs",
274 cidrs: []string{},
275 itfAddrsPairs: []InterfaceAddrsPair{
276 {
277 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
278 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)}},
279 },
280 {
281 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
282 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)}},
283 },
284 },
285 expected: map[v1.IPFamily]expectation{
286 v1.IPv4Protocol: {
287 matchAll: true,
288 ips: nil,
289 },
290 v1.IPv6Protocol: {
291 matchAll: true,
292 ips: sets.New[string]("2001:db8::1", "::1"),
293 },
294 },
295 },
296 {
297 name: "IPv4 redundant CIDRs",
298 cidrs: []string{"1.2.3.0/24", "0.0.0.0/0"},
299 itfAddrsPairs: []InterfaceAddrsPair{
300 {
301 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
302 addrs: []net.Addr{&net.IPNet{IP: netutils.ParseIPSloppy("1.2.3.4"), Mask: net.CIDRMask(30, 32)}},
303 },
304 },
305 expected: map[v1.IPFamily]expectation{
306 v1.IPv4Protocol: {
307 matchAll: true,
308 ips: sets.New[string]("1.2.3.4"),
309 },
310 v1.IPv6Protocol: {
311 matchAll: true,
312 ips: nil,
313 },
314 },
315 },
316 {
317 name: "Dual-stack, redundant IPv4",
318 cidrs: []string{"0.0.0.0/0", "1.2.3.0/24", "2001:db8::1/128"},
319 itfAddrsPairs: []InterfaceAddrsPair{
320 {
321 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
322 addrs: []net.Addr{
323 &net.IPNet{IP: netutils.ParseIPSloppy("1.2.3.4"), Mask: net.CIDRMask(30, 32)},
324 &net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
325 },
326 },
327 {
328 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
329 addrs: []net.Addr{
330 &net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
331 &net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)},
332 },
333 },
334 },
335 expected: map[v1.IPFamily]expectation{
336 v1.IPv4Protocol: {
337 matchAll: true,
338 ips: sets.New[string]("1.2.3.4", "127.0.0.1"),
339 },
340 v1.IPv6Protocol: {
341 ips: sets.New[string]("2001:db8::1"),
342 },
343 },
344 },
345 {
346 name: "Dual-stack, redundant IPv6",
347 cidrs: []string{"::/0", "1.2.3.0/24", "2001:db8::1/128"},
348 itfAddrsPairs: []InterfaceAddrsPair{
349 {
350 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
351 addrs: []net.Addr{
352 &net.IPNet{IP: netutils.ParseIPSloppy("1.2.3.4"), Mask: net.CIDRMask(30, 32)},
353 &net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
354 },
355 },
356 {
357 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
358 addrs: []net.Addr{
359 &net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
360 &net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)},
361 },
362 },
363 },
364 expected: map[v1.IPFamily]expectation{
365 v1.IPv4Protocol: {
366 ips: sets.New[string]("1.2.3.4"),
367 },
368 v1.IPv6Protocol: {
369 matchAll: true,
370 ips: sets.New[string]("2001:db8::1", "::1"),
371 },
372 },
373 },
374 {
375 name: "ipv4 with nodeIP",
376 itfAddrsPairs: []InterfaceAddrsPair{
377 {
378 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
379 addrs: []net.Addr{
380 &net.IPNet{IP: netutils.ParseIPSloppy("1.2.3.4"), Mask: net.CIDRMask(30, 32)},
381 },
382 },
383 {
384 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
385 addrs: []net.Addr{
386 &net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
387 },
388 },
389 },
390 expected: map[v1.IPFamily]expectation{
391 v1.IPv4Protocol: {
392 ips: sets.New[string]("1.2.3.4"),
393 },
394 },
395 nodeIP: netutils.ParseIPSloppy("1.2.3.4"),
396 },
397 {
398 name: "ipv6 with nodeIP",
399 itfAddrsPairs: []InterfaceAddrsPair{
400 {
401 itf: net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0},
402 addrs: []net.Addr{
403 &net.IPNet{IP: netutils.ParseIPSloppy("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
404 },
405 },
406 {
407 itf: net.Interface{Index: 1, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0},
408 addrs: []net.Addr{
409 &net.IPNet{IP: netutils.ParseIPSloppy("::1"), Mask: net.CIDRMask(128, 128)},
410 },
411 },
412 },
413 expected: map[v1.IPFamily]expectation{
414 v1.IPv6Protocol: {
415 matchAll: true,
416 ips: sets.New[string]("2001:db8::1", "::1"),
417 },
418 },
419 nodeIP: netutils.ParseIPSloppy("1.2.3.4"),
420 },
421 }
422
423 for _, tc := range testCases {
424 t.Run(tc.name, func(t *testing.T) {
425 nw := fake.NewFakeNetwork()
426 for _, pair := range tc.itfAddrsPairs {
427 nw.AddInterfaceAddr(&pair.itf, pair.addrs)
428 }
429
430 for _, family := range []v1.IPFamily{v1.IPv4Protocol, v1.IPv6Protocol} {
431 if tc.nodeIP != nil && v1.IPFamily(fmt.Sprintf("IPv%s", netutils.IPFamilyOf(tc.nodeIP))) != family {
432 continue
433 }
434 npa := NewNodePortAddresses(family, tc.cidrs, tc.nodeIP)
435
436 if npa.MatchAll() != tc.expected[family].matchAll {
437 t.Errorf("unexpected MatchAll(%s), expected: %v", family, tc.expected[family].matchAll)
438 }
439
440 ips, err := npa.GetNodeIPs(nw)
441 expectedIPs := tc.expected[family].ips
442
443
444
445
446 if err != nil {
447 t.Errorf("unexpected error: %v", err)
448 }
449 err = checkNodeIPs(expectedIPs, ips)
450 if err != nil {
451 t.Errorf("unexpected mismatch for %s: %v", family, err)
452 }
453 }
454 })
455 }
456 }
457
458 func TestContainsIPv4Loopback(t *testing.T) {
459 tests := []struct {
460 name string
461 cidrStrings []string
462 want bool
463 }{
464 {
465 name: "empty",
466 want: true,
467 },
468 {
469 name: "all zeros ipv4",
470 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "0.0.0.0/0"},
471 want: true,
472 },
473 {
474 name: "all zeros ipv6",
475 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "::/0"},
476 want: false,
477 },
478 {
479 name: "ipv4 loopback",
480 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "127.0.0.0/8"},
481 want: true,
482 },
483 {
484 name: "ipv6 loopback",
485 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "::1/128"},
486 want: false,
487 },
488 {
489 name: "ipv4 loopback smaller range",
490 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "127.0.2.0/28"},
491 want: true,
492 },
493 {
494 name: "ipv4 loopback within larger range",
495 cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "64.0.0.0/2"},
496 want: true,
497 },
498 {
499 name: "non loop loopback",
500 cidrStrings: []string{"128.0.2.0/28", "224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64"},
501 want: false,
502 },
503 }
504 for _, tt := range tests {
505 t.Run(tt.name, func(t *testing.T) {
506 npa := NewNodePortAddresses(v1.IPv4Protocol, tt.cidrStrings, nil)
507 if got := npa.ContainsIPv4Loopback(); got != tt.want {
508 t.Errorf("IPv4 ContainsIPv4Loopback() = %v, want %v", got, tt.want)
509 }
510
511 npa = NewNodePortAddresses(v1.IPv6Protocol, tt.cidrStrings, nil)
512 if got := npa.ContainsIPv4Loopback(); got {
513 t.Errorf("IPv6 ContainsIPv4Loopback() = %v, want %v", got, false)
514 }
515 })
516 }
517 }
518
View as plain text