Skip to content

Commit

Permalink
datapath: Reload systemd-sysctl after changing config
Browse files Browse the repository at this point in the history
This commit adds logic which triggers the  `systemd-sysctl` unit to
reload the config and apply it. This is necessary for any existing
network interfaces. To avoid runtime dependencies on the
`systemctl` command we trigger the reload of the unit directly via
the D-bus.

The tool is now idempotent, it only updates the config file it doesn't
match the desired state, and only triggers a systemd unit reload if the
config changed.

Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
  • Loading branch information
dylandreimerink authored and kkourt committed Jun 17, 2022
1 parent 6432558 commit b303543
Show file tree
Hide file tree
Showing 52 changed files with 8,406 additions and 19 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
github.com/cilium/workerpool v1.1.3
github.com/containernetworking/cni v1.1.1
github.com/containernetworking/plugins v1.1.1
github.com/coreos/go-systemd/v22 v22.3.2
github.com/davecgh/go-spew v1.1.1
github.com/docker/docker v20.10.17+incompatible
github.com/docker/libnetwork v0.8.0-dev.2.0.20210525090646-64b7a4574d14
Expand Down Expand Up @@ -126,7 +127,6 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
Expand All @@ -149,6 +149,7 @@ require (
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gobuffalo/flect v0.2.3 // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum

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

116 changes: 98 additions & 18 deletions tools/sysctlfix/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,136 @@
package main

import (
"context"
"fmt"
"io"
"os"
"path"
"strings"
"time"

"github.com/coreos/go-systemd/v22/dbus"
"github.com/spf13/pflag"
)

// This tool attempts to write a sysctl config file to the sysctl config directory with the highest precedence so
// we can overwrite any other config and ensure correct sysctl options for Cilium to function.

const (
sysctlD = "/etc/sysctl.d/"
var (
flagSet = pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)

sysctlD = flagSet.String("sysctl-conf-dir", "/etc/sysctl.d/", "Path to the sysctl config directory")
// The 99-zzz prefix ensures our config file gets precedence over most if not all other files.
ciliumOverwrites = "99-zzz-override_cilium.conf"
ciliumOverwrites = flagSet.String(
"sysctl-config-file",
"99-zzz-override_cilium.conf",
"Filename of the cilium sysctl overwrites config file",
)
// Name of the systemd-sysctl unit to restart after making changes
sysctlUnit = flagSet.String(
"systemd-sysctl-unit",
"systemd-sysctl.service",
"Name of the systemd sysctl unit to reload",
)
)

var sysctlConfig = strings.Join([]string{
"# Disable rp_filter on Cilium interfaces since it may cause mangled packets to be dropped",
"net.ipv4.conf.lxc*.rp_filter = 0",
"net.ipv4.conf.cilium_*.rp_filter = 0",
"",
}, "\n")
var sysctlConfig = `
# Disable rp_filter on Cilium interfaces since it may cause mangled packets to be dropped
net.ipv4.conf.lxc*.rp_filter = 0
net.ipv4.conf.cilium_*.rp_filter = 0
# The kernel uses max(conf.all, conf.{dev}) as its value, so we need to set .all. to 0 as well.
# Otherwise it will overrule the device specific settings.
net.ipv4.conf.all.rp_filter = 0
`

// This program is executed by an init container so we purposely don't
// exit with any error codes. In case of errors, the function will print warnings,
// but we don't block cilium agent pod from running.
func main() {
info, err := os.Stat(sysctlD)
err := flagSet.Parse(os.Args[1:])
if err != nil {
fmt.Printf("can't stat sysctl.d dir '%s': %s", sysctlD, err)
fmt.Printf("parse flags: %s\n", err)
return
}

info, err := os.Stat(*sysctlD)
if err != nil {
fmt.Printf("can't stat sysctl.d dir '%s': %s\n", *sysctlD, err)
return
}

if !info.IsDir() {
fmt.Printf("'%s' is not a directory", sysctlD)
fmt.Printf("'%s' is not a directory\n", *sysctlD)
return
}

overwritesPath := path.Join(sysctlD, ciliumOverwrites)
f, err := os.OpenFile(overwritesPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
overwritesPath := path.Join(*sysctlD, *ciliumOverwrites)
f, err := os.OpenFile(overwritesPath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("unable to create cilium sysctl overwrites config: %s", err)
fmt.Printf("unable to create cilium sysctl overwrites config: %s\n", err)
return
}
defer f.Close()

currentContents, err := io.ReadAll(f)
if err != nil {
fmt.Printf("read config: %s\n", err)
return
}

if string(currentContents) == sysctlConfig {
fmt.Println("sysctl config up-to-date, nothing to do")
return
}

_, err = f.Seek(0, io.SeekStart)
if err != nil {
fmt.Printf("error while seeking to start of sysctl config: %s\n", err)
return
}

// Truncate the whole file
err = f.Truncate(0)
if err != nil {
fmt.Printf("error while truncating sysctl config: %s\n", err)
return
}

_, err = fmt.Fprint(f, sysctlConfig)
if err != nil {
fmt.Printf("error while writing to sysctl config: %s", err)
fmt.Printf("error while writing to sysctl config: %s\n", err)
return
}

fmt.Println("sysctl config created/updated")

ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

conn, err := dbus.NewSystemdConnectionContext(ctx)
if err != nil {
fmt.Printf("error while creating SystemD D-Bus connection: %s\n", err)
return
}

_, err = conn.GetUnitPropertiesContext(ctx, *sysctlUnit)
if err != nil {
fmt.Printf("can't verify unit '%s' exists: %s\n", *sysctlUnit, err)
return
}

// https://www.freedesktop.org/wiki/Software/systemd/dbus/
// "The mode needs to be one of replace, fail, isolate, ignore-dependencies, ignore-requirements.
// If "replace" the call will start the unit and its dependencies, possibly replacing already queued jobs that
// conflict with this."
const mode = "replace"

// Restart the systemd-sysctl unit, this will trigger SystemD to apply the new config to all existing interfaces
// which is required for host-interfaces and reloads on existing cilium deployments.
_, err = conn.RestartUnitContext(ctx, *sysctlUnit, mode, nil)
if err != nil {
fmt.Printf("error while restarting unit '%s': %s\n", *sysctlUnit, err)
return
}

fmt.Println("sysctl config written")
fmt.Printf("systemd unit '%s' restarted\n", *sysctlUnit)
}
Loading

0 comments on commit b303543

Please sign in to comment.