The optional bootp
- element specifies BOOTP options to be provided by the DHCP server.
+ element specifies BOOTP options to be provided by the DHCP
+ server for IPv4 only.
Two attributes are supported: file
is mandatory and
gives the file to be used for the boot image; server
is
optional and gives the address of the TFTP server from which the boot
@@ -680,6 +699,29 @@
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
</network>
+
+
+ Below is a variation of the above example which adds an IPv6
+ dhcp range definition.
+
+
+
+ <network>
+ <name>default6</name>
+ <bridge name="virbr0" />
+ <forward mode="nat"/>
+ <ip address="192.168.122.1" netmask="255.255.255.0">
+ <dhcp>
+ <range start="192.168.122.2" end="192.168.122.254" />
+ </dhcp>
+ </ip>
+ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" >
+ <dhcp>
+ <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" />
+ </dhcp>
+ </ip>
+ </network>
+
@@ -704,6 +746,29 @@
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
</network>
+
+ Below is another IPv6 varition. Instead of a dhcp range being
+ specified, this example has a couple of IPv6 host definitions.
+
+
+
+ <network>
+ <name>local6</name>
+ <bridge name="virbr1" />
+ <forward mode="route" dev="eth1"/>
+ <ip address="192.168.122.1" netmask="255.255.255.0">
+ <dhcp>
+ <range start="192.168.122.2" end="192.168.122.254" />
+ </dhcp>
+ </ip>
+ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" >
+ <dhcp>
+ <host name="paul" ip="2001:db8:ca2:2:3::1" />
+ <host name="bob" ip="2001:db8:ca2:2:3::2" />
+ </dhcp>
+ </ip>
+ </network>
+
@@ -726,6 +791,24 @@
<ip family="ipv6" address="2001:db8:ca2:3::1" prefix="64" />
</network>
+
+
+
+ This variation of an isolated network defines only IPv6.
+
+
+
+ <network>
+ <name>sixnet</name>
+ <bridge name="virbr6" />
+ <ip family="ipv6" address="2001:db8:ca2:6::1" prefix="64" >
+ <dhcp>
+ <host name="peter" ip="2001:db8:ca2:6:6::1" />
+ <host name="dariusz" ip="2001:db8:ca2:6:6::2" />
+ </dhcp>
+ </ip>
+ </network>
+
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 0d67f7f902..09d7c7350e 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -218,7 +218,7 @@
-
+
@@ -272,15 +272,17 @@
-
-
+
+
-
+
+
+
-
+
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 18fe702151..f9bc37138b 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -654,6 +654,7 @@ cleanup:
static int
virNetworkDHCPHostDefParseXML(const char *networkName,
+ const virNetworkIpDefPtr def,
xmlNodePtr node,
virNetworkDHCPHostDefPtr host,
bool partialOkay)
@@ -665,6 +666,13 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
mac = virXMLPropString(node, "mac");
if (mac != NULL) {
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid to specify MAC address '%s' "
+ "in network '%s' IPv6 static host definition"),
+ mac, networkName);
+ goto cleanup;
+ }
if (virMacAddrParse(mac, &addr) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Cannot parse MAC address '%s' in network '%s'"),
@@ -707,10 +715,20 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
networkName);
}
} else {
- /* normal usage - you need at least one MAC address or one host name */
- if (!(mac || name)) {
+ /* normal usage - you need at least name (IPv6) or one of MAC
+ * address or name (IPv4)
+ */
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
+ if (!name) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Static host definition in IPv6 network '%s' "
+ "must have name attribute"),
+ networkName);
+ goto cleanup;
+ }
+ } else if (!(mac || name)) {
virReportError(VIR_ERR_XML_ERROR,
- _("Static host definition in network '%s' "
+ _("Static host definition in IPv4 network '%s' "
"must have mac or name attribute"),
networkName);
goto cleanup;
@@ -769,15 +787,16 @@ virNetworkDHCPDefParseXML(const char *networkName,
virReportOOMError();
return -1;
}
- if (virNetworkDHCPHostDefParseXML(networkName, cur,
+ if (virNetworkDHCPHostDefParseXML(networkName, def, cur,
&def->hosts[def->nhosts],
false) < 0) {
return -1;
}
def->nhosts++;
- } else if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "bootp")) {
+ } else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) &&
+ cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "bootp")) {
char *file;
char *server;
virSocketAddr inaddr;
@@ -1189,30 +1208,29 @@ virNetworkIPDefParseXML(const char *networkName,
}
}
- if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
- /* parse IPv4-related info */
- cur = node->children;
- while (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
- result = virNetworkDHCPDefParseXML(networkName, cur, def);
- if (result)
- goto cleanup;
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
+ if (virNetworkDHCPDefParseXML(networkName, cur, def) < 0)
+ goto cleanup;
+ } else if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "tftp")) {
+ char *root;
- } else if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "tftp")) {
- char *root;
-
- if (!(root = virXMLPropString(cur, "root"))) {
- cur = cur->next;
- continue;
- }
-
- def->tftproot = (char *)root;
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported element in an IPv6 element in network '%s'"),
+ networkName);
+ goto cleanup;
}
-
- cur = cur->next;
+ if (!(root = virXMLPropString(cur, "root"))) {
+ cur = cur->next;
+ continue;
+ }
+ def->tftproot = (char *)root;
}
+ cur = cur->next;
}
result = 0;
@@ -2494,11 +2512,9 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex)
/* first find which ip element's dhcp host list to work on */
if (parentIndex >= 0) {
ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, parentIndex);
- if (!(ipdef &&
- VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET))) {
+ if (!(ipdef)) {
virReportError(VIR_ERR_OPERATION_INVALID,
- _("couldn't update dhcp host entry - "
- "no "
+ _("couldn't update dhcp host entry - no "
"element found at index %d in network '%s'"),
parentIndex, def->name);
}
@@ -2511,17 +2527,17 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex)
for (ii = 0;
(ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii));
ii++) {
- if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) &&
- (ipdef->nranges || ipdef->nhosts)) {
+ if (ipdef->nranges || ipdef->nhosts)
break;
- }
}
- if (!ipdef)
+ if (!ipdef) {
ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0);
+ if (!ipdef)
+ ipdef = virNetworkDefGetIpByIndex(def, AF_INET6, 0);
+ }
if (!ipdef) {
virReportError(VIR_ERR_OPERATION_INVALID,
- _("couldn't update dhcp host entry - "
- "no "
+ _("couldn't update dhcp host entry - no "
"element found in network '%s'"), def->name);
}
return ipdef;
@@ -2551,8 +2567,10 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
/* parse the xml into a virNetworkDHCPHostDef */
if (command == VIR_NETWORK_UPDATE_COMMAND_MODIFY) {
- if (virNetworkDHCPHostDefParseXML(def->name, ctxt->node, &host, false) < 0)
+ if (virNetworkDHCPHostDefParseXML(def->name, ipdef,
+ ctxt->node, &host, false) < 0) {
goto cleanup;
+ }
/* search for the entry with this (mac|name),
* and update the IP+(mac|name) */
@@ -2584,8 +2602,10 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
} else if ((command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) ||
(command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST)) {
- if (virNetworkDHCPHostDefParseXML(def->name, ctxt->node, &host, true) < 0)
+ if (virNetworkDHCPHostDefParseXML(def->name, ipdef,
+ ctxt->node, &host, true) < 0) {
goto cleanup;
+ }
/* log error if an entry with same name/address/ip already exists */
for (ii = 0; ii < ipdef->nhosts; ii++) {
@@ -2618,8 +2638,10 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
}
} else if (command == VIR_NETWORK_UPDATE_COMMAND_DELETE) {
- if (virNetworkDHCPHostDefParseXML(def->name, ctxt->node, &host, false) < 0)
+ if (virNetworkDHCPHostDefParseXML(def->name, ipdef,
+ ctxt->node, &host, false) < 0) {
goto cleanup;
+ }
/* find matching entry - all specified attributes must match */
for (ii = 0; ii < ipdef->nhosts; ii++) {
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index c94a9a19f8..b8c8ba92dc 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1,3 +1,4 @@
+
/*
* bridge_driver.c: core driver methods for managing network
*
@@ -564,20 +565,35 @@ cleanup:
return ret;
}
+ /* the following does not build a file, it builds a list
+ * which is later saved into a file
+ */
+
static int
-networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
- virNetworkIpDefPtr ipdef,
+networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
+ virNetworkIpDefPtr ipdef)
+{
+ unsigned int i;
+ bool ipv6 = false;
+
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
+ ipv6 = true;
+ for (i = 0; i < ipdef->nhosts; i++) {
+ virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
+ if (VIR_SOCKET_ADDR_VALID(&host->ip))
+ if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name, ipv6) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
virNetworkDNSDefPtr dnsdef)
{
unsigned int i, j;
- for (i = 0; i < ipdef->nhosts; i++) {
- virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
- if ((host->mac) && VIR_SOCKET_ADDR_VALID(&host->ip))
- if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name) < 0)
- return -1;
- }
-
if (dnsdef) {
for (i = 0; i < dnsdef->nhosts; i++) {
virNetworkDNSHostDefPtr host = &(dnsdef->hosts[i]);
@@ -595,7 +611,6 @@ networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
static int
networkBuildDnsmasqArgv(virNetworkObjPtr network,
- virNetworkIpDefPtr ipdef,
const char *pidfile,
virCommandPtr cmd,
dnsmasqContext *dctx,
@@ -609,7 +624,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
char *recordWeight = NULL;
char *recordPriority = NULL;
virNetworkDNSDefPtr dns = &network->def->dns;
- virNetworkIpDefPtr tmpipdef;
+ virNetworkIpDefPtr tmpipdef, ipdef, ipv4def, ipv6def;
+ bool ipv6SLAAC;
/*
* NB, be careful about syntax for dnsmasq options in long format.
@@ -634,14 +650,17 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
* Needed to ensure dnsmasq uses same algorithm for processing
* multiple namedriver entries in /etc/resolv.conf as GLibC.
*/
- virCommandAddArg(cmd, "--strict-order");
+ virCommandAddArgList(cmd, "--strict-order",
+ "--domain-needed",
+ NULL);
- if (network->def->domain)
+ if (network->def->domain) {
virCommandAddArgPair(cmd, "--domain", network->def->domain);
+ virCommandAddArg(cmd, "--expand-hosts");
+ }
/* need to specify local even if no domain specified */
virCommandAddArgFormat(cmd, "--local=/%s/",
network->def->domain ? network->def->domain : "");
- virCommandAddArg(cmd, "--domain-needed");
if (pidfile)
virCommandAddArgPair(cmd, "--pid-file", pidfile);
@@ -763,7 +782,60 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
}
}
- if (ipdef) {
+ /* Find the first dhcp for both IPv4 and IPv6 */
+ for (ii = 0, ipv4def = NULL, ipv6def = NULL, ipv6SLAAC = false;
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
+ ii++) {
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) {
+ if (ipdef->nranges || ipdef->nhosts) {
+ if (ipv4def) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("For IPv4, multiple DHCP definitions cannot "
+ "be specified."));
+ goto cleanup;
+ } else {
+ ipv4def = ipdef;
+ }
+ }
+ }
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) {
+ if (ipdef->nranges || ipdef->nhosts) {
+ if (!DNSMASQ_DHCPv6_SUPPORT(caps)) {
+ unsigned long version = dnsmasqCapsGetVersion(caps);
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("The version of dnsmasq on this host (%d.%d) doesn't "
+ "adequately support IPv6 dhcp range or dhcp host "
+ "specification. Version %d.%d or later is required."),
+ (int)version / 1000000, (int)(version % 1000000) / 1000,
+ DNSMASQ_DHCPv6_MAJOR_REQD, DNSMASQ_DHCPv6_MINOR_REQD);
+ goto cleanup;
+ }
+ if (ipv6def) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("For IPv6, multiple DHCP definitions cannot "
+ "be specified."));
+ goto cleanup;
+ } else {
+ ipv6def = ipdef;
+ }
+ } else {
+ ipv6SLAAC = true;
+ }
+ }
+ }
+
+ if (ipv6def && ipv6SLAAC) {
+ VIR_WARN("For IPv6, when DHCP is specified for one address, then "
+ "state-full Router Advertising will occur. The additional "
+ "IPv6 addresses specified require manually configured guest "
+ "network to work properly since both state-full (DHCP) "
+ "and state-less (SLAAC) addressing are not supported "
+ "on the same network interface.");
+ }
+
+ ipdef = ipv4def ? ipv4def : ipv6def;
+
+ while (ipdef) {
for (r = 0 ; r < ipdef->nranges ; r++) {
char *saddr = virSocketAddrFormat(&ipdef->ranges[r].start);
if (!saddr)
@@ -784,7 +856,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
/*
* For static-only DHCP, i.e. with no range but at least one host element,
* we have to add a special --dhcp-range option to enable the service in
- * dnsmasq.
+ * dnsmasq. (this is for dhcp-hosts= support)
*/
if (!ipdef->nranges && ipdef->nhosts) {
char *bridgeaddr = virSocketAddrFormat(&ipdef->address);
@@ -795,61 +867,91 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
VIR_FREE(bridgeaddr);
}
- if (ipdef->nranges > 0) {
- char *leasefile = networkDnsmasqLeaseFileName(network->def->name);
- if (!leasefile)
- goto cleanup;
- virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile);
- VIR_FREE(leasefile);
- virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases);
- }
-
- if (ipdef->nranges || ipdef->nhosts)
- virCommandAddArg(cmd, "--dhcp-no-override");
-
- /* add domain to any non-qualified hostnames in /etc/hosts or addn-hosts */
- if (network->def->domain)
- virCommandAddArg(cmd, "--expand-hosts");
-
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, &network->def->dns) < 0)
+ if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0)
goto cleanup;
- /* Even if there are currently no static hosts, if we're
- * listening for DHCP, we should write a 0-length hosts
- * file to allow for runtime additions.
- */
- if (ipdef->nranges || ipdef->nhosts)
- virCommandAddArgPair(cmd, "--dhcp-hostsfile",
- dctx->hostsfile->path);
+ /* Note: the following is IPv4 only */
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) {
+ if (ipdef->nranges || ipdef->nhosts)
+ virCommandAddArg(cmd, "--dhcp-no-override");
- /* Likewise, always create this file and put it on the commandline, to allow for
- * for runtime additions.
- */
- virCommandAddArgPair(cmd, "--addn-hosts",
- dctx->addnhostsfile->path);
+ if (ipdef->tftproot) {
+ virCommandAddArgList(cmd, "--enable-tftp",
+ "--tftp-root", ipdef->tftproot,
+ NULL);
+ }
- if (ipdef->tftproot) {
- virCommandAddArgList(cmd, "--enable-tftp",
- "--tftp-root", ipdef->tftproot,
- NULL);
- }
- if (ipdef->bootfile) {
- virCommandAddArg(cmd, "--dhcp-boot");
- if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) {
- char *bootserver = virSocketAddrFormat(&ipdef->bootserver);
+ if (ipdef->bootfile) {
+ virCommandAddArg(cmd, "--dhcp-boot");
+ if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) {
+ char *bootserver = virSocketAddrFormat(&ipdef->bootserver);
- if (!bootserver)
- goto cleanup;
- virCommandAddArgFormat(cmd, "%s%s%s",
+ if (!bootserver) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ virCommandAddArgFormat(cmd, "%s%s%s",
ipdef->bootfile, ",,", bootserver);
- VIR_FREE(bootserver);
- } else {
- virCommandAddArg(cmd, ipdef->bootfile);
+ VIR_FREE(bootserver);
+ } else {
+ virCommandAddArg(cmd, ipdef->bootfile);
+ }
+ }
+ }
+ ipdef = (ipdef == ipv6def) ? NULL : ipv6def;
+ }
+
+ if (nbleases > 0) {
+ char *leasefile = networkDnsmasqLeaseFileName(network->def->name);
+ if (!leasefile) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile);
+ VIR_FREE(leasefile);
+ virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases);
+ }
+
+ /* this is done once per interface */
+ if (networkBuildDnsmasqHostsList(dctx, dns) < 0)
+ goto cleanup;
+
+ /* Even if there are currently no static hosts, if we're
+ * listening for DHCP, we should write a 0-length hosts
+ * file to allow for runtime additions.
+ */
+ if (ipv4def || ipv6def)
+ virCommandAddArgPair(cmd, "--dhcp-hostsfile",
+ dctx->hostsfile->path);
+
+ /* Likewise, always create this file and put it on the commandline,
+ * to allow for runtime additions.
+ */
+ virCommandAddArgPair(cmd, "--addn-hosts",
+ dctx->addnhostsfile->path);
+
+ /* Are we doing RA instead of radvd? */
+ if (DNSMASQ_RA_SUPPORT(caps)) {
+ if (ipv6def)
+ virCommandAddArg(cmd, "--enable-ra");
+ else {
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
+ ii++) {
+ if (!(ipdef->nranges || ipdef->nhosts)) {
+ char *bridgeaddr = virSocketAddrFormat(&ipdef->address);
+ if (!bridgeaddr)
+ goto cleanup;
+ virCommandAddArgFormat(cmd, "--dhcp-range=%s,ra-only",
+ bridgeaddr);
+ VIR_FREE(bridgeaddr);
+ }
}
}
}
ret = 0;
+
cleanup:
VIR_FREE(record);
VIR_FREE(recordPort);
@@ -864,33 +966,12 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdou
dnsmasqCapsPtr caps)
{
virCommandPtr cmd = NULL;
- int ret = -1, ii;
- virNetworkIpDefPtr ipdef;
+ int ret = -1;
network->dnsmasqPid = -1;
- /* Look for first IPv4 address that has dhcp defined. */
- /* We support dhcp config on 1 IPv4 interface only. */
- for (ii = 0;
- (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
- ii++) {
- if (ipdef->nranges || ipdef->nhosts)
- break;
- }
- /* If no IPv4 addresses had dhcp info, pick the first (if there were any). */
- if (!ipdef)
- ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
-
- /* If there are no IP addresses at all (v4 or v6), return now, since
- * there won't be any address for dnsmasq to listen on anyway.
- * If there are any addresses, even if no dhcp ranges or static entries,
- * we should continue and run dnsmasq, just for the DNS capabilities.
- */
- if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0))
- return 0;
-
cmd = virCommandNew(dnsmasqCapsGetBinaryPath(caps));
- if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx, caps) < 0) {
+ if (networkBuildDnsmasqArgv(network, pidfile, cmd, dctx, caps) < 0) {
goto cleanup;
}
@@ -911,11 +992,9 @@ networkStartDhcpDaemon(struct network_driver *driver,
char *pidfile = NULL;
int ret = -1;
dnsmasqContext *dctx = NULL;
- virNetworkIpDefPtr ipdef;
- int i;
if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) {
- /* no IPv6 addresses, so we don't need to run radvd */
+ /* no IP addresses, so we don't need to run */
ret = 0;
goto cleanup;
}
@@ -956,18 +1035,6 @@ networkStartDhcpDaemon(struct network_driver *driver,
if (ret < 0)
goto cleanup;
- /* populate dnsmasq hosts file */
- for (i = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); i++) {
- if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) &&
- (ipdef->nranges || ipdef->nhosts)) {
- if (networkBuildDnsmasqHostsfile(dctx, ipdef,
- &network->def->dns) < 0)
- goto cleanup;
-
- break;
- }
- }
-
ret = dnsmasqSave(dctx);
if (ret < 0)
goto cleanup;
@@ -1000,7 +1067,8 @@ cleanup:
/* networkRefreshDhcpDaemon:
* Update dnsmasq config files, then send a SIGHUP so that it rereads
- * them.
+ * them. This only works for the dhcp-hostsfile and the
+ * addn-hosts file.
*
* Returns 0 on success, -1 on failure.
*/
@@ -1009,34 +1077,51 @@ networkRefreshDhcpDaemon(struct network_driver *driver,
virNetworkObjPtr network)
{
int ret = -1, ii;
- virNetworkIpDefPtr ipdef;
+ virNetworkIpDefPtr ipdef, ipv4def, ipv6def;
dnsmasqContext *dctx = NULL;
+ /* if no IP addresses specified, nothing to do */
+ if (virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0))
+ return 0;
+
/* if there's no running dnsmasq, just start it */
if (network->dnsmasqPid <= 0 || (kill(network->dnsmasqPid, 0) < 0))
return networkStartDhcpDaemon(driver, network);
- /* Look for first IPv4 address that has dhcp defined. */
- /* We support dhcp config on 1 IPv4 interface only. */
+ VIR_INFO("Refreshing dnsmasq for network %s", network->def->bridge);
+ if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR)))
+ goto cleanup;
+
+ /* Look for first IPv4 address that has dhcp defined.
+ * We only support dhcp-host config on one IPv4 subnetwork
+ * and on one IPv6 subnetwork.
+ */
+ ipv4def = NULL;
for (ii = 0;
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
ii++) {
- if (ipdef->nranges || ipdef->nhosts)
- break;
+ if (!ipv4def && (ipdef->nranges || ipdef->nhosts))
+ ipv4def = ipdef;
}
/* If no IPv4 addresses had dhcp info, pick the first (if there were any). */
if (!ipdef)
ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
- if (!ipdef) {
- /* no elements, so nothing to do */
- return 0;
+ ipv6def = NULL;
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
+ ii++) {
+ if (!ipv6def && (ipdef->nranges || ipdef->nhosts))
+ ipv6def = ipdef;
}
- if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR)))
- goto cleanup;
+ if (ipv4def && (networkBuildDnsmasqDhcpHostsList(dctx, ipv4def) < 0))
+ goto cleanup;
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, &network->def->dns) < 0)
+ if (ipv6def && (networkBuildDnsmasqDhcpHostsList(dctx, ipv6def) < 0))
+ goto cleanup;
+
+ if (networkBuildDnsmasqHostsList(dctx, &network->def->dns) < 0)
goto cleanup;
if ((ret = dnsmasqSave(dctx)) < 0)
@@ -1069,27 +1154,51 @@ networkRestartDhcpDaemon(struct network_driver *driver,
return networkStartDhcpDaemon(driver, network);
}
+static char radvd1[] = " AdvOtherConfigFlag off;\n\n";
+static char radvd2[] = " AdvAutonomous off;\n";
+static char radvd3[] = " AdvOnLink on;\n"
+ " AdvAutonomous on;\n"
+ " AdvRouterAddr off;\n";
+
static int
networkRadvdConfContents(virNetworkObjPtr network, char **configstr)
{
virBuffer configbuf = VIR_BUFFER_INITIALIZER;
int ret = -1, ii;
virNetworkIpDefPtr ipdef;
- bool v6present = false;
+ bool v6present = false, dhcp6 = false;
*configstr = NULL;
+ /* Check if DHCPv6 is needed */
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
+ ii++) {
+ v6present = true;
+ if (ipdef->nranges || ipdef->nhosts) {
+ dhcp6 = true;
+ break;
+ }
+ }
+
+ /* If there are no IPv6 addresses, then we are done */
+ if (!v6present) {
+ ret = 0;
+ goto cleanup;
+ }
+
/* create radvd config file appropriate for this network;
* IgnoreIfMissing allows radvd to start even when the bridge is down
*/
virBufferAsprintf(&configbuf, "interface %s\n"
"{\n"
" AdvSendAdvert on;\n"
- " AdvManagedFlag off;\n"
- " AdvOtherConfigFlag off;\n"
" IgnoreIfMissing on;\n"
- "\n",
- network->def->bridge);
+ " AdvManagedFlag %s;\n"
+ "%s",
+ network->def->bridge,
+ dhcp6 ? "on" : "off",
+ dhcp6 ? "\n" : radvd1);
/* add a section for each IPv6 address in the config */
for (ii = 0;
@@ -1098,7 +1207,6 @@ networkRadvdConfContents(virNetworkObjPtr network, char **configstr)
int prefix;
char *netaddr;
- v6present = true;
prefix = virNetworkIpDefPrefix(ipdef);
if (prefix < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1110,12 +1218,9 @@ networkRadvdConfContents(virNetworkObjPtr network, char **configstr)
goto cleanup;
virBufferAsprintf(&configbuf,
" prefix %s/%d\n"
- " {\n"
- " AdvOnLink on;\n"
- " AdvAutonomous on;\n"
- " AdvRouterAddr off;\n"
- " };\n",
- netaddr, prefix);
+ " {\n%s };\n",
+ netaddr, prefix,
+ dhcp6 ? radvd2 : radvd3);
VIR_FREE(netaddr);
}
@@ -1181,7 +1286,8 @@ cleanup:
}
static int
-networkStartRadvd(virNetworkObjPtr network)
+networkStartRadvd(struct network_driver *driver ATTRIBUTE_UNUSED,
+ virNetworkObjPtr network)
{
char *pidfile = NULL;
char *radvdpidbase = NULL;
@@ -1191,6 +1297,12 @@ networkStartRadvd(virNetworkObjPtr network)
network->radvdPid = -1;
+ /* Is dnsmasq handling RA? */
+ if (DNSMASQ_RA_SUPPORT(driver->dnsmasqCaps)) {
+ ret = 0;
+ goto cleanup;
+ }
+
if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
/* no IPv6 addresses, so we don't need to run radvd */
ret = 0;
@@ -1267,9 +1379,27 @@ static int
networkRefreshRadvd(struct network_driver *driver ATTRIBUTE_UNUSED,
virNetworkObjPtr network)
{
+ char *radvdpidbase;
+
+ /* Is dnsmasq handling RA? */
+ if (DNSMASQ_RA_SUPPORT(driver->dnsmasqCaps)) {
+ if (network->radvdPid <= 0)
+ return 0;
+ /* radvd should not be running but in case it is */
+ if ((networkKillDaemon(network->radvdPid, "radvd",
+ network->def->name) >= 0) &&
+ ((radvdpidbase = networkRadvdPidfileBasename(network->def->name))
+ != NULL)) {
+ virPidFileDelete(NETWORK_PID_DIR, radvdpidbase);
+ VIR_FREE(radvdpidbase);
+ }
+ network->radvdPid = -1;
+ return 0;
+ }
+
/* if there's no running radvd, just start it */
if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0))
- return networkStartRadvd(network);
+ return networkStartRadvd(driver, network);
if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
/* no IPv6 addresses, so we don't need to run radvd */
@@ -1296,7 +1426,7 @@ networkRestartRadvd(struct network_driver *driver,
* since there's really no better recovery to be done than to
* just push ahead (and that may be exactly what's needed).
*/
- if ((networkKillDaemon(network->dnsmasqPid, "radvd",
+ if ((networkKillDaemon(network->radvdPid, "radvd",
network->def->name) >= 0) &&
((radvdpidbase = networkRadvdPidfileBasename(network->def->name))
!= NULL)) {
@@ -1590,11 +1720,9 @@ networkRemoveRoutingIptablesRules(struct network_driver *driver,
}
/* Add all once/network rules required for IPv6.
-
* If no IPv6 addresses are defined and is
- * specified, then allow IPv6 commuinications between guests connected
- * to this network. If any IPv6 addresses are defined, then add all
- * rules for regular operation (including inter-guest communication).
+ * specified, then allow IPv6 commuinications between virtual systems.
+ * If any IPv6 addresses are defined, then add the rules for regular operation.
*/
static int
networkAddGeneralIp6tablesRules(struct network_driver *driver,
@@ -1654,9 +1782,19 @@ networkAddGeneralIp6tablesRules(struct network_driver *driver,
goto err5;
}
+ if (iptablesAddUdpInput(driver->iptables, AF_INET6,
+ network->def->bridge, 547) < 0) {
+ virReportError(VIR_ERR_SYSTEM_ERROR,
+ _("failed to add ip6tables rule to allow DHCP6 requests from '%s'"),
+ network->def->bridge);
+ goto err6;
+ }
+
return 0;
/* unwind in reverse order from the point of failure */
+err6:
+ iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53);
err5:
iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53);
err4:
@@ -1674,9 +1812,11 @@ networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
virNetworkObjPtr network)
{
if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0) &&
- !network->def->ipv6nogw)
+ !network->def->ipv6nogw) {
return;
+ }
if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
+ iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 547);
iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53);
iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53);
}
@@ -2268,7 +2408,7 @@ networkStartNetworkVirtual(struct network_driver *driver,
goto err3;
/* start radvd if there are any ipv6 addresses */
- if (v6present && networkStartRadvd(network) < 0)
+ if (v6present && networkStartRadvd(driver, network) < 0)
goto err4;
/* DAD has happened (dnsmasq waits for it), dnsmasq is now bound to the
@@ -2729,8 +2869,7 @@ networkValidate(struct network_driver *driver,
bool vlanUsed, vlanAllowed, badVlanUse = false;
virPortGroupDefPtr defaultPortGroup = NULL;
virNetworkIpDefPtr ipdef;
- bool ipv4def = false;
- int i;
+ bool ipv4def = false, ipv6def = false;
/* check for duplicate networks */
if (virNetworkObjIsDuplicate(&driver->networks, def, check_active) < 0)
@@ -2777,17 +2916,36 @@ networkValidate(struct network_driver *driver,
}
}
- /* We only support dhcp on one IPv4 address per defined network */
- for (i = 0; (ipdef = virNetworkDefGetIpByIndex(def, AF_INET, i)); i++) {
- if (ipdef->nranges || ipdef->nhosts) {
- if (ipv4def) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Multiple dhcp sections found. "
+ /* We only support dhcp on one IPv4 address and
+ * on one IPv6 address per defined network
+ */
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii));
+ ii++) {
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) {
+ if (ipdef->nranges || ipdef->nhosts) {
+ if (ipv4def) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Multiple IPv4 dhcp sections found -- "
"dhcp is supported only for a "
"single IPv4 address on each network"));
- return -1;
- } else {
- ipv4def = true;
+ return -1;
+ } else {
+ ipv4def = true;
+ }
+ }
+ }
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) {
+ if (ipdef->nranges || ipdef->nhosts) {
+ if (ipv6def) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Multiple IPv6 dhcp sections found -- "
+ "dhcp is supported only for a "
+ "single IPv6 address on each network"));
+ return -1;
+ } else {
+ ipv6def = true;
+ }
}
}
}
diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c
index bee3b614b2..e8eab1e29e 100644
--- a/src/util/dnsmasq.c
+++ b/src/util/dnsmasq.c
@@ -293,11 +293,15 @@ hostsfileFree(dnsmasqHostsfile *hostsfile)
VIR_FREE(hostsfile);
}
+/* Note: There are many additional dhcp-host specifications
+ * supported by dnsmasq. There are only the basic ones.
+ */
static int
hostsfileAdd(dnsmasqHostsfile *hostsfile,
const char *mac,
virSocketAddr *ip,
- const char *name)
+ const char *name,
+ bool ipv6)
{
char *ipstr = NULL;
if (VIR_REALLOC_N(hostsfile->hosts, hostsfile->nhosts + 1) < 0)
@@ -306,16 +310,24 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
if (!(ipstr = virSocketAddrFormat(ip)))
return -1;
- if (name) {
- if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s",
- mac, ipstr, name) < 0) {
+ /* the first test determines if it is a dhcpv6 host */
+ if (ipv6) {
+ if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,[%s]",
+ name, ipstr) < 0)
+ goto alloc_error;
+ }
+ else if (name && mac) {
+ if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s",
+ mac, ipstr, name) < 0)
+ goto alloc_error;
+ } else if (name && !mac){
+ if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
+ name, ipstr) < 0)
goto alloc_error;
- }
} else {
if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
- mac, ipstr) < 0) {
+ mac, ipstr) < 0)
goto alloc_error;
- }
}
VIR_FREE(ipstr);
@@ -496,9 +508,10 @@ int
dnsmasqAddDhcpHost(dnsmasqContext *ctx,
const char *mac,
virSocketAddr *ip,
- const char *name)
+ const char *name,
+ bool ipv6)
{
- return hostsfileAdd(ctx->hostsfile, mac, ip, name);
+ return hostsfileAdd(ctx->hostsfile, mac, ip, name, ipv6);
}
/*
diff --git a/src/util/dnsmasq.h b/src/util/dnsmasq.h
index e8881a01eb..7a39232e53 100644
--- a/src/util/dnsmasq.h
+++ b/src/util/dnsmasq.h
@@ -82,7 +82,8 @@ void dnsmasqContextFree(dnsmasqContext *ctx);
int dnsmasqAddDhcpHost(dnsmasqContext *ctx,
const char *mac,
virSocketAddr *ip,
- const char *name);
+ const char *name,
+ bool ipv6);
int dnsmasqAddHost(dnsmasqContext *ctx,
virSocketAddr *ip,
const char *name);
@@ -99,4 +100,18 @@ int dnsmasqCapsRefresh(dnsmasqCapsPtr *caps, const char *binaryPath);
bool dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag);
const char *dnsmasqCapsGetBinaryPath(dnsmasqCapsPtr caps);
unsigned long dnsmasqCapsGetVersion(dnsmasqCapsPtr caps);
+
+# define DNSMASQ_DHCPv6_MAJOR_REQD 2
+# define DNSMASQ_DHCPv6_MINOR_REQD 64
+# define DNSMASQ_RA_MAJOR_REQD 2
+# define DNSMASQ_RA_MINOR_REQD 64
+
+# define DNSMASQ_DHCPv6_SUPPORT(CAPS) \
+ (dnsmasqCapsGetVersion(CAPS) >= \
+ (DNSMASQ_DHCPv6_MAJOR_REQD * 1000000) + \
+ (DNSMASQ_DHCPv6_MINOR_REQD * 1000))
+# define DNSMASQ_RA_SUPPORT(CAPS) \
+ (dnsmasqCapsGetVersion(CAPS) >= \
+ (DNSMASQ_RA_MAJOR_REQD * 1000000) + \
+ (DNSMASQ_RA_MINOR_REQD * 1000))
#endif /* __DNSMASQ_H__ */
diff --git a/tests/networkxml2argvdata/dhcp6-nat-network.argv b/tests/networkxml2argvdata/dhcp6-nat-network.argv
new file mode 100644
index 0000000000..df8c507581
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-nat-network.argv
@@ -0,0 +1,15 @@
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-dynamic \
+--interface virbr0 \
+--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
+--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \
+--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
+--dhcp-lease-max=493 \
+--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \
+--enable-ra\
diff --git a/tests/networkxml2argvdata/dhcp6-nat-network.xml b/tests/networkxml2argvdata/dhcp6-nat-network.xml
new file mode 100644
index 0000000000..72103f7139
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-nat-network.xml
@@ -0,0 +1,24 @@
+
+ default
+ 81ff0d90-c91e-6742-64da-4a736edb9a9b
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/networkxml2argvdata/dhcp6-network.argv b/tests/networkxml2argvdata/dhcp6-network.argv
new file mode 100644
index 0000000000..059c4188e7
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-network.argv
@@ -0,0 +1,15 @@
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--domain=mynet \
+--expand-hosts \
+--local=/mynet/ \
+--conf-file= \
+--bind-dynamic \
+--interface virbr0 \
+--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \
+--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
+--dhcp-lease-max=240 \
+--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \
+--enable-ra\
diff --git a/tests/networkxml2argvdata/dhcp6-network.xml b/tests/networkxml2argvdata/dhcp6-network.xml
new file mode 100644
index 0000000000..311013ad52
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-network.xml
@@ -0,0 +1,14 @@
+
+ default
+ 81ff0d90-c91e-6742-64da-4a736edb9a9b
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/networkxml2argvdata/dhcp6host-routed-network.argv b/tests/networkxml2argvdata/dhcp6host-routed-network.argv
new file mode 100644
index 0000000000..8f6d7d3b2d
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6host-routed-network.argv
@@ -0,0 +1,13 @@
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-dynamic \
+--interface virbr1 \
+--dhcp-range 192.168.122.1,static \
+--dhcp-no-override \
+--dhcp-range 2001:db8:ac10:fd01::1,static \
+--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/local.hostsfile \
+--addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts \
+--enable-ra\
diff --git a/tests/networkxml2argvdata/dhcp6host-routed-network.xml b/tests/networkxml2argvdata/dhcp6host-routed-network.xml
new file mode 100644
index 0000000000..38d9ebf892
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6host-routed-network.xml
@@ -0,0 +1,19 @@
+
+ local
+ 81ff0d90-c91e-6742-64da-4a736edb9a9b
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/networkxml2argvdata/isolated-network.argv b/tests/networkxml2argvdata/isolated-network.argv
index 3d8601e686..31633859bd 100644
--- a/tests/networkxml2argvdata/isolated-network.argv
+++ b/tests/networkxml2argvdata/isolated-network.argv
@@ -1,10 +1,16 @@
-@DNSMASQ@ --strict-order \
---local=// --domain-needed --conf-file= \
---bind-interfaces --except-interface lo \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-interfaces \
+--except-interface lo \
--listen-address 192.168.152.1 \
---dhcp-option=3 --no-resolv \
+--dhcp-option=3 \
+--no-resolv \
--dhcp-range 192.168.152.2,192.168.152.254 \
---dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases --dhcp-lease-max=253 \
--dhcp-no-override \
+--dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases \
+--dhcp-lease-max=253 \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile \
--addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts\
diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
index e5143acbbe..fee137f561 100644
--- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
@@ -1,5 +1,10 @@
-@DNSMASQ@ --strict-order --domain=example.com \
---local=/example.com/ --domain-needed \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
--conf-file= \
---bind-dynamic --interface virbr0 \
---expand-hosts --addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
+--bind-dynamic \
+--interface virbr0 \
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
index 031da3fc3c..a86b2d2d20 100644
--- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
@@ -1,7 +1,10 @@
@DNSMASQ@ \
--strict-order \
---local=// --domain-needed --conf-file= \
---bind-interfaces --except-interface lo \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-interfaces \
+--except-interface lo \
--listen-address 192.168.122.1 \
--listen-address 192.168.123.1 \
--listen-address fc00:db8:ac10:fe01::1 \
@@ -9,8 +12,8 @@
--listen-address 10.24.10.1 \
--srv-host=name.tcp.,,,, \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
--dhcp-lease-max=253 \
---dhcp-no-override \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
index beff591a34..e7ecaa502d 100644
--- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
@@ -1,11 +1,14 @@
@DNSMASQ@ \
--strict-order \
---local=// --domain-needed --conf-file= \
---bind-dynamic --interface virbr0 \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-dynamic \
+--interface virbr0 \
--srv-host=name.tcp.test-domain-name,.,1024,10,10 \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
--dhcp-lease-max=253 \
---dhcp-no-override \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
index fc164f6d78..8ea00044f6 100644
--- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
@@ -1,9 +1,13 @@
-@DNSMASQ@ --strict-order \
---local=// --domain-needed --conf-file= \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
--bind-dynamic --interface virbr0 \
'--txt-record=example,example value' \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
---dhcp-lease-max=253 --dhcp-no-override \
+--dhcp-lease-max=253 \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
diff --git a/tests/networkxml2argvdata/nat-network.argv b/tests/networkxml2argvdata/nat-network.argv
index 6303f76a1f..578a5ff432 100644
--- a/tests/networkxml2argvdata/nat-network.argv
+++ b/tests/networkxml2argvdata/nat-network.argv
@@ -1,8 +1,15 @@
-@DNSMASQ@ --strict-order \
---local=// --domain-needed --conf-file= \
---bind-dynamic --interface virbr0 \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-dynamic \
+--interface virbr0 \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
---dhcp-lease-max=253 --dhcp-no-override \
+--dhcp-lease-max=253 \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \
---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \
+--dhcp-range=2001:db8:ac10:fe01::1,ra-only \
+--dhcp-range=2001:db8:ac10:fd01::1,ra-only\
diff --git a/tests/networkxml2argvdata/netboot-network.argv b/tests/networkxml2argvdata/netboot-network.argv
index 9699e5c8eb..11ed16a245 100644
--- a/tests/networkxml2argvdata/netboot-network.argv
+++ b/tests/networkxml2argvdata/netboot-network.argv
@@ -1,10 +1,19 @@
-@DNSMASQ@ --strict-order --domain=example.com \
---local=/example.com/ --domain-needed --conf-file= \
---bind-interfaces --except-interface lo --listen-address 192.168.122.1 \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
+--conf-file= \
+--bind-interfaces \
+--except-interface lo \
+--listen-address 192.168.122.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
---dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \
---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \
---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \
---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \
+--dhcp-no-override \
--enable-tftp \
---tftp-root /var/lib/tftproot --dhcp-boot pxeboot.img\
+--tftp-root /var/lib/tftproot \
+--dhcp-boot pxeboot.img \
+--dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \
+--dhcp-lease-max=253 \
+--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \
+--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\
diff --git a/tests/networkxml2argvdata/netboot-proxy-network.argv b/tests/networkxml2argvdata/netboot-proxy-network.argv
index 9ac301832e..ea31a325e0 100644
--- a/tests/networkxml2argvdata/netboot-proxy-network.argv
+++ b/tests/networkxml2argvdata/netboot-proxy-network.argv
@@ -1,10 +1,17 @@
-@DNSMASQ@ --strict-order --domain=example.com \
---local=/example.com/ --domain-needed --conf-file= \
---bind-interfaces --except-interface lo \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
+--conf-file= \
+--bind-interfaces \
+--except-interface lo \
--listen-address 192.168.122.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
+--dhcp-boot pxeboot.img,,10.20.30.40 \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \
---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \
+--dhcp-lease-max=253 \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \
---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \
---dhcp-boot pxeboot.img,,10.20.30.40\
+--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\
diff --git a/tests/networkxml2argvdata/routed-network.argv b/tests/networkxml2argvdata/routed-network.argv
index 700c904a3f..b3fbf49ef0 100644
--- a/tests/networkxml2argvdata/routed-network.argv
+++ b/tests/networkxml2argvdata/routed-network.argv
@@ -1,4 +1,8 @@
-@DNSMASQ@ --strict-order \
---local=// --domain-needed --conf-file= \
---bind-dynamic --interface virbr1 \
+@DNSMASQ@ \
+--strict-order \
+--domain-needed \
+--local=// \
+--conf-file= \
+--bind-dynamic \
+--interface virbr1 \
--addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\
diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c
index 69cbd1c1c6..a020db910b 100644
--- a/tests/networkxml2argvtest.c
+++ b/tests/networkxml2argvtest.c
@@ -152,6 +152,8 @@ mymain(void)
= dnsmasqCapsNewFromBuffer("Dnsmasq version 2.48", DNSMASQ);
dnsmasqCapsPtr full
= dnsmasqCapsNewFromBuffer("Dnsmasq version 2.63\n--bind-dynamic", DNSMASQ);
+ dnsmasqCapsPtr dhcpv6
+ = dnsmasqCapsNewFromBuffer("Dnsmasq version 2.64\n--bind-dynamic", DNSMASQ);
networkDnsmasqLeaseFileName = testDnsmasqLeaseFileName;
@@ -172,10 +174,13 @@ mymain(void)
DO_TEST("netboot-proxy-network", restricted);
DO_TEST("nat-network-dns-srv-record-minimal", restricted);
DO_TEST("routed-network", full);
- DO_TEST("nat-network", full);
+ DO_TEST("nat-network", dhcpv6);
DO_TEST("nat-network-dns-txt-record", full);
DO_TEST("nat-network-dns-srv-record", full);
DO_TEST("nat-network-dns-hosts", full);
+ DO_TEST("dhcp6-network", dhcpv6);
+ DO_TEST("dhcp6-nat-network", dhcpv6);
+ DO_TEST("dhcp6host-routed-network", dhcpv6);
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}