On 10/1/23 21:27, Mohammad Heib wrote:
> Currently OVN allows users to create DNS records
> and define domains within these records.
>
> These domains can be associated with IPV4 or IPv6
> or both, when the user creates a domain with both
> IPv4 and IPv6 ovn will answer each query for this
> domain immediately and everything works as expected.
>
> But if the user only creates a domain with only IPv4
> or IPv6 this will cause the DNS queries respond take
> longer than the usual since OVN will forward query and
> user will keep waiting for an answer until someone else
> replies for this query or timeout occur.
>
> The above behavior is a bit problematic if the user knows
> that this domain is only configured in OVN we should not
> forward queries for this domain to the outside.
>
> This patch adds an option:ovn-owned for the DNS table
> that can be set to "true" when creating a DNS.
>
> When setting this option to "true" all the domains within
> this table will be treated as local domains only,
> and queries for domains within this table that don't have
> an accurate IP will be refused immediately to save the time
> of waiting for timeout.
>
> Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1946662
> Signed-off-by: Mohammad Heib <[email protected]>
> ---
Hi Mohammad,
Thanks for the patch. It looks OK overall, I have a few remark inline.
> NEWS | 5 ++++
> controller/pinctrl.c | 47 ++++++++++++++++++++++++++++++--
> northd/northd.c | 11 ++++++++
> ovn-nb.ovsschema | 9 ++++--
> ovn-nb.xml | 14 ++++++++++
> ovn-sb.ovsschema | 9 ++++--
> ovn-sb.xml | 6 ++++
> tests/ovn.at | 65 ++++++++++++++++++++++++++++++++++++++++++++
> 8 files changed, 160 insertions(+), 6 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 425dfe0a8..465649430 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -1,5 +1,10 @@
> Post v23.09.0
> -------------
> + - DNS now have an "options" column for configuration of extra options.
> + - A new DNS option "ovn-owned" has been added to allow defining domains
> + that are owned only by ovn, queries for that domain will not be processed
> + externally.
> +
>
> OVN v23.09.0 - 15 Sep 2023
> --------------------------
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index ff5a3444c..e1e558595 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -179,6 +179,7 @@ struct pinctrl {
> struct latch pinctrl_thread_exit;
> bool mac_binding_can_timestamp;
> bool fdb_can_timestamp;
> + bool dns_can_ovn_owned;
I think I'd call this "dns_supports_ovn_owned".
> };
>
> static struct pinctrl pinctrl;
> @@ -2709,6 +2710,7 @@ struct dns_data {
> uint64_t *dps;
> size_t n_dps;
> struct smap records;
> + struct smap options;
> bool delete;
> };
>
> @@ -2737,6 +2739,7 @@ sync_dns_cache(const struct sbrec_dns_table *dns_table)
> if (!dns_data) {
> dns_data = xmalloc(sizeof *dns_data);
> smap_init(&dns_data->records);
> + smap_init(&dns_data->options);
> shash_add(&dns_cache, dns_id, dns_data);
> dns_data->n_dps = 0;
> dns_data->dps = NULL;
> @@ -2751,6 +2754,12 @@ sync_dns_cache(const struct sbrec_dns_table *dns_table)
> smap_clone(&dns_data->records, &sbrec_dns->records);
> }
>
> + if (pinctrl.dns_can_ovn_owned
> + && !smap_equal(&dns_data->options, &sbrec_dns->options)) {
> + smap_destroy(&dns_data->options);
> + smap_clone(&dns_data->options, &sbrec_dns->options);
> + }
> +
> dns_data->n_dps = sbrec_dns->n_datapaths;
> dns_data->dps = xcalloc(dns_data->n_dps, sizeof(uint64_t));
> for (size_t i = 0; i < sbrec_dns->n_datapaths; i++) {
> @@ -2763,6 +2772,7 @@ sync_dns_cache(const struct sbrec_dns_table *dns_table)
> if (d->delete) {
> shash_delete(&dns_cache, iter);
> smap_destroy(&d->records);
> + smap_destroy(&d->options);
> free(d->dps);
> free(d);
> }
> @@ -2777,6 +2787,7 @@ destroy_dns_cache(void)
> struct dns_data *d = iter->data;
> shash_delete(&dns_cache, iter);
> smap_destroy(&d->records);
> + smap_destroy(&d->options);
> free(d->dps);
> free(d);
> }
> @@ -2850,6 +2861,8 @@ dns_build_ptr_answer(
> free(encoded);
> }
>
> +#define DNS_RCODE_SERVER_REFUSE 0x5
> +
> /* Called with in the pinctrl_handler thread context. */
> static void
> pinctrl_handle_dns_lookup(
> @@ -2863,6 +2876,7 @@ pinctrl_handle_dns_lookup(
> enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
> struct dp_packet *pkt_out_ptr = NULL;
> uint32_t success = 0;
> + bool send_query_rejection = false;
Nit: I think I'd rename this to 'send_refuse'.
Nit: reverse xmas tree.
>
> /* Parse result field. */
> const struct mf_field *f;
> @@ -2962,6 +2976,7 @@ pinctrl_handle_dns_lookup(
>
> uint64_t dp_key = ntohll(pin->flow_metadata.flow.metadata);
> const char *answer_data = NULL;
> + bool ovn_owned = false;
> struct shash_node *iter;
> SHASH_FOR_EACH (iter, &dns_cache) {
> struct dns_data *d = iter->data;
> @@ -2974,6 +2989,7 @@ pinctrl_handle_dns_lookup(
> answer_data = smap_get(&d->records, query_name_lower);
> free(query_name_lower);
> if (answer_data) {
> + ovn_owned = smap_get_bool(&d->options, "ovn-owned",
> false);
> break;
> }
> }
> @@ -3020,10 +3036,22 @@ pinctrl_handle_dns_lookup(
> ancount++;
> }
> }
> +
> + /* DNS is configured with a record for this domain with
> + * an IPv4/IPV6 only, so instead of ignoring this A/AAAA query,
> + * we can reply with RCODE = 5 (server refuses) and that
> + * will speed up the DNS process by not letting the customer
> + * wait for a timeout.
> + */
> + if (ovn_owned && (query_type == DNS_QUERY_TYPE_AAAA ||
> + query_type == DNS_QUERY_TYPE_A) && !ancount) {
> + send_query_rejection = true;
> + }
> +
> destroy_lport_addresses(&ip_addrs);
> }
>
> - if (!ancount) {
> + if (!ancount && !send_query_rejection) {
> ofpbuf_uninit(&dns_answer);
> goto exit;
> }
> @@ -3058,13 +3086,19 @@ pinctrl_handle_dns_lookup(
>
> /* Set the answer RRs. */
> out_dns_header->ancount = htons(ancount);
> + if (send_query_rejection) {
> + /* set RCODE = 5 (server refuses). */
> + out_dns_header->hi_flag |= DNS_RCODE_SERVER_REFUSE;
> + }
> out_dns_header->arcount = 0;
>
> /* Copy the Query section. */
> dp_packet_put(&pkt_out, dp_packet_data(pkt_in), dp_packet_size(pkt_in));
>
> /* Copy the answer sections. */
> - dp_packet_put(&pkt_out, dns_answer.data, dns_answer.size);
> + if (!send_query_rejection) {
We don't really need the "if", right?
"dp_packet_put(&packet_out, foo, 0)" basically a no-op.
> + dp_packet_put(&pkt_out, dns_answer.data, dns_answer.size);
> + }
> ofpbuf_uninit(&dns_answer);
>
> out_udp->udp_len = htons(new_l4_size);
> @@ -3521,6 +3555,15 @@ pinctrl_update(const struct ovsdb_idl *idl, const char
> *br_int_name)
> notify_pinctrl_handler();
> }
>
> + bool dns_can_ovn_owned = sbrec_server_has_dns_table_col_options(idl);
> + if (dns_can_ovn_owned != pinctrl.dns_can_ovn_owned) {
> + pinctrl.dns_can_ovn_owned = dns_can_ovn_owned;
> +
> + /* Notify pinctrl_handler that fdb timestamp column
> + * availability has changed. */
> + notify_pinctrl_handler();
> + }
> +
> ovs_mutex_unlock(&pinctrl_mutex);
> }
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 528027d07..70a2922f2 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -17196,6 +17196,17 @@ sync_dns_entries(struct ovsdb_idl_txn *ovnsb_txn,
> free(dns_id);
> }
>
> +
> + /* Copy DNS options to SB*/
> + struct smap options;
> + smap_clone(&options, &dns_info->sb_dns->options);
> +
> + bool ovn_owned = smap_get_bool(&dns_info->nb_dns->options,
> + "ovn-owned", false);
> + smap_replace(&options, "ovn-owned",
> + ovn_owned? "true" : "false");
> + sbrec_dns_set_options(dns_info->sb_dns, &options);
> +
We leak "options" here, CI caught this:
https://github.com/ovsrobot/ovn/actions/runs/6372957589/job/17296298390#step:8:5644
> /* Set the datapaths and records. If nothing has changed, then
> * this will be a no-op.
> */
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index e103360ec..b2e0993e0 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Northbound",
> - "version": "7.1.0",
> - "cksum": "217362582 33949",
> + "version": "7.2.0",
> + "cksum": "1069338687 34162",
> "tables": {
> "NB_Global": {
> "columns": {
> @@ -560,6 +560,11 @@
> "value": "string",
> "min": 0,
> "max": "unlimited"}},
> + "options": {
> + "type": {"key": "string",
> + "value": "string",
> + "min": 0,
> + "max": "unlimited"}},
> "external_ids": {"type": {"key": "string",
> "value": "string",
> "min": 0,
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 1de0c3041..4aa8107a5 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -4542,6 +4542,20 @@ or
> <p><b>Example: </b> "4.0.0.10.in-addr.arpa" = "vm1.ovn.org"</p>
> </column>
>
> + <column name="options" key="ovn-owned">
> + If set to true, then the OVN will be the main responsible for
> + <code>DNS Records</code> within this Table.
> +
> + <p> A <code>DNS Table</code> with this option set to <code>true</code>
> + can be created for domains that the user needs to configure
> + locally and don't care about IPv6 only interested in IPv4 or
> + vice versa.
> +
> + This will let ovn send IPv4 DNS reply and reject/ignore IPv6
> + queries to save the waiting for a timeout on those uninteresting
> + queries.</p>
> + </column>
> +
> <column name="external_ids">
> See <em>External IDs</em> at the beginning of this document.
> </column>
> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
> index 7b20aa21b..b5a16d231 100644
> --- a/ovn-sb.ovsschema
> +++ b/ovn-sb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Southbound",
> - "version": "20.27.4",
> - "cksum": "3194181501 30531",
> + "version": "20.28.0",
> + "cksum": "160983021 30744",
> "tables": {
> "SB_Global": {
> "columns": {
> @@ -363,6 +363,11 @@
> "refTable":
> "Datapath_Binding"},
> "min": 1,
> "max": "unlimited"}},
> + "options": {
> + "type": {"key": "string",
> + "value": "string",
> + "min": 0,
> + "max": "unlimited"}},
> "external_ids": {"type": {"key": "string",
> "value": "string",
> "min": 0,
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index 46aedf973..55fcf59b2 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -4500,6 +4500,12 @@ tcp.flags = RST;
> in this column.
> </column>
>
> + <column name="options" key="ovn-owned">
> + This column indicates that all the <code>Domains</code> in this table
> + are owned by OVN, and all <code>DNS queries</code> for those domains
> + will be answered locally by either an IP address or
> + <code>DNS rejection</code>.
> +</column>
> <group title="Common Columns">
> <column name="external_ids">
> See <em>External IDs</em> at the beginning of this document.
> diff --git a/tests/ovn.at b/tests/ovn.at
> index dfe535f36..88357fd33 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -11302,6 +11302,71 @@ reset_pcap_file hv1-vif2 hv1/vif2
> rm -f 1.expected
> rm -f 2.expected
>
> +# send AAAA query for a server known domain that don't have
> +# any IPV6 address associated with this domain, and expected
> +# server refused DNS reply to save the sender time of waiting for timeout.
> +AS_BOX([Test IPv6 (AAAA records) NO timeout.])
> +# Add back the DNS options for ls1-lp1 without ipv6.
> +check ovn-nbctl remove DNS $DNS1 records vm1.ovn.org
> +check ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4"
> +check ovn-nbctl --wait=hv set DNS $DNS1 options:ovn-owned=true
> +ovn-sbctl list DNS > dns6
> +AT_CAPTURE_FILE([dns6])
> +ovn-sbctl dump-flows > sbflows6
> +AT_CAPTURE_FILE([sbflows6])
> +
> +set_dns_params vm1_ipv6_only
> +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 13.
> +OVS_WAIT_UNTIL([test 13 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> +
> +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
> +# dns hdr with server refuse RCODE
> +echo "01028125" > expout
> +#only check for the DNS HDR flags since we are not getting any DNS answer
> +AT_CHECK([cat 2.packets | cut -c -92 | cut -c 85-], [0], [expout])
> +
> +reset_pcap_file hv1-vif1 hv1/vif1
> +reset_pcap_file hv1-vif2 hv1/vif2
> +rm -f 1.expected
> +rm -f 2.expected
> +
> +# send A query for a server known domain that don't have
> +# any IPv4 address associated with this domain, and expected
> +# server refused DNS reply to save the sender time of waiting for timeout.
> +AS_BOX([Test IPv4 (A records) NO timeout.])
> +# Add back the DNS options for ls1-lp1 without ipv4.
> +check ovn-nbctl remove DNS $DNS1 records vm1.ovn.org
> +check ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="aef0::4"
> +check ovn-nbctl --wait=hv set DNS $DNS1 options:ovn-owned=true
> +ovn-sbctl list DNS > dns7
> +AT_CAPTURE_FILE([dns7])
> +ovn-sbctl dump-flows > sbflows7
> +AT_CAPTURE_FILE([sbflows7])
> +
> +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 14.
> +OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> +
> +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
> +# dns hdr with server refuse RCODE
> +echo "01028125" > expout
> +#only check for the DNS HDR flags since we are not getting any DNS answer
> +AT_CHECK([cat 2.packets | cut -c -92 | cut -c 85-], [0], [expout])
> +
> +reset_pcap_file hv1-vif1 hv1/vif1
> +reset_pcap_file hv1-vif2 hv1/vif2
> +rm -f 1.expected
> +rm -f 2.expected
> OVN_CLEANUP([hv1])
>
> AT_CLEANUP
Regards,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev