-
Notifications
You must be signed in to change notification settings - Fork 494
OCPBUGS-84303: fix(api): add IPv6 OVN join subnet config to prevent dual-stack routing collision #8421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OCPBUGS-84303: fix(api): add IPv6 OVN join subnet config to prevent dual-stack routing collision #8421
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,20 @@ import ( | |
| operatorv1 "github.com/openshift/api/operator/v1" | ||
| ) | ||
|
|
||
| const ( | ||
| // KubevirtDefaultV6InternalJoinSubnet is the default IPv6 OVN join subnet | ||
| // for KubeVirt hosted clusters. The upstream OVN-Kubernetes default is fd98::/64, | ||
| // but KubeVirt guests use fd99::/64 to avoid collisions with the management | ||
| // cluster's join subnet when both run OVN-Kubernetes. | ||
| KubevirtDefaultV6InternalJoinSubnet = "fd99::/64" | ||
|
|
||
| // KubevirtDefaultV4InternalSubnet is the default IPv4 OVN internal subnet | ||
| // for KubeVirt hosted clusters. The upstream OVN-Kubernetes default gateway | ||
| // router LRP CIDR is 100.64.0.0/16 and the default UDNs is 100.65.0.0/16. | ||
| // KubeVirt guests use 100.66.0.0/16 to avoid collisions with the management cluster. | ||
| KubevirtDefaultV4InternalSubnet = "100.66.0.0/16" | ||
| ) | ||
|
|
||
| // +kubebuilder:validation:Enum="";Normal;Debug;Trace;TraceAll | ||
| type LogLevel string | ||
|
|
||
|
|
@@ -38,6 +52,7 @@ type ClusterVersionOperatorSpec struct { | |
| OperatorLogLevel LogLevel `json:"operatorLogLevel,omitempty"` | ||
| } | ||
|
|
||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.ovnKubernetesConfig) || has(self.ovnKubernetesConfig)", message="ovnKubernetesConfig is immutable once set and cannot be removed" | ||
| type ClusterNetworkOperatorSpec struct { | ||
| // disableMultiNetwork when set to true disables the Multus CNI plugin and related components | ||
| // in the hosted cluster. This prevents the installation of multus daemon sets in the | ||
|
|
@@ -62,7 +77,11 @@ type ClusterNetworkOperatorSpec struct { | |
| // OVNKubernetesConfig contains OVN-Kubernetes specific configuration options. | ||
| // https://github.com/openshift/api/blob/6d3c4e25a8d3aeb57ad61649d80c38cbd27d1cc8/operator/v1/types_network.go#L400-L471 | ||
| // +kubebuilder:validation:XValidation:rule="!has(self.ipv4) || !has(self.ipv4.internalJoinSubnet) || !has(self.ipv4.internalTransitSwitchSubnet) || self.ipv4.internalJoinSubnet != self.ipv4.internalTransitSwitchSubnet", message="internalJoinSubnet and internalTransitSwitchSubnet must not be the same" | ||
| // +kubebuilder:validation:XValidation:rule="!has(self.ipv6) || !has(self.ipv6.internalJoinSubnet) || !has(self.ipv6.internalTransitSwitchSubnet) || self.ipv6.internalJoinSubnet != self.ipv6.internalTransitSwitchSubnet", message="ipv6 internalJoinSubnet and internalTransitSwitchSubnet must not be the same" | ||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.mtu) || has(self.mtu)",message="mtu is immutable once set and cannot be removed" | ||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || has(self.ipv6)", message="ipv6 is immutable once set and cannot be removed" | ||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalJoinSubnet) || (has(self.ipv6) && has(self.ipv6.internalJoinSubnet))", message="ipv6.internalJoinSubnet cannot be removed once set" | ||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalTransitSwitchSubnet) || (has(self.ipv6) && has(self.ipv6.internalTransitSwitchSubnet))", message="ipv6.internalTransitSwitchSubnet cannot be removed once set" | ||
| // +kubebuilder:validation:MinProperties=1 | ||
| type OVNKubernetesConfig struct { | ||
| // ipv4 allows users to configure IP settings for IPv4 connections. When omitted, | ||
|
|
@@ -71,6 +90,15 @@ type OVNKubernetesConfig struct { | |
| // +optional | ||
| IPv4 *OVNIPv4Config `json:"ipv4,omitempty"` | ||
|
|
||
| // ipv6 allows users to configure IP settings for IPv6 connections. When omitted, | ||
| // this means no opinions and the default configuration is used. Check individual | ||
| // fields within ipv6 for details of default values. | ||
| // For KubeVirt hosted clusters using dual-stack networking, it is recommended to | ||
| // set ipv6.internalJoinSubnet to a value different from the management cluster's | ||
| // join subnet (default fd98::/64) to avoid IPv6 routing conflicts. | ||
| // +optional | ||
| IPv6 OVNIPv6Config `json:"ipv6,omitzero,omitempty"` | ||
|
|
||
| // mtu is the MTU to use for the tunnel interface on hosted cluster nodes. | ||
| // This must be 100 bytes smaller than the uplink MTU. | ||
| // When unset, the cluster-network-operator will determine the MTU automatically | ||
|
|
@@ -126,6 +154,52 @@ type OVNIPv4Config struct { | |
| InternalJoinSubnet string `json:"internalJoinSubnet,omitempty"` | ||
| } | ||
|
|
||
| // OVNIPv6Config contains IPv6-specific configuration options for OVN-Kubernetes. | ||
| // https://github.com/openshift/api/blob/6d3c4e25a8d3aeb57ad61649d80c38cbd27d1cc8/operator/v1/types_network.go#L541-L570 | ||
| // +kubebuilder:validation:MinProperties=1 | ||
| type OVNIPv6Config struct { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per our
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are no existing round-trip serialization test patterns in this repo's api/ module. The
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New optional fields that are correctly omitted (as this one is) will round trip successfully. |
||
| // internalTransitSwitchSubnet is a v6 subnet in IPv6 CIDR format used internally | ||
| // by OVN-Kubernetes for the distributed transit switch in the OVN Interconnect | ||
| // architecture that connects the cluster routers on each node together to enable | ||
| // east west traffic. The subnet chosen should not overlap with other networks | ||
| // specified for OVN-Kubernetes as well as other networks used on the host. | ||
| // When omitted, this means no opinion and the platform is left to choose a reasonable | ||
| // default which is subject to change over time. | ||
| // The current default subnet is fd97::/64. | ||
| // The subnet must be large enough to accommodate one IP per node in your cluster. | ||
| // The value must be a valid IPv6 CIDR (e.g. fd97::/64). IPv4 addresses, | ||
| // IPv4-mapped IPv6 addresses, and dual-stack addresses are not permitted. | ||
| // The prefix length must be in the range /0 to /125 inclusive. | ||
| // This field is immutable once set. | ||
| // +kubebuilder:validation:MaxLength=48 | ||
| // +kubebuilder:validation:MinLength=3 | ||
| // +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).ip().family() == 6", message="Subnet must be in valid IPv6 CIDR format (e.g., fd97::/64)" | ||
| // +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).prefixLength() <= 125", message="subnet must be in the range /0 to /125 inclusive" | ||
|
everettraven marked this conversation as resolved.
Comment on lines
+176
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General note for any other reviewers - the CIDR CEL library was introduced in Kubernetes 1.30. From what I recall being told, we are look for N-4 compatibility from the kube version skew, so with 4.22 being based on 1.35 that means the oldest kube version we would end up running on would be 1.31. This should be OK. |
||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="internalTransitSwitchSubnet is immutable" | ||
| // +optional | ||
| InternalTransitSwitchSubnet string `json:"internalTransitSwitchSubnet,omitempty"` | ||
| // internalJoinSubnet is a v6 subnet used internally by ovn-kubernetes in case the | ||
| // default one is being already used by something else. It must not overlap with | ||
| // any other subnet being used by OpenShift or by the node network. The size of the | ||
| // subnet must be larger than the number of nodes. | ||
| // The current default value is fd98::/64. | ||
| // For KubeVirt hosted clusters, if this field is not set, HyperShift will | ||
| // automatically use fd99::/64 to avoid collisions with the management cluster's | ||
| // default join subnet (fd98::/64). | ||
| // The subnet must be large enough to accommodate one IP per node in your cluster. | ||
| // The value must be a valid IPv6 CIDR (e.g. fd98::/64). IPv4 addresses, | ||
| // IPv4-mapped IPv6 addresses, and dual-stack addresses are not permitted. | ||
| // The prefix length must be in the range /0 to /125 inclusive. | ||
| // This field is immutable once set. | ||
| // +kubebuilder:validation:MaxLength=48 | ||
| // +kubebuilder:validation:MinLength=3 | ||
| // +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).ip().family() == 6", message="Subnet must be in valid IPv6 CIDR format (e.g., fd98::/64)" | ||
| // +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).prefixLength() <= 125", message="subnet must be in the range /0 to /125 inclusive" | ||
|
everettraven marked this conversation as resolved.
|
||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="internalJoinSubnet is immutable" | ||
| // +optional | ||
| InternalJoinSubnet string `json:"internalJoinSubnet,omitempty"` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not for this PR, but changing the OVN join or transit subnet on a running cluster would break networking pretty badly. Might be worth a follow-up to add
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we know ahead of time that making a change to the networking configuration on a running cluster would be bad, let's go ahead and prevent it where we can now. Just a note that immutability rules must be done on the closest parent field that is required, otherwise the field isn't actually immutable because it can be removed by removing an optional parent (or the field itself) in the chain and re-applying with a different value.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, added three layers of protection:
|
||
| } | ||
|
|
||
| // IngressOperatorSpec is the specification of the desired behavior of the Ingress Operator. | ||
| type IngressOperatorSpec struct { | ||
| // endpointPublishingStrategy is used to publish the default ingress controller endpoints. | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these immutability rules, what happens if the parent
ovnKubernetesConfigfield is set, removed, and re-applied with new values?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right, good catch. if we remove the entire
ovnKubernetesConfigfield, then re-apply with different values, the immutability rules could be bypassed.to mitigate that, i'm adding a validation rule of "cannot be removed once set" on
ClusterNetworkOperatorSpecforovnKubernetesConfig.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It still looks like you could just remove the
ClusterNetworkOperatorSpecfield based on:hypershift/api/hypershift/v1beta1/hostedcluster_types.go
Lines 2372 to 2375 in f76be88
If you want true immutability you need to set the rule all the way up to the first required field, which looks like it means on the root
HostedClustertype because evenspecis optional:hypershift/api/hypershift/v1beta1/hostedcluster_types.go
Lines 2411 to 2413 in f76be88
I'd make sure you pay particular attention to how immutability applies as you go up the chain of fields to make sure you don't make something that is OK to mutate immutable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. how do you suggest to proceed? should I modify the validation rules of the higher fields in the chain, up to the root? I feel it's beyond the scope of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong preference. If this significantly increases the scope of this work to achieve this, I'm OK with best effort immutability for now with a more concrete immutability story for the near-ish future.