bbaaef
From afd10572ecbdd1d538ed4280f705ee79423b3c07 Mon Sep 17 00:00:00 2001
bbaaef
Message-Id: <afd10572ecbdd1d538ed4280f705ee79423b3c07.1594300918.git.lorenzo.bianconi@redhat.com>
bbaaef
From: Mark Michelson <mmichels@redhat.com>
bbaaef
Date: Mon, 20 Apr 2020 09:25:09 -0400
bbaaef
Subject: [PATCH] DNS: Make DNS lookups case insensitive.
bbaaef
bbaaef
From RFC 1035 Section 2.3.3:
bbaaef
bbaaef
"For all parts of the DNS that are part of the official protocol, all
bbaaef
comparisons between character strings (e.g., labels, domain names, etc.)
bbaaef
are done in a case-insensitive manner."
bbaaef
bbaaef
OVN was using case-sensitive lookups and therefore was not complying.
bbaaef
This change makes lookups case insensitive by storing lowercase record
bbaaef
names in the southbound database and converting incoming query names to
bbaaef
lowercase.
bbaaef
bbaaef
Change-Id: I164898989a39c2eddac3f54396c5861ad8c18bb1
bbaaef
Signed-off-by: Mark Michelson <mmichels@redhat.com>
bbaaef
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819069
bbaaef
Reported-by: Jianlin Shi <jishi@redhat.com>
bbaaef
Acked-by: Numan Siddique <numans@ovn.org>
bbaaef
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
bbaaef
---
bbaaef
 ovn/controller/pinctrl.c |  7 ++++-
bbaaef
 ovn/lib/ovn-util.c       | 15 ++++++++++
bbaaef
 ovn/lib/ovn-util.h       |  5 ++++
bbaaef
 ovn/northd/ovn-northd.c  | 15 +++++++++-
bbaaef
 ovn/ovn-sb.xml           |  3 +-
bbaaef
 tests/ovn.at             | 61 +++++++++++++++++++++++++++++-----------
bbaaef
 6 files changed, 87 insertions(+), 19 deletions(-)
bbaaef
bbaaef
--- a/ovn/controller/pinctrl.c
bbaaef
+++ b/ovn/controller/pinctrl.c
bbaaef
@@ -1807,7 +1807,12 @@ pinctrl_handle_dns_lookup(
bbaaef
         struct dns_data *d = iter->data;
bbaaef
         for (size_t i = 0; i < d->n_dps; i++) {
bbaaef
             if (d->dps[i] == dp_key) {
bbaaef
-                answer_ips = smap_get(&d->records, ds_cstr(&query_name));
bbaaef
+                /* DNS records in SBDB are stored in lowercase. Convert to
bbaaef
+                 * lowercase to perform case insensitive lookup
bbaaef
+                 */
bbaaef
+                char *query_name_lower = str_tolower(ds_cstr(&query_name));
bbaaef
+                answer_ips = smap_get(&d->records, query_name_lower);
bbaaef
+                free(query_name_lower);
bbaaef
                 if (answer_ips) {
bbaaef
                     break;
bbaaef
                 }
bbaaef
--- a/ovn/lib/ovn-util.c
bbaaef
+++ b/ovn/lib/ovn-util.c
bbaaef
@@ -19,6 +19,7 @@
bbaaef
 #include "openvswitch/ofp-parse.h"
bbaaef
 #include "ovn/lib/ovn-nb-idl.h"
bbaaef
 #include "ovn/lib/ovn-sb-idl.h"
bbaaef
+#include <ctype.h>
bbaaef
 
bbaaef
 VLOG_DEFINE_THIS_MODULE(ovn_util);
bbaaef
 
bbaaef
@@ -411,3 +412,17 @@ datapath_is_switch(const struct sbrec_da
bbaaef
 {
bbaaef
     return smap_get(&ldp->external_ids, "logical-switch") != NULL;
bbaaef
 }
bbaaef
+
bbaaef
+char *
bbaaef
+str_tolower(const char *orig)
bbaaef
+{
bbaaef
+    char *copy = xmalloc(strlen(orig) + 1);
bbaaef
+    char *p = copy;
bbaaef
+
bbaaef
+    while (*orig) {
bbaaef
+        *p++ = tolower(*orig++);
bbaaef
+    }
bbaaef
+    *p = '\0';
bbaaef
+
bbaaef
+    return copy;
bbaaef
+}
bbaaef
--- a/ovn/lib/ovn-util.h
bbaaef
+++ b/ovn/lib/ovn-util.h
bbaaef
@@ -87,4 +87,9 @@ uint32_t ovn_logical_flow_hash(const str
bbaaef
                                uint16_t priority,
bbaaef
                                const char *match, const char *actions);
bbaaef
 bool datapath_is_switch(const struct sbrec_datapath_binding *);
bbaaef
+
bbaaef
+/* Returns a lowercase copy of orig.
bbaaef
+ * Caller must free the returned string.
bbaaef
+ */
bbaaef
+char *str_tolower(const char *orig);
bbaaef
 #endif
bbaaef
--- a/ovn/northd/ovn-northd.c
bbaaef
+++ b/ovn/northd/ovn-northd.c
bbaaef
@@ -9767,7 +9767,20 @@ sync_dns_entries(struct northd_context *
bbaaef
             dns_info->sb_dns,
bbaaef
             (struct sbrec_datapath_binding **)dns_info->sbs,
bbaaef
             dns_info->n_sbs);
bbaaef
-        sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
bbaaef
+
bbaaef
+        /* DNS lookups are case-insensitive. Convert records to lowercase so
bbaaef
+         * we can do consistent lookups when DNS requests arrive
bbaaef
+         */
bbaaef
+        struct smap lower_records = SMAP_INITIALIZER(&lower_records);
bbaaef
+        struct smap_node *node;
bbaaef
+        SMAP_FOR_EACH (node, &dns_info->nb_dns->records) {
bbaaef
+            smap_add_nocopy(&lower_records, xstrdup(node->key),
bbaaef
+                            str_tolower(node->value));
bbaaef
+        }
bbaaef
+
bbaaef
+        sbrec_dns_set_records(dns_info->sb_dns, &lower_records);
bbaaef
+
bbaaef
+        smap_destroy(&lower_records);
bbaaef
         free(dns_info->sbs);
bbaaef
         free(dns_info);
bbaaef
     }
bbaaef
--- a/ovn/ovn-sb.xml
bbaaef
+++ b/ovn/ovn-sb.xml
bbaaef
@@ -3502,7 +3502,8 @@ tcp.flags = RST;
bbaaef
     <column name="records">
bbaaef
       Key-value pair of DNS records with DNS query name as the key
bbaaef
       and a string of IP address(es) separated by comma or space as the
bbaaef
-      value.
bbaaef
+      value. ovn-northd stores the DNS query name in all lowercase in order to
bbaaef
+      facilitate case-insensitive lookups.
bbaaef
 
bbaaef
       

Example: "vm1.ovn.org" = "10.0.0.4 aef0::4"

bbaaef
     </column>
bbaaef
--- a/tests/ovn.at
bbaaef
+++ b/tests/ovn.at
bbaaef
@@ -8380,6 +8380,12 @@ set_dns_params() {
bbaaef
         # IPv4 address - 10.0.0.4
bbaaef
         expected_dns_answer=${query_name}00010001${ttl}00040a000004
bbaaef
         ;;
bbaaef
+    VM1)
bbaaef
+        # VM1.OVN.ORG
bbaaef
+        query_name=03564d31034f564e034f524700
bbaaef
+        # IPv4 address - 10.0.0.4
bbaaef
+        expected_dns_answer=${query_name}00010001${ttl}00040a000004
bbaaef
+        ;;
bbaaef
     vm2)
bbaaef
         # vm2.ovn.org
bbaaef
         query_name=03766d32036f766e036f726700
bbaaef
@@ -8542,6 +8548,29 @@ reset_pcap_file hv1-vif2 hv1/vif2
bbaaef
 rm -f 1.expected
bbaaef
 rm -f 2.expected
bbaaef
 
bbaaef
+# Try vm1 again but an all-caps query name
bbaaef
+
bbaaef
+set_dns_params VM1
bbaaef
+src_ip=`ip_to_hex 10 0 0 6`
bbaaef
+dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
+dns_reply=1
bbaaef
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
bbaaef
+
bbaaef
+# NXT_RESUMEs should be 3.
bbaaef
+OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+
bbaaef
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
+cat 2.expected | cut -c -48 > expout
bbaaef
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
bbaaef
+# Skipping the IPv4 checksum.
bbaaef
+cat 2.expected | cut -c 53- > expout
bbaaef
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
bbaaef
+
bbaaef
+reset_pcap_file hv1-vif1 hv1/vif1
bbaaef
+reset_pcap_file hv1-vif2 hv1/vif2
bbaaef
+rm -f 1.expected
bbaaef
+rm -f 2.expected
bbaaef
+
bbaaef
 # Clear the query name options for ls1-lp2
bbaaef
 ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
bbaaef
 
bbaaef
@@ -8551,8 +8580,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=0
bbaaef
 test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 3.
bbaaef
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 4.
bbaaef
+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
bbaaef
 AT_CHECK([cat 1.packets], [0], [])
bbaaef
@@ -8573,8 +8602,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=0
bbaaef
 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 3 only.
bbaaef
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 4 only.
bbaaef
+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
 AT_CHECK([cat 2.packets], [0], [])
bbaaef
@@ -8594,8 +8623,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=1
bbaaef
 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 4.
bbaaef
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 5.
bbaaef
+OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
 cat 2.expected | cut -c -48 > expout
bbaaef
@@ -8616,8 +8645,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=1
bbaaef
 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 5.
bbaaef
-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 6.
bbaaef
+OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
 cat 2.expected | cut -c -48 > expout
bbaaef
@@ -8638,8 +8667,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=0
bbaaef
 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 6.
bbaaef
-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 7.
bbaaef
+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
 AT_CHECK([cat 2.packets], [0], [])
bbaaef
@@ -8656,8 +8685,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=0
bbaaef
 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 7.
bbaaef
-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 8.
bbaaef
+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
bbaaef
 AT_CHECK([cat 2.packets], [0], [])
bbaaef
@@ -8676,8 +8705,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
bbaaef
 dns_reply=1
bbaaef
 test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 8.
bbaaef
-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 9.
bbaaef
+OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
bbaaef
 cat 1.expected | cut -c -48 > expout
bbaaef
@@ -8698,8 +8727,8 @@ dst_ip=aef00000000000000000000000000001
bbaaef
 dns_reply=1
bbaaef
 test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
bbaaef
 
bbaaef
-# NXT_RESUMEs should be 9.
bbaaef
-OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
+# NXT_RESUMEs should be 10
bbaaef
+OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
bbaaef
 
bbaaef
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
bbaaef
 # Skipping the UDP checksum.