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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list