On 12/13/2016 08:52 AM, Jiri Denemark wrote:
Similarly to localOnly DNS domain, local PTR domains can be used to
tell
the DNS server not to forward reverse lookups for unknown IPs which
belong to the virtual network.
What's here is useful, but the <ptr> element doesn't fit with the
purpose of a PTR record in an actual DNS server. We should implement
<ptr> according to RFC 1035 so that it can be used to fill in a
"ptr-record" in dnsmasq.conf. An example from there is:
10.IN-ADDR.ARPA. PTR
MILNET-GW.ISI.EDU.
(note the terminating "."s - those are *very* important!)
To support that our XML syntax would at least need two attributes, but I
think it's the *2nd* one that should more properly called "domain". I'm
trying to find a reference to a good name for the 1st attribute, but
it's been about 15 years since I configured bind, and I can't seem to
concentrate long enough to follow the links in google. Anyway, it would
need to be something like this:
<ptr name='10.IN-ADDR.ARPA." domain="MILNET-GW.ISI.EDU."/>
(I think in the RFC, the first is the RDATA, and the 2nd is the
PTRDNAME/"domain name pointer"/domain-name).
I *think* this directly translates to:
ptr-record=10.IN-ADDR.ARPA.,MILNET-GW.ISI.EDU.
in dnsmasq.conf, but I'm completely extrapolating from examples I've
found, and haven't tested it, so I may be totally wrong.
(you'll notice that the item you've called "domain" is *not* what I
called "domain" in my suggested syntax)
A subset of that could be a <ptr> that has a "name" (or whatever we call
it) but no domain, and that might have the effect of making that
particular range local-only (so it would fail if it couldn't be
resolved.). We should make sure there is no other valid use of a PTR
with no domain though.
Note that it should already be possible to forward PTR requests for
particular ranges to an external server (i.e. the opposite of
"localPtr") by adding a "<forwarder domain='0.10.in-addr.arpa.'
addr='8.8.8.8'/>".
In the meantime, could you separate the parts of this patch that
implement "localPtr='yes'" from the <ptr> parts? I think that can
be
pushed as it is so it's not held up by disscussion of the full <ptr>.
Signed-off-by: Jiri Denemark <jdenemar(a)redhat.com>
---
docs/formatnetwork.html.in | 37 ++++++++++---
docs/schemas/basictypes.rng | 6 +++
docs/schemas/network.rng | 8 +++
src/conf/network_conf.c | 72 ++++++++++++++++++++++++-
src/conf/network_conf.h | 4 ++
src/network/bridge_driver.c | 47 ++++++++++++++++
tests/networkxml2confdata/ptr-domains-auto.conf | 20 +++++++
tests/networkxml2confdata/ptr-domains-auto.xml | 21 ++++++++
tests/networkxml2confdata/ptr-domains.conf | 24 +++++++++
tests/networkxml2confdata/ptr-domains.xml | 24 +++++++++
tests/networkxml2conftest.c | 2 +
11 files changed, 258 insertions(+), 7 deletions(-)
create mode 100644 tests/networkxml2confdata/ptr-domains-auto.conf
create mode 100644 tests/networkxml2confdata/ptr-domains-auto.xml
create mode 100644 tests/networkxml2confdata/ptr-domains.conf
create mode 100644 tests/networkxml2confdata/ptr-domains.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 9cf940052..3c9414779 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -855,7 +855,7 @@
<hostname>myhostalias</hostname>
</host>
</dns>
-<ip address="192.168.122.1" netmask="255.255.255.0">
+<ip address="192.168.122.1" netmask="255.255.255.0"
localPtr="yes">
<dhcp>
<range start="192.168.122.100"
end="192.168.122.254"/>
<host mac="00:16:3e:77:e2:ed" name="foo.example.com"
ip="192.168.122.10"/>
@@ -863,6 +863,10 @@
</dhcp>
</ip>
<ip family="ipv6" address="2001:db8:ca2:2::1"
prefix="64"/>
+<ip family="ipv6" address="fec0::1" prefix="31"
localPtr="yes">
+ <ptr domain="0.0.0.0.0.c.e.f.ip6.arpa"/>
+ <ptr domain="1.0.0.0.0.c.e.f.ip6.arpa"/>
+</ip>
<route family="ipv6" address="2001:db9:ca1:1::"
prefix="64" gateway="2001:db8:ca2:2::2"/>
</pre>
@@ -983,11 +987,20 @@
to specify the type of address — <code>ipv4</code> or
<code>ipv6</code>; if no <code>family</code> is given,
<code>ipv4</code> is assumed. More than one address of each family
can
- be defined for a network. The <code>ip</code> element is supported
- <span class="since">since 0.3.0</span>. IPv6, multiple
addresses on a
- single network, <code>family</code>, and
<code>prefix</code> are
- supported <span class="since">since 0.8.7</span>. The
<code>ip</code>
- element may contain the following elements:
+ be defined for a network. The optional <code>localPtr</code>
attribute
+ (<span class="since">since 3.0.0</span>) configures the
DNS server not
+ to forward any reverse DNS requests for IP addresses from the network
+ configured by the <code>address</code> and
+ <code>netmask</code>/<code>prefix</code> attributes. For
some unusual
+ network prefixes (not divisible by 8 for IPv4 or not divisible by 4 for
+ IPv6) libvirt may be unable to compute the PTR domain automatically,
+ in which case the domain has to be specified explicitly by the
+ <code>ptr</code> element described below. The
<code>ip</code> element
+ is supported <span class="since">since 0.3.0</span>. IPv6,
multiple
+ addresses on a single network, <code>family</code>, and
+ <code>prefix</code> are supported <span
class="since">since
+ 0.8.7</span>. The <code>ip</code> element may contain the
following
+ elements:
<dl>
<dt><code>tftp</code></dt>
@@ -1051,6 +1064,18 @@
</dd>
</dl>
</dd>
+
+ <dt><code>ptr</code></dt>
+ <dd>The <code>domain</code> attribute of the optional
+ <code>ptr</code> element specifies the PTR domain used for
reverse
+ DNS lookups. The domain has to end with ".in-addr.arpa" for IPv4
+ and ".ip6.arpa" for IPv6. Each <code>ip</code> element
may contain
+ zero or more <code>ptr</code> elements. When
+ <code>localPtr='yes'</code> attribute is set in the
parent
+ <code>ip</code> element the DNS server for this network will be
+ configured not to forward any reverse lookups within the specified
+ domains. <span class="since">Since 3.0.0</span>
+ </dd>
</dl>
</dd>
</dl>
diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
index 1b4f980e7..f99c76392 100644
--- a/docs/schemas/basictypes.rng
+++ b/docs/schemas/basictypes.rng
@@ -239,6 +239,12 @@
</data>
</define>
+ <define name="ptrName">
+ <data type="string">
+ <param
name="pattern">(((((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){1,3}in-addr)|((([0-9a-fA-F]\.){1,31})ip6)).arpa</param>
+ </data>
+ </define>
+
<define name="deviceName">
<data type="string">
<param name="pattern">[a-zA-Z0-9_\.\-\\:/]+</param>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 986119596..1a3705a1f 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -339,6 +339,9 @@
<optional>
<attribute name="family"><ref
name="addr-family"/></attribute>
</optional>
+ <optional>
+ <attribute name="localPtr"><ref
name="virYesNo"/></attribute>
+ </optional>
<interleave>
<optional>
<element name="tftp">
@@ -384,6 +387,11 @@
</interleave>
</element>
</optional>
+ <zeroOrMore>
+ <element name='ptr'>
+ <attribute name='domain'><ref
name="ptrName"/></attribute>
+ </element>
+ </zeroOrMore>
</interleave>
</element>
</zeroOrMore>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index b6849ceab..ce0bd0260 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -315,6 +315,10 @@ static void
virNetworkIPDefClear(virNetworkIPDefPtr def)
{
VIR_FREE(def->family);
+
+ virStringListFreeCount(def->ptrs, def->nptrs);
+ def->ptrs = NULL;
+
VIR_FREE(def->ranges);
while (def->nhosts)
@@ -1507,6 +1511,10 @@ virNetworkIPDefParseXML(const char *networkName,
unsigned long prefix = 0;
int prefixRc;
int result = -1;
+ char *localPtr = NULL;
+ xmlNodePtr *nodes = NULL;
+ size_t i;
+ int n;
save = ctxt->node;
ctxt->node = node;
@@ -1549,6 +1557,17 @@ virNetworkIPDefParseXML(const char *networkName,
else
def->prefix = prefix;
+ localPtr = virXPathString("string(./@localPtr)", ctxt);
+ if (localPtr) {
+ def->localPTR = virTristateBoolTypeFromString(localPtr);
+ if (def->localPTR <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid localPtr value '%s' in network
'%s'"),
+ localPtr, networkName);
+ goto cleanup;
+ }
+ }
+
/* validate address, etc. for each family */
if ((def->family == NULL) || (STREQ(def->family, "ipv4"))) {
if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ||
@@ -1604,6 +1623,46 @@ virNetworkIPDefParseXML(const char *networkName,
goto cleanup;
}
+ if ((n = virXPathNodeSet("./ptr", ctxt, &nodes)) < 0)
+ goto cleanup;
+
+ if (n > 0) {
+ if (VIR_ALLOC_N(def->ptrs, n) < 0)
+ goto cleanup;
+ def->nptrs = n;
+ }
+ for (i = 0; i < n; i++) {
+ char *domain;
+ const char *suffix;
+ size_t len;
+ size_t suflen;
+
+ if (!(domain = virXMLPropString(nodes[i], "domain"))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing PTR domain in network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET))
+ suffix = VIR_SOCKET_ADDR_IPV4_ARPA;
+ else
+ suffix = VIR_SOCKET_ADDR_IPV6_ARPA;
+
+ len = strlen(domain);
+ suflen = strlen(suffix);
+ if (len <= suflen ||
+ STRNEQ(domain + len - suflen, suffix) ||
+ domain[len - suflen - 1] != '.') {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Invalid PTR domain '%s' in network
'%s'"),
+ domain, networkName);
+ VIR_FREE(domain);
+ goto cleanup;
+ }
+ def->ptrs[i] = domain;
+ }
+
if ((dhcp = virXPathNode("./dhcp[1]", ctxt)) &&
virNetworkDHCPDefParseXML(networkName, dhcp, def) < 0)
goto cleanup;
@@ -1627,6 +1686,8 @@ virNetworkIPDefParseXML(const char *networkName,
virNetworkIPDefClear(def);
VIR_FREE(address);
VIR_FREE(netmask);
+ VIR_FREE(localPtr);
+ VIR_FREE(nodes);
ctxt->node = save;
return result;
@@ -2630,6 +2691,7 @@ static int
virNetworkIPDefFormat(virBufferPtr buf,
const virNetworkIPDef *def)
{
+ size_t i;
int result = -1;
virBufferAddLit(buf, "<ip");
@@ -2652,15 +2714,23 @@ virNetworkIPDefFormat(virBufferPtr buf,
}
if (def->prefix > 0)
virBufferAsprintf(buf, " prefix='%u'", def->prefix);
+
+ if (def->localPTR) {
+ virBufferAsprintf(buf, " localPtr='%s'",
+ virTristateBoolTypeToString(def->localPTR));
+ }
+
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
+ for (i = 0; i < def->nptrs; i++)
+ virBufferAsprintf(buf, "<ptr domain='%s'/>\n",
def->ptrs[i]);
+
if (def->tftproot) {
virBufferEscapeString(buf, "<tftp root='%s'/>\n",
def->tftproot);
}
if ((def->nranges || def->nhosts)) {
- size_t i;
virBufferAddLit(buf, "<dhcp>\n");
virBufferAdjustIndent(buf, 2);
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 09e091616..edf1a1abd 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -162,6 +162,10 @@ struct _virNetworkIPDef {
unsigned int prefix; /* ipv6 - only prefix allowed */
virSocketAddr netmask; /* ipv4 - either netmask or prefix specified */
+ int localPTR; /* virTristateBool */
+ size_t nptrs;
+ char **ptrs;
+
size_t nranges; /* Zero or more dhcp ranges */
virSocketAddrRangePtr ranges;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index ae1589d8c..49753737f 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -994,6 +994,49 @@ networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
}
+static int
+networkDnsmasqConfLocalPTRs(virBufferPtr buf,
+ virNetworkDefPtr def)
+{
+ virNetworkIPDefPtr ip;
+ size_t i, j;
+ char *ptr = NULL;
+ int rc;
+
+ for (i = 0; i < def->nips; i++) {
+ ip = def->ips + i;
+
+ if (ip->localPTR != VIR_TRISTATE_BOOL_YES)
+ continue;
+
+ for (j = 0; j < ip->nptrs; j++)
+ virBufferAsprintf(buf, "local=/%s/\n", ip->ptrs[j]);
+
+ if (ip->nptrs > 0)
+ continue;
+
+ if ((rc = virSocketAddrPTRDomain(&ip->address,
+ virNetworkIPDefPrefix(ip),
+ &ptr)) < 0) {
+ if (rc == -2) {
+ int family = VIR_SOCKET_ADDR_FAMILY(&ip->address);
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("PTR domain for %s network with prefix %u "
+ "cannot be automatically created"),
+ (family == AF_INET) ? "IPv4" :
"IPv6",
+ virNetworkIPDefPrefix(ip));
+ }
+ return -1;
+ }
+
+ virBufferAsprintf(buf, "local=/%s/\n", ptr);
+ VIR_FREE(ptr);
+ }
+
+ return 0;
+}
+
+
int
networkDnsmasqConfContents(virNetworkObjPtr network,
const char *pidfile,
@@ -1079,6 +1122,10 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
network->def->domain);
}
+ if (wantDNS &&
+ networkDnsmasqConfLocalPTRs(&configbuf, network->def) < 0)
+ goto cleanup;
+
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
diff --git a/tests/networkxml2confdata/ptr-domains-auto.conf
b/tests/networkxml2confdata/ptr-domains-auto.conf
new file mode 100644
index 000000000..7f1a393dd
--- /dev/null
+++ b/tests/networkxml2confdata/ptr-domains-auto.conf
@@ -0,0 +1,20 @@
+##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 default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+local=/122.168.192.in-addr.arpa/
+local=/1.0.e.f.0.1.c.a.8.b.d.0.1.0.0.2.ip6.arpa/
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254
+dhcp-no-override
+dhcp-authoritative
+dhcp-lease-max=253
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+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/networkxml2confdata/ptr-domains-auto.xml
b/tests/networkxml2confdata/ptr-domains-auto.xml
new file mode 100644
index 000000000..7fe12dc67
--- /dev/null
+++ b/tests/networkxml2confdata/ptr-domains-auto.xml
@@ -0,0 +1,21 @@
+<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'
localPtr='yes'>
+ <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' localPtr='no'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1'
prefix='64' localPtr='yes'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1'
prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2confdata/ptr-domains.conf
b/tests/networkxml2confdata/ptr-domains.conf
new file mode 100644
index 000000000..2900eebce
--- /dev/null
+++ b/tests/networkxml2confdata/ptr-domains.conf
@@ -0,0 +1,24 @@
+##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 test
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+local=/30.20.10.in-addr.arpa/
+local=/31.20.10.in-addr.arpa/
+local=/2.0.0.0.0.c.e.f.ip6.arpa/
+local=/0.0.0.0.0.c.e.f.ip6.arpa/
+local=/1.0.0.0.0.c.e.f.ip6.arpa/
+local=/1.0.f.e.d.c.b.a.4.0.0.0.0.c.e.f.ip6.arpa/
+except-interface=lo
+bind-dynamic
+interface=virbr2
+dhcp-range=10.20.30.1,static
+dhcp-no-override
+dhcp-authoritative
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/test.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/test.addnhosts
+dhcp-range=fec0:2::1,ra-only
+dhcp-range=fec0::1,ra-only
+dhcp-range=fec0:4:abcd:ef01::1,ra-only
diff --git a/tests/networkxml2confdata/ptr-domains.xml
b/tests/networkxml2confdata/ptr-domains.xml
new file mode 100644
index 000000000..64a329676
--- /dev/null
+++ b/tests/networkxml2confdata/ptr-domains.xml
@@ -0,0 +1,24 @@
+<network>
+ <name>test</name>
+ <uuid>cafecafe-cafe-cafe-cafe-cafecafecafe</uuid>
+ <forward mode='nat'/>
+ <bridge name='virbr2' stp='on' delay='0'/>
+ <mac address='52:54:00:eb:c2:e8'/>
+ <ip address='10.20.30.1' prefix='23' localPtr='yes'>
+ <ptr domain='30.20.10.in-addr.arpa'/>
+ <ptr domain='31.20.10.in-addr.arpa'/>
+ <dhcp>
+ <host mac='ca:fe:ca:fe:ca:fe' name='ble'
ip='10.20.30.2'/>
+ <host mac='ca:fe:ca:fe:ca:fe' name='bla'
ip='10.20.30.3'/>
+ <host mac='52:54:00:16:c6:1a' name='pxe'
ip='10.20.30.4'/>
+ </dhcp>
+ </ip>
+ <ip family='ipv6' address='fec0:2::1' prefix='32'
localPtr='yes'>
+ </ip>
+ <ip family='ipv6' address='fec0::1' prefix='31'
localPtr='yes'>
+ <ptr domain='0.0.0.0.0.c.e.f.ip6.arpa'/>
+ <ptr domain='1.0.0.0.0.c.e.f.ip6.arpa'/>
+ </ip>
+ <ip family='ipv6' address='fec0:4:abcd:ef01::1' prefix='64'
localPtr='yes'>
+ </ip>
+</network>
diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
index 65a0e3218..b6c967c45 100644
--- a/tests/networkxml2conftest.c
+++ b/tests/networkxml2conftest.c
@@ -129,6 +129,8 @@ mymain(void)
DO_TEST("dhcp6-network", dhcpv6);
DO_TEST("dhcp6-nat-network", dhcpv6);
DO_TEST("dhcp6host-routed-network", dhcpv6);
+ DO_TEST("ptr-domains", dhcpv6);
+ DO_TEST("ptr-domains-auto", dhcpv6);
virObjectUnref(dhcpv6);
virObjectUnref(full);