1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package net
18
19import (
20	"net"
21	"testing"
22)
23
24func TestParseCIDRs(t *testing.T) {
25	testCases := []struct {
26		cidrs         []string
27		errString     string
28		errorExpected bool
29	}{
30		{
31			cidrs:         []string{},
32			errString:     "should not return an error for an empty slice",
33			errorExpected: false,
34		},
35		{
36			cidrs:         []string{"10.0.0.0/8", "not-a-valid-cidr", "2000::/10"},
37			errString:     "should return error for bad cidr",
38			errorExpected: true,
39		},
40		{
41			cidrs:         []string{"10.0.0.0/8", "2000::/10"},
42			errString:     "should not return error for good  cidrs",
43			errorExpected: false,
44		},
45	}
46
47	for _, tc := range testCases {
48		cidrs, err := ParseCIDRs(tc.cidrs)
49		if tc.errorExpected {
50			if err == nil {
51				t.Errorf("%v", tc.errString)
52			}
53			continue
54		}
55		if err != nil {
56			t.Errorf("%v error:%v", tc.errString, err)
57		}
58
59		// validate lengths
60		if len(cidrs) != len(tc.cidrs) {
61			t.Errorf("cidrs should be of the same lengths %v != %v", len(cidrs), len(tc.cidrs))
62		}
63
64	}
65}
66func TestDualStackIPs(t *testing.T) {
67	testCases := []struct {
68		ips            []string
69		errMessage     string
70		expectedResult bool
71		expectError    bool
72	}{
73		{
74			ips:            []string{"1.1.1.1"},
75			errMessage:     "should fail because length is not at least 2",
76			expectedResult: false,
77			expectError:    false,
78		},
79		{
80			ips:            []string{},
81			errMessage:     "should fail because length is not at least 2",
82			expectedResult: false,
83			expectError:    false,
84		},
85		{
86			ips:            []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"},
87			errMessage:     "should fail because all are v4",
88			expectedResult: false,
89			expectError:    false,
90		},
91		{
92			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"},
93			errMessage:     "should fail because all are v6",
94			expectedResult: false,
95			expectError:    false,
96		},
97		{
98			ips:            []string{"1.1.1.1", "not-a-valid-ip"},
99			errMessage:     "should fail because 2nd ip is invalid",
100			expectedResult: false,
101			expectError:    true,
102		},
103		{
104			ips:            []string{"not-a-valid-ip", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
105			errMessage:     "should fail because 1st ip is invalid",
106			expectedResult: false,
107			expectError:    true,
108		},
109		{
110			ips:            []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
111			errMessage:     "expected success, but found failure",
112			expectedResult: true,
113			expectError:    false,
114		},
115		{
116			ips:            []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff0"},
117			errMessage:     "expected success, but found failure",
118			expectedResult: true,
119			expectError:    false,
120		},
121		{
122			ips:            []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "10.0.0.0"},
123			errMessage:     "expected success, but found failure",
124			expectedResult: true,
125			expectError:    false,
126		},
127		{
128			ips:            []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1"},
129			errMessage:     "expected success, but found failure",
130			expectedResult: true,
131			expectError:    false,
132		},
133	}
134	// for each test case, test the regular func and the string func
135	for _, tc := range testCases {
136		dualStack, err := IsDualStackIPStrings(tc.ips)
137		if err == nil && tc.expectError {
138			t.Errorf("%s", tc.errMessage)
139			continue
140		}
141		if err != nil && !tc.expectError {
142			t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
143			continue
144		}
145		if dualStack != tc.expectedResult {
146			t.Errorf("%v for %v", tc.errMessage, tc.ips)
147		}
148	}
149
150	for _, tc := range testCases {
151		ips := make([]net.IP, 0, len(tc.ips))
152		for _, ip := range tc.ips {
153			parsedIP := net.ParseIP(ip)
154			ips = append(ips, parsedIP)
155		}
156		dualStack, err := IsDualStackIPs(ips)
157		if err == nil && tc.expectError {
158			t.Errorf("%s", tc.errMessage)
159			continue
160		}
161		if err != nil && !tc.expectError {
162			t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
163			continue
164		}
165		if dualStack != tc.expectedResult {
166			t.Errorf("%v for %v", tc.errMessage, tc.ips)
167		}
168	}
169}
170
171func TestDualStackCIDRs(t *testing.T) {
172	testCases := []struct {
173		cidrs          []string
174		errMessage     string
175		expectedResult bool
176		expectError    bool
177	}{
178		{
179			cidrs:          []string{"10.10.10.10/8"},
180			errMessage:     "should fail because length is not at least 2",
181			expectedResult: false,
182			expectError:    false,
183		},
184		{
185			cidrs:          []string{},
186			errMessage:     "should fail because length is not at least 2",
187			expectedResult: false,
188			expectError:    false,
189		},
190		{
191			cidrs:          []string{"10.10.10.10/8", "20.20.20.20/8", "30.30.30.30/8"},
192			errMessage:     "should fail because all cidrs are v4",
193			expectedResult: false,
194			expectError:    false,
195		},
196		{
197			cidrs:          []string{"2000::/10", "3000::/10"},
198			errMessage:     "should fail because all cidrs are v6",
199			expectedResult: false,
200			expectError:    false,
201		},
202		{
203			cidrs:          []string{"10.10.10.10/8", "not-a-valid-cidr"},
204			errMessage:     "should fail because 2nd cidr is invalid",
205			expectedResult: false,
206			expectError:    true,
207		},
208		{
209			cidrs:          []string{"not-a-valid-ip", "2000::/10"},
210			errMessage:     "should fail because 1st cidr is invalid",
211			expectedResult: false,
212			expectError:    true,
213		},
214		{
215			cidrs:          []string{"10.10.10.10/8", "2000::/10"},
216			errMessage:     "expected success, but found failure",
217			expectedResult: true,
218			expectError:    false,
219		},
220		{
221			cidrs:          []string{"2000::/10", "10.10.10.10/8"},
222			errMessage:     "expected success, but found failure",
223			expectedResult: true,
224			expectError:    false,
225		},
226		{
227			cidrs:          []string{"2000::/10", "10.10.10.10/8", "3000::/10"},
228			errMessage:     "expected success, but found failure",
229			expectedResult: true,
230			expectError:    false,
231		},
232	}
233
234	// for each test case, test the regular func and the string func
235	for _, tc := range testCases {
236		dualStack, err := IsDualStackCIDRStrings(tc.cidrs)
237		if err == nil && tc.expectError {
238			t.Errorf("%s", tc.errMessage)
239			continue
240		}
241		if err != nil && !tc.expectError {
242			t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
243			continue
244		}
245		if dualStack != tc.expectedResult {
246			t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
247		}
248	}
249
250	for _, tc := range testCases {
251		cidrs := make([]*net.IPNet, 0, len(tc.cidrs))
252		for _, cidr := range tc.cidrs {
253			_, parsedCIDR, _ := net.ParseCIDR(cidr)
254			cidrs = append(cidrs, parsedCIDR)
255		}
256
257		dualStack, err := IsDualStackCIDRs(cidrs)
258		if err == nil && tc.expectError {
259			t.Errorf("%s", tc.errMessage)
260			continue
261		}
262		if err != nil && !tc.expectError {
263			t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
264			continue
265		}
266		if dualStack != tc.expectedResult {
267			t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
268		}
269	}
270}
271
272func TestIsIPv6String(t *testing.T) {
273	testCases := []struct {
274		ip         string
275		expectIPv6 bool
276	}{
277		{
278			ip:         "127.0.0.1",
279			expectIPv6: false,
280		},
281		{
282			ip:         "192.168.0.0",
283			expectIPv6: false,
284		},
285		{
286			ip:         "1.2.3.4",
287			expectIPv6: false,
288		},
289		{
290			ip:         "bad ip",
291			expectIPv6: false,
292		},
293		{
294			ip:         "::1",
295			expectIPv6: true,
296		},
297		{
298			ip:         "fd00::600d:f00d",
299			expectIPv6: true,
300		},
301		{
302			ip:         "2001:db8::5",
303			expectIPv6: true,
304		},
305	}
306	for i := range testCases {
307		isIPv6 := IsIPv6String(testCases[i].ip)
308		if isIPv6 != testCases[i].expectIPv6 {
309			t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
310		}
311	}
312}
313
314func TestIsIPv6(t *testing.T) {
315	testCases := []struct {
316		ip         net.IP
317		expectIPv6 bool
318	}{
319		{
320			ip:         net.IPv4zero,
321			expectIPv6: false,
322		},
323		{
324			ip:         net.IPv4bcast,
325			expectIPv6: false,
326		},
327		{
328			ip:         net.ParseIP("127.0.0.1"),
329			expectIPv6: false,
330		},
331		{
332			ip:         net.ParseIP("10.20.40.40"),
333			expectIPv6: false,
334		},
335		{
336			ip:         net.ParseIP("172.17.3.0"),
337			expectIPv6: false,
338		},
339		{
340			ip:         nil,
341			expectIPv6: false,
342		},
343		{
344			ip:         net.IPv6loopback,
345			expectIPv6: true,
346		},
347		{
348			ip:         net.IPv6zero,
349			expectIPv6: true,
350		},
351		{
352			ip:         net.ParseIP("fd00::600d:f00d"),
353			expectIPv6: true,
354		},
355		{
356			ip:         net.ParseIP("2001:db8::5"),
357			expectIPv6: true,
358		},
359	}
360	for i := range testCases {
361		isIPv6 := IsIPv6(testCases[i].ip)
362		if isIPv6 != testCases[i].expectIPv6 {
363			t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
364		}
365	}
366}
367
368func TestIsIPv6CIDRString(t *testing.T) {
369	testCases := []struct {
370		desc         string
371		cidr         string
372		expectResult bool
373	}{
374		{
375			desc:         "ipv4 CIDR 1",
376			cidr:         "10.0.0.0/8",
377			expectResult: false,
378		},
379		{
380			desc:         "ipv4 CIDR 2",
381			cidr:         "192.168.0.0/16",
382			expectResult: false,
383		},
384		{
385			desc:         "ipv6 CIDR 1",
386			cidr:         "::/1",
387			expectResult: true,
388		},
389		{
390			desc:         "ipv6 CIDR 2",
391			cidr:         "2000::/10",
392			expectResult: true,
393		},
394		{
395			desc:         "ipv6 CIDR 3",
396			cidr:         "2001:db8::/32",
397			expectResult: true,
398		},
399	}
400
401	for _, tc := range testCases {
402		res := IsIPv6CIDRString(tc.cidr)
403		if res != tc.expectResult {
404			t.Errorf("%v: want IsIPv6CIDRString=%v, got %v", tc.desc, tc.expectResult, res)
405		}
406	}
407}
408
409func TestIsIPv6CIDR(t *testing.T) {
410	testCases := []struct {
411		desc         string
412		cidr         string
413		expectResult bool
414	}{
415		{
416			desc:         "ipv4 CIDR 1",
417			cidr:         "10.0.0.0/8",
418			expectResult: false,
419		},
420		{
421			desc:         "ipv4 CIDR 2",
422			cidr:         "192.168.0.0/16",
423			expectResult: false,
424		},
425		{
426			desc:         "ipv6 CIDR 1",
427			cidr:         "::/1",
428			expectResult: true,
429		},
430		{
431			desc:         "ipv6 CIDR 2",
432			cidr:         "2000::/10",
433			expectResult: true,
434		},
435		{
436			desc:         "ipv6 CIDR 3",
437			cidr:         "2001:db8::/32",
438			expectResult: true,
439		},
440	}
441
442	for _, tc := range testCases {
443		_, cidr, _ := net.ParseCIDR(tc.cidr)
444		res := IsIPv6CIDR(cidr)
445		if res != tc.expectResult {
446			t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res)
447		}
448	}
449}
450
451func TestIsIPv4String(t *testing.T) {
452	testCases := []struct {
453		ip         string
454		expectIPv4 bool
455	}{
456		{
457			ip:         "127.0.0.1",
458			expectIPv4: true,
459		},
460		{
461			ip:         "192.168.0.0",
462			expectIPv4: true,
463		},
464		{
465			ip:         "1.2.3.4",
466			expectIPv4: true,
467		},
468		{
469			ip:         "bad ip",
470			expectIPv4: false,
471		},
472		{
473			ip:         "::1",
474			expectIPv4: false,
475		},
476		{
477			ip:         "fd00::600d:f00d",
478			expectIPv4: false,
479		},
480		{
481			ip:         "2001:db8::5",
482			expectIPv4: false,
483		},
484	}
485	for i := range testCases {
486		isIPv4 := IsIPv4String(testCases[i].ip)
487		if isIPv4 != testCases[i].expectIPv4 {
488			t.Errorf("[%d] Expect ipv4 %v, got %v", i+1, testCases[i].expectIPv4, isIPv4)
489		}
490	}
491}
492
493func TestIsIPv4(t *testing.T) {
494	testCases := []struct {
495		ip         net.IP
496		expectIPv4 bool
497	}{
498		{
499			ip:         net.IPv4zero,
500			expectIPv4: true,
501		},
502		{
503			ip:         net.IPv4bcast,
504			expectIPv4: true,
505		},
506		{
507			ip:         net.ParseIP("127.0.0.1"),
508			expectIPv4: true,
509		},
510		{
511			ip:         net.ParseIP("10.20.40.40"),
512			expectIPv4: true,
513		},
514		{
515			ip:         net.ParseIP("172.17.3.0"),
516			expectIPv4: true,
517		},
518		{
519			ip:         nil,
520			expectIPv4: false,
521		},
522		{
523			ip:         net.IPv6loopback,
524			expectIPv4: false,
525		},
526		{
527			ip:         net.IPv6zero,
528			expectIPv4: false,
529		},
530		{
531			ip:         net.ParseIP("fd00::600d:f00d"),
532			expectIPv4: false,
533		},
534		{
535			ip:         net.ParseIP("2001:db8::5"),
536			expectIPv4: false,
537		},
538	}
539	for i := range testCases {
540		isIPv4 := IsIPv4(testCases[i].ip)
541		if isIPv4 != testCases[i].expectIPv4 {
542			t.Errorf("[%d] Expect ipv4 %v, got %v", i+1, testCases[i].expectIPv4, isIPv4)
543		}
544	}
545}
546
547func TestIsIPv4CIDRString(t *testing.T) {
548	testCases := []struct {
549		desc         string
550		cidr         string
551		expectResult bool
552	}{
553		{
554			desc:         "ipv4 CIDR 1",
555			cidr:         "10.0.0.0/8",
556			expectResult: true,
557		},
558		{
559			desc:         "ipv4 CIDR 2",
560			cidr:         "192.168.0.0/16",
561			expectResult: true,
562		},
563		{
564			desc:         "ipv6 CIDR 1",
565			cidr:         "::/1",
566			expectResult: false,
567		},
568		{
569			desc:         "ipv6 CIDR 2",
570			cidr:         "2000::/10",
571			expectResult: false,
572		},
573		{
574			desc:         "ipv6 CIDR 3",
575			cidr:         "2001:db8::/32",
576			expectResult: false,
577		},
578	}
579
580	for _, tc := range testCases {
581		res := IsIPv4CIDRString(tc.cidr)
582		if res != tc.expectResult {
583			t.Errorf("%v: want IsIPv4CIDRString=%v, got %v", tc.desc, tc.expectResult, res)
584		}
585	}
586}
587
588func TestIsIPv4CIDR(t *testing.T) {
589	testCases := []struct {
590		desc         string
591		cidr         string
592		expectResult bool
593	}{
594		{
595			desc:         "ipv4 CIDR 1",
596			cidr:         "10.0.0.0/8",
597			expectResult: true,
598		},
599		{
600			desc:         "ipv4 CIDR 2",
601			cidr:         "192.168.0.0/16",
602			expectResult: true,
603		},
604		{
605			desc:         "ipv6 CIDR 1",
606			cidr:         "::/1",
607			expectResult: false,
608		},
609		{
610			desc:         "ipv6 CIDR 2",
611			cidr:         "2000::/10",
612			expectResult: false,
613		},
614		{
615			desc:         "ipv6 CIDR 3",
616			cidr:         "2001:db8::/32",
617			expectResult: false,
618		},
619	}
620
621	for _, tc := range testCases {
622		_, cidr, _ := net.ParseCIDR(tc.cidr)
623		res := IsIPv4CIDR(cidr)
624		if res != tc.expectResult {
625			t.Errorf("%v: want IsIPv4CIDR=%v, got %v", tc.desc, tc.expectResult, res)
626		}
627	}
628}
629
630func TestParsePort(t *testing.T) {
631	var tests = []struct {
632		name          string
633		port          string
634		allowZero     bool
635		expectedPort  int
636		expectedError bool
637	}{
638		{
639			name:         "valid port: 1",
640			port:         "1",
641			expectedPort: 1,
642		},
643		{
644			name:         "valid port: 1234",
645			port:         "1234",
646			expectedPort: 1234,
647		},
648		{
649			name:         "valid port: 65535",
650			port:         "65535",
651			expectedPort: 65535,
652		},
653		{
654			name:          "invalid port: not a number",
655			port:          "a",
656			expectedError: true,
657			allowZero:     false,
658		},
659		{
660			name:          "invalid port: too small",
661			port:          "0",
662			expectedError: true,
663		},
664		{
665			name:          "invalid port: negative",
666			port:          "-10",
667			expectedError: true,
668		},
669		{
670			name:          "invalid port: too big",
671			port:          "65536",
672			expectedError: true,
673		},
674		{
675			name:      "zero port: allowed",
676			port:      "0",
677			allowZero: true,
678		},
679		{
680			name:          "zero port: not allowed",
681			port:          "0",
682			expectedError: true,
683		},
684	}
685
686	for _, rt := range tests {
687		t.Run(rt.name, func(t *testing.T) {
688			actualPort, actualError := ParsePort(rt.port, rt.allowZero)
689
690			if actualError != nil && !rt.expectedError {
691				t.Errorf("%s unexpected failure: %v", rt.name, actualError)
692				return
693			}
694			if actualError == nil && rt.expectedError {
695				t.Errorf("%s passed when expected to fail", rt.name)
696				return
697			}
698			if actualPort != rt.expectedPort {
699				t.Errorf("%s returned wrong port: got %d, expected %d", rt.name, actualPort, rt.expectedPort)
700			}
701		})
702	}
703}
704
705func TestRangeSize(t *testing.T) {
706	testCases := []struct {
707		name  string
708		cidr  string
709		addrs int64
710	}{
711		{
712			name:  "supported IPv4 cidr",
713			cidr:  "192.168.1.0/24",
714			addrs: 256,
715		},
716		{
717			name:  "unsupported IPv4 cidr",
718			cidr:  "192.168.1.0/1",
719			addrs: 0,
720		},
721		{
722			name:  "unsupported IPv6 mask",
723			cidr:  "2001:db8::/1",
724			addrs: 0,
725		},
726	}
727
728	for _, tc := range testCases {
729		_, cidr, err := net.ParseCIDR(tc.cidr)
730		if err != nil {
731			t.Errorf("failed to parse cidr for test %s, unexpected error: '%s'", tc.name, err)
732		}
733		if size := RangeSize(cidr); size != tc.addrs {
734			t.Errorf("test %s failed. %s should have a range size of %d, got %d",
735				tc.name, tc.cidr, tc.addrs, size)
736		}
737	}
738}
739
740func TestGetIndexedIP(t *testing.T) {
741	testCases := []struct {
742		cidr        string
743		index       int
744		expectError bool
745		expectedIP  string
746	}{
747		{
748			cidr:        "192.168.1.0/24",
749			index:       20,
750			expectError: false,
751			expectedIP:  "192.168.1.20",
752		},
753		{
754			cidr:        "192.168.1.0/30",
755			index:       10,
756			expectError: true,
757		},
758		{
759			cidr:        "192.168.1.0/24",
760			index:       255,
761			expectError: false,
762			expectedIP:  "192.168.1.255",
763		},
764		{
765			cidr:        "255.255.255.0/24",
766			index:       256,
767			expectError: true,
768		},
769		{
770			cidr:        "fd:11:b2:be::/120",
771			index:       20,
772			expectError: false,
773			expectedIP:  "fd:11:b2:be::14",
774		},
775		{
776			cidr:        "fd:11:b2:be::/126",
777			index:       10,
778			expectError: true,
779		},
780		{
781			cidr:        "fd:11:b2:be::/120",
782			index:       255,
783			expectError: false,
784			expectedIP:  "fd:11:b2:be::ff",
785		},
786		{
787			cidr:        "00:00:00:be::/120",
788			index:       255,
789			expectError: false,
790			expectedIP:  "::be:0:0:0:ff",
791		},
792	}
793
794	for _, tc := range testCases {
795		_, subnet, err := net.ParseCIDR(tc.cidr)
796		if err != nil {
797			t.Errorf("failed to parse cidr %s, unexpected error: '%s'", tc.cidr, err)
798		}
799
800		ip, err := GetIndexedIP(subnet, tc.index)
801		if err == nil && tc.expectError || err != nil && !tc.expectError {
802			t.Errorf("expectedError is %v and err is %s", tc.expectError, err)
803			continue
804		}
805
806		if err == nil {
807			ipString := ip.String()
808			if ipString != tc.expectedIP {
809				t.Errorf("expected %s but instead got %s", tc.expectedIP, ipString)
810			}
811		}
812
813	}
814}
815