[lxc-devel] [lxd/master] lxd/device/nic/routed: Adds veth routed NIC device
tomponline on Github
lxc-bot at linuxcontainers.org
Sun Nov 10 16:37:42 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 508 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191110/74ced9cf/attachment.bin>
-------------- next part --------------
From a800a1276a62e46649c1b863038d300bc6564136 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Sun, 10 Nov 2019 16:35:25 +0000
Subject: [PATCH] lxd/device/nic/routed: Adds veth routed NIC device
Example usage:
lxc config device add c1 eth0 nic nictype=routed parent=eth0 ipv4.address=192.168.1.200 ipv6.address=2a02:xxx:xxx:3::200/128
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/daemon.go | 1 +
lxd/device/nic.go | 1 +
lxd/device/nic_routed.go | 240 +++++++++++++++++++++++++++++++++++++++
3 files changed, 242 insertions(+)
create mode 100644 lxd/device/nic_routed.go
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 0de75b098c..2ad1a2d318 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -635,6 +635,7 @@ func (d *Daemon) init() error {
"network_l2proxy",
"network_gateway_device_route",
"network_phys_macvlan_mtu",
+ "network_veth_router",
}
for _, extension := range lxcExtensions {
d.os.LXCFeatures[extension] = lxc.HasApiExtension(extension)
diff --git a/lxd/device/nic.go b/lxd/device/nic.go
index f3b3f80330..7f7f942ebc 100644
--- a/lxd/device/nic.go
+++ b/lxd/device/nic.go
@@ -11,6 +11,7 @@ var nicTypes = map[string]func() device{
"ipvlan": func() device { return &nicIPVLAN{} },
"p2p": func() device { return &nicP2P{} },
"bridged": func() device { return &nicBridged{} },
+ "routed": func() device { return &nicRouted{} },
"macvlan": func() device { return &nicMACVLAN{} },
"sriov": func() device { return &nicSRIOV{} },
}
diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go
new file mode 100644
index 0000000000..f838e49e42
--- /dev/null
+++ b/lxd/device/nic_routed.go
@@ -0,0 +1,240 @@
+package device
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/lxc/lxd/lxd/instance/instancetype"
+ "github.com/lxc/lxd/shared"
+)
+
+type nicRouted struct {
+ deviceCommon
+}
+
+func (d *nicRouted) CanHotPlug() (bool, []string) {
+ return false, []string{}
+}
+
+// validateConfig checks the supplied config for correctness.
+func (d *nicRouted) validateConfig() error {
+ if d.instance.Type() != instancetype.Container {
+ return ErrUnsupportedDevType
+ }
+
+ requiredFields := []string{"parent"}
+ optionalFields := []string{
+ "name",
+ "mtu",
+ "hwaddr",
+ "host_name",
+ "vlan",
+ }
+
+ rules := nicValidationRules(requiredFields, optionalFields)
+ rules["ipv4.address"] = func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return NetworkValidAddressV4List(value)
+ }
+ rules["ipv6.address"] = func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return NetworkValidAddressV6List(value)
+ }
+
+ err := d.config.Validate(rules)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// validateEnvironment checks the runtime environment for correctness.
+func (d *nicRouted) validateEnvironment() error {
+ if d.config["name"] == "" {
+ return fmt.Errorf("Requires name property to start")
+ }
+
+ 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_veth_router"] || !extensions["network_l2proxy"] {
+ return fmt.Errorf("Requires liblxc has following API extensions: network_veth_router, network_l2proxy")
+ }
+
+ if d.config["ipv4.address"] != "" {
+ // Check necessary sysctls are configured for use with l2proxy parent for routed mode.
+ ipv4FwdPath := fmt.Sprintf("ipv4/conf/%s/forwarding", d.config["parent"])
+ sysctlVal, err := NetworkSysctlGet(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("Routed mode requires sysctl net.ipv4.conf.%s.forwarding=1", d.config["parent"])
+ }
+ }
+
+ if d.config["ipv6.address"] != "" {
+ // Check necessary sysctls are configured for use with l2proxy parent for routed mode.
+ ipv6FwdPath := fmt.Sprintf("ipv6/conf/%s/forwarding", d.config["parent"])
+ sysctlVal, err := NetworkSysctlGet(ipv6FwdPath)
+ if err != nil {
+ return fmt.Errorf("Error reading net sysctl %s: %v", ipv6FwdPath, err)
+ }
+ if sysctlVal != "1\n" {
+ return fmt.Errorf("Routed mode requires sysctl net.ipv6.conf.%s.forwarding=1", d.config["parent"])
+ }
+
+ ipv6ProxyNdpPath := fmt.Sprintf("ipv6/conf/%s/proxy_ndp", d.config["parent"])
+ sysctlVal, err = NetworkSysctlGet(ipv6ProxyNdpPath)
+ if err != nil {
+ return fmt.Errorf("Error reading net sysctl %s: %v", ipv6ProxyNdpPath, err)
+ }
+ if sysctlVal != "1\n" {
+ return fmt.Errorf("Routed mode requires sysctl net.ipv6.conf.%s.proxy_ndp=1", d.config["parent"])
+ }
+ }
+
+ return nil
+}
+
+// Start is run when the instance is starting up (Routed mode doesn't support hot plugging).
+func (d *nicRouted) Start() (*RunConfig, error) {
+ err := d.validateEnvironment()
+ if err != nil {
+ return nil, err
+ }
+
+ // Lock to avoid issues with containers starting in parallel.
+ networkCreateSharedDeviceLock.Lock()
+ defer networkCreateSharedDeviceLock.Unlock()
+
+ saveData := make(map[string]string)
+
+ // Decide which parent we should use based on VLAN setting.
+ parentName := NetworkGetHostDevice(d.config["parent"], d.config["vlan"])
+
+ statusDev, err := NetworkCreateVlanDeviceIfNeeded(d.state, d.config["parent"], parentName, d.config["vlan"])
+ if err != nil {
+ return nil, err
+ }
+
+ // 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" {
+ err := d.setupParentSysctls(parentName)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ err = d.volatileSet(saveData)
+ if err != nil {
+ return nil, err
+ }
+
+ runConf := RunConfig{}
+ nic := []RunConfigItem{
+ {Key: "name", Value: d.config["name"]},
+ {Key: "type", Value: "veth"},
+ {Key: "flags", Value: "up"},
+ {Key: "veth.mode", Value: "router"},
+ {Key: "l2proxy", Value: "1"},
+ {Key: "link", Value: parentName},
+ }
+
+ if d.config["mtu"] != "" {
+ nic = append(nic, RunConfigItem{Key: "mtu", Value: d.config["mtu"]})
+ }
+
+ if d.config["ipv4.address"] != "" {
+ for _, addr := range strings.Split(d.config["ipv4.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ nic = append(nic, RunConfigItem{Key: "ipv4.address", Value: fmt.Sprintf("%s/32", addr)})
+ }
+
+ nic = append(nic, RunConfigItem{Key: "ipv4.gateway", Value: "auto"})
+ }
+
+ if d.config["ipv6.address"] != "" {
+ for _, addr := range strings.Split(d.config["ipv6.address"], ",") {
+ addr = strings.TrimSpace(addr)
+ nic = append(nic, RunConfigItem{Key: "ipv6.address", Value: fmt.Sprintf("%s/128", addr)})
+ }
+
+ nic = append(nic, RunConfigItem{Key: "ipv6.gateway", Value: "auto"})
+ }
+
+ runConf.NetworkInterface = nic
+ return &runConf, nil
+}
+
+// setupParentSysctls configures the required sysctls on the parent to allow l2proxy to work.
+// Because of our policy not to modify sysctls on existing interfaces, this should only be called
+// if we created the parent interface.
+func (d *nicRouted) setupParentSysctls(parentName string) error {
+ if d.config["ipv4.address"] != "" {
+ // Set necessary sysctls for use with l2proxy parent in routed mode.
+ ipv4FwdPath := fmt.Sprintf("ipv4/conf/%s/forwarding", parentName)
+ err := NetworkSysctlSet(ipv4FwdPath, "1")
+ if err != nil {
+ return fmt.Errorf("Error setting net sysctl %s: %v", ipv4FwdPath, err)
+ }
+ }
+
+ if d.config["ipv6.address"] != "" {
+ // Set necessary sysctls use with l2proxy parent in routed mode.
+ ipv6FwdPath := fmt.Sprintf("ipv6/conf/%s/forwarding", parentName)
+ err := NetworkSysctlSet(ipv6FwdPath, "1")
+ if err != nil {
+ return fmt.Errorf("Error reading net sysctl %s: %v", ipv6FwdPath, err)
+ }
+
+ ipv6ProxyNdpPath := fmt.Sprintf("ipv6/conf/%s/proxy_ndp", parentName)
+ err = NetworkSysctlSet(ipv6ProxyNdpPath, "1")
+ if err != nil {
+ return fmt.Errorf("Error reading net sysctl %s: %v", ipv6ProxyNdpPath, err)
+ }
+ }
+
+ return nil
+}
+
+// Stop is run when the device is removed from the instance.
+func (d *nicRouted) Stop() (*RunConfig, error) {
+ runConf := RunConfig{
+ PostHooks: []func() error{d.postStop},
+ }
+
+ return &runConf, nil
+}
+
+// postStop is run after the device is removed from the instance.
+func (d *nicRouted) postStop() error {
+ defer d.volatileSet(map[string]string{
+ "last_state.created": "",
+ })
+
+ v := d.volatileGet()
+
+ // This will delete the parent interface if we created it for VLAN parent.
+ if shared.IsTrue(v["last_state.created"]) {
+ parentName := NetworkGetHostDevice(d.config["parent"], d.config["vlan"])
+ err := NetworkRemoveInterfaceIfNeeded(d.state, parentName, d.instance, d.config["parent"], d.config["vlan"])
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
More information about the lxc-devel
mailing list