diff --git a/pkg/proxy/util/utils.go b/pkg/proxy/util/utils.go index 93cf26323a1..d766a1d4ebb 100644 --- a/pkg/proxy/util/utils.go +++ b/pkg/proxy/util/utils.go @@ -20,8 +20,10 @@ import ( "fmt" "net" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/tools/record" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/core/helper" utilnet "k8s.io/kubernetes/pkg/util/net" @@ -129,3 +131,18 @@ func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error) } return uniqueAddressList, nil } + +// LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event. +func LogAndEmitIncorrectIPVersionEvent(recorder record.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) { + errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName) + glog.Errorf("%s (service %s/%s).", errMsg, svcNamespace, svcName) + if recorder != nil { + recorder.Eventf( + &v1.ObjectReference{ + Kind: "Service", + Name: svcName, + Namespace: svcNamespace, + UID: svcUID, + }, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", errMsg) + } +} diff --git a/pkg/util/net/net.go b/pkg/util/net/net.go index ea0ae022bb7..f838864cf5b 100644 --- a/pkg/util/net/net.go +++ b/pkg/util/net/net.go @@ -30,3 +30,32 @@ func IsIPv6String(ip string) bool { netIP := net.ParseIP(ip) return IsIPv6(netIP) } + +// IsIPv6CIDR returns if cidr is IPv6. +// This assumes cidr is a valid CIDR. +func IsIPv6CIDR(cidr string) bool { + ip, _, _ := net.ParseCIDR(cidr) + return IsIPv6(ip) +} + +// FilterIncorrectIPVersion filters out the incorrect IP version case from a slice of IP strings. +func FilterIncorrectIPVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) { + return filterWithCondition(ipStrings, isIPv6Mode, IsIPv6String) +} + +// FilterIncorrectCIDRVersion filters out the incorrect IP version case from a slice of CIDR strings. +func FilterIncorrectCIDRVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) { + return filterWithCondition(ipStrings, isIPv6Mode, IsIPv6CIDR) +} + +func filterWithCondition(strs []string, expectedCondition bool, conditionFunc func(string) bool) ([]string, []string) { + var corrects, incorrects []string + for _, str := range strs { + if conditionFunc(str) != expectedCondition { + incorrects = append(incorrects, str) + } else { + corrects = append(corrects, str) + } + } + return corrects, incorrects +} diff --git a/pkg/util/net/net_test.go b/pkg/util/net/net_test.go index a01af105551..c2d2f30f775 100644 --- a/pkg/util/net/net_test.go +++ b/pkg/util/net/net_test.go @@ -18,6 +18,7 @@ package net import ( "net" + "reflect" "testing" ) @@ -116,3 +117,170 @@ func TestIsIPv6(t *testing.T) { } } } + +func TestIsIPv6CIDR(t *testing.T) { + testCases := []struct { + desc string + cidr string + expectResult bool + }{ + { + desc: "ipv4 CIDR 1", + cidr: "10.0.0.0/8", + expectResult: false, + }, + { + desc: "ipv4 CIDR 2", + cidr: "192.168.0.0/16", + expectResult: false, + }, + { + desc: "ipv6 CIDR 1", + cidr: "::/1", + expectResult: true, + }, + { + desc: "ipv6 CIDR 2", + cidr: "2000::/10", + expectResult: true, + }, + { + desc: "ipv6 CIDR 3", + cidr: "2001:db8::/32", + expectResult: true, + }, + } + + for _, tc := range testCases { + res := IsIPv6CIDR(tc.cidr) + if res != tc.expectResult { + t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res) + } + } +} + +func TestFilterIncorrectIPVersion(t *testing.T) { + testCases := []struct { + desc string + isIPv6 bool + ipStrings []string + expectCorrects []string + expectIncorrects []string + }{ + { + desc: "all ipv4 strings in ipv4 mode", + isIPv6: false, + ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + expectIncorrects: nil, + }, + { + desc: "all ipv6 strings in ipv4 mode", + isIPv6: false, + ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + expectCorrects: nil, + expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + }, + { + desc: "mixed versions in ipv4 mode", + isIPv6: false, + ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"}, + expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + }, + { + desc: "all ipv4 strings in ipv6 mode", + isIPv6: true, + ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + expectCorrects: nil, + expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + }, + { + desc: "all ipv6 strings in ipv6 mode", + isIPv6: true, + ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + expectIncorrects: nil, + }, + { + desc: "mixed versions in ipv6 mode", + isIPv6: true, + ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"}, + expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"}, + expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"}, + }, + } + + for _, tc := range testCases { + corrects, incorrects := FilterIncorrectIPVersion(tc.ipStrings, tc.isIPv6) + if !reflect.DeepEqual(tc.expectCorrects, corrects) { + t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects) + } + if !reflect.DeepEqual(tc.expectIncorrects, incorrects) { + t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects) + } + } +} + +func TestFilterIncorrectCIDRVersion(t *testing.T) { + testCases := []struct { + desc string + isIPv6 bool + cidrStrings []string + expectCorrects []string + expectIncorrects []string + }{ + { + desc: "all ipv4 strings in ipv4 mode", + isIPv6: false, + cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"}, + expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"}, + expectIncorrects: nil, + }, + { + desc: "all ipv6 strings in ipv4 mode", + isIPv6: false, + cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectCorrects: nil, + expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + }, + { + desc: "mixed versions in ipv4 mode", + isIPv6: false, + cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"}, + expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + }, + { + desc: "all ipv4 strings in ipv6 mode", + isIPv6: true, + cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"}, + expectCorrects: nil, + expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"}, + }, + { + desc: "all ipv6 strings in ipv6 mode", + isIPv6: true, + cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectIncorrects: nil, + }, + { + desc: "mixed versions in ipv6 mode", + isIPv6: true, + cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"}, + expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"}, + }, + } + + for _, tc := range testCases { + corrects, incorrects := FilterIncorrectCIDRVersion(tc.cidrStrings, tc.isIPv6) + if !reflect.DeepEqual(tc.expectCorrects, corrects) { + t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects) + } + if !reflect.DeepEqual(tc.expectIncorrects, incorrects) { + t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects) + } + } +}