diff --git a/api/core/v1alpha1/evpninstance_types.go b/api/core/v1alpha1/evpninstance_types.go index 6c055c44a..50bdeec6c 100644 --- a/api/core/v1alpha1/evpninstance_types.go +++ b/api/core/v1alpha1/evpninstance_types.go @@ -85,6 +85,11 @@ type EVPNInstanceSpec struct { // +optional // +kubebuilder:validation:XValidation:rule="self.name == oldSelf.name",message="VRFRef is immutable" VRFRef *LocalObjectReference `json:"vrfRef,omitempty"` + + // SuppressARP overrides the NVE-level ARP suppression setting for this VNI. + // When unset, the NVE-level SuppressARP setting takes precedence. + // +optional + SuppressARP *bool `json:"suppressARP,omitempty"` } // EVPNInstanceType defines the type of EVPN instance. diff --git a/api/core/v1alpha1/zz_generated.deepcopy.go b/api/core/v1alpha1/zz_generated.deepcopy.go index 5e0a83e3a..999db7845 100644 --- a/api/core/v1alpha1/zz_generated.deepcopy.go +++ b/api/core/v1alpha1/zz_generated.deepcopy.go @@ -2049,6 +2049,11 @@ func (in *EVPNInstanceSpec) DeepCopyInto(out *EVPNInstanceSpec) { *out = new(LocalObjectReference) **out = **in } + if in.SuppressARP != nil { + in, out := &in.SuppressARP, &out.SuppressARP + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EVPNInstanceSpec. diff --git a/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml index c9b00bfb1..f60ae8062 100644 --- a/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml +++ b/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml @@ -168,6 +168,11 @@ spec: x-kubernetes-list-map-keys: - value x-kubernetes-list-type: map + suppressARP: + description: |- + SuppressARP overrides the NVE-level ARP suppression setting for this VNI. + When unset, the NVE-level SuppressARP setting takes precedence. + type: boolean type: description: |- Type specifies the EVPN instance type. diff --git a/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml b/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml index b407a4b2f..c9ac37195 100644 --- a/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml +++ b/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml @@ -165,6 +165,11 @@ spec: x-kubernetes-list-map-keys: - value x-kubernetes-list-type: map + suppressARP: + description: |- + SuppressARP overrides the NVE-level ARP suppression setting for this VNI. + When unset, the NVE-level SuppressARP setting takes precedence. + type: boolean type: description: |- Type specifies the EVPN instance type. diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md index e7664c675..8d5a46bdf 100644 --- a/docs/api-reference/index.md +++ b/docs/api-reference/index.md @@ -1943,6 +1943,7 @@ _Appears in:_ | `routeTargets` _[EVPNRouteTarget](#evpnroutetarget) array_ | RouteTargets is the list of route targets for the EVI. | | MinItems: 1
Optional: \{\}
| | `vlanRef` _[LocalObjectReference](#localobjectreference)_ | VLANRef is a reference to a VLAN resource for which this EVPNInstance builds the MAC-VRF.
This field is only applicable when Type is Bridged (L2VNI).
The VLAN resource must exist in the same namespace.
Immutable. | | Optional: \{\}
| | `vrfRef` _[LocalObjectReference](#localobjectreference)_ | VRFRef is a reference to a VRF resource for which this EVPNInstance provides the L3VNI.
This field is only applicable when Type is Routed (L3VNI).
The VRF resource must exist in the same namespace.
Immutable. | | Optional: \{\}
| +| `suppressARP` _boolean_ | SuppressARP overrides the NVE-level ARP suppression setting for this VNI.
When unset, the NVE-level SuppressARP setting takes precedence. | | Optional: \{\}
| #### EVPNInstanceStatus diff --git a/internal/provider/cisco/nxos/nve.go b/internal/provider/cisco/nxos/nve.go index a1272c1ba..d920e6a15 100644 --- a/internal/provider/cisco/nxos/nve.go +++ b/internal/provider/cisco/nxos/nve.go @@ -64,10 +64,17 @@ func (n *NVE) XPath() string { return "System/eps-items/epId-items/Ep-list[epId=1]" } +const ( + suppressARPOff = "off" + suppressARPEnabled = "enabled" + suppressARPDisabled = "disabled" +) + type VNI struct { AssociateVrfFlag bool `json:"associateVrfFlag"` McastGroup Option[string] `json:"mcastGroup"` Vni int32 `json:"vni"` + SuppressARP string `json:"suppressARP"` } func (*VNI) IsListItem() {} diff --git a/internal/provider/cisco/nxos/nve_test.go b/internal/provider/cisco/nxos/nve_test.go index 30667cb71..b0711ab2b 100644 --- a/internal/provider/cisco/nxos/nve_test.go +++ b/internal/provider/cisco/nxos/nve_test.go @@ -18,10 +18,23 @@ func init() { Register("nve", nve) vni := &VNI{ - Vni: 100010, - McastGroup: NewOption("239.1.1.100"), + Vni: 100010, + McastGroup: NewOption("239.1.1.100"), + SuppressARP: suppressARPOff, } Register("vni", vni) + + vniSuppressARPTrue := &VNI{ + Vni: 100011, + SuppressARP: suppressARPEnabled, + } + Register("vni_suppress_arp_true", vniSuppressARPTrue) + + vniSuppressARPFalse := &VNI{ + Vni: 100012, + SuppressARP: suppressARPDisabled, + } + Register("vni_suppress_arp_false", vniSuppressARPFalse) nveInfraVLANs := &NVEInfraVLANs{ InfraVLANList: []*NVEInfraVLAN{ {ID: 4052}, diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go index 697eec851..501391142 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -1117,6 +1117,14 @@ func (p *Provider) EnsureEVPNInstance(ctx context.Context, req *provider.EVPNIns if req.EVPNInstance.Spec.MulticastGroupAddress != "" { vni.McastGroup = NewOption(req.EVPNInstance.Spec.MulticastGroupAddress) } + switch { + case req.EVPNInstance.Spec.SuppressARP == nil: + vni.SuppressARP = suppressARPOff + case *req.EVPNInstance.Spec.SuppressARP: + vni.SuppressARP = suppressARPEnabled + default: + vni.SuppressARP = suppressARPDisabled + } sb.Update(vni) switch req.EVPNInstance.Spec.Type { diff --git a/internal/provider/cisco/nxos/testdata/vni.json b/internal/provider/cisco/nxos/testdata/vni.json index 8f4e8cfe0..b8f008234 100644 --- a/internal/provider/cisco/nxos/testdata/vni.json +++ b/internal/provider/cisco/nxos/testdata/vni.json @@ -10,7 +10,8 @@ { "associateVrfFlag": false, "mcastGroup": "239.1.1.100", - "vni": 100010 + "vni": 100010, + "suppressARP": "off" } ] } diff --git a/internal/provider/cisco/nxos/testdata/vni_suppress_arp_false.json b/internal/provider/cisco/nxos/testdata/vni_suppress_arp_false.json new file mode 100644 index 000000000..227624317 --- /dev/null +++ b/internal/provider/cisco/nxos/testdata/vni_suppress_arp_false.json @@ -0,0 +1,23 @@ +{ + "eps-items": { + "epId-items": { + "Ep-list": [ + { + "epId": "1", + "nws-items": { + "vni-items": { + "Nw-list": [ + { + "associateVrfFlag": false, + "mcastGroup": "DME_UNSET_PROPERTY_MARKER", + "vni": 100012, + "suppressARP": "disabled" + } + ] + } + } + } + ] + } + } +} diff --git a/internal/provider/cisco/nxos/testdata/vni_suppress_arp_true.json b/internal/provider/cisco/nxos/testdata/vni_suppress_arp_true.json new file mode 100644 index 000000000..3ee15787d --- /dev/null +++ b/internal/provider/cisco/nxos/testdata/vni_suppress_arp_true.json @@ -0,0 +1,23 @@ +{ + "eps-items": { + "epId-items": { + "Ep-list": [ + { + "epId": "1", + "nws-items": { + "vni-items": { + "Nw-list": [ + { + "associateVrfFlag": false, + "mcastGroup": "DME_UNSET_PROPERTY_MARKER", + "vni": 100011, + "suppressARP": "enabled" + } + ] + } + } + } + ] + } + } +} diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar b/test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar index fd0fae846..1dd37c90f 100644 --- a/test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar +++ b/test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar @@ -86,7 +86,8 @@ spec: { "associateVrfFlag": false, "mcastGroup": "DME_UNSET_PROPERTY_MARKER", - "vni": 10100 + "vni": 10100, + "suppressARP": "off" } ] }