If you define a libvirt virtual network with one or more IP addresses,
it starts up an instance of dnsmasq. It's always been possible to
avoid dnsmasq's dhcp server (simply don't include a <dhcp> element),
but until now it wasn't possible to avoid having the DNS server
listening; even if the network has no <dns> element, it is started
using default settings.
This patch adds a new attribute to <dns>: enable='yes|no'. For
backward compatibility, it defaults to 'yes', but if you don't want a
DNS server created for the network, you can simply add:
<dns enable='no'/>
to the network configuration, and next time the network is started
there will be no dns server created (if there is dhcp configuration,
dnsmasq will be started with "port=0" which disables the DNS server;
if there is no dhcp configuration, dnsmasq won't be started at all).
---
docs/formatnetwork.html.in | 12 ++
docs/schemas/network.rng | 5 +
src/conf/network_conf.c | 36 ++++-
src/conf/network_conf.h | 1 +
src/network/bridge_driver.c | 146 ++++++++++++---------
.../networkxml2confdata/routed-network-no-dns.conf | 11 ++
.../networkxml2confdata/routed-network-no-dns.xml | 10 ++
tests/networkxml2conftest.c | 1 +
tests/networkxml2xmlin/routed-network-no-dns.xml | 10 ++
tests/networkxml2xmlout/routed-network-no-dns.xml | 12 ++
tests/networkxml2xmltest.c | 1 +
11 files changed, 179 insertions(+), 66 deletions(-)
create mode 100644 tests/networkxml2confdata/routed-network-no-dns.conf
create mode 100644 tests/networkxml2confdata/routed-network-no-dns.xml
create mode 100644 tests/networkxml2xmlin/routed-network-no-dns.xml
create mode 100644 tests/networkxml2xmlout/routed-network-no-dns.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 12d1bed..e103dd7 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -886,6 +886,18 @@
server <span class="since">Since 0.9.3</span>.
<p>
+ The dns element can have an optional <code>enable</code>
+ attribute <span class="since">Since 2.2.0</span>.
+ If <code>enable</code> is "no", then no DNS server will
be
+ setup by libvirt for this network (and any other
+ configuration in <code><dns></code> will be
ignored).
+ If <code>enable</code> is "yes" or unspecified
(including
+ the complete absence of any <code><dns></code>
+ element) then a DNS server will be setup by libvirt to
+ listen on all IP addresses specified in the network's
+ configuration.
+ </p>
+ <p>
The dns element
can have an optional <code>forwardPlainNames</code>
attribute <span class="since">Since 1.1.2</span>.
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 621f16e..12d4b34 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -248,6 +248,11 @@
<optional>
<element name="dns">
<optional>
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
<attribute name="forwardPlainNames">
<ref name="virYesNo"/>
</attribute>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 6820bde..490574f 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1335,6 +1335,7 @@ virNetworkDNSDefParseXML(const char *networkName,
xmlNodePtr *txtNodes = NULL;
xmlNodePtr *fwdNodes = NULL;
char *forwardPlainNames = NULL;
+ char *enable = NULL;
int nhosts, nsrvs, ntxts, nfwds;
size_t i;
int ret = -1;
@@ -1342,6 +1343,18 @@ virNetworkDNSDefParseXML(const char *networkName,
ctxt->node = node;
+ enable = virXPathString("string(./@enable)", ctxt);
+ if (enable) {
+ def->enable = virTristateBoolTypeFromString(enable);
+ if (def->enable <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid dns enable setting '%s' "
+ "in network '%s'"),
+ enable, networkName);
+ goto cleanup;
+ }
+ }
+
forwardPlainNames = virXPathString("string(./@forwardPlainNames)", ctxt);
if (forwardPlainNames) {
def->forwardPlainNames = virTristateBoolTypeFromString(forwardPlainNames);
@@ -1440,6 +1453,7 @@ virNetworkDNSDefParseXML(const char *networkName,
ret = 0;
cleanup:
+ VIR_FREE(enable);
VIR_FREE(forwardPlainNames);
VIR_FREE(fwdNodes);
VIR_FREE(hostNodes);
@@ -2496,12 +2510,22 @@ virNetworkDNSDefFormat(virBufferPtr buf,
{
size_t i, j;
- if (!(def->forwardPlainNames || def->nfwds || def->nhosts ||
+ if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts
||
def->nsrvs || def->ntxts))
return 0;
virBufferAddLit(buf, "<dns");
- /* default to "yes", but don't format it in the XML */
+ if (def->enable) {
+ const char *fwd = virTristateBoolTypeToString(def->enable);
+
+ if (!fwd) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown enable type %d in network"),
+ def->enable);
+ return -1;
+ }
+ virBufferAsprintf(buf, " enable='%s'", fwd);
+ }
if (def->forwardPlainNames) {
const char *fwd = virTristateBoolTypeToString(def->forwardPlainNames);
@@ -2512,10 +2536,10 @@ virNetworkDNSDefFormat(virBufferPtr buf,
return -1;
}
virBufferAsprintf(buf, " forwardPlainNames='%s'", fwd);
- if (!(def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) {
- virBufferAddLit(buf, "/>\n");
- return 0;
- }
+ }
+ if (!(def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) {
+ virBufferAddLit(buf, "/>\n");
+ return 0;
}
virBufferAddLit(buf, ">\n");
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 1ce4257..9ebd4a7 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -128,6 +128,7 @@ struct _virNetworkDNSHostDef {
typedef struct _virNetworkDNSDef virNetworkDNSDef;
typedef virNetworkDNSDef *virNetworkDNSDefPtr;
struct _virNetworkDNSDef {
+ int enable; /* enum virTristateBool */
int forwardPlainNames; /* enum virTristateBool */
size_t ntxts;
virNetworkDNSTxtDefPtr txts;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 23036e8..49c0a2f 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -916,6 +916,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
int nbleases = 0;
size_t i;
virNetworkDNSDefPtr dns = &network->def->dns;
+ bool wantDNS = dns->enable != VIR_TRISTATE_BOOL_NO;
virNetworkIPDefPtr tmpipdef, ipdef, ipv4def, ipv6def;
bool ipv6SLAAC;
char *saddr = NULL, *eaddr = NULL;
@@ -948,7 +949,13 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
"strict-order\n",
network->def->name);
- if (network->def->dns.forwarders) {
+ /* if dns is disabled, set its listening port to 0, which
+ * tells dnsmasq to not listen
+ */
+ if (!wantDNS)
+ virBufferAddLit(&configbuf, "port=0\n");
+
+ if (wantDNS && network->def->dns.forwarders) {
virBufferAddLit(&configbuf, "no-resolv\n");
for (i = 0; i < network->def->dns.nfwds; i++) {
virBufferAsprintf(&configbuf, "server=%s\n",
@@ -968,7 +975,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
network->def->domain);
}
- if (network->def->dns.forwardPlainNames == VIR_TRISTATE_BOOL_NO) {
+ if (wantDNS && network->def->dns.forwardPlainNames ==
VIR_TRISTATE_BOOL_NO) {
virBufferAddLit(&configbuf, "domain-needed\n");
/* need to specify local=// whether or not a domain is
* specified, unless the config says we should forward "plain"
@@ -1061,64 +1068,66 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
}
}
- for (i = 0; i < dns->ntxts; i++) {
- virBufferAsprintf(&configbuf, "txt-record=%s,%s\n",
- dns->txts[i].name,
- dns->txts[i].value);
- }
-
- for (i = 0; i < dns->nsrvs; i++) {
- /* service/protocol are required, and should have been validated
- * by the parser.
- */
- if (!dns->srvs[i].service) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Missing required 'service' "
- "attribute in SRV record of network
'%s'"),
- network->def->name);
- goto cleanup;
+ if (wantDNS) {
+ for (i = 0; i < dns->ntxts; i++) {
+ virBufferAsprintf(&configbuf, "txt-record=%s,%s\n",
+ dns->txts[i].name,
+ dns->txts[i].value);
}
- if (!dns->srvs[i].protocol) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Missing required 'service' "
- "attribute in SRV record of network
'%s'"),
- network->def->name);
- goto cleanup;
- }
- /* RFC2782 requires that service and protocol be preceded by
- * an underscore.
- */
- virBufferAsprintf(&configbuf, "srv-host=_%s._%s",
- dns->srvs[i].service, dns->srvs[i].protocol);
- /* domain is optional - it defaults to the domain of this network */
- if (dns->srvs[i].domain)
- virBufferAsprintf(&configbuf, ".%s", dns->srvs[i].domain);
+ for (i = 0; i < dns->nsrvs; i++) {
+ /* service/protocol are required, and should have been validated
+ * by the parser.
+ */
+ if (!dns->srvs[i].service) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing required 'service' "
+ "attribute in SRV record of network
'%s'"),
+ network->def->name);
+ goto cleanup;
+ }
+ if (!dns->srvs[i].protocol) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing required 'service' "
+ "attribute in SRV record of network
'%s'"),
+ network->def->name);
+ goto cleanup;
+ }
+ /* RFC2782 requires that service and protocol be preceded by
+ * an underscore.
+ */
+ virBufferAsprintf(&configbuf, "srv-host=_%s._%s",
+ dns->srvs[i].service, dns->srvs[i].protocol);
- /* If target is empty or ".", that means "the service is
- * decidedly not available at this domain" (RFC2782). In that
- * case, any port, priority, or weight is irrelevant.
- */
- if (dns->srvs[i].target && STRNEQ(dns->srvs[i].target,
".")) {
-
- virBufferAsprintf(&configbuf, ",%s", dns->srvs[i].target);
- /* port, priority, and weight are optional, but are
- * identified by their position in the line. If an item is
- * unspecified, but something later in the line *is*
- * specified, we need to give the default value for the
- * unspecified item. (According to the dnsmasq manpage,
- * the default for port is 1).
+ /* domain is optional - it defaults to the domain of this network */
+ if (dns->srvs[i].domain)
+ virBufferAsprintf(&configbuf, ".%s",
dns->srvs[i].domain);
+
+ /* If target is empty or ".", that means "the service is
+ * decidedly not available at this domain" (RFC2782). In that
+ * case, any port, priority, or weight is irrelevant.
*/
- if (dns->srvs[i].port ||
- dns->srvs[i].priority || dns->srvs[i].weight)
- virBufferAsprintf(&configbuf, ",%d",
- dns->srvs[i].port ? dns->srvs[i].port : 1);
- if (dns->srvs[i].priority || dns->srvs[i].weight)
- virBufferAsprintf(&configbuf, ",%d",
dns->srvs[i].priority);
- if (dns->srvs[i].weight)
- virBufferAsprintf(&configbuf, ",%d",
dns->srvs[i].weight);
+ if (dns->srvs[i].target && STRNEQ(dns->srvs[i].target,
".")) {
+
+ virBufferAsprintf(&configbuf, ",%s",
dns->srvs[i].target);
+ /* port, priority, and weight are optional, but are
+ * identified by their position in the line. If an item is
+ * unspecified, but something later in the line *is*
+ * specified, we need to give the default value for the
+ * unspecified item. (According to the dnsmasq manpage,
+ * the default for port is 1).
+ */
+ if (dns->srvs[i].port ||
+ dns->srvs[i].priority || dns->srvs[i].weight)
+ virBufferAsprintf(&configbuf, ",%d",
+ dns->srvs[i].port ? dns->srvs[i].port : 1);
+ if (dns->srvs[i].priority || dns->srvs[i].weight)
+ virBufferAsprintf(&configbuf, ",%d",
dns->srvs[i].priority);
+ if (dns->srvs[i].weight)
+ virBufferAsprintf(&configbuf, ",%d",
dns->srvs[i].weight);
+ }
+ virBufferAddLit(&configbuf, "\n");
}
- virBufferAddLit(&configbuf, "\n");
}
/* Find the first dhcp for both IPv4 and IPv6 */
@@ -1198,7 +1207,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
virBufferAsprintf(&configbuf, "dhcp-range=%s,%s",
saddr, eaddr);
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
- virBufferAsprintf(&configbuf, ",%d", prefix);
+ virBufferAsprintf(&configbuf, ",%d", prefix);
virBufferAddLit(&configbuf, "\n");
VIR_FREE(saddr);
@@ -1225,7 +1234,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
virBufferAsprintf(&configbuf, "dhcp-range=%s,static",
bridgeaddr);
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
- virBufferAsprintf(&configbuf, ",%d", prefix);
+ virBufferAsprintf(&configbuf, ",%d", prefix);
virBufferAddLit(&configbuf, "\n");
VIR_FREE(bridgeaddr);
}
@@ -1278,8 +1287,10 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
/* Likewise, always create this file and put it on the
* commandline, to allow for runtime additions.
*/
- virBufferAsprintf(&configbuf, "addn-hosts=%s\n",
- dctx->addnhostsfile->path);
+ if (wantDNS) {
+ virBufferAsprintf(&configbuf, "addn-hosts=%s\n",
+ dctx->addnhostsfile->path);
+ }
/* Are we doing RA instead of radvd? */
if (DNSMASQ_RA_SUPPORT(caps)) {
@@ -1375,17 +1386,32 @@ static int
networkStartDhcpDaemon(virNetworkDriverStatePtr driver,
virNetworkObjPtr network)
{
+ virNetworkIPDefPtr ipdef;
+ size_t i;
+ bool needDnsmasq = false;
virCommandPtr cmd = NULL;
char *pidfile = NULL;
int ret = -1;
dnsmasqContext *dctx = NULL;
- if (!virNetworkDefGetIPByIndex(network->def, AF_UNSPEC, 0)) {
+ if (!(ipdef = virNetworkDefGetIPByIndex(network->def, AF_UNSPEC, 0))) {
/* no IP addresses, so we don't need to run */
ret = 0;
goto cleanup;
}
+ /* see if there are any IP addresses that need a dhcp server */
+ for (i = 0; ipdef && !needDnsmasq;
+ ipdef = virNetworkDefGetIPByIndex(network->def, AF_UNSPEC, i + 1)) {
+ if (ipdef->nranges || ipdef->nhosts)
+ needDnsmasq = true;
+ }
+
+ if (!needDnsmasq && network->def->dns.enable == VIR_TRISTATE_BOOL_NO)
{
+ ret = 0;
+ goto cleanup;
+ }
+
if (virFileMakePath(driver->pidDir) < 0) {
virReportSystemError(errno,
_("cannot create directory %s"),
diff --git a/tests/networkxml2confdata/routed-network-no-dns.conf
b/tests/networkxml2confdata/routed-network-no-dns.conf
new file mode 100644
index 0000000..83cc85e
--- /dev/null
+++ b/tests/networkxml2confdata/routed-network-no-dns.conf
@@ -0,0 +1,11 @@
+##WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST. Changes to this configuration should be made using:
+## virsh net-edit local
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+port=0
+except-interface=lo
+bind-dynamic
+interface=virbr1
diff --git a/tests/networkxml2confdata/routed-network-no-dns.xml
b/tests/networkxml2confdata/routed-network-no-dns.xml
new file mode 100644
index 0000000..70d0417
--- /dev/null
+++ b/tests/networkxml2confdata/routed-network-no-dns.xml
@@ -0,0 +1,10 @@
+<network>
+ <name>local</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <bridge name="virbr1"/>
+ <mac address='12:34:56:78:9A:BC'/>
+ <forward mode="route" dev="eth1"/>
+ <dns enable='no'/>
+ <ip address="192.168.122.1" netmask="255.255.255.0">
+ </ip>
+</network>
diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
index 77acc53..5c71c79 100644
--- a/tests/networkxml2conftest.c
+++ b/tests/networkxml2conftest.c
@@ -117,6 +117,7 @@ mymain(void)
DO_TEST("nat-network-dns-srv-record-minimal", restricted);
DO_TEST("nat-network-name-with-quotes", restricted);
DO_TEST("routed-network", full);
+ DO_TEST("routed-network-no-dns", full);
DO_TEST("open-network", full);
DO_TEST("nat-network", dhcpv6);
DO_TEST("nat-network-dns-txt-record", full);
diff --git a/tests/networkxml2xmlin/routed-network-no-dns.xml
b/tests/networkxml2xmlin/routed-network-no-dns.xml
new file mode 100644
index 0000000..70d0417
--- /dev/null
+++ b/tests/networkxml2xmlin/routed-network-no-dns.xml
@@ -0,0 +1,10 @@
+<network>
+ <name>local</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <bridge name="virbr1"/>
+ <mac address='12:34:56:78:9A:BC'/>
+ <forward mode="route" dev="eth1"/>
+ <dns enable='no'/>
+ <ip address="192.168.122.1" netmask="255.255.255.0">
+ </ip>
+</network>
diff --git a/tests/networkxml2xmlout/routed-network-no-dns.xml
b/tests/networkxml2xmlout/routed-network-no-dns.xml
new file mode 100644
index 0000000..f68ce8a
--- /dev/null
+++ b/tests/networkxml2xmlout/routed-network-no-dns.xml
@@ -0,0 +1,12 @@
+<network>
+ <name>local</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='route'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr1' stp='on' delay='0'/>
+ <mac address='12:34:56:78:9a:bc'/>
+ <dns enable='no'/>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ </ip>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index 32544d0..b17674e 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -127,6 +127,7 @@ mymain(void)
DO_TEST("empty-allow-ipv6");
DO_TEST("isolated-network");
DO_TEST("routed-network");
+ DO_TEST("routed-network-no-dns");
DO_TEST("open-network");
DO_TEST_PARSE_ERROR("open-network-with-forward-dev");
DO_TEST("nat-network");
--
2.7.4