Skip to content

Commit

Permalink
Merge pull request containers#11607 from Luap99/ipvlan
Browse files Browse the repository at this point in the history
CNI: add ipvlan driver support and macvlan modes
  • Loading branch information
openshift-merge-robot authored Sep 17, 2021
2 parents 9a5987c + aee0ab9 commit 08e1bb5
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 28 deletions.
4 changes: 2 additions & 2 deletions cmd/podman/common/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,7 @@ func AutocompleteManifestFormat(cmd *cobra.Command, args []string, toComplete st
// AutocompleteNetworkDriver - Autocomplete network driver option.
// -> "bridge", "macvlan"
func AutocompleteNetworkDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
drivers := []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}
drivers := []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}
return drivers, cobra.ShellCompDirectiveNoFileComp
}

Expand Down Expand Up @@ -1257,7 +1257,7 @@ func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete st
"id=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeIDs) },
"label=": nil,
"driver=": func(_ string) ([]string, cobra.ShellCompDirective) {
return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp
return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp
},
"until=": nil,
}
Expand Down
17 changes: 12 additions & 5 deletions docs/source/markdown/podman-network-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,23 @@ resolution.

#### **--driver**, **-d**

Driver to manage the network. Currently `bridge` and `macvlan` is supported. Defaults to `bridge`.
As rootless the `macvlan` driver has no access to the host network interfaces because rootless networking requires a separate network namespace.
Driver to manage the network. Currently `bridge`, `macvlan` and `ipvlan` are supported. Defaults to `bridge`.
As rootless the `macvlan` and `ipvlan` driver have no access to the host network interfaces because rootless networking requires a separate network namespace.

#### **--opt**=*option*, **-o**

Set driver specific options.

For the `bridge` driver the following options are supported: `mtu` and `vlan`.
The `mtu` option sets the Maximum Transmission Unit (MTU) and takes an integer value.
The `vlan` option assign VLAN tag and enables vlan\_filtering. Defaults to none.
All drivers accept the `mtu` option. The `mtu` option sets the Maximum Transmission Unit (MTU) and takes an integer value.

Additionally the `bridge` driver supports the following option:
- `vlan`: This option assign VLAN tag and enables vlan\_filtering. Defaults to none.

The `macvlan` and `ipvlan` driver support the following options:
- `parent`: The host device which should be used for the macvlan interface. Defaults to the default route interface.
- `mode`: This options sets the specified ip/macvlan mode on the interface.
- Supported values for `macvlan` are `bridge`, `private`, `vepa`, `passthru`. Defaults to `bridge`.
- Supported values for `ipvlan` are `l2`, `l3`, `l3s`. Defaults to `l2`.

#### **--gateway**

Expand Down
41 changes: 32 additions & 9 deletions libpod/network/cni/cni_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +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 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
}
Expand Down Expand Up @@ -207,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.
Expand Down Expand Up @@ -237,6 +241,7 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ

vlan := 0
mtu := 0
vlanPluginMode := ""
for k, v := range network.Options {
switch k {
case "mtu":
Expand All @@ -251,6 +256,21 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
return nil, "", err
}

case "mode":
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)
}
vlanPluginMode = v

default:
return nil, "", errors.Errorf("unsupported network option %s", k)
}
Expand Down Expand Up @@ -281,7 +301,10 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
}

case types.MacVLANNetworkDriver:
plugins = append(plugins, newMacVLANPlugin(network.NetworkInterface, 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)
Expand Down
20 changes: 12 additions & 8 deletions libpod/network/cni/cni_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type hostLocalBridge struct {
PromiscMode bool `json:"promiscMode,omitempty"`
Vlan int `json:"vlan,omitempty"`
IPAM ipamConfig `json:"ipam"`
Capabilities map[string]bool `json:"capabilities"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
}

// ipamConfig describes an IPAM configuration
Expand Down Expand Up @@ -82,13 +82,14 @@ 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"`
MTU int `json:"mtu,omitempty"`
Capabilities map[string]bool `json:"capabilities"`
Mode string `json:"mode,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
}

// firewallConfig describes the firewall plugin
Expand Down Expand Up @@ -259,15 +260,18 @@ func hasDNSNamePlugin(paths []string) bool {
return false
}

// newMacVLANPlugin creates a macvlanconfig with a given device name
func newMacVLANPlugin(device 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 {
m.MTU = mtu
}
if len(mode) > 0 {
m.Mode = mode
}
// CNI is supposed to use the default route if a
// parent device is not provided
if len(device) > 0 {
Expand Down
6 changes: 3 additions & 3 deletions libpod/network/cni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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")
}
Expand Down
102 changes: 102 additions & 0 deletions libpod/network/cni/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,67 @@ 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{
Driver: "macvlan",
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("macvlan"))
Expect(network1.Options).To(HaveKeyWithValue("mode", mode))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp"))
grepInFile(path, `"mode": "`+mode+`"`)
}
})

It("create macvlan config with invalid mode", func() {
network := types.Network{
Driver: "macvlan",
Options: map[string]string{
"mode": "test",
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(`unknown macvlan mode "test"`))
})

It("create macvlan config with invalid device", func() {
network := types.Network{
Driver: "macvlan",
Expand All @@ -270,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)
Expand Down
2 changes: 1 addition & 1 deletion libpod/network/cni/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions libpod/network/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 08e1bb5

Please sign in to comment.