[lxc-devel] [lxd/master] NIC IPVLAN: Adds l2 mode support
tomponline on Github
lxc-bot at linuxcontainers.org
Wed Apr 15 15:46:53 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1222 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200415/c2764cc1/attachment-0001.bin>
-------------- next part --------------
From e1245418b5e590c0dbefebc31922bf1880e7cdc8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 10:45:35 +0100
Subject: [PATCH 1/9] lxd/device/nic/ipvlan: Improve validation of sysctl
settings when vlan setting used
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 38 +++++++++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index e51af7d96f..2ed0cf9fcd 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -66,45 +66,61 @@ func (d *nicIPVLAN) validateEnvironment() error {
return fmt.Errorf("Requires name property to start")
}
+ extensions := d.state.OS.LXCFeatures
+ if !extensions["network_ipvlan"] || !extensions["network_l2proxy"] || !extensions["network_gateway_device_route"] {
+ return fmt.Errorf("Requires liblxc has following API extensions: network_ipvlan, network_l2proxy, network_gateway_device_route")
+ }
+
if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
}
- extensions := d.state.OS.LXCFeatures
- if !extensions["network_ipvlan"] || !extensions["network_l2proxy"] || !extensions["network_gateway_device_route"] {
- return fmt.Errorf("Requires liblxc has following API extensions: network_ipvlan, network_l2proxy, network_gateway_device_route")
+ if d.config["parent"] == "" && d.config["vlan"] != "" {
+ return fmt.Errorf("The vlan setting can only be used when combined with a parent interface")
+ }
+
+ // Generate effective parent name, including the VLAN part if option used.
+ effectiveParentName := network.GetHostDevice(d.config["parent"], d.config["vlan"])
+
+ // If the effective parent doesn't exist and the vlan option is specified, it means we are going to create
+ // the VLAN parent at start, and we will configure the needed sysctls so don't need to check them yet.
+ if d.config["vlan"] != "" && !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", effectiveParentName)) {
+ return nil
}
if d.config["ipv4.address"] != "" {
// Check necessary sysctls are configured for use with l2proxy parent in IPVLAN l3s mode.
- ipv4FwdPath := fmt.Sprintf("net/ipv4/conf/%s/forwarding", d.config["parent"])
+ ipv4FwdPath := fmt.Sprintf("net/ipv4/conf/%s/forwarding", effectiveParentName)
sysctlVal, err := util.SysctlGet(ipv4FwdPath)
if err != nil || sysctlVal != "1\n" {
return fmt.Errorf("Error reading net sysctl %s: %v", ipv4FwdPath, err)
}
if sysctlVal != "1\n" {
- return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv4.conf.%s.forwarding=1", d.config["parent"])
+ // Replace . in parent name with / for sysctl formatting.
+ return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv4.conf.%s.forwarding=1", strings.Replace(effectiveParentName, ".", "/", -1))
}
}
if d.config["ipv6.address"] != "" {
// Check necessary sysctls are configured for use with l2proxy parent in IPVLAN l3s mode.
- ipv6FwdPath := fmt.Sprintf("net/ipv6/conf/%s/forwarding", d.config["parent"])
+ ipv6FwdPath := fmt.Sprintf("net/ipv6/conf/%s/forwarding", effectiveParentName)
sysctlVal, err := util.SysctlGet(ipv6FwdPath)
if err != nil {
return fmt.Errorf("Error reading net sysctl %s: %v", ipv6FwdPath, err)
}
if sysctlVal != "1\n" {
- return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv6.conf.%s.forwarding=1", d.config["parent"])
+ // Replace . in parent name with / for sysctl formatting.
+ return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv6.conf.%s.forwarding=1", strings.Replace(effectiveParentName, ".", "/", -1))
}
- ipv6ProxyNdpPath := fmt.Sprintf("net/ipv6/conf/%s/proxy_ndp", d.config["parent"])
+ ipv6ProxyNdpPath := fmt.Sprintf("net/ipv6/conf/%s/proxy_ndp", effectiveParentName)
sysctlVal, err = util.SysctlGet(ipv6ProxyNdpPath)
if err != nil {
return fmt.Errorf("Error reading net sysctl %s: %v", ipv6ProxyNdpPath, err)
}
if sysctlVal != "1\n" {
- return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv6.conf.%s.proxy_ndp=1", d.config["parent"])
+ // Replace . in parent name with / for sysctl formatting.
+ return fmt.Errorf("IPVLAN in L3S mode requires sysctl net.ipv6.conf.%s.proxy_ndp=1", strings.Replace(effectiveParentName, ".", "/", -1))
}
}
@@ -207,13 +223,13 @@ func (d *nicIPVLAN) setupParentSysctls(parentName string) error {
ipv6FwdPath := fmt.Sprintf("net/ipv6/conf/%s/forwarding", parentName)
err := util.SysctlSet(ipv6FwdPath, "1")
if err != nil {
- return fmt.Errorf("Error reading net sysctl %s: %v", ipv6FwdPath, err)
+ return fmt.Errorf("Error setting net sysctl %s: %v", ipv6FwdPath, err)
}
ipv6ProxyNdpPath := fmt.Sprintf("net/ipv6/conf/%s/proxy_ndp", parentName)
err = util.SysctlSet(ipv6ProxyNdpPath, "1")
if err != nil {
- return fmt.Errorf("Error reading net sysctl %s: %v", ipv6ProxyNdpPath, err)
+ return fmt.Errorf("Error setting net sysctl %s: %v", ipv6ProxyNdpPath, err)
}
}
From 468b144f1028f9392c86e9a09217969f8e7f5955 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 11:48:22 +0100
Subject: [PATCH 2/9] lxd/device/nic/ipvlan: Adds host_table setting support
Fixes #7123
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 62 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index 2ed0cf9fcd..6d55826f89 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -34,6 +34,8 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
"vlan",
"ipv4.gateway",
"ipv6.gateway",
+ "ipv4.host_table",
+ "ipv6.host_table",
}
rules := nicValidationRules(requiredFields, optionalFields)
@@ -202,6 +204,7 @@ func (d *nicIPVLAN) Start() (*deviceConfig.RunConfig, error) {
}
runConf.NetworkInterface = nic
+ runConf.PostHooks = append(runConf.PostHooks, d.postStart)
return &runConf, nil
}
@@ -236,6 +239,39 @@ func (d *nicIPVLAN) setupParentSysctls(parentName string) error {
return nil
}
+// postStart is run after the instance is started.
+func (d *nicIPVLAN) postStart() error {
+ if d.config["ipv4.address"] != "" {
+ // Add static routes to instance IPs to custom routing tables if specified.
+ // This is in addition to the static route added by liblxc to the main routing table.
+ if d.config["ipv4.host_table"] != "" {
+ for _, addr := range strings.Split(d.config["ipv4.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ _, err := shared.RunCommand("ip", "-4", "route", "add", "table", d.config["ipv4.host_table"], fmt.Sprintf("%s/32", addr), "dev", "lo")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ if d.config["ipv6.address"] != "" {
+ // Add static routes to instance IPs to custom routing tables if specified.
+ // This is in addition to the static route added by liblxc to the main routing table.
+ if d.config["ipv6.host_table"] != "" {
+ for _, addr := range strings.Split(d.config["ipv6.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ _, err := shared.RunCommand("ip", "-6", "route", "add", "table", d.config["ipv6.host_table"], fmt.Sprintf("%s/128", addr), "dev", "lo")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
// Stop is run when the device is removed from the instance.
func (d *nicIPVLAN) Stop() (*deviceConfig.RunConfig, error) {
runConf := deviceConfig.RunConfig{
@@ -253,6 +289,32 @@ func (d *nicIPVLAN) postStop() error {
v := d.volatileGet()
+ if d.config["ipv4.address"] != "" {
+ // Remove static routes to instance IPs to custom routing tables if specified.
+ if d.config["ipv4.host_table"] != "" {
+ for _, addr := range strings.Split(d.config["ipv4.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ _, err := shared.RunCommand("ip", "-4", "route", "delete", "table", d.config["ipv4.host_table"], fmt.Sprintf("%s/32", addr), "dev", "lo")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ if d.config["ipv6.address"] != "" {
+ // Remove static routes to instance IPs to custom routing tables if specified.
+ if d.config["ipv6.host_table"] != "" {
+ for _, addr := range strings.Split(d.config["ipv6.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ _, err := shared.RunCommand("ip", "-6", "route", "delete", "table", d.config["ipv6.host_table"], fmt.Sprintf("%s/128", addr), "dev", "lo")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
// This will delete the parent interface if we created it for VLAN parent.
if shared.IsTrue(v["last_state.created"]) {
parentName := network.GetHostDevice(d.config["parent"], d.config["vlan"])
From 1abd0dce0572f9e1ad3a1add7124a0a77ac68a0c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 11:50:44 +0100
Subject: [PATCH 3/9] api: Adds container_nic_ipvlan_host_table extension
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
doc/api-extensions.md | 4 ++++
shared/version/api.go | 1 +
2 files changed, 5 insertions(+)
diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index bdd514d9b3..15ab193712 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1017,3 +1017,7 @@ Those are taken from the os-release data on the system.
## container\_nic\_routed\_host\_table
This introduces the `ipv4.host_table` and `ipv6.host_table` NIC config keys that can be used to add static routes
for the instance's IPs to a custom policy routing table by ID.
+
+## container\_nic\_ipvlan\_host\_table
+This introduces the `ipv4.host_table` and `ipv6.host_table` NIC config keys that can be used to add static routes
+for the instance's IPs to a custom policy routing table by ID.
diff --git a/shared/version/api.go b/shared/version/api.go
index 6c914d1aeb..9673f9cce5 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -206,6 +206,7 @@ var APIExtensions = []string{
"resources_cpu_core_die",
"api_os",
"container_nic_routed_host_table",
+ "container_nic_ipvlan_host_table",
}
// APIExtensionsCount returns the number of available API extensions.
From cdb44209fa6094a4078a188f6dccd0429f39771a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 11:51:36 +0100
Subject: [PATCH 4/9] doc: Adds documentation for ipvlan NIC host_table setting
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
doc/instances.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/doc/instances.md b/doc/instances.md
index 0fa86f16a5..75da20da02 100644
--- a/doc/instances.md
+++ b/doc/instances.md
@@ -371,8 +371,10 @@ mtu | integer | parent MTU | no | The MTU of
hwaddr | string | randomly assigned | no | The MAC address of the new interface
ipv4.address | string | - | no | Comma delimited list of IPv4 static addresses to add to the instance
ipv4.gateway | string | auto | no | Whether to add an automatic default IPv4 gateway, can be "auto" or "none"
+ipv4.host\_table | integer | - | no | The custom policy routing table ID to add IPv4 static routes to (in addition to main routing table).
ipv6.address | string | - | no | Comma delimited list of IPv6 static addresses to add to the instance
ipv6.gateway | string | auto | no | Whether to add an automatic default IPv6 gateway, can be "auto" or "none"
+ipv6.host\_table | integer | - | no | The custom policy routing table ID to add IPv6 static routes to (in addition to main routing table).
vlan | integer | - | no | The VLAN ID to attach to
#### nictype: p2p
From c7f991d03c6102bf1e738ffbdd2be8e988698088 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 16:01:58 +0100
Subject: [PATCH 5/9] test/suites/container/devices/nic/ipvlan: Adds tests for
custom routing tables
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
test/suites/container_devices_nic_ipvlan.sh | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/test/suites/container_devices_nic_ipvlan.sh b/test/suites/container_devices_nic_ipvlan.sh
index 3e82618ea1..23473801e7 100644
--- a/test/suites/container_devices_nic_ipvlan.sh
+++ b/test/suites/container_devices_nic_ipvlan.sh
@@ -64,9 +64,11 @@ test_container_devices_nic_ipvlan() {
lxc exec "${ctName}2" -- ping6 -c2 -W1 "2001:db8::1${ipRand}"
lxc stop -f "${ctName}2"
- # Check IPVLAN ontop of VLAN parent.
+ # Check IPVLAN ontop of VLAN parent with custom routing tables.
lxc stop -f "${ctName}"
lxc config device set "${ctName}" eth0 vlan 1234
+ lxc config device set "${ctName}" eth0 ipv4.host_table=100
+ lxc config device set "${ctName}" eth0 ipv6.host_table=101
lxc start "${ctName}"
# Check VLAN interface created
@@ -75,6 +77,10 @@ test_container_devices_nic_ipvlan() {
false
fi
+ # Check static routes added to custom routing table
+ ip -4 route show table 100 | grep "192.0.2.1${ipRand}"
+ ip -6 route show table 101 | grep "2001:db8::1${ipRand}"
+
# Check volatile cleanup on stop.
lxc stop -f "${ctName}"
if lxc config show "${ctName}" | grep volatile.eth0 | grep -v volatile.eth0.hwaddr | grep -v volatile.eth0.name ; then
@@ -88,6 +94,10 @@ test_container_devices_nic_ipvlan() {
false
fi
+ # Check static routes are removed from custom routing table
+ ! ip -4 route show table 100 | grep "192.0.2.1${ipRand}"
+ ! ip -6 route show table 101 | grep "2001:db8::1${ipRand}"
+
# Check we haven't left any NICS lying around.
endNicCount=$(find /sys/class/net | wc -l)
if [ "$startNicCount" != "$endNicCount" ]; then
From dd96e8558053f697a4b91abf5d2250a3821adcf9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 14:59:07 +0100
Subject: [PATCH 6/9] api: Adds container_nic_ipvlan_mode extension
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
doc/api-extensions.md | 9 +++++++++
shared/version/api.go | 1 +
2 files changed, 10 insertions(+)
diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 15ab193712..60779580ee 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1021,3 +1021,12 @@ for the instance's IPs to a custom policy routing table by ID.
## container\_nic\_ipvlan\_host\_table
This introduces the `ipv4.host_table` and `ipv6.host_table` NIC config keys that can be used to add static routes
for the instance's IPs to a custom policy routing table by ID.
+
+## container\_nic\_ipvlan\_mode
+This introduces the `mode` NIC config key that can be used to switch the `ipvlan` mode into either `l2` or `l3s`.
+If not specified, the default value is `l3s` (which is the old behavior).
+
+In `l2` mode the `ipv4.address` and `ipv6.address` keys will accept addresses in either CIDR or singular formats.
+If singular format is used, the default subnet size is taken to be /24 and /64 for IPv4 and IPv6 respectively.
+
+In `l2` mode the `ipv4.gateway` and `ipv6.gateway` keys accept only a singular IP address.
diff --git a/shared/version/api.go b/shared/version/api.go
index 9673f9cce5..e7a86874cf 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -207,6 +207,7 @@ var APIExtensions = []string{
"api_os",
"container_nic_routed_host_table",
"container_nic_ipvlan_host_table",
+ "container_nic_ipvlan_mode",
}
// APIExtensionsCount returns the number of available API extensions.
From 9639d283ef3f44c42f59bf63de3b24515efe2eed Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 14:59:38 +0100
Subject: [PATCH 7/9] lxd/device/nic/ipvlan: Adds support for l2 mode
Fixes #6964
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 144 ++++++++++++++++++++++++++++++++++++---
1 file changed, 136 insertions(+), 8 deletions(-)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index 6d55826f89..86dda86fa7 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -2,6 +2,7 @@ package device
import (
"fmt"
+ "net"
"strings"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
@@ -12,6 +13,9 @@ import (
"github.com/lxc/lxd/shared"
)
+const ipvlanModeL3S = "l3s"
+const ipvlanModeL2 = "l2"
+
type nicIPVLAN struct {
deviceCommon
}
@@ -44,6 +48,28 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
return nil
}
+ if d.config["mode"] == ipvlanModeL2 {
+ for _, v := range strings.Split(value, ",") {
+ v = strings.TrimSpace(v)
+
+ // If valid non-CIDR address specified, append a /24 subnet.
+ if NetworkValidAddressV4(v) == nil {
+ v = fmt.Sprintf("%s/24", v)
+ }
+
+ ip, _, err := net.ParseCIDR(v)
+ if err != nil {
+ return err
+ }
+
+ if ip.To4() == nil {
+ return fmt.Errorf("Not an IPv4 CIDR address: %s", v)
+ }
+ }
+
+ return nil
+ }
+
return NetworkValidAddressV4List(value)
}
rules["ipv6.address"] = func(value string) error {
@@ -51,14 +77,70 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
return nil
}
+ if d.config["mode"] == ipvlanModeL2 {
+ for _, v := range strings.Split(value, ",") {
+ v = strings.TrimSpace(v)
+
+ // If valid non-CIDR address specified, append a /64 subnet.
+ if NetworkValidAddressV6(v) == nil {
+ v = fmt.Sprintf("%s/64", v)
+ }
+
+ ip, _, err := net.ParseCIDR(v)
+ if err != nil {
+ return err
+ }
+
+ if ip == nil || ip.To4() != nil {
+ return fmt.Errorf("Not an IPv6 CIDR address: %s", v)
+ }
+ }
+
+ return nil
+ }
+
return NetworkValidAddressV6List(value)
}
+ rules["mode"] = func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ validModes := []string{ipvlanModeL3S, ipvlanModeL2}
+ if !shared.StringInSlice(value, validModes) {
+ return fmt.Errorf("Must be one of: %v", strings.Join(validModes, ", "))
+ }
+
+ return nil
+ }
+
+ if d.config["mode"] == ipvlanModeL2 {
+ rules["ipv4.gateway"] = func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return NetworkValidAddressV4(value)
+ }
+
+ rules["ipv6.gateway"] = func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return NetworkValidAddressV6(value)
+ }
+ }
err := d.config.Validate(rules)
if err != nil {
return err
}
+ if d.config["mode"] == ipvlanModeL2 && d.config["host_table"] != "" {
+ return fmt.Errorf("host_table option cannot be used in l2 mode")
+ }
+
return nil
}
@@ -81,6 +163,11 @@ func (d *nicIPVLAN) validateEnvironment() error {
return fmt.Errorf("The vlan setting can only be used when combined with a parent interface")
}
+ // Only check sysctls for l2proxy if mode is l3s.
+ if d.mode() != ipvlanModeL3S {
+ return nil
+ }
+
// Generate effective parent name, including the VLAN part if option used.
effectiveParentName := network.GetHostDevice(d.config["parent"], d.config["vlan"])
@@ -153,8 +240,10 @@ func (d *nicIPVLAN) Start() (*deviceConfig.RunConfig, error) {
// Record whether we created this device or not so it can be removed on stop.
saveData["last_state.created"] = fmt.Sprintf("%t", statusDev != "existing")
- // If we created a VLAN interface, we need to setup the sysctls on that interface.
- if statusDev == "created" {
+ mode := d.mode()
+
+ // If we created a VLAN interface, we need to setup the sysctls on that interface for l3s mode l2proxy.
+ if statusDev == "created" && mode == ipvlanModeL3S {
err := d.setupParentSysctls(parentName)
if err != nil {
return nil, err
@@ -171,12 +260,16 @@ func (d *nicIPVLAN) Start() (*deviceConfig.RunConfig, error) {
{Key: "name", Value: d.config["name"]},
{Key: "type", Value: "ipvlan"},
{Key: "flags", Value: "up"},
- {Key: "ipvlan.mode", Value: "l3s"},
+ {Key: "ipvlan.mode", Value: mode},
{Key: "ipvlan.isolation", Value: "bridge"},
- {Key: "l2proxy", Value: "1"},
{Key: "link", Value: parentName},
}
+ // Enable l2proxy for l3s mode.
+ if mode == ipvlanModeL3S {
+ nic = append(nic, deviceConfig.RunConfigItem{Key: "l2proxy", Value: "1"})
+ }
+
if d.config["mtu"] != "" {
nic = append(nic, deviceConfig.RunConfigItem{Key: "mtu", Value: d.config["mtu"]})
}
@@ -184,23 +277,49 @@ func (d *nicIPVLAN) Start() (*deviceConfig.RunConfig, error) {
if d.config["ipv4.address"] != "" {
for _, addr := range strings.Split(d.config["ipv4.address"], ",") {
addr = strings.TrimSpace(addr)
- nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv4.address", Value: fmt.Sprintf("%s/32", addr)})
+
+ if mode == ipvlanModeL3S {
+ addr = fmt.Sprintf("%s/32", addr)
+ }
+
+ if mode == ipvlanModeL2 && NetworkValidAddressV4(addr) == nil {
+ addr = fmt.Sprintf("%s/24", addr)
+ }
+
+ nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv4.address", Value: addr})
}
- if nicHasAutoGateway(d.config["ipv4.gateway"]) {
+ if mode == ipvlanModeL3S && nicHasAutoGateway(d.config["ipv4.gateway"]) {
nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv4.gateway", Value: "dev"})
}
+
+ if mode == ipvlanModeL2 && d.config["ipv4.gateway"] != "" {
+ nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv4.gateway", Value: d.config["ipv4.gateway"]})
+ }
}
if d.config["ipv6.address"] != "" {
for _, addr := range strings.Split(d.config["ipv6.address"], ",") {
addr = strings.TrimSpace(addr)
- nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv6.address", Value: fmt.Sprintf("%s/128", addr)})
+
+ if mode == ipvlanModeL3S {
+ addr = fmt.Sprintf("%s/128", addr)
+ }
+
+ if mode == "l2" && NetworkValidAddressV6(addr) == nil {
+ addr = fmt.Sprintf("%s/64", addr)
+ }
+
+ nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv6.address", Value: addr})
}
- if nicHasAutoGateway(d.config["ipv6.gateway"]) {
+ if mode == ipvlanModeL3S && nicHasAutoGateway(d.config["ipv6.gateway"]) {
nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv6.gateway", Value: "dev"})
}
+
+ if mode == ipvlanModeL2 && d.config["ipv6.gateway"] != "" {
+ nic = append(nic, deviceConfig.RunConfigItem{Key: "ipv6.gateway", Value: d.config["ipv6.gateway"]})
+ }
}
runConf.NetworkInterface = nic
@@ -326,3 +445,12 @@ func (d *nicIPVLAN) postStop() error {
return nil
}
+
+// mode returns the ipvlan mode to use.
+func (d *nicIPVLAN) mode() string {
+ if d.config["mode"] == ipvlanModeL2 {
+ return ipvlanModeL2
+ }
+
+ return ipvlanModeL3S
+}
From c4187cc593d689ef6b25b8e544b73dcd241a1cf1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 15:02:33 +0100
Subject: [PATCH 8/9] doc/instances: Documents ipvlan l2 mode
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
doc/instances.md | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/doc/instances.md b/doc/instances.md
index 75da20da02..7736100693 100644
--- a/doc/instances.md
+++ b/doc/instances.md
@@ -363,19 +363,20 @@ net.ipv6.conf.<parent>.proxy_ndp=1
Device configuration properties:
-Key | Type | Default | Required | Description
-:-- | :-- | :-- | :-- | :--
-parent | string | - | yes | The name of the host device
-name | string | kernel assigned | no | The name of the interface inside the instance
-mtu | integer | parent MTU | no | The MTU of the new interface
-hwaddr | string | randomly assigned | no | The MAC address of the new interface
-ipv4.address | string | - | no | Comma delimited list of IPv4 static addresses to add to the instance
-ipv4.gateway | string | auto | no | Whether to add an automatic default IPv4 gateway, can be "auto" or "none"
-ipv4.host\_table | integer | - | no | The custom policy routing table ID to add IPv4 static routes to (in addition to main routing table).
-ipv6.address | string | - | no | Comma delimited list of IPv6 static addresses to add to the instance
-ipv6.gateway | string | auto | no | Whether to add an automatic default IPv6 gateway, can be "auto" or "none"
-ipv6.host\_table | integer | - | no | The custom policy routing table ID to add IPv6 static routes to (in addition to main routing table).
-vlan | integer | - | no | The VLAN ID to attach to
+Key | Type | Default | Required | Description
+:-- | :-- | :-- | :-- | :--
+parent | string | - | yes | The name of the host device
+name | string | kernel assigned | no | The name of the interface inside the instance
+mtu | integer | parent MTU | no | The MTU of the new interface
+mode | string | l3s | no | The IPVLAN mode (either `l2` or `l3s`)
+hwaddr | string | randomly assigned | no | The MAC address of the new interface
+ipv4.address | string | - | no | Comma delimited list of IPv4 static addresses to add to the instance. In `l2` mode these can be specified as CIDR values or singular addresses (if singular a subnet of /24 is used).
+ipv4.gateway | string | auto | no | In `l3s` mode, whether to add an automatic default IPv4 gateway, can be `auto` or `none`. In `l2` mode specifies the IPv4 address of the gateway.
+ipv4.host\_table | integer | - | no | The custom policy routing table ID to add IPv4 static routes to (in addition to main routing table).
+ipv6.address | string | - | no | Comma delimited list of IPv6 static addresses to add to the instance. In `l2` mode these can be specified as CIDR values or singular addresses (if singular a subnet of /64 is used).
+ipv6.gateway | string | auto (l3s), - (l2) | no | In `l3s` mode, whether to add an automatic default IPv6 gateway, can be `auto` or `none`. In `l2` mode specifies the IPv6 address of the gateway.
+ipv6.host\_table | integer | - | no | The custom policy routing table ID to add IPv6 static routes to (in addition to main routing table).
+vlan | integer | - | no | The VLAN ID to attach to
#### nictype: p2p
From 98cee553e03d49a3da0ad49895d756962d14fe08 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Apr 2020 16:37:42 +0100
Subject: [PATCH 9/9] test/suites/container/devices/nic/ipvlan: Adds l2 mode
tests
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
test/suites/container_devices_nic_ipvlan.sh | 46 +++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/test/suites/container_devices_nic_ipvlan.sh b/test/suites/container_devices_nic_ipvlan.sh
index 23473801e7..eec9cdbeda 100644
--- a/test/suites/container_devices_nic_ipvlan.sh
+++ b/test/suites/container_devices_nic_ipvlan.sh
@@ -26,6 +26,8 @@ test_container_devices_nic_ipvlan() {
parent=${ctName} \
ipv4.address="192.0.2.1${ipRand}" \
ipv6.address="2001:db8::1${ipRand}" \
+ ipv4.gateway=auto \
+ ipv6.gateway=auto \
mtu=1400
lxc start "${ctName}"
@@ -69,6 +71,11 @@ test_container_devices_nic_ipvlan() {
lxc config device set "${ctName}" eth0 vlan 1234
lxc config device set "${ctName}" eth0 ipv4.host_table=100
lxc config device set "${ctName}" eth0 ipv6.host_table=101
+
+ # Check gateway settings don't accept IPs in default l3s mode.
+ ! lxc config device set "${ctName}" eth0 ipv4.gateway=192.0.2.254
+ ! lxc config device set "${ctName}" eth0 ipv6.gateway=2001:db8::FFFF
+
lxc start "${ctName}"
# Check VLAN interface created
@@ -98,6 +105,45 @@ test_container_devices_nic_ipvlan() {
! ip -4 route show table 100 | grep "192.0.2.1${ipRand}"
! ip -6 route show table 101 | grep "2001:db8::1${ipRand}"
+ # Check ipvlan l2 mode with mixture of singular and CIDR IPs, and gateway IPs.
+ lxc config device remove "${ctName}" eth0
+ lxc config device add "${ctName}" eth0 nic \
+ nictype=ipvlan \
+ mode=l2 \
+ parent=${ctName} \
+ ipv4.address="192.0.2.1${ipRand},192.0.2.2${ipRand}/32" \
+ ipv6.address="2001:db8::1${ipRand},2001:db8::2${ipRand}/128" \
+ ipv4.gateway=192.0.2.254 \
+ ipv6.gateway=2001:db8::FFFF \
+ mtu=1400
+ lxc start "${ctName}"
+
+ lxc config device remove "${ctName}2" eth0
+ lxc config device add "${ctName}2" eth0 nic \
+ nictype=ipvlan \
+ parent=${ctName} \
+ ipv4.address="192.0.2.3${ipRand}" \
+ ipv6.address="2001:db8::3${ipRand}" \
+ mtu=1400
+ lxc start "${ctName}2"
+
+ # Add an internally configured address (only possible in l2 mode).
+ lxc exec "${ctName}2" -- ip -4 addr add "192.0.2.4${ipRand}/32" dev eth0
+ lxc exec "${ctName}2" -- ip -6 addr add "2001:db8::4${ipRand}/128" dev eth0
+
+ # Check comms between containers.
+ lxc exec "${ctName}" -- ping -c2 -W1 "192.0.2.3${ipRand}"
+ lxc exec "${ctName}" -- ping -c2 -W1 "192.0.2.4${ipRand}"
+ lxc exec "${ctName}" -- ping6 -c2 -W1 "2001:db8::3${ipRand}"
+ lxc exec "${ctName}" -- ping6 -c2 -W1 "2001:db8::4${ipRand}"
+ lxc exec "${ctName}2" -- ping -c2 -W1 "192.0.2.1${ipRand}"
+ lxc exec "${ctName}2" -- ping -c2 -W1 "192.0.2.2${ipRand}"
+ lxc exec "${ctName}2" -- ping6 -c2 -W1 "2001:db8::1${ipRand}"
+ lxc exec "${ctName}2" -- ping6 -c2 -W1 "2001:db8::2${ipRand}"
+
+ lxc stop -f "${ctName}"
+ lxc stop -f "${ctName}2"
+
# Check we haven't left any NICS lying around.
endNicCount=$(find /sys/class/net | wc -l)
if [ "$startNicCount" != "$endNicCount" ]; then
More information about the lxc-devel
mailing list