diff --git a/plugins/meta/tuning/tuning.go b/plugins/meta/tuning/tuning.go index 39017e059..dcab9b9d6 100644 --- a/plugins/meta/tuning/tuning.go +++ b/plugins/meta/tuning/tuning.go @@ -29,6 +29,7 @@ import ( "github.com/j-keck/arping" "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" @@ -44,11 +45,12 @@ const defaultDataDir = "/run/cni/tuning" // TuningConf represents the network tuning configuration. type TuningConf struct { types.NetConf - DataDir string `json:"dataDir,omitempty"` - SysCtl map[string]string `json:"sysctl"` - Mac string `json:"mac,omitempty"` - Promisc bool `json:"promisc,omitempty"` - Mtu int `json:"mtu,omitempty"` + DataDir string `json:"dataDir,omitempty"` + SysCtl map[string]string `json:"sysctl"` + Mac string `json:"mac,omitempty"` + Promisc bool `json:"promisc,omitempty"` + Mtu int `json:"mtu,omitempty"` + Allmulti *bool `json:"allmulti,omitempty"` RuntimeConfig struct { Mac string `json:"mac,omitempty"` @@ -59,17 +61,19 @@ type TuningConf struct { } type IPAMArgs struct { - SysCtl *map[string]string `json:"sysctl"` - Mac *string `json:"mac,omitempty"` - Promisc *bool `json:"promisc,omitempty"` - Mtu *int `json:"mtu,omitempty"` + SysCtl *map[string]string `json:"sysctl"` + Mac *string `json:"mac,omitempty"` + Promisc *bool `json:"promisc,omitempty"` + Mtu *int `json:"mtu,omitempty"` + Allmulti *bool `json:"allmulti,omitempty"` } // configToRestore will contain interface attributes that should be restored on cmdDel type configToRestore struct { - Mac string `json:"mac,omitempty"` - Promisc *bool `json:"promisc,omitempty"` - Mtu int `json:"mtu,omitempty"` + Mac string `json:"mac,omitempty"` + Promisc *bool `json:"promisc,omitempty"` + Mtu int `json:"mtu,omitempty"` + Allmulti *bool `json:"allmulti,omitempty"` } // MacEnvArgs represents CNI_ARG @@ -126,6 +130,9 @@ func parseConf(data []byte, envArgs string) (*TuningConf, error) { conf.Mtu = *conf.Args.A.Mtu } + if conf.Args.A.Allmulti != nil { + conf.Allmulti = conf.Args.A.Allmulti + } } return &conf, nil @@ -181,6 +188,18 @@ func changeMtu(ifName string, mtu int) error { return netlink.LinkSetMTU(link, mtu) } +func changeAllmulti(ifName string, val bool) error { + link, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("failed to get %q: %v", ifName, err) + } + + if val { + return netlink.LinkSetAllmulticastOn(link) + } + return netlink.LinkSetAllmulticastOff(link) +} + func createBackup(ifName, containerID, backupPath string, tuningConf *TuningConf) error { config := configToRestore{} link, err := netlink.LinkByName(ifName) @@ -197,6 +216,10 @@ func createBackup(ifName, containerID, backupPath string, tuningConf *TuningConf if tuningConf.Mtu != 0 { config.Mtu = link.Attrs().MTU } + if tuningConf.Allmulti != nil { + config.Allmulti = new(bool) + *config.Allmulti = (link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0) + } if _, err := os.Stat(backupPath); os.IsNotExist(err) { if err = os.MkdirAll(backupPath, 0600); err != nil { @@ -258,6 +281,12 @@ func restoreBackup(ifName, containerID, backupPath string) error { errStr = append(errStr, err.Error()) } } + if config.Allmulti != nil { + if err = changeAllmulti(ifName, *config.Allmulti); err != nil { + err = fmt.Errorf("failed to restore all-multicast mode: %v", err) + errStr = append(errStr, err.Error()) + } + } if len(errStr) > 0 { return fmt.Errorf(strings.Join(errStr, "; ")) @@ -310,7 +339,7 @@ func cmdAdd(args *skel.CmdArgs) error { } } - if tuningConf.Mac != "" || tuningConf.Mtu != 0 || tuningConf.Promisc { + if tuningConf.Mac != "" || tuningConf.Mtu != 0 || tuningConf.Promisc || tuningConf.Allmulti != nil { if err = createBackup(args.IfName, args.ContainerID, tuningConf.DataDir, tuningConf); err != nil { return err } @@ -341,6 +370,12 @@ func cmdAdd(args *skel.CmdArgs) error { return err } } + + if tuningConf.Allmulti != nil { + if err = changeAllmulti(args.IfName, *tuningConf.Allmulti); err != nil { + return err + } + } return nil }) if err != nil { @@ -358,7 +393,7 @@ func cmdDel(args *skel.CmdArgs) error { } ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { - // MAC address, MTU and promiscuous mode settings will be restored + // MAC address, MTU, promiscuous and all-multicast mode settings will be restored return restoreBackup(args.IfName, args.ContainerID, tuningConf.DataDir) }) return nil @@ -434,6 +469,14 @@ func cmdCheck(args *skel.CmdArgs) error { args.IfName, tuningConf.Mtu, link.Attrs().MTU) } } + + if tuningConf.Allmulti != nil { + allmulti := (link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0) + if allmulti != *tuningConf.Allmulti { + return fmt.Errorf("Error: Tuning configured all-multicast mode of %s is %v, current value is %v", + args.IfName, tuningConf.Allmulti, allmulti) + } + } return nil }) if err != nil { diff --git a/plugins/meta/tuning/tuning_test.go b/plugins/meta/tuning/tuning_test.go index 81973c951..ada53541f 100644 --- a/plugins/meta/tuning/tuning_test.go +++ b/plugins/meta/tuning/tuning_test.go @@ -24,6 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" + "golang.org/x/sys/unix" "github.com/vishvananda/netlink" @@ -104,6 +105,8 @@ var _ = Describe("tuning plugin", func() { beforeConf.Mtu = link.Attrs().MTU beforeConf.Promisc = new(bool) *beforeConf.Promisc = (link.Attrs().Promisc != 0) + beforeConf.Allmulti = new(bool) + *beforeConf.Allmulti = (link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0) return nil }) Expect(err).NotTo(HaveOccurred()) @@ -842,5 +845,147 @@ var _ = Describe("tuning plugin", func() { }) Expect(err).NotTo(HaveOccurred()) }) + + It(fmt.Sprintf("[%s] configures and deconfigures all-multicast mode with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "allmulti": true, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0).To(BeTrue()) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0).To(Equal(*beforeConf.Allmulti)) + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures all-multicast mode from args with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "args": { + "cni": { + "allmulti": true + } + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0).To(BeTrue()) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0).To(Equal(*beforeConf.Allmulti)) + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) } })