[libvirt] [PATCH] Add support for DNS TXT records

Michal Novotny minovotn at redhat.com
Thu Mar 24 13:58:35 UTC 2011


Hi,
this is the patch to add DNS TXT record support to libvirt networking
driver since this is feature that's supported by DNSMasq that's being
used by the bridge driver.

Maybe you fail to understand the reasons why to implement such a feature
however it's a good thing IMHO since user could provide some information
in the DNS TXT record headers. The headers are, of course, configurable
in the network XML description and the idea got to me when I was reading
an article about DKIM (DomainKeys Identified Mail) since it's using TXT
records in the DNS to provide the public keys. This inspired me to
implement the DNS TXT record support to libvirt bridge driver to allow
users expose some information to the guest if they want to do so etc.

Limitations:
 - Records names and values containing space (' ') arguments are altered
   to change spaces to underscores ('_'). This is because of proper
   argument handling when spawning dnsmasq.

Technical details:

The --txt-record argument should be supported by all version of DNSMasq
which allows us to use it in all of the cases for the libvirt bridge
driver. The only thing user has to do is to edit the network XML
description in libvirt and append:

    <dns>
      <txt_record name='some name' value='some value' />
    </dns>

after the DHCP elements of network IP (<ip>) tree. After creating such
a definition user has to restart this virtual network for changes to
take effect, i.e. to spawn DNSMasq with new --txt-record arguments.

User can confirm the proper configuration of DNS TXT records both by
looking to the dnsmasq command-line (i.e. `ps aux | grep dnsmasq`)
where information about --txt-record=some_name,some_value should be
present or test it in the host/guest itself by digging the TXT record
from there, i.e. using `dig TXT some_name @ip` from the host (since
the it's running on the @ip and not the gateway for host) or `dig TXT
some_name` from the guest where the value "some_value" should be output
in both cases.

This has been developed and tested on Fedora i386 box and everything
was working fine.

Michal

Signed-off-by: Michal Novotny <minovotn at redhat.com>
---
 src/conf/network_conf.c     |   64 +++++++++++++++++++++++++++++++++++++++++++
 src/conf/network_conf.h     |   16 +++++++++++
 src/network/bridge_driver.c |   28 +++++++++++++++++++
 3 files changed, 108 insertions(+), 0 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index dcab9de..3e07496 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -435,6 +435,53 @@ virNetworkDHCPRangeDefParseXML(const char *networkName,
 }
 
 static int
+virNetworkDNSDefParseXML(virNetworkIpDefPtr def,
+                         xmlNodePtr node)
+{
+
+    xmlNodePtr cur;
+
+    if (VIR_ALLOC(def->dns)) {
+        virReportOOMError();
+        return -1;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE &&
+            xmlStrEqual(cur->name, BAD_CAST "txt_record")) {
+            char *name, *value;
+
+            if (!(name = virXMLPropString(cur, "name"))) {
+                cur = cur->next;
+                continue;
+            }
+            if (!(value = virXMLPropString(cur, "value"))) {
+                VIR_FREE(name);
+                cur = cur->next;
+                continue;
+            }
+
+            if (VIR_REALLOC_N(def->dns->txtrecords, def->dns->ntxtrecords + 1) < 0) {
+                virReportOOMError();
+                return -1;
+            }
+
+            def->dns->txtrecords[def->dns->ntxtrecords].name = strdup(name);
+            def->dns->txtrecords[def->dns->ntxtrecords].value = strdup(value);
+            def->dns->ntxtrecords++;
+
+            VIR_FREE(name);
+            VIR_FREE(value);
+        }
+
+        cur = cur->next;
+    }
+
+    return 0;
+}
+
+static int
 virNetworkIPParseXML(const char *networkName,
                      virNetworkIpDefPtr def,
                      xmlNodePtr node,
@@ -550,6 +597,12 @@ virNetworkIPParseXML(const char *networkName,
                     goto error;
 
             } else if (cur->type == XML_ELEMENT_NODE &&
+                       xmlStrEqual(cur->name, BAD_CAST "dns")) {
+                result = virNetworkDNSDefParseXML(def, cur);
+                if (result)
+                    goto error;
+
+            } else if (cur->type == XML_ELEMENT_NODE &&
                        xmlStrEqual(cur->name, BAD_CAST "tftp")) {
                 char *root;
 
@@ -828,6 +881,17 @@ virNetworkIpDefFormat(virBufferPtr buf,
 
         virBufferAddLit(buf, "    </dhcp>\n");
     }
+    if ((def->dns != NULL) && (def->dns->ntxtrecords)) {
+        int ii;
+
+        virBufferAddLit(buf, "    <dns>\n");
+        for (ii = 0 ; ii < def->dns->ntxtrecords ; ii++) {
+            virBufferVSprintf(buf, "      <txt_record name='%s' value='%s' />\n",
+                              def->dns->txtrecords[ii].name,
+                              def->dns->txtrecords[ii].value);
+        }
+        virBufferAddLit(buf, "    </dns>\n");
+    }
 
     virBufferAddLit(buf, "  </ip>\n");
 
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 281124b..5f47595 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -57,6 +57,20 @@ struct _virNetworkDHCPHostDef {
     virSocketAddr ip;
 };
 
+typedef struct _virNetworkDNSTxtRecordsDef virNetworkDNSTxtRecordsDef;
+typedef virNetworkDNSTxtRecordsDef *virNetworkDNSTxtRecordsDefPtr;
+struct _virNetworkDNSTxtRecordsDef {
+    char *name;
+    char *value;
+};
+
+struct virNetworkDNSDef {
+    unsigned int ntxtrecords;
+    virNetworkDNSTxtRecordsDefPtr txtrecords;
+} virNetworkDNSDef;
+
+typedef struct virNetworkDNSDef *virNetworkDNSDefPtr;
+
 typedef struct _virNetworkIpDef virNetworkIpDef;
 typedef virNetworkIpDef *virNetworkIpDefPtr;
 struct _virNetworkIpDef {
@@ -75,6 +89,8 @@ struct _virNetworkIpDef {
     unsigned int nranges;        /* Zero or more dhcp ranges */
     virNetworkDHCPRangeDefPtr ranges;
 
+    virNetworkDNSDefPtr dns;     /* DNS related settings for DNSMasq */
+
     unsigned int nhosts;         /* Zero or more dhcp hosts */
     virNetworkDHCPHostDefPtr hosts;
 
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index c30620a..89c1431 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -442,6 +442,19 @@ networkSaveDnsmasqHostsfile(virNetworkIpDefPtr ipdef,
     return 0;
 }
 
+static char *
+replace_all(char *input, int chr1, int chr2)
+{
+    int pos;
+    char *tmp;
+    char *out;
+
+    out = strdup(input);
+    while ((tmp = strchr(out, chr1)) != NULL)
+        out[ strlen(input) - strlen(tmp) ] = chr2;
+
+    return out;
+}
 
 static int
 networkBuildDnsmasqArgv(virNetworkObjPtr network,
@@ -497,6 +510,21 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
     if (network->def->forwardType == VIR_NETWORK_FORWARD_NONE)
         virCommandAddArg(cmd, "--dhcp-option=3");
 
+    if (ipdef->dns != NULL) {
+        int i;
+
+        for (i = 0; i < ipdef->dns->ntxtrecords; i++) {
+            virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+            virBufferVSprintf(&buf, "%s,%s",
+                              replace_all(ipdef->dns->txtrecords[i].name, ' ', '_'),
+                              replace_all(ipdef->dns->txtrecords[i].value, ' ', '_'));
+
+            virCommandAddArgPair(cmd, "--txt-record", virBufferContentAndReset(&buf));
+            VIR_FREE(buf);
+        }
+    }
+
     /*
      * --interface does not actually work with dnsmasq < 2.47,
      * due to DAD for ipv6 addresses on the interface.
-- 
1.7.3.2




More information about the libvir-list mailing list