---
docs/formatnetwork.html.in | 108 ++++++-
src/conf/network_conf.c | 100 ++++---
src/conf/network_conf.h | 1 +
src/network/bridge_driver.c | 333 +++++++++++++++------
src/util/dnsmasq.c | 9 +-
tests/networkxml2argvdata/dhcp6-network.argv | 17 ++
tests/networkxml2argvdata/dhcp6-network.xml | 16 +
tests/networkxml2argvdata/isolated-network.argv | 15 +-
tests/networkxml2argvdata/nat-network-dhcp6.argv | 20 ++
tests/networkxml2argvdata/nat-network-dhcp6.xml | 26 ++
.../networkxml2argvdata/nat-network-dns-hosts.argv | 15 +-
.../nat-network-dns-srv-record-minimal.argv | 11 +-
.../nat-network-dns-srv-record.argv | 11 +-
.../nat-network-dns-txt-record.argv | 23 +-
tests/networkxml2argvdata/nat-network.argv | 23 +-
tests/networkxml2argvdata/netboot-network.argv | 23 +-
.../networkxml2argvdata/netboot-proxy-network.argv | 20 +-
.../routed-network-dhcphost.argv | 15 +
.../routed-network-dhcphost.xml | 19 ++
tests/networkxml2argvdata/routed-network.argv | 11 +-
tests/networkxml2argvtest.c | 4 +
21 files changed, 633 insertions(+), 187 deletions(-)
create mode 100644 tests/networkxml2argvdata/dhcp6-network.argv
create mode 100644 tests/networkxml2argvdata/dhcp6-network.xml
create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.argv
create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.xml
create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.argv
create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 7b3b25c..50e00f9 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -577,8 +577,10 @@
dotted-decimal format, or an IPv6 address in standard
colon-separated hexadecimal format, that will be configured on
the bridge
- device associated with the virtual network. To the guests this
- address will be their default route. For IPv4 addresses, the <code>netmask</code>
+ device associated with the virtual network. To the guests this IPv4
+ address will be their IPv4 default route. For IPv6, the default route is
+ established via Router Advertisement.
+ For IPv4 addresses, the <code>netmask</code>
attribute defines the significant bits of the network address,
again specified in dotted-decimal format. For IPv6 addresses,
and as an alternate method for IPv4 addresses, you can specify
@@ -587,10 +589,13 @@
could also be given as <code>prefix='24'</code>. The <code>family</code>
attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no
<code>family</code> is given, 'ipv4' is assumed. A network can have more than
- one of each family of address defined, but only a single address can have a
- <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0;
+ one of each family of address defined, but only a single IPv4 address can have a
+ <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0 </span>
IPv6, multiple addresses on a single network, <code>family</code>, and
- <code>prefix</code> since 0.8.7</span>
+ <code>prefix</code>. <span class="since">Since 0.8.7</span> In addition
+ to the one IPv4 address which has a <code>dhcp</code> definition, one IPv6
+ address can have a <code>dhcp</code> definition.
+ <span class="since"> Since 1.0.1</span>
<dl>
<dt><code>tftp</code></dt>
<dd>Immediately within
@@ -611,27 +616,46 @@
<code>dhcp</code> element is not supported for IPv6, and
is only supported on a single IP address per network for IPv4.
<span class="since">Since 0.3.0</span>
+ The <code>dhcp</code> element is now supported for IPv6.
+ Again, there is a restriction that only one IPv6 address definition
+ is able to have a <code>dhcp</code> element.
+ <span class="since">Since 1.0.1</span>
<dl>
<dt><code>range</code></dt>
<dd>The <code>start</code> and <code>end</code> attributes on the
<code>range</code> element specify the boundaries of a pool of
- IPv4 addresses to be provided to DHCP clients. These two addresses
+ addresses to be provided to DHCP clients. These two addresses
must lie within the scope of the network defined on the parent
- <code>ip</code> element. <span class="since">Since 0.3.0</span>
+ <code>ip</code> element. There may be zero or more
+ <code>range</code> elements specified.
+ <span class="since">Since 0.3.0</span>
+ <code>Range</code> can be specified for one IPv4 address,
+ one IPv6 address, or both. <span class="since">Since 1.0.1</span>
</dd>
<dt><code>host</code></dt>
<dd>Within the <code>dhcp</code> element there may be zero or more
- <code>host</code> elements; these specify hosts which will be given
+ <code>host</code> elements. These specify hosts which will be given
names and predefined IP addresses by the built-in DHCP server. Any
- such element must specify the MAC address of the host to be assigned
+ such IPv4 element must specify the MAC address of the host to be assigned
a given name (via the <code>mac</code> attribute), the IP to be
assigned to that host (via the <code>ip</code> attribute), and the
name to be given that host by the DHCP server (via the
<code>name</code> attribute). <span class="since">Since 0.4.5</span>
+ Within the IPv6 <code>dhcp</code> element zero or more
+ <code>host</code> elements are now supported. The definition for
+ an IPv6 <code>host</code> element differs from that for IPv4:
+ there is no <code>mac</code> attribute since a MAC address has no
+ defined meaning in IPv6. Instead, the <code>name</code> attribute is
+ used to identify the host to be assigned the IPv6 address. For DHCPv6,
+ the name is the plain name of the client host sent by the
+ client to the server. Note that this method of assigning a
+ specific IP address can be used instead of the <code>mac</code>
+ attribute for IPv4. <span class="since">Since 1.0.1</span>
</dd>
<dt><code>bootp</code></dt>
<dd>The optional <code>bootp</code>
- 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: <code>file</code> is mandatory and
gives the file to be used for the boot image; <code>server</code> is
optional and gives the address of the TFTP server from which the boot
@@ -674,6 +698,29 @@
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
</network></pre>
+
+ <p>
+ Below is a variation of the above example which adds an IPv6
+ dhcp range definition.
+ </p>
+
+ <pre>
+ <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></pre>
+
<h3><a name="examplesRoute">Routed network config</a></h3>
<p>
@@ -698,6 +745,29 @@
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
</network></pre>
+ <p>
+ Below is another IPv6 varition. Instead of a dhcp range being
+ specified, this example has a couple of IPv6 host definitions.
+ </p>
+
+ <pre>
+ <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></pre>
+
<h3><a name="examplesPrivate">Isolated network config</a></h3>
<p>
@@ -720,6 +790,24 @@
<ip family="ipv6" address="2001:db8:ca2:3::1" prefix="64" />
</network></pre>
+ <h3><a name="examplesPrivate6">Isolated IPv6 network config</a></h3>
+
+ <p>
+ This variation of an isolated network defines only IPv6.
+ </p>
+
+ <pre>
+ <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></pre>
+
<h3><a name="examplesBridge">Using an existing host bridge</a></h3>
<p>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 228951d..a56b2e6 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -633,6 +633,7 @@ cleanup:
static int
virNetworkDHCPHostDefParse(const char *networkName,
+ virNetworkIpDefPtr def,
xmlNodePtr node,
virNetworkDHCPHostDefPtr host,
bool partialOkay)
@@ -644,6 +645,13 @@ virNetworkDHCPHostDefParse(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 IPv6 network '%s'"),
+ mac, networkName);
+ goto cleanup;
+ }
if (virMacAddrParse(mac, &addr) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Cannot parse MAC address '%s' in network '%s'"),
@@ -686,10 +694,19 @@ virNetworkDHCPHostDefParse(const char *networkName,
networkName);
}
} else {
+ 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;
+ }
+ }
/* normal usage - you need at least one MAC address or one host name */
- if (!(mac || name)) {
+ 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;
@@ -748,36 +765,39 @@ virNetworkDHCPDefParse(const char *networkName,
virReportOOMError();
return -1;
}
- if (virNetworkDHCPHostDefParse(networkName, cur,
+ if (virNetworkDHCPHostDefParse(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")) {
- char *file;
- char *server;
- virSocketAddr inaddr;
- memset(&inaddr, 0, sizeof(inaddr));
-
- if (!(file = virXMLPropString(cur, "file"))) {
- cur = cur->next;
- continue;
- }
- server = virXMLPropString(cur, "server");
+ } else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
+ /* the following only applies to IPv4 */
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "bootp")) {
+ char *file;
+ char *server;
+ virSocketAddr inaddr;
+ memset(&inaddr, 0, sizeof(inaddr));
+
+ if (!(file = virXMLPropString(cur, "file"))) {
+ cur = cur->next;
+ continue;
+ }
+ server = virXMLPropString(cur, "server");
+
+ if (server &&
+ virSocketAddrParse(&inaddr, server, AF_UNSPEC) < 0) {
+ VIR_FREE(file);
+ VIR_FREE(server);
+ return -1;
+ }
- if (server &&
- virSocketAddrParse(&inaddr, server, AF_UNSPEC) < 0) {
- VIR_FREE(file);
+ def->bootfile = file;
+ def->bootserver = inaddr;
VIR_FREE(server);
- return -1;
}
-
- def->bootfile = file;
- def->bootserver = inaddr;
- VIR_FREE(server);
}
cur = cur->next;
@@ -1139,6 +1159,20 @@ virNetworkIPParseXML(const char *networkName,
}
}
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
+ /* parse IPv6-related info */
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
+ result = virNetworkDHCPDefParse(networkName, def, cur);
+ if (result)
+ goto error;
+ }
+ cur = cur->next;
+ }
+ }
+
result = 0;
error:
@@ -2347,11 +2381,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 <ip family='ipv4'> "
"element found at index %d in network '%s'"),
parentIndex, def->name);
}
@@ -2364,17 +2396,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 <ip family='ipv4'> "
"element found in network '%s'"), def->name);
}
return ipdef;
@@ -2404,7 +2436,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
/* parse the xml into a virNetworkDHCPHostDef */
if (command == VIR_NETWORK_UPDATE_COMMAND_MODIFY) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0)
+ if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0)
goto cleanup;
/* search for the entry with this (mac|name),
@@ -2437,7 +2469,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
} else if ((command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) ||
(command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST)) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, true) < 0)
+ if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, true) < 0)
goto cleanup;
/* log error if an entry with same name/address/ip already exists */
@@ -2483,7 +2515,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
} else if (command == VIR_NETWORK_UPDATE_COMMAND_DELETE) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0)
+ if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0)
goto cleanup;
/* find matching entry - all specified attributes must match */
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 3e46304..226da04 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -218,6 +218,7 @@ struct _virNetworkObj {
unsigned int active : 1;
unsigned int autostart : 1;
unsigned int persistent : 1;
+ unsigned int dnsmasqVersion;
virNetworkDefPtr def; /* The current definition */
virNetworkDefPtr newDef; /* New definition to activate at shutdown */
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 9c67348..19a9170 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -75,6 +75,11 @@
#define VIR_FROM_THIS VIR_FROM_NETWORK
+#define DNSMASQ_PREFIX_TEXT "Dnsmasq version "
+
+#define CHECK_DNSMASQ_VERSION() \
+ (network->dnsmasqVersion >= 2064)
+
/* Main driver state */
struct network_driver {
virMutex lock;
@@ -582,20 +587,32 @@ 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,
- virNetworkDNSDefPtr dnsdef)
+networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
+ virNetworkIpDefPtr ipdef)
{
- unsigned int i, j;
+ unsigned int i;
for (i = 0; i < ipdef->nhosts; i++) {
virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
- if ((host->mac) && VIR_SOCKET_ADDR_VALID(&host->ip))
+ if (VIR_SOCKET_ADDR_VALID(&host->ip))
if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name) < 0)
return -1;
}
+ return 0;
+}
+
+static int
+networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
+ virNetworkDNSDefPtr dnsdef)
+{
+ unsigned int i, j;
+
if (dnsdef) {
for (i = 0; i < dnsdef->nhosts; i++) {
virNetworkDNSHostsDefPtr host = &(dnsdef->hosts[i]);
@@ -613,7 +630,6 @@ networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
static int
networkBuildDnsmasqArgv(virNetworkObjPtr network,
- virNetworkIpDefPtr ipdef,
const char *pidfile,
virCommandPtr cmd,
dnsmasqContext *dctx)
@@ -625,7 +641,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
char *recordPort = NULL;
char *recordWeight = NULL;
char *recordPriority = NULL;
- virNetworkIpDefPtr tmpipdef;
+ virNetworkIpDefPtr tmpipdef, ipdef, ipv4def, ipv6def;
+ bool dhcp4flag, dhcp6flag;
/*
* NB, be careful about syntax for dnsmasq options in long format.
@@ -650,14 +667,19 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
* Needed to ensure dnsmasq uses same algorithm for processing
* multiple namedriver entries in /etc/resolv.conf as GLibC.
*/
- virCommandAddArgList(cmd, "--strict-order", "--bind-interfaces", NULL);
+ virCommandAddArgList(cmd, "--strict-order",
+ "--bind-interfaces",
+ "--domain-needed",
+ "--except-interface=lo",
+ 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);
@@ -665,10 +687,6 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
/* *no* conf file */
virCommandAddArg(cmd, "--conf-file=");
- virCommandAddArgList(cmd,
- "--except-interface", "lo",
- NULL);
-
/* If this is an isolated network, set the default route option
* (3) to be empty to avoid setting a default route that's
* guaranteed to not work, and set --no-resolv so that no dns
@@ -751,7 +769,39 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
VIR_FREE(ipaddr);
}
- if (ipdef) {
+ /* Find the first dhcp for both IPv4 and IPv6 */
+ for (ii = 0, ipv4def = NULL, ipv6def = NULL, dhcp4flag = false, dhcp6flag = 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) {
+ ipv4def = ipdef;
+ dhcp4flag = true;
+ }
+ }
+ }
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) {
+ if (ipdef->nranges || ipdef->nhosts) {
+ if (!CHECK_DNSMASQ_VERSION()) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("dnsmasq version >= 2.64 required to specify DHCPv6 range or host"));
+ goto cleanup;
+ }
+ if (!ipv6def) {
+ ipv6def = ipdef;
+ dhcp6flag = true;
+ }
+ }
+ }
+ }
+
+ if (ipv4def)
+ ipdef = ipv4def;
+ else
+ ipdef = ipv6def;
+
+ while (ipdef) {
for (r = 0 ; r < ipdef->nranges ; r++) {
char *saddr = virSocketAddrFormat(&ipdef->ranges[r].start);
if (!saddr)
@@ -772,7 +822,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);
@@ -783,61 +833,93 @@ 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 (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0)
+ goto cleanup;
- if (ipdef->nranges || ipdef->nhosts)
- virCommandAddArg(cmd, "--dhcp-no-override");
+ /* 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");
- /* add domain to any non-qualified hostnames in /etc/hosts or addn-hosts */
- if (network->def->domain)
- virCommandAddArg(cmd, "--expand-hosts");
+ if (ipdef->tftproot) {
+ virCommandAddArgList(cmd, "--enable-tftp",
+ "--tftp-root", ipdef->tftproot,
+ NULL);
+ }
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0)
- goto cleanup;
+ if (ipdef->bootfile) {
+ virCommandAddArg(cmd, "--dhcp-boot");
+ if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) {
+ char *bootserver = virSocketAddrFormat(&ipdef->bootserver);
- /* 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);
+ if (!bootserver)
+ goto cleanup;
+ virCommandAddArgFormat(cmd, "%s%s%s",
+ ipdef->bootfile, ",,", bootserver);
+ VIR_FREE(bootserver);
+ } else {
+ virCommandAddArg(cmd, ipdef->bootfile);
+ }
+ }
+ }
+ if (ipdef == ipv6def)
+ ipdef = NULL;
+ else
+ ipdef = ipv6def;
+ }
- /* 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 (nbleases > 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->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);
+ /* this is done once per interface */
+ if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0)
+ goto cleanup;
- if (!bootserver)
+ /* 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 (dhcp4flag || dhcp6flag)
+ virCommandAddArgPair(cmd, "--dhcp-hostsfile",
+ dctx->hostsfile->path);
+
+ /* Likewise, always create this file and put it on the commandline, to allow for
+ * for runtime additions.
+ */
+ virCommandAddArgPair(cmd, "--addn-hosts",
+ dctx->addnhostsfile->path);
+
+ /* we are doing RA instead of radvd */
+ if (CHECK_DNSMASQ_VERSION()) {
+ if (dhcp6flag)
+ virCommandAddArg(cmd, "--enable-ra");
+ else {
+ char *bridgeaddr = NULL;
+ ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, 0);
+ if (ipdef) {
+ bridgeaddr = virSocketAddrFormat(&ipdef->address);
+ if (bridgeaddr) {
+ virCommandAddArgFormat(cmd,
+ "--dhcp-range=%s,ra-only", bridgeaddr);
+ } else {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid IPv6 configuration for %s ra-only"),
+ network->def->name);
goto cleanup;
- virCommandAddArgFormat(cmd, "%s%s%s",
- ipdef->bootfile, ",,", bootserver);
- VIR_FREE(bootserver);
- } else {
- virCommandAddArg(cmd, ipdef->bootfile);
+ }
+ VIR_FREE(bridgeaddr);
}
}
}
ret = 0;
+
cleanup:
VIR_FREE(record);
VIR_FREE(recordPort);
@@ -877,7 +959,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdou
return 0;
cmd = virCommandNew(DNSMASQ);
- if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx) < 0) {
+ if (networkBuildDnsmasqArgv(network, pidfile, cmd, dctx) < 0) {
goto cleanup;
}
@@ -894,18 +976,45 @@ static int
networkStartDhcpDaemon(virNetworkObjPtr network)
{
virCommandPtr cmd = NULL;
+ const char *cmdname = DNSMASQ;
+ char *tmp, *version = NULL;
+ unsigned int major = 0, minor = 0;
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;
}
+ if (!virFileIsExecutable(cmdname)) {
+ VIR_WARN("file %s missing or not executable", cmdname);
+ goto cleanup;
+ }
+ VIR_INFO("starting dhcp daemon (%s)", cmdname);
+ network->dnsmasqVersion = 0;
+
+ cmd = virCommandNew(cmdname);
+ virCommandAddArg(cmd, "--version");
+ virCommandSetOutputBuffer(cmd, &version);
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+ virCommandFree(cmd);
+ cmd = NULL;
+
+ if ((version!=NULL) &&
+ (STREQLEN(version, DNSMASQ_PREFIX_TEXT, strlen(DNSMASQ_PREFIX_TEXT)))) {
+ tmp = version + strlen(DNSMASQ_PREFIX_TEXT);
+ if (virStrToLong_ui(tmp, &tmp, 10, &major) >= 0) {
+ if ((*tmp == '.') &&
+ virStrToLong_ui(tmp + 1, &tmp, 10, &minor) >= 0) {
+ network->dnsmasqVersion = (major * 1000) + minor;
+ }
+ }
+ }
+
if (virFileMakePath(NETWORK_PID_DIR) < 0) {
virReportSystemError(errno,
_("cannot create directory %s"),
@@ -939,18 +1048,6 @@ networkStartDhcpDaemon(virNetworkObjPtr network)
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;
@@ -976,6 +1073,7 @@ networkStartDhcpDaemon(virNetworkObjPtr network)
ret = 0;
cleanup:
VIR_FREE(pidfile);
+ VIR_FREE(version);
virCommandFree(cmd);
dnsmasqContextFree(dctx);
return ret;
@@ -994,31 +1092,35 @@ networkRefreshDhcpDaemon(virNetworkObjPtr network)
virNetworkIpDefPtr ipdef;
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(network);
- /* Look for first IPv4 address that has dhcp defined. */
- /* We support dhcp config on 1 IPv4 interface only. */
+ VIR_INFO("REFRESH: DhcpDaemon: for %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 1 IPv4 interface.
+ */
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 (!ipdef) {
- /* no <ip> elements, so nothing to do */
- return 0;
- }
-
- if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR)))
- goto cleanup;
+ if (ipdef)
+ if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0)
+ goto cleanup;
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0)
+ if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0)
goto cleanup;
if ((ret = dnsmasqSave(dctx)) < 0)
@@ -1170,6 +1272,12 @@ networkStartRadvd(virNetworkObjPtr network)
virCommandPtr cmd = NULL;
int ret = -1;
+ /* is dnsmasq handling RA */
+ if (CHECK_DNSMASQ_VERSION()) {
+ ret = 0;
+ goto cleanup;
+ }
+
network->radvdPid = -1;
if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
@@ -1247,6 +1355,10 @@ cleanup:
static int
networkRefreshRadvd(virNetworkObjPtr network)
{
+ /* is dnsmasq handling RA */
+ if (CHECK_DNSMASQ_VERSION())
+ return 0;
+
/* if there's no running radvd, just start it */
if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0))
return networkStartRadvd(network);
@@ -1626,9 +1738,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:
@@ -1646,6 +1768,7 @@ networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
virNetworkObjPtr network)
{
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);
}
@@ -2695,8 +2818,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)
@@ -2715,17 +2837,36 @@ networkValidate(struct network_driver *driver,
virNetworkSetBridgeMacAddr(def);
}
- /* 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 9d1c07b..b0cf3ce 100644
--- a/src/util/dnsmasq.c
+++ b/src/util/dnsmasq.c
@@ -304,7 +304,14 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
if (!(ipstr = virSocketAddrFormat(ip)))
return -1;
- if (name) {
+ /* the first test determins if it is a dhcpv6 host */
+ if (mac==NULL) {
+ if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,[%s]",
+ name, ipstr) < 0) {
+ goto alloc_error;
+ }
+ }
+ else if (name) {
if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s",
mac, ipstr, name) < 0) {
goto alloc_error;
diff --git a/tests/networkxml2argvdata/dhcp6-network.argv b/tests/networkxml2argvdata/dhcp6-network.argv
new file mode 100644
index 0000000..87c68ac
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-network.argv
@@ -0,0 +1,17 @@
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--domain=mynet \
+--expand-hosts \
+--local=/mynet/ \
+--conf-file= \
+--listen-address 2001:db8:ac10:fe01::1 \
+--listen-address 2001:db8:ac10:fd01::1 \
+--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 0000000..990b403
--- /dev/null
+++ b/tests/networkxml2argvdata/dhcp6-network.xml
@@ -0,0 +1,16 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'/>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <domain name='mynet'/>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ <dhcp>
+ <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' />
+ <host name='ralph' ip='2001:db8:ac10:fd01::1:20' />
+ <host name='paul' ip='2001:db8:ac10:fd01::1:21' />
+ </dhcp>
+ </ip>
+</network>
diff --git a/tests/networkxml2argvdata/isolated-network.argv b/tests/networkxml2argvdata/isolated-network.argv
index 13e77b2..97f7b11 100644
--- a/tests/networkxml2argvdata/isolated-network.argv
+++ b/tests/networkxml2argvdata/isolated-network.argv
@@ -1,9 +1,16 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo --dhcp-option=3 --no-resolv \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+--dhcp-option=3 \
+--no-resolv \
--listen-address 192.168.152.1 \
--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-dhcp6.argv b/tests/networkxml2argvdata/nat-network-dhcp6.argv
new file mode 100644
index 0000000..a4795e5
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dhcp6.argv
@@ -0,0 +1,20 @@
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+--listen-address 192.168.122.1 \
+--listen-address 192.168.123.1 \
+--listen-address 2001:db8:ac10:fe01::1 \
+--listen-address 2001:db8:ac10:fd01::1 \
+--listen-address 10.24.10.1 \
+--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/nat-network-dhcp6.xml b/tests/networkxml2argvdata/nat-network-dhcp6.xml
new file mode 100644
index 0000000..f993a26
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dhcp6.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'/>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ <dhcp>
+ <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' />
+ <host name='ralph' ip='2001:db8:ac10:fd01::1:20' />
+ <host name='paul' ip='2001:db8:ac10:fd01::1:21' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
index 03a0676..32ad19f 100644
--- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
@@ -1,4 +1,11 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
---local=/example.com/ --domain-needed \
---conf-file= --except-interface lo --listen-address 192.168.122.1 \
---expand-hosts --addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
+--conf-file= \
+--listen-address 192.168.122.1 \
+--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 210a60c..4c21ff1 100644
--- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
@@ -1,8 +1,10 @@
@DNSMASQ@ \
--strict-order \
--bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
--srv-host=name.tcp.,,,, \
--listen-address 192.168.122.1 \
--listen-address 192.168.123.1 \
@@ -10,8 +12,9 @@
--listen-address 2001:db8:ac10:fd01::1 \
--listen-address 10.24.10.1 \
--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\
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \
+--dhcp-range=2001:db8:ac10:fe01::1,ra-only\
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
index 833d3cd..246dbeb 100644
--- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
@@ -1,8 +1,10 @@
@DNSMASQ@ \
--strict-order \
--bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
--srv-host=name.tcp.test-domain-name,.,1024,10,10 \
--listen-address 192.168.122.1 \
--listen-address 192.168.123.1 \
@@ -10,8 +12,9 @@
--listen-address 2001:db8:ac10:fd01::1 \
--listen-address 10.24.10.1 \
--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\
+--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \
+--dhcp-range=2001:db8:ac10:fe01::1,ra-only\
diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
index 3481507..8d18f3f 100644
--- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
+++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv
@@ -1,11 +1,20 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo '--txt-record=example,example value' \
---listen-address 192.168.122.1 --listen-address 192.168.123.1 \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+'--txt-record=example,example value' \
+--listen-address 192.168.122.1 \
+--listen-address 192.168.123.1 \
--listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \
+--listen-address 2001:db8:ac10:fd01::1 \
+--listen-address 10.24.10.1 \
--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\
diff --git a/tests/networkxml2argvdata/nat-network.argv b/tests/networkxml2argvdata/nat-network.argv
index 37fd2fc..32afeaa 100644
--- a/tests/networkxml2argvdata/nat-network.argv
+++ b/tests/networkxml2argvdata/nat-network.argv
@@ -1,10 +1,19 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
---listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+--listen-address 192.168.122.1 \
+--listen-address 192.168.123.1 \
+--listen-address 2001:db8:ac10:fe01::1 \
+--listen-address 2001:db8:ac10:fd01::1 \
+--listen-address 10.24.10.1 \
--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\
diff --git a/tests/networkxml2argvdata/netboot-network.argv b/tests/networkxml2argvdata/netboot-network.argv
index 5408eb7..41e9416 100644
--- a/tests/networkxml2argvdata/netboot-network.argv
+++ b/tests/networkxml2argvdata/netboot-network.argv
@@ -1,10 +1,19 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
---local=/example.com/ --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
+--conf-file= \
+--listen-address 192.168.122.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
+--dhcp-no-override \
+--enable-tftp \
+--tftp-root /var/lib/tftproot \
+--dhcp-boot pxeboot.img \
--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 \
---enable-tftp \
---tftp-root /var/lib/tftproot --dhcp-boot pxeboot.img\
+--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 21e01e3..eebe560 100644
--- a/tests/networkxml2argvdata/netboot-proxy-network.argv
+++ b/tests/networkxml2argvdata/netboot-proxy-network.argv
@@ -1,9 +1,17 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
---local=/example.com/ --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--domain=example.com \
+--expand-hosts \
+--local=/example.com/ \
+--conf-file= \
+--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-dhcphost.argv b/tests/networkxml2argvdata/routed-network-dhcphost.argv
new file mode 100644
index 0000000..9f52b98
--- /dev/null
+++ b/tests/networkxml2argvdata/routed-network-dhcphost.argv
@@ -0,0 +1,15 @@
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+--listen-address 192.168.122.1 \
+--listen-address 2001:db8:ac10:fd01::1 \
+--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/routed-network-dhcphost.xml b/tests/networkxml2argvdata/routed-network-dhcphost.xml
new file mode 100644
index 0000000..38d9ebf
--- /dev/null
+++ b/tests/networkxml2argvdata/routed-network-dhcphost.xml
@@ -0,0 +1,19 @@
+<network>
+ <name>local</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='route'/>
+ <bridge name='virbr1' stp='on' delay='0' />
+ <mac address='12:34:56:78:9A:BC'/>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ <dhcp>
+ <host name='ralph' ip='2001:db8:ac10:fd01::1:20' />
+ <host name='paul' ip='2001:db8:ac10:fd01::1:21' />
+ </dhcp>
+ </ip>
+</network>
diff --git a/tests/networkxml2argvdata/routed-network.argv b/tests/networkxml2argvdata/routed-network.argv
index 9fedb2b..78ad8a2 100644
--- a/tests/networkxml2argvdata/routed-network.argv
+++ b/tests/networkxml2argvdata/routed-network.argv
@@ -1,4 +1,9 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
---local=// --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+@DNSMASQ@ \
+--strict-order \
+--bind-interfaces \
+--domain-needed \
+--except-interface=lo \
+--local=// \
+--conf-file= \
+--listen-address 192.168.122.1 \
--addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\
diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c
index 87519e4..206e2d7 100644
--- a/tests/networkxml2argvtest.c
+++ b/tests/networkxml2argvtest.c
@@ -73,6 +73,7 @@ static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) {
goto fail;
obj->def = dev;
+ obj->dnsmasqVersion = 2064;
dctx = dnsmasqContextNew(dev->name, "/var/lib/libvirt/dnsmasq");
if (dctx == NULL)
@@ -157,6 +158,9 @@ mymain(void)
DO_TEST("nat-network-dns-srv-record");
DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
+ DO_TEST("nat-network-dhcp6");
+ DO_TEST("routed-network-dhcphost");
+ DO_TEST("dhcp6-network");
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}