Skip to content

Commit 8125090

Browse files
author
OpenShift Bot
authored
Merge pull request #13508 from danwinship/cidr-validation
Merged by openshift-bot
2 parents 42c3bfa + a0822dd commit 8125090

File tree

6 files changed

+128
-19
lines changed

6 files changed

+128
-19
lines changed

pkg/sdn/api/validation/validation.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ import (
99
"k8s.io/kubernetes/pkg/util/validation/field"
1010

1111
sdnapi "github.com/openshift/origin/pkg/sdn/api"
12+
"github.com/openshift/origin/pkg/util/netutils"
1213
)
1314

1415
// ValidateClusterNetwork tests if required fields in the ClusterNetwork are set.
1516
func ValidateClusterNetwork(clusterNet *sdnapi.ClusterNetwork) field.ErrorList {
1617
allErrs := validation.ValidateObjectMeta(&clusterNet.ObjectMeta, false, path.ValidatePathSegmentName, field.NewPath("metadata"))
1718

18-
clusterIP, clusterIPNet, err := net.ParseCIDR(clusterNet.Network)
19+
clusterIPNet, err := netutils.ParseCIDRMask(clusterNet.Network)
1920
if err != nil {
2021
allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, err.Error()))
2122
} else {
@@ -25,36 +26,36 @@ func ValidateClusterNetwork(clusterNet *sdnapi.ClusterNetwork) field.ErrorList {
2526
}
2627
}
2728

28-
serviceIP, serviceIPNet, err := net.ParseCIDR(clusterNet.ServiceNetwork)
29+
serviceIPNet, err := netutils.ParseCIDRMask(clusterNet.ServiceNetwork)
2930
if err != nil {
3031
allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, err.Error()))
3132
}
3233

33-
if (clusterIPNet != nil) && (serviceIP != nil) && clusterIPNet.Contains(serviceIP) {
34+
if (clusterIPNet != nil) && (serviceIPNet != nil) && clusterIPNet.Contains(serviceIPNet.IP) {
3435
allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, "service network overlaps with cluster network"))
3536
}
36-
if (serviceIPNet != nil) && (clusterIP != nil) && serviceIPNet.Contains(clusterIP) {
37+
if (serviceIPNet != nil) && (clusterIPNet != nil) && serviceIPNet.Contains(clusterIPNet.IP) {
3738
allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, "cluster network overlaps with service network"))
3839
}
3940

4041
return allErrs
4142
}
4243

4344
func validateNewNetwork(obj *sdnapi.ClusterNetwork, old *sdnapi.ClusterNetwork) *field.Error {
44-
oldBase, oldNet, err := net.ParseCIDR(old.Network)
45+
oldNet, err := netutils.ParseCIDRMask(old.Network)
4546
if err != nil {
4647
// Shouldn't happen, but if the existing value is invalid, then any change should be an improvement...
4748
return nil
4849
}
4950
oldSize, _ := oldNet.Mask.Size()
50-
_, newNet, err := net.ParseCIDR(obj.Network)
51+
newNet, err := netutils.ParseCIDRMask(obj.Network)
5152
if err != nil {
5253
return field.Invalid(field.NewPath("network"), obj.Network, err.Error())
5354
}
5455
newSize, _ := newNet.Mask.Size()
5556
// oldSize/newSize is, eg the "16" in "10.1.0.0/16", so "newSize < oldSize" means
5657
// the new network is larger
57-
if newSize < oldSize && newNet.Contains(oldBase) {
58+
if newSize < oldSize && newNet.Contains(oldNet.IP) {
5859
return nil
5960
} else {
6061
return field.Invalid(field.NewPath("network"), obj.Network, "cannot change the cluster's network CIDR to a value that does not include the existing network.")
@@ -96,7 +97,7 @@ func ValidateHostSubnet(hs *sdnapi.HostSubnet) field.ErrorList {
9697
allErrs = append(allErrs, field.Invalid(field.NewPath("subnet"), hs.Subnet, "field cannot be empty"))
9798
}
9899
} else {
99-
_, _, err := net.ParseCIDR(hs.Subnet)
100+
_, err := netutils.ParseCIDRMask(hs.Subnet)
100101
if err != nil {
101102
allErrs = append(allErrs, field.Invalid(field.NewPath("subnet"), hs.Subnet, err.Error()))
102103
}
@@ -147,7 +148,7 @@ func ValidateEgressNetworkPolicy(policy *sdnapi.EgressNetworkPolicy) field.Error
147148
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("egress").Index(i).Child("type"), rule.Type, "invalid policy type"))
148149
}
149150

150-
_, _, err := net.ParseCIDR(rule.To.CIDRSelector)
151+
_, err := netutils.ParseCIDRMask(rule.To.CIDRSelector)
151152
if err != nil {
152153
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("egress").Index(i).Child("to"), rule.To.CIDRSelector, err.Error()))
153154
}

pkg/sdn/api/validation/validation_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ func TestValidateClusterNetwork(t *testing.T) {
3636
},
3737
expectedErrors: 1,
3838
},
39+
{
40+
name: "Bad network CIDR",
41+
cn: &api.ClusterNetwork{
42+
ObjectMeta: kapi.ObjectMeta{Name: "any"},
43+
Network: "10.20.0.1/16",
44+
HostSubnetLength: 8,
45+
ServiceNetwork: "172.30.0.0/16",
46+
},
47+
expectedErrors: 1,
48+
},
3949
{
4050
name: "Invalid subnet length",
4151
cn: &api.ClusterNetwork{
@@ -56,6 +66,16 @@ func TestValidateClusterNetwork(t *testing.T) {
5666
},
5767
expectedErrors: 1,
5868
},
69+
{
70+
name: "Bad service network CIDR",
71+
cn: &api.ClusterNetwork{
72+
ObjectMeta: kapi.ObjectMeta{Name: "any"},
73+
Network: "10.20.0.0/16",
74+
HostSubnetLength: 8,
75+
ServiceNetwork: "172.30.1.0/16",
76+
},
77+
expectedErrors: 1,
78+
},
5979
{
6080
name: "Service network overlaps with cluster network",
6181
cn: &api.ClusterNetwork{
@@ -129,6 +149,18 @@ func TestValidateHostSubnet(t *testing.T) {
129149
},
130150
expectedErrors: 1,
131151
},
152+
{
153+
name: "Malformed subnet CIDR",
154+
hs: &api.HostSubnet{
155+
ObjectMeta: kapi.ObjectMeta{
156+
Name: "abc.def.com",
157+
},
158+
Host: "abc.def.com",
159+
HostIP: "10.20.30.40",
160+
Subnet: "8.8.0.1/24",
161+
},
162+
expectedErrors: 1,
163+
},
132164
}
133165

134166
for _, tc := range tests {

pkg/sdn/plugin/common.go

+13-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
osclient "github.com/openshift/origin/pkg/client"
1212
osapi "github.com/openshift/origin/pkg/sdn/api"
13+
"github.com/openshift/origin/pkg/util/netutils"
1314

1415
kapi "k8s.io/kubernetes/pkg/api"
1516
"k8s.io/kubernetes/pkg/apis/extensions"
@@ -39,13 +40,21 @@ type NetworkInfo struct {
3940
}
4041

4142
func parseNetworkInfo(clusterNetwork string, serviceNetwork string) (*NetworkInfo, error) {
42-
_, cn, err := net.ParseCIDR(clusterNetwork)
43+
cn, err := netutils.ParseCIDRMask(clusterNetwork)
4344
if err != nil {
44-
return nil, fmt.Errorf("failed to parse ClusterNetwork CIDR %s: %v", clusterNetwork, err)
45+
_, cn, err := net.ParseCIDR(clusterNetwork)
46+
if err != nil {
47+
return nil, fmt.Errorf("failed to parse ClusterNetwork CIDR %s: %v", clusterNetwork, err)
48+
}
49+
glog.Errorf("Configured clusterNetworkCIDR value %q is invalid; treating it as %q", clusterNetwork, cn.String())
4550
}
46-
_, sn, err := net.ParseCIDR(serviceNetwork)
51+
sn, err := netutils.ParseCIDRMask(serviceNetwork)
4752
if err != nil {
48-
return nil, fmt.Errorf("failed to parse ServiceNetwork CIDR %s: %v", serviceNetwork, err)
53+
_, sn, err := net.ParseCIDR(serviceNetwork)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to parse ServiceNetwork CIDR %s: %v", serviceNetwork, err)
56+
}
57+
glog.Errorf("Configured serviceNetworkCIDR value %q is invalid; treating it as %q", serviceNetwork, sn.String())
4958
}
5059

5160
return &NetworkInfo{

pkg/util/netutils/common.go

+19
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,22 @@ func GetNodeIP(nodeName string) (string, error) {
9494
}
9595
return ip.String(), nil
9696
}
97+
98+
// ParseCIDRMask parses a CIDR string and ensures that it has no bits set beyond the
99+
// network mask length. Use this when the input is supposed to be either a description of
100+
// a subnet (eg, "192.168.1.0/24", meaning "192.168.1.0 to 192.168.1.255"), or a mask for
101+
// matching against (eg, "192.168.1.15/32", meaning "must match all 32 bits of the address
102+
// "192.168.1.15"). Use net.ParseCIDR() when the input is a host address that also
103+
// describes the subnet that it is on (eg, "192.168.1.15/24", meaning "the address
104+
// 192.168.1.15 on the network 192.168.1.0/24").
105+
func ParseCIDRMask(cidr string) (*net.IPNet, error) {
106+
ip, net, err := net.ParseCIDR(cidr)
107+
if err != nil {
108+
return nil, err
109+
}
110+
if !ip.Equal(net.IP) {
111+
maskLen, addrLen := net.Mask.Size()
112+
return nil, fmt.Errorf("CIDR network specification %q is not in canonical form (should be %s/%d or %s/%d?)", cidr, ip.Mask(net.Mask).String(), maskLen, ip.String(), addrLen)
113+
}
114+
return net, nil
115+
}

pkg/util/netutils/common_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package netutils
22

33
import (
44
"net"
5+
"strings"
56
"testing"
67
)
78

@@ -40,3 +41,50 @@ func TestGenerateGateway(t *testing.T) {
4041
t.Fatalf("Did not get expected gateway IP Address (gatewayIP=%s)", gatewayIP.String())
4142
}
4243
}
44+
45+
func TestParseCIDRMask(t *testing.T) {
46+
tests := []struct {
47+
cidr string
48+
fixedShort string
49+
fixedLong string
50+
}{
51+
{
52+
cidr: "192.168.0.0/16",
53+
},
54+
{
55+
cidr: "192.168.1.0/24",
56+
},
57+
{
58+
cidr: "192.168.1.1/32",
59+
},
60+
{
61+
cidr: "192.168.1.0/16",
62+
fixedShort: "192.168.0.0/16",
63+
fixedLong: "192.168.1.0/32",
64+
},
65+
{
66+
cidr: "192.168.1.1/24",
67+
fixedShort: "192.168.1.0/24",
68+
fixedLong: "192.168.1.1/32",
69+
},
70+
}
71+
72+
for _, test := range tests {
73+
_, err := ParseCIDRMask(test.cidr)
74+
if test.fixedShort == "" && test.fixedLong == "" {
75+
if err != nil {
76+
t.Fatalf("unexpected error parsing CIDR mask %q: %v", test.cidr, err)
77+
}
78+
} else {
79+
if err == nil {
80+
t.Fatalf("unexpected lack of error parsing CIDR mask %q", test.cidr)
81+
}
82+
if !strings.Contains(err.Error(), test.fixedShort) {
83+
t.Fatalf("error does not contain expected string %q: %v", test.fixedShort, err)
84+
}
85+
if !strings.Contains(err.Error(), test.fixedLong) {
86+
t.Fatalf("error does not contain expected string %q: %v", test.fixedLong, err)
87+
}
88+
}
89+
}
90+
}

test/integration/etcd_storage_path_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -268,29 +268,29 @@ var etcdStorageData = map[unversioned.GroupVersionResource]struct {
268268
expectedGVK: gvkP("", "v1", "NetNamespace"), // expect the legacy group to be persisted
269269
},
270270
gvr("", "v1", "hostsubnets"): {
271-
stub: `{"host": "hostname", "hostIP": "192.168.1.1", "metadata": {"name": "hostname"}, "subnet": "192.168.1.1/24"}`,
271+
stub: `{"host": "hostname", "hostIP": "192.168.1.1", "metadata": {"name": "hostname"}, "subnet": "192.168.1.0/24"}`,
272272
expectedEtcdPath: "openshift.io/registry/sdnsubnets/hostname",
273273
},
274274
gvr("network.openshift.io", "v1", "hostsubnets"): {
275-
stub: `{"host": "hostnameg", "hostIP": "192.168.1.1", "metadata": {"name": "hostnameg"}, "subnet": "192.168.1.1/24"}`,
275+
stub: `{"host": "hostnameg", "hostIP": "192.168.1.1", "metadata": {"name": "hostnameg"}, "subnet": "192.168.1.0/24"}`,
276276
expectedEtcdPath: "openshift.io/registry/sdnsubnets/hostnameg",
277277
expectedGVK: gvkP("", "v1", "HostSubnet"), // expect the legacy group to be persisted
278278
},
279279
gvr("", "v1", "clusternetworks"): {
280-
stub: `{"metadata": {"name": "cn1"}, "network": "192.168.0.1/24", "serviceNetwork": "192.168.1.1/24"}`,
280+
stub: `{"metadata": {"name": "cn1"}, "network": "192.168.0.0/24", "serviceNetwork": "192.168.1.0/24"}`,
281281
expectedEtcdPath: "openshift.io/registry/sdnnetworks/cn1",
282282
},
283283
gvr("network.openshift.io", "v1", "clusternetworks"): {
284-
stub: `{"metadata": {"name": "cn1g"}, "network": "192.168.0.1/24", "serviceNetwork": "192.168.1.1/24"}`,
284+
stub: `{"metadata": {"name": "cn1g"}, "network": "192.168.0.0/24", "serviceNetwork": "192.168.1.0/24"}`,
285285
expectedEtcdPath: "openshift.io/registry/sdnnetworks/cn1g",
286286
expectedGVK: gvkP("", "v1", "ClusterNetwork"), // expect the legacy group to be persisted
287287
},
288288
gvr("", "v1", "egressnetworkpolicies"): {
289-
stub: `{"metadata": {"name": "enp1"}, "spec": {"egress": [{"to": {"cidrSelector": "192.168.1.1/24"}, "type": "Allow"}]}}`,
289+
stub: `{"metadata": {"name": "enp1"}, "spec": {"egress": [{"to": {"cidrSelector": "192.168.1.0/24"}, "type": "Allow"}]}}`,
290290
expectedEtcdPath: "openshift.io/registry/egressnetworkpolicy/etcdstoragepathtestnamespace/enp1",
291291
},
292292
gvr("network.openshift.io", "v1", "egressnetworkpolicies"): {
293-
stub: `{"metadata": {"name": "enp1g"}, "spec": {"egress": [{"to": {"cidrSelector": "192.168.1.1/24"}, "type": "Allow"}]}}`,
293+
stub: `{"metadata": {"name": "enp1g"}, "spec": {"egress": [{"to": {"cidrSelector": "192.168.1.0/24"}, "type": "Allow"}]}}`,
294294
expectedEtcdPath: "openshift.io/registry/egressnetworkpolicy/etcdstoragepathtestnamespace/enp1g",
295295
expectedGVK: gvkP("", "v1", "EgressNetworkPolicy"), // expect the legacy group to be persisted
296296
},

0 commit comments

Comments
 (0)