From d671d29ad5b5d72e423ea74402bafe2edacadbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Caama=C3=B1o=20Ruiz?= Date: Mon, 13 Jan 2020 14:40:42 +0100 Subject: [PATCH] Improve support of sysctl name seprators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sysctl names can use dots or slashes as separator: - if dots are used, dots and slashes are interchanged. - if slashes are used, slashes and dots are left intact. Separator in use is determined by firt ocurrence. Reference: http://man7.org/linux/man-pages/man5/sysctl.d.5.html Signed-off-by: Jaime CaamaƱo Ruiz --- pkg/utils/sysctl/sysctl_linux.go | 28 ++++++- pkg/utils/sysctl/sysctl_linux_test.go | 113 ++++++++++++++++++++++++++ pkg/utils/sysctl/sysctl_suite_test.go | 13 +++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 pkg/utils/sysctl/sysctl_linux_test.go create mode 100644 pkg/utils/sysctl/sysctl_suite_test.go diff --git a/pkg/utils/sysctl/sysctl_linux.go b/pkg/utils/sysctl/sysctl_linux.go index fe06d2d9..aaa16327 100644 --- a/pkg/utils/sysctl/sysctl_linux.go +++ b/pkg/utils/sysctl/sysctl_linux.go @@ -35,7 +35,7 @@ func Sysctl(name string, params ...string) (string, error) { } func getSysctl(name string) (string, error) { - fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1)) + fullName := filepath.Join("/proc/sys", toNormalName(name)) fullName = filepath.Clean(fullName) data, err := ioutil.ReadFile(fullName) if err != nil { @@ -46,7 +46,7 @@ func getSysctl(name string) (string, error) { } func setSysctl(name, value string) (string, error) { - fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1)) + fullName := filepath.Join("/proc/sys", toNormalName(name)) fullName = filepath.Clean(fullName) if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil { return "", err @@ -54,3 +54,27 @@ func setSysctl(name, value string) (string, error) { return getSysctl(name) } + +// Normalize names by using slash as separator +// Sysctl names can use dots or slashes as separator: +// - if dots are used, dots and slashes are interchanged. +// - if slashes are used, slashes and dots are left intact. +// Separator in use is determined by firt ocurrence. +func toNormalName(name string) string { + interchange := false + for _, c := range name { + if c == '.' { + interchange = true + break + } + if c == '/' { + break + } + } + + if interchange { + r := strings.NewReplacer(".", "/", "/", ".") + return r.Replace(name) + } + return name +} diff --git a/pkg/utils/sysctl/sysctl_linux_test.go b/pkg/utils/sysctl/sysctl_linux_test.go new file mode 100644 index 00000000..b54a8a2b --- /dev/null +++ b/pkg/utils/sysctl/sysctl_linux_test.go @@ -0,0 +1,113 @@ +// Copyright 2017-2018 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sysctl + +import ( + "fmt" + "math/rand" + "runtime" + "strings" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vishvananda/netlink" +) + +const ( + sysctlDotKeyTemplate = "net.ipv4.conf.%s.proxy_arp" + sysctlSlashKeyTemplate = "net/ipv4/conf/%s/proxy_arp" +) + +var _ = Describe("Sysctl tests", func() { + var testIfaceName string + var cleanup func() + + BeforeEach(func() { + + // Save a reference to the original namespace, + // Add a new NS + currNs, err := ns.GetCurrentNS() + Expect(err).NotTo(HaveOccurred()) + + testNs, err := testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000)) + testIface := &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: testIfaceName, + Namespace: netlink.NsFd(int(testNs.Fd())), + }, + } + + err = netlink.LinkAdd(testIface) + Expect(err).NotTo(HaveOccurred()) + + runtime.LockOSThread() + err = testNs.Set() + Expect(err).NotTo(HaveOccurred()) + + cleanup = func() { + netlink.LinkDel(testIface) + currNs.Set() + } + + }) + + AfterEach(func() { + cleanup() + }) + + Describe("Sysctl", func() { + It("reads keys with dot separators", func() { + sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/") + sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName) + + _, err := Sysctl(sysctlKey) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Describe("Sysctl", func() { + It("reads keys with slash separators", func() { + sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName) + + _, err := Sysctl(sysctlKey) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Describe("Sysctl", func() { + It("writes keys with dot separators", func() { + sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/") + sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName) + + _, err := Sysctl(sysctlKey, "1") + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Describe("Sysctl", func() { + It("writes keys with slash separators", func() { + sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName) + + _, err := Sysctl(sysctlKey, "1") + Expect(err).NotTo(HaveOccurred()) + }) + }) + +}) diff --git a/pkg/utils/sysctl/sysctl_suite_test.go b/pkg/utils/sysctl/sysctl_suite_test.go new file mode 100644 index 00000000..e2d52be1 --- /dev/null +++ b/pkg/utils/sysctl/sysctl_suite_test.go @@ -0,0 +1,13 @@ +package sysctl_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestSysctl(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Sysctl Suite") +}