Skip to content

Commit 481a72b

Browse files
Allow to use a IRB Interface as redundant distributed anycast gateway
1 parent ed08e77 commit 481a72b

File tree

9 files changed

+93
-0
lines changed

9 files changed

+93
-0
lines changed

api/core/v1alpha1/interface_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
// +kubebuilder:validation:XValidation:rule="self.type == 'RoutedVLAN' || !has(self.vlanRef)", message="vlanRef must only be specified on interfaces of type RoutedVLAN"
1919
// +kubebuilder:validation:XValidation:rule="self.type != 'RoutedVLAN' || !has(self.switchport)", message="switchport must not be specified for interfaces of type RoutedVLAN"
2020
// +kubebuilder:validation:XValidation:rule="self.type != 'RoutedVLAN' || !has(self.aggregation)", message="aggregation must not be specified for interfaces of type RoutedVLAN"
21+
// +kubebuilder:validation:XValidation:rule="self.type == 'RoutedVLAN' || !has(self.ipv4) || !self.ipv4.anycastGateway", message="anycastGateway can only be enabled for interfaces of type RoutedVLAN"
2122
// +kubebuilder:validation:XValidation:rule="self.type != 'Aggregate' || !has(self.vrfRef)", message="vrfRef must not be specified for interfaces of type Aggregate"
2223
// +kubebuilder:validation:XValidation:rule="self.type != 'Physical' || !has(self.switchport) || !has(self.vrfRef)", message="vrfRef must not be specified for Physical interfaces with switchport configuration"
2324
type InterfaceSpec struct {
@@ -158,6 +159,7 @@ const (
158159

159160
// InterfaceIPv4 defines the IPv4 configuration for an interface.
160161
// +kubebuilder:validation:XValidation:rule="!has(self.addresses) || !has(self.unnumbered)", message="addresses and unnumbered are mutually exclusive"
162+
// +kubebuilder:validation:XValidation:rule="!has(self.unnumbered) || !self.anycastGateway", message="anycastGateway and unnumbered are mutually exclusive"
161163
type InterfaceIPv4 struct {
162164
// Addresses defines the list of IPv4 addresses assigned to the interface.
163165
// The first address in the list is considered the primary address,
@@ -171,6 +173,14 @@ type InterfaceIPv4 struct {
171173
// When specified, the interface borrows the IP address from another interface.
172174
// +optional
173175
Unnumbered *InterfaceIPv4Unnumbered `json:"unnumbered,omitempty"`
176+
177+
// AnycastGateway enables distributed anycast gateway functionality.
178+
// When enabled, this interface uses the virtual MAC configured in the
179+
// device's NVE resource for active-active default gateway redundancy.
180+
// Only applicable for RoutedVLAN interfaces in EVPN/VXLAN fabrics.
181+
// +optional
182+
// +kubebuilder:default=false
183+
AnycastGateway bool `json:"anycastGateway,omitempty"`
174184
}
175185

176186
// InterfaceIPv4Unnumbered defines the unnumbered interface configuration.

charts/network-operator/templates/crd/networking.metal.ironcore.dev_interfaces.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,14 @@ spec:
191191
minItems: 1
192192
type: array
193193
x-kubernetes-list-type: atomic
194+
anycastGateway:
195+
default: false
196+
description: |-
197+
AnycastGateway enables distributed anycast gateway functionality.
198+
When enabled, this interface uses the virtual MAC configured in the
199+
device's NVE resource for active-active default gateway redundancy.
200+
Only applicable for RoutedVLAN interfaces in EVPN/VXLAN fabrics.
201+
type: boolean
194202
unnumbered:
195203
description: |-
196204
Unnumbered defines the unnumbered interface configuration.
@@ -219,6 +227,8 @@ spec:
219227
x-kubernetes-validations:
220228
- message: addresses and unnumbered are mutually exclusive
221229
rule: '!has(self.addresses) || !has(self.unnumbered)'
230+
- message: anycastGateway and unnumbered are mutually exclusive
231+
rule: '!has(self.unnumbered) || !self.anycastGateway'
222232
mtu:
223233
description: MTU (Maximum Transmission Unit) specifies the size of
224234
the largest packet that can be sent over the interface.
@@ -391,6 +401,8 @@ spec:
391401
rule: self.type != 'RoutedVLAN' || !has(self.switchport)
392402
- message: aggregation must not be specified for interfaces of type RoutedVLAN
393403
rule: self.type != 'RoutedVLAN' || !has(self.aggregation)
404+
- message: anycastGateway can only be enabled for interfaces of type RoutedVLAN
405+
rule: self.type == 'RoutedVLAN' || !has(self.ipv4) || !self.ipv4.anycastGateway
394406
- message: vrfRef must not be specified for interfaces of type Aggregate
395407
rule: self.type != 'Aggregate' || !has(self.vrfRef)
396408
- message: vrfRef must not be specified for Physical interfaces with switchport

config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ spec:
185185
minItems: 1
186186
type: array
187187
x-kubernetes-list-type: atomic
188+
anycastGateway:
189+
default: false
190+
description: |-
191+
AnycastGateway enables distributed anycast gateway functionality.
192+
When enabled, this interface uses the virtual MAC configured in the
193+
device's NVE resource for active-active default gateway redundancy.
194+
Only applicable for RoutedVLAN interfaces in EVPN/VXLAN fabrics.
195+
type: boolean
188196
unnumbered:
189197
description: |-
190198
Unnumbered defines the unnumbered interface configuration.
@@ -213,6 +221,8 @@ spec:
213221
x-kubernetes-validations:
214222
- message: addresses and unnumbered are mutually exclusive
215223
rule: '!has(self.addresses) || !has(self.unnumbered)'
224+
- message: anycastGateway and unnumbered are mutually exclusive
225+
rule: '!has(self.unnumbered) || !self.anycastGateway'
216226
mtu:
217227
description: MTU (Maximum Transmission Unit) specifies the size of
218228
the largest packet that can be sent over the interface.
@@ -385,6 +395,8 @@ spec:
385395
rule: self.type != 'RoutedVLAN' || !has(self.switchport)
386396
- message: aggregation must not be specified for interfaces of type RoutedVLAN
387397
rule: self.type != 'RoutedVLAN' || !has(self.aggregation)
398+
- message: anycastGateway can only be enabled for interfaces of type RoutedVLAN
399+
rule: self.type == 'RoutedVLAN' || !has(self.ipv4) || !self.ipv4.anycastGateway
388400
- message: vrfRef must not be specified for interfaces of type Aggregate
389401
rule: self.type != 'Aggregate' || !has(self.vrfRef)
390402
- message: vrfRef must not be specified for Physical interfaces with switchport

config/samples/v1alpha1_interface.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ spec:
155155
ipv4:
156156
addresses:
157157
- 192.168.10.254/24
158+
anycastGateway: true
158159
---
159160
apiVersion: networking.metal.ironcore.dev/v1alpha1
160161
kind: Interface

internal/provider/cisco/nxos/intf.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var (
2828
_ gnmiext.Configurable = (*SwitchVirtualInterface)(nil)
2929
_ gnmiext.Configurable = (*SwitchVirtualInterfaceOperItems)(nil)
3030
_ gnmiext.Configurable = (*AddrItem)(nil)
31+
_ gnmiext.Configurable = (*FabricFwdIf)(nil)
3132
)
3233

3334
// Loopback represents a loopback interface on a NX-OS device.
@@ -305,6 +306,25 @@ const (
305306
IntfAddrTypeSecondary IntfAddrType = "secondary"
306307
)
307308

309+
// FabricFwdIf that represents an Interface configured as part of the HMM Fabric Forwarding Instance.
310+
type FabricFwdIf struct {
311+
AdminSt AdminSt `json:"adminSt"`
312+
ID string `json:"id"`
313+
Mode FwdMode `json:"mode"`
314+
}
315+
316+
func (f *FabricFwdIf) XPath() string {
317+
return "System/hmm-items/fwdinst-items/if-items/FwdIf-list[id=" + f.ID + "]"
318+
}
319+
320+
type FwdMode string
321+
322+
const (
323+
FwdModeStandard FwdMode = "standard"
324+
FwdModeAnycastGateway FwdMode = "anycastGW"
325+
FwdModeProxyGateway FwdMode = "proxyGW"
326+
)
327+
308328
// Range provides a string representation of identifiers (typically VLAN IDs) that formats the range in a human-readable way.
309329
// Consecutive IDs are represented as a range (e.g., "10-12"), while single IDs are shown individually (e.g., "15").
310330
// All values are joined in a comma-separated list of ranges and individual IDs, e.g. "10-12,15,20-22".

internal/provider/cisco/nxos/intf_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,11 @@ func init() {
7171
VlanID: 10,
7272
}
7373
Register("svi", svi)
74+
75+
fwif := &FabricFwdIf{
76+
AdminSt: AdminStEnabled,
77+
ID: "vlan10",
78+
Mode: FwdModeAnycastGateway,
79+
}
80+
Register("fwif", fwif)
7481
}

internal/provider/cisco/nxos/provider.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,20 @@ func (p *Provider) EnsureInterface(ctx context.Context, req *provider.EnsureInte
746746
svi.VlanID = req.VLAN.Spec.ID
747747
svi.RtvrfMbrItems = NewVrfMember(name, vrf)
748748

749+
fwif := new(FabricFwdIf)
750+
fwif.ID = name
751+
752+
switch {
753+
case req.Interface.Spec.IPv4 != nil && req.Interface.Spec.IPv4.AnycastGateway:
754+
fwif.AdminSt = AdminStEnabled
755+
fwif.Mode = FwdModeAnycastGateway
756+
conf = append(conf, fwif)
757+
default:
758+
if err := p.client.Delete(ctx, fwif); err != nil {
759+
return err
760+
}
761+
}
762+
749763
conf = append(conf, svi)
750764

751765
default:
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"hmm-items": {
3+
"fwdinst-items": {
4+
"if-items": {
5+
"FwdIf-list": [
6+
{
7+
"adminSt": "enabled",
8+
"id": "vlan10",
9+
"mode": "anycastGW"
10+
}
11+
]
12+
}
13+
}
14+
}
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
interface Vlan10
2+
fabric forwarding mode anycast-gateway

0 commit comments

Comments
 (0)