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

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

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