From: TJ <linux(a)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(a)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