[lxc-devel] [lxd/master] Use DHCP Release to remove dnsmasq leases to avoid restarting dnsmasq

tomponline on Github lxc-bot at linuxcontainers.org
Mon Jun 17 14:12:25 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 570 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190617/baa0da0f/attachment.bin>
-------------- next part --------------
From d2eb90a1ee8973ce80d5d3935884909e1737feda Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 17 Jun 2019 15:08:16 +0100
Subject: [PATCH 1/3] container/lxc: Adds error checking for calls to
 networkClearLease

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_lxc.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index f5cad36e9e..ec4c845803 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -4103,7 +4103,10 @@ func (c *containerLXC) Delete() error {
 				continue
 			}
 
-			networkClearLease(c.state, c.name, m["parent"], m["hwaddr"])
+			err = networkClearLease(c.state, c.name, m["parent"], m["hwaddr"])
+			if err != nil {
+				logger.Error("Failed to delete DHCP lease", log.Ctx{"name": c.Name(), "err": err, "device": k, "hwaddr": m["hwaddr"]})
+			}
 		}
 	}
 

From 2915a2fb22d9d21ffcf258e05adfee794c282032 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 17 Jun 2019 15:09:11 +0100
Subject: [PATCH 2/3] networks/utils: Adds networkDHCPRelease function

This function sends a manually crafted DHCP release packet to the local dnsmasq server to remove the lease.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks_utils.go | 56 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index 7fd48a5caa..5f9ef034cb 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -20,6 +20,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
 	"golang.org/x/net/context"
 	"golang.org/x/sys/unix"
 
@@ -1443,3 +1445,57 @@ func networkApplyBootRoutesV6(devName string, routes []string) error {
 
 	return nil
 }
+
+// networkDHCPRelease sends a DHCP release packet to a DHCP server
+func networkDHCPRelease(srcMAC net.HardwareAddr, srcIP net.IP, dstIP net.IP) error {
+	dstAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:67", dstIP.String()))
+	if err != nil {
+		return err
+	}
+	conn, err := net.DialUDP("udp", nil, dstAddr)
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	//Random DHCP transaction ID
+	xid := rand.Uint32()
+
+	// Construct a DHCP packet pretending to be from the source IP and MAC supplied.
+	dhcp := layers.DHCPv4{
+		Operation:    layers.DHCPOpRequest,
+		HardwareType: layers.LinkTypeEthernet,
+		ClientHWAddr: srcMAC,
+		ClientIP:     srcIP,
+		Xid:          xid,
+	}
+
+	// addOption function is used to append DHCP options.
+	addOption := func(optType layers.DHCPOpt, data []byte) {
+		dhcp.Options = append(dhcp.Options, layers.DHCPOption{
+			Type:   optType,
+			Data:   data,
+			Length: uint8(len(data)),
+		})
+		return
+	}
+
+	// Add two options, one definging the message type (release), and one targetting the DHCP
+	// server ID that originally allocated the lease (so it responds to our packet).
+	addOption(layers.DHCPOptMessageType, []byte{byte(layers.DHCPMsgTypeRelease)})
+	addOption(layers.DHCPOptServerID, dstIP.To4())
+
+	buf := gopacket.NewSerializeBuffer()
+	opts := gopacket.SerializeOptions{
+		ComputeChecksums: true,
+		FixLengths:       true,
+	}
+
+	err = gopacket.SerializeLayers(buf, opts, &dhcp)
+	if err != nil {
+		return err
+	}
+
+	_, err = conn.Write(buf.Bytes())
+	return err
+}

From ae573b1fe313a1dbb28f713f0895f53e3185d80f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 17 Jun 2019 15:10:02 +0100
Subject: [PATCH 3/3] networks/utils: Updates networkClearLease to use
 networkDHCPRelease

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/networks_utils.go | 75 ++++++++++++++++++-------------------------
 1 file changed, 32 insertions(+), 43 deletions(-)

diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index 5f9ef034cb..6bc9cc2420 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -1180,65 +1180,54 @@ func networkClearLease(s *state.State, name string, network string, hwaddr strin
 		return nil
 	}
 
-	// Restart the network when we're done here
-	n, err := networkLoadByName(s, network)
+	iface, err := net.InterfaceByName(network)
 	if err != nil {
 		return err
 	}
-	defer func() {
-		err := n.Start()
-		if err != nil {
-			logger.Errorf("Failed to reload network '%s': %v", network, err)
-		}
-	}()
 
-	// Stop dnsmasq
-	err = networkKillDnsmasq(network, false)
-	if err != nil {
-		return err
+	// Use first IPv4 address on network interface as destination IP to dnsmasq.
+	addrs, err := iface.Addrs()
+	var dstIP net.IP
+	if err == nil {
+		for _, addr := range addrs {
+			ip, _, err := net.ParseCIDR(addr.String())
+			if err != nil {
+				return err
+			}
+			if ip.To4() != nil {
+				dstIP = ip
+				break
+			}
+		}
 	}
 
-	// Mangle the lease file
-	leases, err := ioutil.ReadFile(leaseFile)
-	if err != nil {
-		return err
+	if dstIP == nil {
+		return fmt.Errorf("No IPv4 addresses found on interface %s", iface.Name)
 	}
 
-	fd, err := os.Create(leaseFile)
+	// Get the IP allocated to MAC address.
+	file, err := os.Open(leaseFile)
 	if err != nil {
 		return err
 	}
+	defer file.Close()
 
-	knownMac := networkGetMacSlice(hwaddr)
-	for _, lease := range strings.Split(string(leases), "\n") {
-		if lease == "" {
-			continue
-		}
-
-		fields := strings.Fields(lease)
-		if len(fields) > 2 {
-			if strings.Contains(fields[1], ":") {
-				leaseMac := networkGetMacSlice(fields[1])
-				leaseMacStr := strings.Join(leaseMac, ":")
-
-				knownMacStr := strings.Join(knownMac[len(knownMac)-len(leaseMac):], ":")
-				if knownMacStr == leaseMacStr {
-					continue
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		fields := strings.Fields(scanner.Text())
+		if len(fields) >= 5 {
+			if hwaddr == fields[1] {
+				srcMac, err := net.ParseMAC(hwaddr)
+				if err != nil {
+					return err
 				}
-			} else if len(fields) > 3 && fields[3] == name {
-				// Mostly IPv6 leases which don't contain a MAC address...
-				continue
-			}
-		}
 
-		_, err := fd.WriteString(fmt.Sprintf("%s\n", lease))
-		if err != nil {
-			return err
+				srcIP := net.ParseIP(fields[2])
+				return networkDHCPRelease(srcMac, srcIP, dstIP)
+			}
 		}
 	}
-
-	err = fd.Close()
-	if err != nil {
+	if err := scanner.Err(); err != nil {
 		return err
 	}
 


More information about the lxc-devel mailing list