[libvirt] [PATCH 06/10] network: Bridge - Add support for a DHCP Relay Agent

TJ libvirt at iam.tj
Thu Feb 28 02:57:31 UTC 2013


From: TJ <linux at iam.tj>

A DHCP relay daemon will be started that will forward all DHCP/BOOTP
requests on the bridge network via the first declared forward
interface. The relay is broadcast rather than routed so no IP address
is needed on the bridge.

The XML <relay> element's "relay" property of the active DHCP stanza
defaults to 'no'. Set it to 'yes' to enable the relay:

<ip ...>
 <dhcp relay='yes'/>
</ip>

The relay will not be started if the "enable" property is 'no':

<ip ...>
 <dhcp enable='no' relay='yes'/>
</ip>

Signed-off-by: TJ <linux at iam.tj>
---
 src/network/bridge_driver.c | 146 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)

diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 8410c93..c02d3de 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -587,6 +587,145 @@ cleanup:
      * which is later saved into a file
      */
 
+static virNetworkIpDefPtr
+networkGetActiveDhcp(virNetworkObjPtr network)
+{
+    virNetworkIpDefPtr dhcp = NULL;
+
+    if (network->def && network->def->ipv4_dhcp)
+        dhcp = network->def->ipv4_dhcp;
+
+    if (!dhcp &&
+        network->def && network->def->ipv6_dhcp)
+        dhcp = network->def->ipv6_dhcp;
+
+    return dhcp;
+}
+
+static int
+networkBuildDhcpRelayArgv(virNetworkObjPtr network,
+                        const char *pidfile,
+                        virCommandPtr cmd)
+{
+    int ret = -1;
+
+    /* PID file */
+    virCommandAddArgList(cmd, "-r", pidfile, NULL);
+
+    /* Listen for DHCP requests on the bridge interface */
+    virCommandAddArgList(cmd, "-i", network->def->bridge, NULL);
+
+    /* Use the first forwarding device to broadcast to the upstream DHCP server */
+    if (network->def->forward.nifs > 0) {
+        virCommandAddArgList(cmd, "-b", network->def->forward.ifs[0].device.dev, NULL);
+	ret = 0;
+    } else
+	virReportSystemError(VIR_ERR_INVALID_INTERFACE,
+	    _("DHCP relay requires at least one network %s\n"),
+              "<forward ... dev='eth?'/> or <interface dev='eth?'/>");
+
+    return ret;
+}
+
+static int
+networkBuildDhcpRelayCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout,
+                                  char *pidfile)
+{
+    virCommandPtr cmd = NULL;
+    int ret = -1;
+
+    cmd = virCommandNew(DHCPRELAY);
+    if (networkBuildDhcpRelayArgv(network, pidfile, cmd) < 0) {
+        goto cleanup;
+    }
+
+    if (cmdout)
+        *cmdout = cmd;
+    ret = 0;
+cleanup:
+    if (ret < 0)
+        virCommandFree(cmd);
+    return ret;
+}
+
+static int
+networkStartDhcpRelayDaemon(struct network_driver *driver ATTRIBUTE_UNUSED,
+                             virNetworkObjPtr network)
+{
+    virCommandPtr cmd = NULL;
+    virNetworkIpDefPtr ipdef = NULL;
+    char *pidfile = NULL;
+    char *tmp = NULL;
+    int pid_len;
+    int ret = 0;
+    const char *dhcprelay = "dhcprelay_";
+
+    ipdef = networkGetActiveDhcp(network);
+    /* Prepare for DHCP relay agent */
+    if (ipdef && ipdef->dhcp_enabled && ipdef->dhcp_relay) {
+	ret = -1;
+
+        if (virFileMakePath(NETWORK_PID_DIR) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot create directory %s"),
+                                 NETWORK_PID_DIR);
+            goto cleanup;
+        }
+
+        pid_len = strlen(dhcprelay) + strlen(network->def->name);
+        if ( VIR_ALLOC_N(tmp, pid_len+1) >= 0) {
+	    tmp = strcpy(tmp, dhcprelay);
+	    tmp = strncat(tmp, network->def->name, pid_len);
+	    if (!(pidfile = virPidFileBuildPath(NETWORK_PID_DIR, tmp))) {
+	        virReportOOMError();
+	        goto cleanup;
+	    }
+        } else {
+	    virReportOOMError();
+	    goto cleanup;
+	}
+
+        ret = networkBuildDhcpRelayCommandLine(network, &cmd, pidfile);
+        if (ret < 0)
+	    goto cleanup;
+
+        ret = virCommandRun(cmd, NULL);
+        if (ret < 0)
+	    goto cleanup;
+	
+        ret = virPidFileRead(NETWORK_PID_DIR, pidfile, &network->dhcprelayPid);
+        if (ret < 0)
+	    virReportSystemError(errno, _("%s is not running"), DHCPRELAY);
+
+cleanup:
+        VIR_FREE(tmp);
+        VIR_FREE(pidfile);
+        virCommandFree(cmd);
+    }
+    return ret;
+}
+
+static int
+networkRestartDhcpRelayDaemon(struct network_driver *driver,
+                              virNetworkObjPtr network)
+{
+    /* if there is a running DHCP relay agent, kill it */
+    if (network->dhcprelayPid > 0) {
+        networkKillDaemon(network->dhcprelayPid, DHCPRELAY,
+                          network->def->name);
+        network->dhcprelayPid = -1;
+    }
+    /* now start the daemon if it should be started */
+    return networkStartDhcpRelayDaemon(driver, network);
+}
+
+static int
+networkRefreshDhcpRelayDaemon(struct network_driver *driver,
+                              virNetworkObjPtr network)
+{
+    return networkRestartDhcpRelayDaemon(driver, network);
+}
+
 static int
 networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
                                  virNetworkIpDefPtr ipdef)
@@ -1496,6 +1635,7 @@ networkRefreshDaemons(struct network_driver *driver)
              * dnsmasq and/or radvd, or restart them if they've
              * disappeared.
              */
+            networkRefreshDhcpRelayDaemon(driver, network);
             networkRefreshDhcpDaemon(driver, network);
             networkRefreshRadvd(driver, network);
         }
@@ -2462,6 +2602,10 @@ networkStartNetworkVirtual(struct network_driver *driver,
         networkStartDhcpDaemon(driver, network) < 0)
         goto err3;
 
+    /* start DHCP relay-agent (doesn't need IP address(es) to function) */
+    if (networkStartDhcpRelayDaemon(driver, network) < 0)
+	goto err3;
+
     /* start radvd if there are any ipv6 addresses */
     if (v6present && networkStartRadvd(driver, network) < 0)
         goto err4;
@@ -3276,6 +3420,8 @@ networkUpdate(virNetworkPtr net,
              */
             if (networkRestartDhcpDaemon(driver, network) < 0)
                 goto cleanup;
+            if (networkRestartDhcpRelayDaemon(driver, network) < 0)
+                goto cleanup;
 
         } else if (section == VIR_NETWORK_SECTION_IP_DHCP_HOST) {
             /* if we previously weren't listening for dhcp and now we
-- 
1.8.1.2.433.g9808ce0.dirty




More information about the libvir-list mailing list