Skip to content

Commit

Permalink
loader: attach skb programs using tcx by default, add enableTCX Helm …
Browse files Browse the repository at this point in the history
…value

This commit puts the tcx logic in the endpoint attachment path and enables
it by default. The 'enableTCX' Helm value is added to disable tcx attachments
if external tooling hasn't caught up yet, as attaching a tcx program to an
interface disables the legacy tc pipeline.

The agent upgrades and downgrades interfaces seamlessly based on tcx being
enabled or not, so any existing workloads are migrated automatically at
runtime, without having to reboot the node.

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed Apr 26, 2024
1 parent be46dff commit 5b42a5f
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 5 deletions.
1 change: 1 addition & 0 deletions Documentation/cmdref/cilium-agent.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions daemon/cmd/daemon_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,9 @@ func InitGlobalFlags(cmd *cobra.Command, vp *viper.Viper) {
flags.Bool(option.EnableXDPPrefilter, false, "Enable XDP prefiltering")
option.BindEnv(vp, option.EnableXDPPrefilter)

flags.Bool(option.EnableTCX, false, "Attach endpoint programs using tcx if supported by the kernel")
option.BindEnv(vp, option.EnableTCX)

flags.Bool(option.PreAllocateMapsName, defaults.PreAllocateMaps, "Enable BPF map pre-allocation")
option.BindEnv(vp, option.PreAllocateMapsName)

Expand Down
1 change: 1 addition & 0 deletions install/kubernetes/cilium/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions install/kubernetes/cilium/templates/cilium-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,12 @@ data:
enable-ipv6-big-tcp: {{ .Values.enableIPv6BIGTCP | quote }}
enable-ipv6-masquerade: {{ .Values.enableIPv6Masquerade | quote }}

{{- if hasKey .Values "enableTCX" }}
enable-tcx: {{ .Values.enableTCX | quote }}
{{- else }}
enable-tcx: "true"
{{- end }}

{{- if (not (kindIs "invalid" .Values.bpf.masquerade)) }}
enable-bpf-masquerade: {{ .Values.bpf.masquerade | quote }}
{{- else if eq $defaultBpfMasquerade "true" }}
Expand Down
3 changes: 3 additions & 0 deletions install/kubernetes/cilium/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,9 @@
"enableRuntimeDeviceDetection": {
"type": "boolean"
},
"enableTCX": {
"type": "boolean"
},
"enableXTSocketFallback": {
"type": "boolean"
},
Expand Down
2 changes: 2 additions & 0 deletions install/kubernetes/cilium/values.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions install/kubernetes/cilium/values.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,8 @@ enableMasqueradeRouteSource: false
enableIPv4BIGTCP: false
# -- Enables IPv6 BIG TCP support which increases maximum IPv6 GSO/GRO limits for nodes and pods
enableIPv6BIGTCP: false
# -- Attach endpoint programs using tcx instead of legacy tc hooks on supported kernels.
enableTCX: true
egressGateway:
# -- Enables egress gateway to redirect and SNAT the traffic that leaves the
# cluster.
Expand Down
1 change: 1 addition & 0 deletions pkg/datapath/loader/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ func (l *loader) reinitializeIPSec(ctx context.Context) error {
elf: networkObj,
programs: progs,
linkDir: bpffsDeviceLinksDir(bpf.CiliumPath(), device),
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/datapath/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ func (l *loader) reloadHostDatapath(ctx context.Context, ep datapath.Endpoint, o
elf: objPath,
programs: progs,
linkDir: bpffsDeviceLinksDir(bpf.CiliumPath(), host),
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down Expand Up @@ -452,6 +453,7 @@ func (l *loader) reloadHostDatapath(ctx context.Context, ep datapath.Endpoint, o
elf: secondDevObjPath,
programs: progs,
linkDir: bpffsDeviceLinksDir(bpf.CiliumPath(), net),
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down Expand Up @@ -506,6 +508,7 @@ func (l *loader) reloadHostDatapath(ctx context.Context, ep datapath.Endpoint, o
elf: netdevObjPath,
programs: progs,
linkDir: linkDir,
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down Expand Up @@ -575,6 +578,7 @@ func (l *loader) reloadDatapath(ctx context.Context, ep datapath.Endpoint, dirs
elf: objPath,
programs: progs,
linkDir: linkDir,
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down Expand Up @@ -633,6 +637,7 @@ func (l *loader) replaceOverlayDatapath(ctx context.Context, cArgs []string, ifa
elf: overlayObj,
programs: progs,
linkDir: bpffsDeviceLinksDir(bpf.CiliumPath(), device),
tcx: option.Config.EnableTCX,
},
)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/datapath/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func TestReload(t *testing.T) {
elf: objPath,
programs: progs,
linkDir: testutils.TempBPFFS(t),
tcx: true,
}
finalize, err := replaceDatapath(ctx, opts)
require.NoError(t, err)
Expand Down Expand Up @@ -289,6 +290,7 @@ func BenchmarkReplaceDatapath(b *testing.B) {
elf: objPath,
programs: progs,
linkDir: linkDir,
tcx: true,
},
)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/datapath/loader/netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type replaceDatapathOptions struct {
programs []progDefinition // programs that we want to attach/replace
xdpMode string // XDP driver mode, only applies when attaching XDP programs
linkDir string // path to bpffs dir holding bpf_links for the device/endpoint
tcx bool // attempt attaching skb programs using tcx
}

// replaceDatapath replaces the qdisc and BPF program for an endpoint or XDP program.
Expand Down Expand Up @@ -230,7 +231,7 @@ func replaceDatapath(ctx context.Context, opts replaceDatapathOptions) (_ func()
err = attachXDPProgram(link, coll.Programs[prog.progName], prog.progName, opts.linkDir, xdpConfigModeToFlag(opts.xdpMode))
} else {
scopedLog.Debug("Attaching SKB program to interface")
err = attachSKBProgram(link, coll.Programs[prog.progName], prog.progName, opts.linkDir, directionToParent(prog.direction))
err = attachSKBProgram(link, coll.Programs[prog.progName], prog.progName, opts.linkDir, directionToParent(prog.direction), opts.tcx)
}

if err != nil {
Expand Down
26 changes: 24 additions & 2 deletions pkg/datapath/loader/tc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,41 @@
package loader

import (
"errors"
"fmt"
"strings"

"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"

"github.com/cilium/cilium/pkg/option"
)

// attachSKBProgram attaches prog to device using legacy tc.
func attachSKBProgram(device netlink.Link, prog *ebpf.Program, progName, bpffsDir string, parent uint32) error {
// attachSKBProgram attaches prog to device using tcx if available and enabled,
// or legacy tc as a fallback.
func attachSKBProgram(device netlink.Link, prog *ebpf.Program, progName, bpffsDir string, parent uint32, tcxEnabled bool) error {
if tcxEnabled {
// Attach using tcx if available. This is seamless on interfaces with
// existing tc programs since attaching tcx disables legacy tc evaluation.
err := upsertTCXProgram(device, prog, progName, bpffsDir, parent)
if err == nil {
// Created tcx link, clean up any leftover legacy tc attachments.
if err := removeTCFilters(device, parent); err != nil {
return fmt.Errorf("legacy tc cleanup after attaching tcx program %s: %w", progName, err)
}

// Don't fall back to legacy tc.
return nil
}
if !errors.Is(err, link.ErrNotSupported) {
// Unrecoverable error, surface to the caller.
return fmt.Errorf("attaching tcx program %s: %w", progName, err)
}
}

// tcx not available or disabled, fall back to legacy tc.
if err := attachTCProgram(device, prog, progName, parent); err != nil {
return fmt.Errorf("attaching legacy tc program %s: %w", progName, err)
Expand Down
33 changes: 31 additions & 2 deletions pkg/datapath/loader/tc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,42 @@ func TestAttachDetachSKBProgram(t *testing.T) {
prog := mustTCProgram(t)
linkDir := testutils.TempBPFFS(t)

require.NoError(t, attachSKBProgram(lo, prog, "test", linkDir, directionToParent(dirEgress)))
require.NoError(t, attachSKBProgram(lo, prog, "test", linkDir, directionToParent(dirEgress), true))
require.NoError(t, detachSKBProgram(lo, "test", linkDir, directionToParent(dirEgress)))

return nil
})
}

// Upgrade a legacy tc program to tcx.
func TestAttachSKBUpgrade(t *testing.T) {
testutils.PrivilegedTest(t)

ns := netns.NewNetNS(t)
ns.Do(func() error {
prog := mustTCProgramWithName(t, "cil_test")
linkDir := testutils.TempBPFFS(t)

// Use the cil_ prefix so the attachment algorithm knows which tc filter to
// clean up after attaching tcx.
require.NoError(t, attachTCProgram(lo, prog, "cil_test", directionToParent(dirEgress)))

require.NoError(t, attachSKBProgram(lo, prog, "cil_test", linkDir, directionToParent(dirEgress), true))

hasFilters, err := hasCiliumTCFilters(lo, directionToParent(dirEgress))
require.NoError(t, err)
require.False(t, hasFilters)

require.NoError(t, testutils.WaitUntil(func() bool {
hasLinks, err := hasCiliumTCXLinks(lo, ebpf.AttachTCXEgress)
require.NoError(t, err)
return hasLinks
}, time.Second))

return nil
})
}

// Downgrade a tcx program to legacy tc.
func TestAttachSKBDowngrade(t *testing.T) {
testutils.PrivilegedTest(t)
Expand All @@ -83,7 +112,7 @@ func TestAttachSKBDowngrade(t *testing.T) {

require.NoError(t, upsertTCXProgram(lo, prog, "cil_test", linkDir, directionToParent(dirEgress)))

require.NoError(t, attachSKBProgram(lo, prog, "cil_test", linkDir, directionToParent(dirEgress)))
require.NoError(t, attachSKBProgram(lo, prog, "cil_test", linkDir, directionToParent(dirEgress), false))

hasFilters, err := hasCiliumTCFilters(lo, directionToParent(dirEgress))
require.NoError(t, err)
Expand Down
5 changes: 5 additions & 0 deletions pkg/option/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ const (
// EnableXDPPrefilter enables XDP-based prefiltering
EnableXDPPrefilter = "enable-xdp-prefilter"

// EnableTCX enables attaching endpoint programs using tcx
EnableTCX = "enable-tcx"

ProcFs = "procfs"

// PrometheusServeAddr IP:Port on which to serve prometheus metrics (pass ":Port" to bind on all interfaces, "" is off)
Expand Down Expand Up @@ -1350,6 +1353,7 @@ type DaemonConfig struct {
LBDevInheritIPAddr string // Device which IP addr used by bpf_host devices
EnableXDPPrefilter bool // Enable XDP-based prefiltering
XDPMode string // XDP mode, values: { xdpdrv | xdpgeneric | none }
EnableTCX bool // Enable attaching endpoint programs using tcx
HostV4Addr net.IP // Host v4 address of the snooping device
HostV6Addr net.IP // Host v6 address of the snooping device
EncryptInterface []string // Set of network facing interface to encrypt over
Expand Down Expand Up @@ -2896,6 +2900,7 @@ func (c *DaemonConfig) Populate(vp *viper.Viper) {
c.WireguardPersistentKeepalive = vp.GetDuration(WireguardPersistentKeepalive)
c.EnableWellKnownIdentities = vp.GetBool(EnableWellKnownIdentities)
c.EnableXDPPrefilter = vp.GetBool(EnableXDPPrefilter)
c.EnableTCX = vp.GetBool(EnableTCX)
c.DisableCiliumEndpointCRD = vp.GetBool(DisableCiliumEndpointCRDName)
c.MasqueradeInterfaces = vp.GetStringSlice(MasqueradeInterfaces)
c.EgressMasqueradeInterfaces = strings.Join(c.MasqueradeInterfaces, ",")
Expand Down

0 comments on commit 5b42a5f

Please sign in to comment.