Skip to content

Commit

Permalink
Add verification and unit test for InternalMasquerade Subnet
Browse files Browse the repository at this point in the history
Verify that the configured masquerade subnet/s are valid and do not
conflict with cluster/service networks and that they match the ip family
used in the cluser, and add unit tests to check these cases

Signed-off-by: Ben Pickard <bpickard@redhat.com>
  • Loading branch information
bpickard22 committed Dec 20, 2023
1 parent b424f7e commit b0cdb8a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 25 deletions.
5 changes: 3 additions & 2 deletions bindata/network/ovn-kubernetes/managed/004-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,17 @@ data:
v4-internal-masquerade-subnet="{{.V4InternalMasqueradeSubnet}}"
{{- end }}
{{- if (index . "V6InternalMasqueradeSubnet")}}
v4-internal-masquerade-subnet="{{.V6InternalMasqueradeSubnet}}"
v6-internal-masquerade-subnet="{{.V6InternalMasqueradeSubnet}}"
{{- end }}
{{- if .OVNHybridOverlayEnable }}


[logging]
libovsdblogfile=/var/log/ovnkube/libovsdb.log
logfile-maxsize=100
logfile-maxbackups=5
logfile-maxage=0

{{- if .OVNHybridOverlayEnable }}
[hybridoverlay]
enabled=true
{{- if .OVNHybridOverlayNetCIDR }}
Expand Down
3 changes: 1 addition & 2 deletions bindata/network/ovn-kubernetes/self-hosted/004-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ data:
v4-internal-masquerade-subnet="{{.V4InternalMasqueradeSubnet}}"
{{- end }}
{{- if (index . "V6InternalMasqueradeSubnet")}}
v4-internal-masquerade-subnet="{{.V6InternalMasqueradeSubnet}}"
v6-internal-masquerade-subnet="{{.V6InternalMasqueradeSubnet}}"
{{- end }}
{{- if .OVNHybridOverlayEnable }}

[logging]
libovsdblogfile=/var/log/ovnkube/libovsdb.log
Expand Down
78 changes: 68 additions & 10 deletions pkg/network/ovn_kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,17 @@ func renderOVNKubernetes(conf *operv1.NetworkSpec, bootstrapResult *bootstrap.Bo
data.Data["TokenAudience"] = os.Getenv("TOKEN_AUDIENCE")
data.Data["MTU"] = c.MTU
data.Data["RoutableMTU"] = nil
// v4 and v6 join subnet are used when the user wants to use the addresses that we reserve for the join subnet in ovn-k
//TODO: this field is being deprecated and will turn into c.GatewayConfig.IPv4/6.InternalJoinSubnet when we introduce the transit switch config into the api
data.Data["V4JoinSubnet"] = c.V4InternalSubnet
data.Data["V6JoinSubnet"] = c.V6InternalSubnet
data.Data["V4InternalMasqueradeSubnet"] = c.V4InternalMasqueradeSubnet
data.Data["V6InternalMasqueradeSubnet"] = c.V6InternalMasqueradeSubnet
//v4 and v6InternalMasqueradeSubnet are used when the user wants to use the addresses that we reserve in ovn-k for ip masquerading
if c.GatewayConfig != nil && c.GatewayConfig.IPv4.InternalMasqueradeSubnet != "" {
data.Data["V4InternalMasqueradeSubnet"] = c.GatewayConfig.IPv4.InternalMasqueradeSubnet
}
if c.GatewayConfig != nil && c.GatewayConfig.IPv6.InternalMasqueradeSubnet != "" {
data.Data["V6InternalMasqueradeSubnet"] = c.GatewayConfig.IPv6.InternalMasqueradeSubnet
}
data.Data["EnableUDPAggregation"] = !bootstrapResult.OVN.OVNKubernetesConfig.DisableUDPAggregation
data.Data["NETWORK_NODE_IDENTITY_ENABLE"] = bootstrapResult.Infra.NetworkNodeIdentityEnabled
data.Data["NodeIdentityCertDuration"] = OVN_NODE_IDENTITY_CERT_DURATION
Expand Down Expand Up @@ -820,41 +826,41 @@ func validateOVNKubernetes(conf *operv1.NetworkSpec) []error {
var err error
if oc.V4InternalSubnet != "" {
if !cnHasIPv4 {
out = append(out, errors.Errorf("v4InternalSubnet and ClusterNetwork must have matching IP families"))
out = append(out, errors.Errorf("v4InternalSubnet %s and ClusterNetwork must have matching IP families", oc.V4InternalSubnet))
}
_, v4Net, err = net.ParseCIDR(oc.V4InternalSubnet)
if err != nil {
out = append(out, errors.Errorf("v4InternalSubnet is invalid: %s", err))
}
if !isV4InternalSubnetLargeEnough(conf) {
out = append(out, errors.Errorf("v4InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork"))
out = append(out, errors.Errorf("v4InternalSubnet %s is not large enough for the maximum number of nodes which can be supported by ClusterNetwork", oc.V4InternalSubnet))
}
}
if oc.V6InternalSubnet != "" {
if !cnHasIPv6 {
out = append(out, errors.Errorf("v6InternalSubnet and ClusterNetwork must have matching IP families"))
out = append(out, errors.Errorf("v6InternalSubnet %s and ClusterNetwork must have matching IP families", oc.V6InternalSubnet))
}
_, v6Net, err = net.ParseCIDR(oc.V6InternalSubnet)
if err != nil {
out = append(out, errors.Errorf("v6InternalSubnet is invalid: %s", err))
}
if !isV6InternalSubnetLargeEnough(conf) {
out = append(out, errors.Errorf("v6InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork"))
out = append(out, errors.Errorf("v6InternalSubnet %s is not large enough for the maximum number of nodes which can be supported by ClusterNetwork", oc.V6InternalSubnet))
}
}
for _, cn := range conf.ClusterNetwork {
if utilnet.IsIPv6CIDRString(cn.CIDR) {
if oc.V6InternalSubnet != "" {
_, v6ClusterNet, _ := net.ParseCIDR(cn.CIDR)
if iputil.NetsOverlap(*v6Net, *v6ClusterNet) {
out = append(out, errors.Errorf("v6InternalSubnet overlaps with ClusterNetwork %s", cn.CIDR))
out = append(out, errors.Errorf("v6InternalSubnet %s overlaps with ClusterNetwork %s", oc.V6InternalSubnet, cn.CIDR))
}
}
} else {
if oc.V4InternalSubnet != "" {
_, v4ClusterNet, _ := net.ParseCIDR(cn.CIDR)
if iputil.NetsOverlap(*v4Net, *v4ClusterNet) {
out = append(out, errors.Errorf("v4InternalSubnet overlaps with ClusterNetwork %s", cn.CIDR))
out = append(out, errors.Errorf("v4InternalSubnet %s overlaps with ClusterNetwork %s", oc.V4InternalSubnet, cn.CIDR))
}
}
}
Expand All @@ -864,14 +870,66 @@ func validateOVNKubernetes(conf *operv1.NetworkSpec) []error {
if oc.V6InternalSubnet != "" {
_, v6ServiceNet, _ := net.ParseCIDR(sn)
if iputil.NetsOverlap(*v6Net, *v6ServiceNet) {
out = append(out, errors.Errorf("v6InternalSubnet overlaps with ServiceNetwork %s", sn))
out = append(out, errors.Errorf("v6InternalSubnet %s overlaps with ServiceNetwork %s", oc.V6InternalSubnet, sn))
}
}
} else {
if oc.V4InternalSubnet != "" {
_, v4ServiceNet, _ := net.ParseCIDR(sn)
if iputil.NetsOverlap(*v4Net, *v4ServiceNet) {
out = append(out, errors.Errorf("v4InternalSubnet overlaps with ServiceNetwork %s", sn))
out = append(out, errors.Errorf("v4InternalSubnet %s overlaps with ServiceNetwork %s", oc.V4InternalSubnet, sn))
}
}
}
}
if oc.GatewayConfig != nil {
if oc.GatewayConfig.IPv4.InternalMasqueradeSubnet != "" {
if !cnHasIPv4 {
out = append(out, errors.Errorf("v4InternalMasqueradeSubnet %s and ClusterNetwork must have matching IP families", oc.GatewayConfig.IPv4.InternalMasqueradeSubnet))
}
_, v4Net, err = net.ParseCIDR(oc.GatewayConfig.IPv4.InternalMasqueradeSubnet)
if err != nil {
out = append(out, errors.Errorf("v4InternalMasqueradeSubnet is invalid: %s", err))
}
for _, cn := range conf.ClusterNetwork {
if utilnet.IsIPv4CIDRString(cn.CIDR) {
_, v4ClusterNet, _ := net.ParseCIDR(cn.CIDR)
if iputil.NetsOverlap(*v4Net, *v4ClusterNet) {
out = append(out, errors.Errorf("v4InternalMasqueradeSubnet %s overlaps with ClusterNetwork %s", oc.GatewayConfig.IPv4.InternalMasqueradeSubnet, cn.CIDR))
}
}
}
for _, sn := range conf.ServiceNetwork {
if utilnet.IsIPv4CIDRString(sn) {
_, v4ServiceNet, _ := net.ParseCIDR(sn)
if iputil.NetsOverlap(*v4Net, *v4ServiceNet) {
out = append(out, errors.Errorf("v4InternalMasqueradeSubnet %s overlaps with ServiceNetwork %s", oc.GatewayConfig.IPv4.InternalMasqueradeSubnet, sn))
}
}
}
}
if oc.GatewayConfig.IPv6.InternalMasqueradeSubnet != "" {
if !cnHasIPv6 {
out = append(out, errors.Errorf("v6InternalMasqueradeSubnet %s and ClusterNetwork must have matching IP families", oc.GatewayConfig.IPv6.InternalMasqueradeSubnet))
}
_, v6Net, err = net.ParseCIDR(oc.GatewayConfig.IPv6.InternalMasqueradeSubnet)
if err != nil {
out = append(out, errors.Errorf("v6InternalMasqueradeSubnet is invalid: %s", err))
}
for _, cn := range conf.ClusterNetwork {
if utilnet.IsIPv6CIDRString(cn.CIDR) {
_, v6ClusterNet, _ := net.ParseCIDR(cn.CIDR)
if iputil.NetsOverlap(*v6Net, *v6ClusterNet) {
out = append(out, errors.Errorf("v6InternalMasqueradeSubnet %s overlaps with ClusterNetwork %s", oc.GatewayConfig.IPv6.InternalMasqueradeSubnet, cn.CIDR))
}
}
}
for _, sn := range conf.ServiceNetwork {
if utilnet.IsIPv6CIDRString(sn) {
_, v6ServiceNet, _ := net.ParseCIDR(sn)
if iputil.NetsOverlap(*v6Net, *v6ServiceNet) {
out = append(out, errors.Errorf("v6InternalMasqueradeSubnet %s overlaps with ServiceNetwork %s", oc.GatewayConfig.IPv6.InternalMasqueradeSubnet, sn))
}
}
}
}
Expand Down
84 changes: 73 additions & 11 deletions pkg/network/ovn_kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,53 @@ logfile-maxage=0`,
v4InternalSubnet: "100.99.0.0/16",
controlPlaneReplicaCount: 2,
},
{
desc: "custom masquerade subnet",
expected: `
[default]
mtu="1500"
cluster-subnets="10.128.0.0/15/23,10.0.0.0/14/24"
encap-port="8061"
enable-lflow-cache=true
lflow-cache-limit-kb=1048576
enable-udp-aggregation=true
[kubernetes]
service-cidrs="172.30.0.0/16"
ovn-config-namespace="openshift-ovn-kubernetes"
apiserver="https://testing.test:8443"
host-network-namespace="openshift-host-network"
platform-type="GCP"
healthz-bind-address="0.0.0.0:10256"
dns-service-namespace="openshift-dns"
dns-service-name="dns-default"
[ovnkubernetesfeature]
enable-egress-ip=true
enable-egress-firewall=true
enable-egress-qos=true
enable-egress-service=true
egressip-node-healthcheck-port=9107
enable-multi-network=true
enable-multi-external-gateway=true
[gateway]
mode=shared
nodeport=true
v4-internal-masquerade-subnet="100.98.0.0/16"
[logging]
libovsdblogfile=/var/log/ovnkube/libovsdb.log
logfile-maxsize=100
logfile-maxbackups=5
logfile-maxage=0`,
controlPlaneReplicaCount: 2,
gatewayConfig: &operv1.GatewayConfig{
IPv4: operv1.IPv4GatewayConfig{
InternalMasqueradeSubnet: "100.98.0.0/16",
},
},
},
{
desc: "HybridOverlay",
expected: `
Expand Down Expand Up @@ -783,8 +830,11 @@ logfile-maxage=0`,
if tc.hybridOverlayConfig != nil {
OVNKubeConfig.Spec.DefaultNetwork.OVNKubernetesConfig.HybridOverlayConfig = tc.hybridOverlayConfig
}
if tc.hybridOverlayConfig != nil {
if tc.gatewayConfig != nil {
OVNKubeConfig.Spec.DefaultNetwork.OVNKubernetesConfig.GatewayConfig = tc.gatewayConfig
if tc.gatewayConfig.IPv4.InternalMasqueradeSubnet != "" {
OVNKubeConfig.Spec.DefaultNetwork.OVNKubernetesConfig.GatewayConfig.IPv4.InternalMasqueradeSubnet = tc.gatewayConfig.IPv4.InternalMasqueradeSubnet
}
}
if tc.egressIPConfig != nil {
OVNKubeConfig.Spec.DefaultNetwork.OVNKubernetesConfig.EgressIPConfig = *tc.egressIPConfig
Expand Down Expand Up @@ -988,6 +1038,7 @@ func TestValidateOVNKubernetes(t *testing.T) {
crd := OVNKubernetesConfig.DeepCopy()
config := &crd.Spec
ovnConfig := config.DefaultNetwork.OVNKubernetesConfig
ovnConfig.GatewayConfig = &operv1.GatewayConfig{}

err := validateOVNKubernetes(config)
g.Expect(err).To(BeEmpty())
Expand All @@ -1009,15 +1060,21 @@ func TestValidateOVNKubernetes(t *testing.T) {
}

ovnConfig.V4InternalSubnet = "100.64.0.0/22"
errExpect("v4InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork")
errExpect("v4InternalSubnet 100.64.0.0/22 is not large enough for the maximum number of nodes which can be supported by ClusterNetwork")
ovnConfig.V4InternalSubnet = "100.64.0.0/21"
errNotExpect("v4InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork")
errNotExpect("v4InternalSubnet 100.64.0.0/21 is not large enough for the maximum number of nodes which can be supported by ClusterNetwork")
ovnConfig.V6InternalSubnet = "fd01::/48"
errExpect("v6InternalSubnet and ClusterNetwork must have matching IP families")
errExpect("v6InternalSubnet fd01::/48 and ClusterNetwork must have matching IP families")
ovnConfig.V4InternalSubnet = "10.128.0.0/16"
errExpect("v4InternalSubnet overlaps with ClusterNetwork 10.128.0.0/15")
errExpect("v4InternalSubnet 10.128.0.0/16 overlaps with ClusterNetwork 10.128.0.0/15")
ovnConfig.V4InternalSubnet = "172.30.0.0/18"
errExpect("v4InternalSubnet overlaps with ServiceNetwork 172.30.0.0/16")
errExpect("v4InternalSubnet 172.30.0.0/18 overlaps with ServiceNetwork 172.30.0.0/16")
ovnConfig.GatewayConfig.IPv4.InternalMasqueradeSubnet = "10.128.0.0/16"
errExpect("v4InternalMasqueradeSubnet 10.128.0.0/16 overlaps with ClusterNetwork 10.128.0.0/15")
ovnConfig.GatewayConfig.IPv6.InternalMasqueradeSubnet = "fd01::/48"
errExpect("v6InternalMasqueradeSubnet fd01::/48 and ClusterNetwork must have matching IP families")
ovnConfig.GatewayConfig.IPv4.InternalMasqueradeSubnet = "172.30.0.0/18"
errExpect("v4InternalMasqueradeSubnet 172.30.0.0/18 overlaps with ServiceNetwork 172.30.0.0/16")

// set mtu to insanity
ovnConfig.MTU = ptrToUint32(70000)
Expand All @@ -1031,15 +1088,20 @@ func TestValidateOVNKubernetes(t *testing.T) {
config.ClusterNetwork = []operv1.ClusterNetworkEntry{{
CIDR: "fd01::/48", HostPrefix: 64,
}}
errExpect("v4InternalSubnet and ClusterNetwork must have matching IP families")
errExpect("v4InternalSubnet 172.30.0.0/18 and ClusterNetwork must have matching IP families")
errExpect("v4InternalMasqueradeSubnet 172.30.0.0/18 and ClusterNetwork must have matching IP families")
ovnConfig.V6InternalSubnet = "fd01::/64"
errExpect("v6InternalSubnet overlaps with ClusterNetwork fd01::/48")
errExpect("v6InternalSubnet fd01::/64 overlaps with ClusterNetwork fd01::/48")
ovnConfig.V6InternalSubnet = "fd03::/112"
errExpect("v6InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork")
errExpect("v6InternalSubnet fd03::/112 is not large enough for the maximum number of nodes which can be supported by ClusterNetwork")
ovnConfig.V6InternalSubnet = "fd03::/111"
errNotExpect("v6InternalSubnet is no large enough for the maximum number of nodes which can be supported by ClusterNetwork")
errNotExpect("v6InternalSubnet fd03::/111 is not large enough for the maximum number of nodes which can be supported by ClusterNetwork")
ovnConfig.V6InternalSubnet = "fd02::/64"
errExpect("v6InternalSubnet overlaps with ServiceNetwork fd02::/112")
errExpect("v6InternalSubnet fd02::/64 overlaps with ServiceNetwork fd02::/112")
ovnConfig.GatewayConfig.IPv6.InternalMasqueradeSubnet = "fd01::/64"
errExpect("v6InternalMasqueradeSubnet fd01::/64 overlaps with ClusterNetwork fd01::/48")
ovnConfig.GatewayConfig.IPv6.InternalMasqueradeSubnet = "fd02::/64"
errExpect("v6InternalMasqueradeSubnet fd02::/64 overlaps with ServiceNetwork fd02::/112")

// invalid ipv6 mtu
ovnConfig.MTU = ptrToUint32(576)
Expand Down

0 comments on commit b0cdb8a

Please sign in to comment.