From aee0ab98cd3e3b552ac6eb0e7534a9f548b3109f Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 16 Sep 2021 14:51:51 +0200 Subject: CNI: add ipvlan driver Add support for the ipvlan cni plugin. This allows us to create, inspect and list ipvlan networks correctly. Fixes #10478 Signed-off-by: Paul Holzinger --- libpod/network/cni/cni_conversion.go | 42 ++++++++++++++-------- libpod/network/cni/cni_types.go | 12 +++---- libpod/network/cni/config.go | 6 ++-- libpod/network/cni/config_test.go | 69 ++++++++++++++++++++++++++++++++++++ libpod/network/cni/network.go | 2 +- libpod/network/types/const.go | 2 ++ 6 files changed, 108 insertions(+), 25 deletions(-) (limited to 'libpod/network') diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go index 32dcc62de..d69dd7eb3 100644 --- a/libpod/network/cni/cni_conversion.go +++ b/libpod/network/cni/cni_conversion.go @@ -81,24 +81,24 @@ func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath str return nil, err } - case types.MacVLANNetworkDriver: - var macvlan macVLANConfig - err := json.Unmarshal(firstPlugin.Bytes, &macvlan) + case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: + var vlan VLANConfig + err := json.Unmarshal(firstPlugin.Bytes, &vlan) if err != nil { return nil, errors.Wrapf(err, "failed to unmarshal the macvlan plugin config in %s", confPath) } - network.NetworkInterface = macvlan.Master + network.NetworkInterface = vlan.Master // set network options - if macvlan.MTU != 0 { - network.Options["mtu"] = strconv.Itoa(macvlan.MTU) + if vlan.MTU != 0 { + network.Options["mtu"] = strconv.Itoa(vlan.MTU) } - if macvlan.Mode != "" { - network.Options["mode"] = macvlan.Mode + if vlan.Mode != "" { + network.Options["mode"] = vlan.Mode } - err = convertIPAMConfToNetwork(&network, macvlan.IPAM, confPath) + err = convertIPAMConfToNetwork(&network, vlan.IPAM, confPath) if err != nil { return nil, err } @@ -211,7 +211,7 @@ func getNetworkArgsFromConfList(args map[string]interface{}, argType string) map return result } } - return nil + return map[string]string{} } // createCNIConfigListFromNetwork will create a cni config file from the given network. @@ -241,7 +241,7 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ vlan := 0 mtu := 0 - macvlanMode := "" + vlanPluginMode := "" for k, v := range network.Options { switch k { case "mtu": @@ -257,10 +257,19 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ } case "mode": - if !pkgutil.StringInSlice(v, []string{"", "bridge", "private", "vepa", "passthru"}) { - return nil, "", errors.Errorf("unknown macvlan mode %q", v) + switch network.Driver { + case types.MacVLANNetworkDriver: + if !pkgutil.StringInSlice(v, []string{"", "bridge", "private", "vepa", "passthru"}) { + return nil, "", errors.Errorf("unknown macvlan mode %q", v) + } + case types.IPVLANNetworkDriver: + if !pkgutil.StringInSlice(v, []string{"", "l2", "l3", "l3s"}) { + return nil, "", errors.Errorf("unknown ipvlan mode %q", v) + } + default: + return nil, "", errors.Errorf("cannot set option \"mode\" with driver %q", network.Driver) } - macvlanMode = v + vlanPluginMode = v default: return nil, "", errors.Errorf("unsupported network option %s", k) @@ -292,7 +301,10 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ } case types.MacVLANNetworkDriver: - plugins = append(plugins, newMacVLANPlugin(network.NetworkInterface, macvlanMode, mtu, ipamConf)) + plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf)) + + case types.IPVLANNetworkDriver: + plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf)) default: return nil, "", errors.Errorf("driver %q is not supported by cni", network.Driver) diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go index 0a7bb066e..fbf917c2d 100644 --- a/libpod/network/cni/cni_types.go +++ b/libpod/network/cni/cni_types.go @@ -82,8 +82,8 @@ type portMapConfig struct { Capabilities map[string]bool `json:"capabilities"` } -// macVLANConfig describes the macvlan config -type macVLANConfig struct { +// VLANConfig describes the macvlan config +type VLANConfig struct { PluginType string `json:"type"` Master string `json:"master"` IPAM ipamConfig `json:"ipam"` @@ -260,10 +260,10 @@ func hasDNSNamePlugin(paths []string) bool { return false } -// newMacVLANPlugin creates a macvlanconfig with a given device name -func newMacVLANPlugin(device, mode string, mtu int, ipam ipamConfig) macVLANConfig { - m := macVLANConfig{ - PluginType: "macvlan", +// newVLANPlugin creates a macvlanconfig with a given device name +func newVLANPlugin(pluginType, device, mode string, mtu int, ipam ipamConfig) VLANConfig { + m := VLANConfig{ + PluginType: pluginType, IPAM: ipam, } if mtu > 0 { diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go index d31cd3002..2a6ad8eb3 100644 --- a/libpod/network/cni/config.go +++ b/libpod/network/cni/config.go @@ -100,8 +100,8 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (* if err != nil { return nil, err } - case types.MacVLANNetworkDriver: - err = createMacVLAN(&newNetwork) + case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: + err = createIPMACVLAN(&newNetwork) if err != nil { return nil, err } @@ -214,7 +214,7 @@ func (n *cniNetwork) NetworkInspect(nameOrID string) (types.Network, error) { return *network.libpodNet, nil } -func createMacVLAN(network *types.Network) error { +func createIPMACVLAN(network *types.Network) error { if network.Internal { return errors.New("internal is not supported with macvlan") } diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go index 62b6ef837..a0a0ea1af 100644 --- a/libpod/network/cni/config_test.go +++ b/libpod/network/cni/config_test.go @@ -250,6 +250,34 @@ var _ = Describe("Config", func() { grepInFile(path, `"type": "host-local"`) }) + It("create ipvlan config with subnet", func() { + subnet := "10.1.0.0/24" + n, _ := types.ParseCIDR(subnet) + network := types.Network{ + Driver: "ipvlan", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("ipvlan")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.1.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + grepInFile(path, `"type": "host-local"`) + }) + It("create macvlan config with mode", func() { for _, mode := range []string{"bridge", "private", "vepa", "passthru"} { network := types.Network{ @@ -303,6 +331,47 @@ var _ = Describe("Config", func() { Expect(err.Error()).To(ContainSubstring("internal is not supported with macvlan")) }) + It("create ipvlan config with mode", func() { + for _, mode := range []string{"l2", "l3", "l3s"} { + network := types.Network{ + Driver: "ipvlan", + Options: map[string]string{ + "mode": mode, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.Driver).To(Equal("ipvlan")) + Expect(network1.Options).To(HaveKeyWithValue("mode", mode)) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + grepInFile(path, `"mode": "`+mode+`"`) + + // reload configs from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + + network2, err := libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + } + }) + + It("create ipvlan config with invalid mode", func() { + network := types.Network{ + Driver: "ipvlan", + Options: map[string]string{ + "mode": "test", + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`unknown ipvlan mode "test"`)) + }) + It("create bridge with subnet", func() { subnet := "10.0.0.0/24" n, _ := types.ParseCIDR(subnet) diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go index 46e07f780..d77e63a5d 100644 --- a/libpod/network/cni/network.go +++ b/libpod/network/cni/network.go @@ -109,7 +109,7 @@ func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { // Drivers will return the list of supported network drivers // for this interface. func (n *cniNetwork) Drivers() []string { - return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} + return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} } func (n *cniNetwork) loadNetworks() error { diff --git a/libpod/network/types/const.go b/libpod/network/types/const.go index be7ef03cf..916c6e6bf 100644 --- a/libpod/network/types/const.go +++ b/libpod/network/types/const.go @@ -7,6 +7,8 @@ const ( DefaultNetworkDriver = BridgeNetworkDriver // MacVLANNetworkDriver defines the macvlan driver MacVLANNetworkDriver = "macvlan" + // MacVLANNetworkDriver defines the macvlan driver + IPVLANNetworkDriver = "ipvlan" // IPAM drivers // HostLocalIPAMDriver store the ip -- cgit v1.2.3-54-g00ecf