1
16
17 package net
18
19 import (
20 "fmt"
21 "net"
22 "testing"
23 )
24
25 func TestDualStackIPs(t *testing.T) {
26 testCases := []struct {
27 ips []string
28 errMessage string
29 expectedResult bool
30 expectError bool
31 }{
32 {
33 ips: []string{"1.1.1.1"},
34 errMessage: "should fail because length is not at least 2",
35 expectedResult: false,
36 expectError: false,
37 },
38 {
39 ips: []string{},
40 errMessage: "should fail because length is not at least 2",
41 expectedResult: false,
42 expectError: false,
43 },
44 {
45 ips: []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"},
46 errMessage: "should fail because all are v4",
47 expectedResult: false,
48 expectError: false,
49 },
50 {
51 ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff0", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff1"},
52 errMessage: "should fail because all are v6",
53 expectedResult: false,
54 expectError: false,
55 },
56 {
57 ips: []string{"1.1.1.1", "not-a-valid-ip"},
58 errMessage: "should fail because 2nd ip is invalid",
59 expectedResult: false,
60 expectError: true,
61 },
62 {
63 ips: []string{"not-a-valid-ip", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
64 errMessage: "should fail because 1st ip is invalid",
65 expectedResult: false,
66 expectError: true,
67 },
68 {
69 ips: []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
70 errMessage: "expected success, but found failure",
71 expectedResult: true,
72 expectError: false,
73 },
74 {
75 ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff0"},
76 errMessage: "expected success, but found failure",
77 expectedResult: true,
78 expectError: false,
79 },
80 {
81 ips: []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "10.0.0.0"},
82 errMessage: "expected success, but found failure",
83 expectedResult: true,
84 expectError: false,
85 },
86 {
87 ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1"},
88 errMessage: "expected success, but found failure",
89 expectedResult: true,
90 expectError: false,
91 },
92 }
93
94 for _, tc := range testCases {
95 dualStack, err := IsDualStackIPStrings(tc.ips)
96 if err == nil && tc.expectError {
97 t.Errorf("%s", tc.errMessage)
98 continue
99 }
100 if err != nil && !tc.expectError {
101 t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
102 continue
103 }
104 if dualStack != tc.expectedResult {
105 t.Errorf("%v for %v", tc.errMessage, tc.ips)
106 }
107 }
108
109 for _, tc := range testCases {
110 ips := make([]net.IP, 0, len(tc.ips))
111 for _, ip := range tc.ips {
112 parsedIP := ParseIPSloppy(ip)
113 ips = append(ips, parsedIP)
114 }
115 dualStack, err := IsDualStackIPs(ips)
116 if err == nil && tc.expectError {
117 t.Errorf("%s", tc.errMessage)
118 continue
119 }
120 if err != nil && !tc.expectError {
121 t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
122 continue
123 }
124 if dualStack != tc.expectedResult {
125 t.Errorf("%v for %v", tc.errMessage, tc.ips)
126 }
127 }
128 }
129
130 func TestDualStackCIDRs(t *testing.T) {
131 testCases := []struct {
132 cidrs []string
133 errMessage string
134 expectedResult bool
135 expectError bool
136 }{
137 {
138 cidrs: []string{"10.10.10.10/8"},
139 errMessage: "should fail because length is not at least 2",
140 expectedResult: false,
141 expectError: false,
142 },
143 {
144 cidrs: []string{},
145 errMessage: "should fail because length is not at least 2",
146 expectedResult: false,
147 expectError: false,
148 },
149 {
150 cidrs: []string{"10.10.10.10/8", "20.20.20.20/8", "30.30.30.30/8"},
151 errMessage: "should fail because all cidrs are v4",
152 expectedResult: false,
153 expectError: false,
154 },
155 {
156 cidrs: []string{"2000::/10", "3000::/10"},
157 errMessage: "should fail because all cidrs are v6",
158 expectedResult: false,
159 expectError: false,
160 },
161 {
162 cidrs: []string{"10.10.10.10/8", "not-a-valid-cidr"},
163 errMessage: "should fail because 2nd cidr is invalid",
164 expectedResult: false,
165 expectError: true,
166 },
167 {
168 cidrs: []string{"not-a-valid-ip", "2000::/10"},
169 errMessage: "should fail because 1st cidr is invalid",
170 expectedResult: false,
171 expectError: true,
172 },
173 {
174 cidrs: []string{"10.10.10.10/8", "2000::/10"},
175 errMessage: "expected success, but found failure",
176 expectedResult: true,
177 expectError: false,
178 },
179 {
180 cidrs: []string{"2000::/10", "10.10.10.10/8"},
181 errMessage: "expected success, but found failure",
182 expectedResult: true,
183 expectError: false,
184 },
185 {
186 cidrs: []string{"2000::/10", "10.10.10.10/8", "3000::/10"},
187 errMessage: "expected success, but found failure",
188 expectedResult: true,
189 expectError: false,
190 },
191 }
192
193
194 for _, tc := range testCases {
195 dualStack, err := IsDualStackCIDRStrings(tc.cidrs)
196 if err == nil && tc.expectError {
197 t.Errorf("%s", tc.errMessage)
198 continue
199 }
200 if err != nil && !tc.expectError {
201 t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
202 continue
203 }
204 if dualStack != tc.expectedResult {
205 t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
206 }
207 }
208
209 for _, tc := range testCases {
210 cidrs := make([]*net.IPNet, 0, len(tc.cidrs))
211 for _, cidr := range tc.cidrs {
212 _, parsedCIDR, _ := ParseCIDRSloppy(cidr)
213 cidrs = append(cidrs, parsedCIDR)
214 }
215
216 dualStack, err := IsDualStackCIDRs(cidrs)
217 if err == nil && tc.expectError {
218 t.Errorf("%s", tc.errMessage)
219 continue
220 }
221 if err != nil && !tc.expectError {
222 t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
223 continue
224 }
225 if dualStack != tc.expectedResult {
226 t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
227 }
228 }
229 }
230
231 func TestIPFamilyOfString(t *testing.T) {
232 testCases := []struct {
233 desc string
234 ip string
235 family IPFamily
236 }{
237 {
238 desc: "IPv4 1",
239 ip: "127.0.0.1",
240 family: IPv4,
241 },
242 {
243 desc: "IPv4 2",
244 ip: "192.168.0.0",
245 family: IPv4,
246 },
247 {
248 desc: "IPv4 3",
249 ip: "1.2.3.4",
250 family: IPv4,
251 },
252 {
253 desc: "IPv4 with leading 0s is accepted",
254 ip: "001.002.003.004",
255 family: IPv4,
256 },
257 {
258 desc: "IPv4 encoded as IPv6 is IPv4",
259 ip: "::FFFF:1.2.3.4",
260 family: IPv4,
261 },
262 {
263 desc: "IPv6 1",
264 ip: "::1",
265 family: IPv6,
266 },
267 {
268 desc: "IPv6 2",
269 ip: "fd00::600d:f00d",
270 family: IPv6,
271 },
272 {
273 desc: "IPv6 3",
274 ip: "2001:db8::5",
275 family: IPv6,
276 },
277 {
278 desc: "IPv4 with out-of-range octets is not accepted",
279 ip: "1.2.3.400",
280 family: IPFamilyUnknown,
281 },
282 {
283 desc: "IPv6 with out-of-range segment is not accepted",
284 ip: "2001:db8::10005",
285 family: IPFamilyUnknown,
286 },
287 {
288 desc: "IPv4 with empty octet is not accepted",
289 ip: "1.2..4",
290 family: IPFamilyUnknown,
291 },
292 {
293 desc: "IPv6 with multiple empty segments is not accepted",
294 ip: "2001::db8::5",
295 family: IPFamilyUnknown,
296 },
297 {
298 desc: "IPv4 CIDR is not accepted",
299 ip: "1.2.3.4/32",
300 family: IPFamilyUnknown,
301 },
302 {
303 desc: "IPv6 CIDR is not accepted",
304 ip: "2001:db8::/64",
305 family: IPFamilyUnknown,
306 },
307 {
308 desc: "IPv4:port is not accepted",
309 ip: "1.2.3.4:80",
310 family: IPFamilyUnknown,
311 },
312 {
313 desc: "[IPv6] with brackets is not accepted",
314 ip: "[2001:db8::5]",
315 family: IPFamilyUnknown,
316 },
317 {
318 desc: "[IPv6]:port is not accepted",
319 ip: "[2001:db8::5]:80",
320 family: IPFamilyUnknown,
321 },
322 {
323 desc: "IPv6%zone is not accepted",
324 ip: "fe80::1234%eth0",
325 family: IPFamilyUnknown,
326 },
327 {
328 desc: "IPv4 with leading whitespace is not accepted",
329 ip: " 1.2.3.4",
330 family: IPFamilyUnknown,
331 },
332 {
333 desc: "IPv4 with trailing whitespace is not accepted",
334 ip: "1.2.3.4 ",
335 family: IPFamilyUnknown,
336 },
337 {
338 desc: "IPv6 with leading whitespace is not accepted",
339 ip: " 2001:db8::5",
340 family: IPFamilyUnknown,
341 },
342 {
343 desc: "IPv6 with trailing whitespace is not accepted",
344 ip: " 2001:db8::5",
345 family: IPFamilyUnknown,
346 },
347 {
348 desc: "random unparseable string",
349 ip: "bad ip",
350 family: IPFamilyUnknown,
351 },
352 }
353 for _, tc := range testCases {
354 t.Run(tc.desc, func(t *testing.T) {
355 family := IPFamilyOfString(tc.ip)
356 isIPv4 := IsIPv4String(tc.ip)
357 isIPv6 := IsIPv6String(tc.ip)
358
359 if family != tc.family {
360 t.Errorf("Expect %q family %q, got %q", tc.ip, tc.family, family)
361 }
362 if isIPv4 != (tc.family == IPv4) {
363 t.Errorf("Expect %q ipv4 %v, got %v", tc.ip, tc.family == IPv4, isIPv6)
364 }
365 if isIPv6 != (tc.family == IPv6) {
366 t.Errorf("Expect %q ipv6 %v, got %v", tc.ip, tc.family == IPv6, isIPv6)
367 }
368 })
369 }
370 }
371
372
373 func mustParseCIDRIP(cidrstr string) net.IP {
374 ip, _, err := net.ParseCIDR(cidrstr)
375 if ip == nil {
376 panic(fmt.Sprintf("bad test case: %q should have been parseable: %v", cidrstr, err))
377 }
378 return ip
379 }
380
381
382 func mustParseCIDRBase(cidrstr string) net.IP {
383 _, cidr, err := net.ParseCIDR(cidrstr)
384 if cidr == nil {
385 panic(fmt.Sprintf("bad test case: %q should have been parseable: %v", cidrstr, err))
386 }
387 return cidr.IP
388 }
389
390
391 func mustParseCIDRMask(cidrstr string) net.IPMask {
392 _, cidr, err := net.ParseCIDR(cidrstr)
393 if cidr == nil {
394 panic(fmt.Sprintf("bad test case: %q should have been parseable: %v", cidrstr, err))
395 }
396 return cidr.Mask
397 }
398
399 func TestIsIPFamilyOf(t *testing.T) {
400 testCases := []struct {
401 desc string
402 ip net.IP
403 family IPFamily
404 }{
405 {
406 desc: "IPv4 all-zeros",
407 ip: net.IPv4zero,
408 family: IPv4,
409 },
410 {
411 desc: "IPv6 all-zeros",
412 ip: net.IPv6zero,
413 family: IPv6,
414 },
415 {
416 desc: "IPv4 broadcast",
417 ip: net.IPv4bcast,
418 family: IPv4,
419 },
420 {
421 desc: "IPv4 loopback",
422 ip: net.ParseIP("127.0.0.1"),
423 family: IPv4,
424 },
425 {
426 desc: "IPv6 loopback",
427 ip: net.IPv6loopback,
428 family: IPv6,
429 },
430 {
431 desc: "IPv4 1",
432 ip: net.ParseIP("10.20.40.40"),
433 family: IPv4,
434 },
435 {
436 desc: "IPv4 2",
437 ip: net.ParseIP("172.17.3.0"),
438 family: IPv4,
439 },
440 {
441 desc: "IPv4 encoded as IPv6 is IPv4",
442 ip: net.ParseIP("::FFFF:1.2.3.4"),
443 family: IPv4,
444 },
445 {
446 desc: "IPv6 1",
447 ip: net.ParseIP("fd00::600d:f00d"),
448 family: IPv6,
449 },
450 {
451 desc: "IPv6 2",
452 ip: net.ParseIP("2001:db8::5"),
453 family: IPv6,
454 },
455 {
456 desc: "IP from IPv4 CIDR is IPv4",
457 ip: mustParseCIDRIP("192.168.1.1/24"),
458 family: IPv4,
459 },
460 {
461 desc: "CIDR base IP from IPv4 CIDR is IPv4",
462 ip: mustParseCIDRBase("192.168.1.1/24"),
463 family: IPv4,
464 },
465 {
466 desc: "CIDR mask from IPv4 CIDR is IPv4",
467 ip: net.IP(mustParseCIDRMask("192.168.1.1/24")),
468 family: IPv4,
469 },
470 {
471 desc: "IP from IPv6 CIDR is IPv6",
472 ip: mustParseCIDRIP("2001:db8::5/64"),
473 family: IPv6,
474 },
475 {
476 desc: "CIDR base IP from IPv6 CIDR is IPv6",
477 ip: mustParseCIDRBase("2001:db8::5/64"),
478 family: IPv6,
479 },
480 {
481 desc: "CIDR mask from IPv6 CIDR is IPv6",
482 ip: net.IP(mustParseCIDRMask("2001:db8::5/64")),
483 family: IPv6,
484 },
485 {
486 desc: "nil is accepted, but is neither IPv4 nor IPv6",
487 ip: nil,
488 family: IPFamilyUnknown,
489 },
490 {
491 desc: "invalid empty binary net.IP",
492 ip: net.IP([]byte{}),
493 family: IPFamilyUnknown,
494 },
495 {
496 desc: "invalid short binary net.IP",
497 ip: net.IP([]byte{1, 2, 3}),
498 family: IPFamilyUnknown,
499 },
500 {
501 desc: "invalid medium-length binary net.IP",
502 ip: net.IP([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
503 family: IPFamilyUnknown,
504 },
505 {
506 desc: "invalid long binary net.IP",
507 ip: net.IP([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}),
508 family: IPFamilyUnknown,
509 },
510 }
511 for _, tc := range testCases {
512 t.Run(tc.desc, func(t *testing.T) {
513 family := IPFamilyOf(tc.ip)
514 isIPv4 := IsIPv4(tc.ip)
515 isIPv6 := IsIPv6(tc.ip)
516
517 if family != tc.family {
518 t.Errorf("Expect family %q, got %q", tc.family, family)
519 }
520 if isIPv4 != (tc.family == IPv4) {
521 t.Errorf("Expect ipv4 %v, got %v", tc.family == IPv4, isIPv6)
522 }
523 if isIPv6 != (tc.family == IPv6) {
524 t.Errorf("Expect ipv6 %v, got %v", tc.family == IPv6, isIPv6)
525 }
526 })
527 }
528 }
529
530 func TestIPFamilyOfCIDR(t *testing.T) {
531 testCases := []struct {
532 desc string
533 cidr string
534 family IPFamily
535 }{
536 {
537 desc: "IPv4 CIDR 1",
538 cidr: "10.0.0.0/8",
539 family: IPv4,
540 },
541 {
542 desc: "IPv4 CIDR 2",
543 cidr: "192.168.0.0/16",
544 family: IPv4,
545 },
546 {
547 desc: "IPv6 CIDR 1",
548 cidr: "::/1",
549 family: IPv6,
550 },
551 {
552 desc: "IPv6 CIDR 2",
553 cidr: "2000::/10",
554 family: IPv6,
555 },
556 {
557 desc: "IPv6 CIDR 3",
558 cidr: "2001:db8::/32",
559 family: IPv6,
560 },
561 {
562 desc: "bad CIDR",
563 cidr: "foo",
564 family: IPFamilyUnknown,
565 },
566 {
567 desc: "IPv4 with out-of-range octets is not accepted",
568 cidr: "1.2.3.400/32",
569 family: IPFamilyUnknown,
570 },
571 {
572 desc: "IPv6 with out-of-range segment is not accepted",
573 cidr: "2001:db8::10005/64",
574 family: IPFamilyUnknown,
575 },
576 {
577 desc: "IPv4 with out-of-range mask length is not accepted",
578 cidr: "1.2.3.4/64",
579 family: IPFamilyUnknown,
580 },
581 {
582 desc: "IPv6 with out-of-range mask length is not accepted",
583 cidr: "2001:db8::5/192",
584 family: IPFamilyUnknown,
585 },
586 {
587 desc: "IPv4 with empty octet is not accepted",
588 cidr: "1.2..4/32",
589 family: IPFamilyUnknown,
590 },
591 {
592 desc: "IPv6 with multiple empty segments is not accepted",
593 cidr: "2001::db8::5/64",
594 family: IPFamilyUnknown,
595 },
596 {
597 desc: "IPv4 IP is not CIDR",
598 cidr: "192.168.0.0",
599 family: IPFamilyUnknown,
600 },
601 {
602 desc: "IPv6 IP is not CIDR",
603 cidr: "2001:db8::",
604 family: IPFamilyUnknown,
605 },
606 {
607 desc: "IPv4 CIDR with leading whitespace is not accepted",
608 cidr: " 1.2.3.4/32",
609 family: IPFamilyUnknown,
610 },
611 {
612 desc: "IPv4 CIDR with trailing whitespace is not accepted",
613 cidr: "1.2.3.4/32 ",
614 family: IPFamilyUnknown,
615 },
616 {
617 desc: "IPv6 CIDR with leading whitespace is not accepted",
618 cidr: " 2001:db8::5/64",
619 family: IPFamilyUnknown,
620 },
621 {
622 desc: "IPv6 CIDR with trailing whitespace is not accepted",
623 cidr: " 2001:db8::5/64",
624 family: IPFamilyUnknown,
625 },
626 }
627
628 for _, tc := range testCases {
629 t.Run(tc.desc, func(t *testing.T) {
630 family := IPFamilyOfCIDRString(tc.cidr)
631 isIPv4 := IsIPv4CIDRString(tc.cidr)
632 isIPv6 := IsIPv6CIDRString(tc.cidr)
633
634 if family != tc.family {
635 t.Errorf("Expect family %v, got %v", tc.family, family)
636 }
637 if isIPv4 != (tc.family == IPv4) {
638 t.Errorf("Expect %q ipv4 %v, got %v", tc.cidr, tc.family == IPv4, isIPv6)
639 }
640 if isIPv6 != (tc.family == IPv6) {
641 t.Errorf("Expect %q ipv6 %v, got %v", tc.cidr, tc.family == IPv6, isIPv6)
642 }
643
644 _, parsed, _ := ParseCIDRSloppy(tc.cidr)
645 familyParsed := IPFamilyOfCIDR(parsed)
646 isIPv4Parsed := IsIPv4CIDR(parsed)
647 isIPv6Parsed := IsIPv6CIDR(parsed)
648 if familyParsed != family {
649 t.Errorf("%q gives different results for IPFamilyOfCIDR (%v) and IPFamilyOfCIDRString (%v)", tc.cidr, familyParsed, family)
650 }
651 if isIPv4Parsed != isIPv4 {
652 t.Errorf("%q gives different results for IsIPv4CIDR (%v) and IsIPv4CIDRString (%v)", tc.cidr, isIPv4Parsed, isIPv4)
653 }
654 if isIPv6Parsed != isIPv6 {
655 t.Errorf("%q gives different results for IsIPv6CIDR (%v) and IsIPv6CIDRString (%v)", tc.cidr, isIPv6Parsed, isIPv6)
656 }
657 })
658 }
659 }
660
View as plain text