Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/confi
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
.PHONY: controller-gen
controller-gen: ## Download controller-gen locally if necessary.
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.2)
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.20.0)

KUSTOMIZE = $(shell pwd)/bin/kustomize
.PHONY: kustomize
Expand Down
4 changes: 4 additions & 0 deletions api/v1alpha1/layer2networkconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ type Layer2NetworkConfigurationSpec struct {
// Enable ARP / ND suppression
NeighSuppression *bool `json:"neighSuppression,omitempty"`

// Disable segmentation offload on the VLAN, limiting the Linux packet size to the MTU.
// This might be required when you use AF_PACKET on the VLAN.
DisableSegmentation bool `json:"disableSegmentation,omitempty"`

// VRF to attach Layer2 network to, default if not set
VRF string `json:"vrf,omitempty"`

Expand Down
2 changes: 2 additions & 0 deletions api/v1alpha1/nodenetworkconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type Layer2 struct {
IRB *IRB `json:"irb,omitempty"`
// MirrorACLs is a list of mirror ACLs.
MirrorACLs []MirrorACL `json:"mirrorAcls,omitempty"`
// DisableSegmentation indicates whether to disable segmentation for the Layer 2 network.
DisableSegmentation bool `json:"disableSegmentation,omitempty"`
}

// IRB represents the Integrated Routing and Bridging configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: bgppeerings.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: layer2networkconfigurations.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down Expand Up @@ -73,6 +73,11 @@ spec:
createMacVLANInterface:
description: Create MACVLAN attach interface
type: boolean
disableSegmentation:
description: |-
Disable segmentation offload on the VLAN, limiting the Linux packet size to the MTU.
This might be required when you use AF_PACKET on the VLAN.
type: boolean
id:
description: VLAN Id of the layer 2 network
type: integer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: networkconfigrevisions.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down Expand Up @@ -242,6 +242,11 @@ spec:
createMacVLANInterface:
description: Create MACVLAN attach interface
type: boolean
disableSegmentation:
description: |-
Disable segmentation offload on the VLAN, limiting the Linux packet size to the MTU.
This might be required when you use AF_PACKET on the VLAN.
type: boolean
id:
description: VLAN Id of the layer 2 network
type: integer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: nodenetplanconfigs.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: nodenetworkconfigs.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down Expand Up @@ -2558,6 +2558,10 @@ spec:
additionalProperties:
description: Layer2 represents a Layer 2 network configuration.
properties:
disableSegmentation:
description: DisableSegmentation indicates whether to disable
segmentation for the Layer 2 network.
type: boolean
irb:
description: IRB is the Integrated Routing and Bridging configuration.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
controller-gen.kubebuilder.io/version: v0.20.0
name: vrfrouteconfigurations.network.t-caas.telekom.com
spec:
group: network.t-caas.telekom.com
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/open-policy-agent/cert-controller v0.14.0
github.com/prometheus/client_golang v1.23.2
github.com/rackn/netwrangler v0.7.1
github.com/safchain/ethtool v0.7.0
github.com/sirupsen/logrus v1.9.3
github.com/vishvananda/netlink v1.3.1
go.uber.org/mock v0.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ github.com/rackn/netwrangler v0.7.1 h1:LzvDBlBOZ7nMvt50HyLeBM9eapRLf8oXRLyn1shGs
github.com/rackn/netwrangler v0.7.1/go.mod h1:hGj63TX3L7BRiZVZ3jIIsKTwy78VIpDHCoe1tkk//xI=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/safchain/ethtool v0.7.0 h1:rlJzfDetsVvT61uz8x1YIcFn12akMfuPulHtZjtb7Is=
github.com/safchain/ethtool v0.7.0/go.mod h1:MenQKEjXdfkjD3mp2QdCk8B/hwvkrlOTm/FD4gTpFxQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
Expand Down
39 changes: 39 additions & 0 deletions pkg/nl/ethtool_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package nl

import (
"fmt"

"github.com/safchain/ethtool"
)

// EthtoolInterface defines the interface for ethtool operations.
type EthtoolInterface interface {
Change(intf string, config map[string]bool) error
Close()
}

// ethtoolWrapper wraps the real ethtool to implement EthtoolInterface.
type ethtoolWrapper struct {
eth *ethtool.Ethtool
}

func (e *ethtoolWrapper) Change(intf string, config map[string]bool) error {
if err := e.eth.Change(intf, config); err != nil {
return fmt.Errorf("ethtool change failed: %w", err)
}
return nil
}

func (e *ethtoolWrapper) Close() {
e.eth.Close()
}

// newEthtoolFunc is a factory function for creating ethtool instances.
// It can be replaced in tests.
var newEthtoolFunc = func() (EthtoolInterface, error) {
eth, err := ethtool.NewEthtool()
if err != nil {
return nil, fmt.Errorf("failed to create ethtool: %w", err)
}
return &ethtoolWrapper{eth: eth}, nil
}
41 changes: 31 additions & 10 deletions pkg/nl/layer2.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@ const (
var procSysNetPath = "/proc/sys/net"

type Layer2Information struct {
VlanID int `json:"vlanID"`
MTU int `json:"mtu"`
VNI int `json:"vni"`
VRF string `json:"vrf"`
AnycastMAC *string `json:"anycastMAC"`
AnycastGateways []string `json:"anycastGateways"`
NeighSuppression *bool `json:"neighSuppression"`
bridge *netlink.Bridge
vxlan *netlink.Vxlan
vlanInterface *netlink.Vlan
VlanID int `json:"vlanID"`
MTU int `json:"mtu"`
VNI int `json:"vni"`
VRF string `json:"vrf"`
AnycastMAC *string `json:"anycastMAC"`
AnycastGateways []string `json:"anycastGateways"`
NeighSuppression *bool `json:"neighSuppression"`
DisableSegmentation bool `json:"disableSegmentation"`
bridge *netlink.Bridge
vxlan *netlink.Vxlan
vlanInterface *netlink.Vlan
}

type NeighborInformation struct {
Expand Down Expand Up @@ -326,6 +327,11 @@ func (n *Manager) ReconcileL2(current, desired *Layer2Information) error {
return err
}

// Reconcile disableSegmentation
if err := reconcileSegmentation(current.vlanInterface, desired.DisableSegmentation); err != nil {
return err
}

// Reconcile EUI Autogeneration
return n.reconcileEUIAutogeneration(bridgeName, current.bridge, desiredGateways)
}
Expand Down Expand Up @@ -473,6 +479,21 @@ func (*Manager) configureBridge(intfName string) error {
return nil
}

func reconcileSegmentation(vlanInterface *netlink.Vlan, disableSegmentation bool) error {
eth, err := newEthtoolFunc()
if err != nil {
return fmt.Errorf("error creating ethtool client: %w", err)
}
defer eth.Close()

settingsMap := map[string]bool{"gro": !disableSegmentation, "gso": !disableSegmentation, "tso": !disableSegmentation}

if err := eth.Change(vlanInterface.Attrs().Name, settingsMap); err != nil {
return fmt.Errorf("error changing ethtool settings: %w", err)
}
return nil
}

func (n *Manager) ListNeighborInformation() ([]NeighborInformation, error) {
netlinkNeighbors, err := n.listNeighbors()
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions pkg/nl/mock_ethtool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nl

// MockEthtool is a mock implementation of EthtoolInterface for testing.
type MockEthtool struct {
ChangeFunc func(intf string, config map[string]bool) error
Closed bool
}

func (m *MockEthtool) Change(intf string, config map[string]bool) error {
if m.ChangeFunc != nil {
return m.ChangeFunc(intf, config)
}
return nil
}

func (m *MockEthtool) Close() {
m.Closed = true
}
92 changes: 91 additions & 1 deletion pkg/nl/nl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -991,12 +991,18 @@ var _ = Describe("ReconcileL2()", func() {

procSysNetPath = oldProcSysNetPath
})

It("returns no error", func() {
netlinkMock := mock_nl.NewMockToolkitInterface(mockctrl)
nm := NewManager(netlinkMock, &config.BaseConfig{VTEPLoopbackIP: "127.0.0.1"})
oldProcSysNetPath := procSysNetPath

// Mock ethtool to avoid "no such device" error
oldNewEthtoolFunc := newEthtoolFunc
newEthtoolFunc = func() (EthtoolInterface, error) {
return &MockEthtool{}, nil
}
defer func() { newEthtoolFunc = oldNewEthtoolFunc }()

procSysNetPath = tmpDir
current := &Layer2Information{
AnycastGateways: []string{"1.1.1.1"},
Expand Down Expand Up @@ -1201,3 +1207,87 @@ func createInterfaceFile(path string) {
err = os.Chmod(path, 0o777)
Expect(err).ToNot(HaveOccurred())
}

var _ = Describe("reconcileSegmentation()", func() {
var originalNewEthtoolFunc func() (EthtoolInterface, error)

BeforeEach(func() {
originalNewEthtoolFunc = newEthtoolFunc
})

AfterEach(func() {
newEthtoolFunc = originalNewEthtoolFunc
})

It("returns error if creating ethtool client fails", func() {
newEthtoolFunc = func() (EthtoolInterface, error) {
return nil, errors.New("failed to create ethtool client")
}

vlanInterface := &netlink.Vlan{LinkAttrs: netlink.LinkAttrs{Name: "vlan100"}}
err := reconcileSegmentation(vlanInterface, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("error creating ethtool client"))
})

It("returns error if changing ethtool settings fails", func() {
mockEth := &MockEthtool{
ChangeFunc: func(_ string, _ map[string]bool) error {
return errors.New("failed to change settings")
},
}
newEthtoolFunc = func() (EthtoolInterface, error) {
return mockEth, nil
}

vlanInterface := &netlink.Vlan{LinkAttrs: netlink.LinkAttrs{Name: "vlan100"}}
err := reconcileSegmentation(vlanInterface, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("error changing ethtool settings"))
Expect(mockEth.Closed).To(BeTrue())
})

It("returns no error and disables segmentation offloading when disableSegmentation is true", func() {
var capturedIntf string
var capturedConfig map[string]bool
mockEth := &MockEthtool{
ChangeFunc: func(intf string, config map[string]bool) error {
capturedIntf = intf
capturedConfig = config
return nil
},
}
newEthtoolFunc = func() (EthtoolInterface, error) {
return mockEth, nil
}

vlanInterface := &netlink.Vlan{LinkAttrs: netlink.LinkAttrs{Name: "vlan100"}}
err := reconcileSegmentation(vlanInterface, true)
Expect(err).ToNot(HaveOccurred())
Expect(capturedIntf).To(Equal("vlan100"))
Expect(capturedConfig).To(Equal(map[string]bool{"gro": false, "gso": false, "tso": false}))
Expect(mockEth.Closed).To(BeTrue())
})

It("returns no error and enables segmentation offloading when disableSegmentation is false", func() {
var capturedIntf string
var capturedConfig map[string]bool
mockEth := &MockEthtool{
ChangeFunc: func(intf string, config map[string]bool) error {
capturedIntf = intf
capturedConfig = config
return nil
},
}
newEthtoolFunc = func() (EthtoolInterface, error) {
return mockEth, nil
}

vlanInterface := &netlink.Vlan{LinkAttrs: netlink.LinkAttrs{Name: "vlan200"}}
err := reconcileSegmentation(vlanInterface, false)
Expect(err).ToNot(HaveOccurred())
Expect(capturedIntf).To(Equal("vlan200"))
Expect(capturedConfig).To(Equal(map[string]bool{"gro": true, "gso": true, "tso": true}))
Expect(mockEth.Closed).To(BeTrue())
})
})
11 changes: 6 additions & 5 deletions pkg/reconciler/agent-cra-frr/nodenetworkconfig_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ func (a *CRAFRRConfigApplier) convertNodeConfigToNetlink(nodeCfg *v1alpha1.NodeN
neighSuppression := false

nlLayer2 := nl.Layer2Information{
VlanID: int(layer2.VLAN),
MTU: int(layer2.MTU),
VNI: int(layer2.VNI),
NeighSuppression: &neighSuppression,
AnycastMAC: new(string),
VlanID: int(layer2.VLAN),
MTU: int(layer2.MTU),
VNI: int(layer2.VNI),
NeighSuppression: &neighSuppression,
AnycastMAC: new(string),
DisableSegmentation: layer2.DisableSegmentation,
}

if layer2.IRB != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/reconciler/operator/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ func buildPeeringVlanPeer(node *corev1.Node, peer *v1alpha1.BGPRevision, revisio

var irbIPs []string
var vrf string
for _, l2 := range revision.Spec.Layer2 {
for i := range revision.Spec.Layer2 {
l2 := &revision.Spec.Layer2[i]
if !matchSelector(node, l2.NodeSelector) {
continue
}
Expand Down
Loading
Loading