The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6432

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
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.parr...@canonical.com>
From a800a1276a62e46649c1b863038d300bc6564136 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@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.parr...@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
+}
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to