[lxc-devel] [lxd/master] Network: Add EUI64 guessing for bridged NIC state
tomponline on Github
lxc-bot at linuxcontainers.org
Thu Oct 1 16:50:26 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201001/16a7acf2/attachment.bin>
-------------- next part --------------
From 9a0cf626779c141f59b358963cede5632c988563 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 1 Oct 2020 17:07:39 +0100
Subject: [PATCH 1/3] lxd/network/network/utils: Adds GetNeighbourV6Addresses
function
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/network_utils.go | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index c195228a3d..a4ead17f6f 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -805,6 +805,34 @@ func GetHostDevice(parent string, vlan string) string {
return defaultVlan
}
+// GetNeighbourV6Addresses returns the IPv6 addresses in the neighbour cache for a particular interface and MAC.
+func GetNeighbourV6Addresses(interfaceName string, hwaddr string) ([]net.IP, error) {
+ addresses := []net.IP{}
+
+ // Look for neighbour entries for IPv6.
+ out, err := shared.RunCommand("ip", "-6", "neigh", "show", "dev", interfaceName)
+ if err == nil {
+ for _, line := range strings.Split(out, "\n") {
+ // Split fields and early validation.
+ fields := strings.Fields(line)
+ if len(fields) != 4 {
+ continue
+ }
+
+ if fields[2] != hwaddr {
+ continue
+ }
+
+ ip := net.ParseIP(fields[0])
+ if ip != nil {
+ addresses = append(addresses, ip)
+ }
+ }
+ }
+
+ return addresses, nil
+}
+
// GetLeaseAddresses returns the lease addresses for a network and hwaddr.
func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api.InstanceStateNetworkAddress, error) {
addresses := []api.InstanceStateNetworkAddress{}
From 6ba197ad54e2dff9416ff698cdbbc2c6e836aa1c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 1 Oct 2020 17:46:42 +0100
Subject: [PATCH 2/3] lxd/network/network/utils: Updates GetLeaseAddresses to
return only net.IP list
- Removes IPv6 neighbour look as was out of scope for this function's name.
- Returns []net.IP rather than []api.InstanceStateNetworkAddress.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/network_utils.go | 79 ++++--------------------------------
1 file changed, 9 insertions(+), 70 deletions(-)
diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index a4ead17f6f..96d57465ed 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -834,48 +834,10 @@ func GetNeighbourV6Addresses(interfaceName string, hwaddr string) ([]net.IP, err
}
// GetLeaseAddresses returns the lease addresses for a network and hwaddr.
-func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api.InstanceStateNetworkAddress, error) {
- addresses := []api.InstanceStateNetworkAddress{}
-
- // Look for neighborhood entries for IPv6.
- out, err := shared.RunCommand("ip", "-6", "neigh", "show", "dev", networkName)
- if err == nil {
- for _, line := range strings.Split(out, "\n") {
- // Split fields and early validation.
- fields := strings.Fields(line)
- if len(fields) != 4 {
- continue
- }
-
- if fields[2] != hwaddr {
- continue
- }
-
- // Prepare the entry.
- addr := api.InstanceStateNetworkAddress{}
- addr.Address = fields[0]
- addr.Family = "inet6"
-
- if strings.HasPrefix(fields[0], "fe80::") {
- addr.Scope = "link"
- } else {
- addr.Scope = "global"
- }
-
- addresses = append(addresses, addr)
- }
- }
-
- // Look for DHCP leases.
+func GetLeaseAddresses(networkName string, hwaddr string) ([]net.IP, error) {
leaseFile := shared.VarPath("networks", networkName, "dnsmasq.leases")
if !shared.PathExists(leaseFile) {
- return addresses, nil
- }
-
- // Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects.
- dbInfo, err := LoadByName(s, project.Default, networkName)
- if err != nil {
- return nil, err
+ return nil, fmt.Errorf("Leases file not found for network %q", networkName)
}
content, err := ioutil.ReadFile(leaseFile)
@@ -883,13 +845,15 @@ func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api
return nil, err
}
+ addresses := []net.IP{}
+
for _, lease := range strings.Split(string(content), "\n") {
fields := strings.Fields(lease)
if len(fields) < 5 {
continue
}
- // Parse the MAC
+ // Parse the MAC.
mac := GetMACSlice(fields[1])
macStr := strings.Join(mac, ":")
@@ -901,36 +865,11 @@ func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api
continue
}
- // Parse the IP
- addr := api.InstanceStateNetworkAddress{
- Address: fields[2],
- Scope: "global",
- }
-
- ip := net.ParseIP(addr.Address)
- if ip == nil {
- continue
- }
-
- if ip.To4() != nil {
- addr.Family = "inet"
-
- _, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv4.address"])
- if subnet != nil {
- mask, _ := subnet.Mask.Size()
- addr.Netmask = fmt.Sprintf("%d", mask)
- }
- } else {
- addr.Family = "inet6"
-
- _, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv6.address"])
- if subnet != nil {
- mask, _ := subnet.Mask.Size()
- addr.Netmask = fmt.Sprintf("%d", mask)
- }
+ // Parse the IP.
+ ip := net.ParseIP(fields[2])
+ if ip != nil {
+ addresses = append(addresses, ip)
}
-
- addresses = append(addresses, addr)
}
return addresses, nil
From 5d2b5543d6a62aa4d77e4a3a8fedc59765045fc6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 1 Oct 2020 17:21:54 +0100
Subject: [PATCH 3/3] lxd/device/nic/bridged: Updates State() to return partial
data
- Only tries to parse dnsmasq leases file if parent network is managed.
- If parent network is not managed, no longer treat this as complete failure.
- Try and find IPv6 addresses from IP neighbour cache using network.GetNeighbourV6Addresses.
- Load interface MTU using NIC's `host_name` rather than parent interface (should be the same but more consistent with how OVN NIC does it).
- Also means that if the NIC's host veth interface is missing, then this call will not return any results.
- If interface is available, will return interface stats even with no IPs found.
- Guess link-local address netmask size based on IP family.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_bridged.go | 77 ++++++++++++++++++++++++++++++++++-----
1 file changed, 67 insertions(+), 10 deletions(-)
diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index f8a255e4d8..47b0d67379 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -1091,24 +1091,81 @@ func (d *nicBridged) State() (*api.InstanceStateNetwork, error) {
// Populate device config with volatile fields if needed.
networkVethFillFromVolatile(d.config, v)
- if d.config["hwaddr"] == "" {
- return nil, nil
+ ips := []net.IP{}
+
+ // Check if parent is managed network and load config.
+ // Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects.
+ n, err := network.LoadByName(d.state, project.Default, d.config["parent"])
+ if err == nil && d.config["hwaddr"] != "" {
+ // Parse the leases file if parent network is managed.
+ leaseIPs, err := network.GetLeaseAddresses(n.Name(), d.config["hwaddr"])
+ if err == nil {
+ for _, leaseIP := range leaseIPs {
+ ips = append(ips, leaseIP)
+ }
+ }
}
- // Parse the leases file.
- addresses, err := network.GetLeaseAddresses(d.state, d.config["parent"], d.config["hwaddr"])
- if err != nil {
- return nil, err
+ // Get IPv6 addresses from IP neighbour cache if present.
+ neighIPs, err := network.GetNeighbourV6Addresses(d.config["parent"], d.config["hwaddr"])
+ if err == nil {
+ for _, neighIP := range neighIPs {
+ ips = append(ips, neighIP)
+ }
+ }
+
+ // Extract subnet sizes from bridge addresses if available.
+ var v4mask string
+ var v6mask string
+
+ if n != nil {
+ netConfig := n.Config()
+ _, v4subnet, _ := net.ParseCIDR(netConfig["ipv4.address"])
+ _, v6subnet, _ := net.ParseCIDR(netConfig["ipv6.address"])
+
+ if v4subnet != nil {
+ mask, _ := v4subnet.Mask.Size()
+ v4mask = fmt.Sprintf("%d", mask)
+ }
+
+ if v6subnet != nil {
+ mask, _ := v6subnet.Mask.Size()
+ v6mask = fmt.Sprintf("%d", mask)
+ }
}
- if len(addresses) == 0 {
- return nil, nil
+ // Convert IPs to InstanceStateNetworkAddresses.
+ addresses := []api.InstanceStateNetworkAddress{}
+ for _, ip := range ips {
+ addr := api.InstanceStateNetworkAddress{}
+ addr.Address = ip.String()
+ addr.Family = "inet"
+ addr.Netmask = v4mask
+
+ if ip.To4() == nil {
+ addr.Family = "inet6"
+ addr.Netmask = v6mask
+ }
+
+ if ip.IsLinkLocalUnicast() {
+ addr.Scope = "link"
+
+ if addr.Family == "inet6" {
+ addr.Netmask = "64" // Link-local IPv6 addresses are /64.
+ } else {
+ addr.Netmask = "16" // Local-local IPv4 addresses are /16.
+ }
+ } else {
+ addr.Scope = "global"
+ }
+
+ addresses = append(addresses, addr)
}
// Get MTU.
- iface, err := net.InterfaceByName(d.config["parent"])
+ iface, err := net.InterfaceByName(d.config["host_name"])
if err != nil {
- return nil, err
+ return nil, errors.Wrapf(err, "Failed getting host interface state")
}
// Retrieve the host counters, as we report the values from the instance's point of view,
More information about the lxc-devel
mailing list