Thank you Ales.

-Ketan

On Thu, Jan 22, 2026 at 2:03 AM Ales Musil <[email protected]> wrote:

>
>
> On Thu, Jan 22, 2026 at 12:04 AM Ketan Supanekar via dev <
> [email protected]> wrote:
>
>> Add DNS query statistics tracking using OVS coverage counters in
>> ovn-controller for observability into DNS query processing.
>>
>> Tracked metrics include:
>> - Total queries processed
>> - Query types (A, AAAA, PTR, ANY, Other)
>> - Cache performance (hits/misses)
>> - Responses sent
>> - Error conditions (truncated, parse failures, etc.)
>>
>> Statistics can be queried using:
>>   ovn-appctl -t ovn-controller coverage/read-counter <counter_name>
>>   ovn-appctl -t ovn-controller coverage/show
>>
>> The implementation uses coverage counter verification into existing DNS
>> tests
>> in tests/ovn.at.
>>
>> Signed-off-by: Ketan Supanekar <[email protected]>
>> ---
>>
>
> Hello Ketan,
>
> thank you for v6.
>
>
>>  NEWS                 |  5 +++++
>>  controller/pinctrl.c | 48 ++++++++++++++++++++++++++++++++++++++++++++
>>  tests/ovn.at         | 28 ++++++++++++++++++++++++++
>>  3 files changed, 81 insertions(+)
>>
>> diff --git a/NEWS b/NEWS
>> index 9883fb81d..dc9d28f2c 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -1,5 +1,10 @@
>>  Post v25.09.0
>>  -------------
>> +   - Added DNS query statistics tracking in ovn-controller using OVS
>> coverage
>> +     counters. Statistics can be queried using "ovn-appctl -t
>> ovn-controller
>> +     coverage/read-counter <counter_name>" or "coverage/show". Tracked
>> metrics
>> +     include total queries, query types (A, AAAA, PTR, ANY, Other), cache
>> +     performance (hits/misses), responses sent, and error conditions.
>>     - Added support for TLS Server Name Indication (SNI) with the new
>>       --ssl-server-name option in OVN utilities and daemons. This allows
>>       specifying the server name for SNI, which is useful when connecting
>> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
>> index 6f7ae4037..ab8a0a37c 100644
>> --- a/controller/pinctrl.c
>> +++ b/controller/pinctrl.c
>> @@ -391,6 +391,22 @@ COVERAGE_DEFINE(pinctrl_drop_put_vport_binding);
>>  COVERAGE_DEFINE(pinctrl_notify_main_thread);
>>  COVERAGE_DEFINE(pinctrl_total_pin_pkts);
>>
>> +/* DNS query statistics - thread-safe coverage counters */
>> +COVERAGE_DEFINE(dns_query_total);
>> +COVERAGE_DEFINE(dns_query_type_a);
>> +COVERAGE_DEFINE(dns_query_type_aaaa);
>> +COVERAGE_DEFINE(dns_query_type_ptr);
>> +COVERAGE_DEFINE(dns_query_type_any);
>> +COVERAGE_DEFINE(dns_query_type_other);
>> +COVERAGE_DEFINE(dns_cache_hit);
>> +COVERAGE_DEFINE(dns_cache_miss);
>> +COVERAGE_DEFINE(dns_error_truncated);
>> +COVERAGE_DEFINE(dns_skipped_not_request);
>> +COVERAGE_DEFINE(dns_error_no_query);
>> +COVERAGE_DEFINE(dns_error_parse_failure);
>> +COVERAGE_DEFINE(dns_unsupported_ovn_owned);
>> +COVERAGE_DEFINE(dns_response_sent);
>> +
>>  struct empty_lb_backends_event {
>>      struct hmap_node hmap_node;
>>      long long int timestamp;
>> @@ -3366,6 +3382,9 @@ pinctrl_handle_dns_lookup(
>>      uint32_t success = 0;
>>      bool send_refuse = false;
>>
>> +    /* Track total DNS queries received */
>> +    COVERAGE_INC(dns_query_total);
>> +
>>      /* Parse result field. */
>>      const struct mf_field *f;
>>      enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
>> @@ -3392,6 +3411,7 @@ pinctrl_handle_dns_lookup(
>>      /* Check that the packet stores at least the minimal headers. */
>>      if (dp_packet_l4_size(pkt_in) < (UDP_HEADER_LEN + DNS_HEADER_LEN)) {
>>          VLOG_WARN_RL(&rl, "truncated dns packet");
>> +        COVERAGE_INC(dns_error_truncated);
>>          goto exit;
>>      }
>>
>> @@ -3399,17 +3419,20 @@ pinctrl_handle_dns_lookup(
>>      struct dns_header const *in_dns_header =
>> dp_packet_get_udp_payload(pkt_in);
>>      if (!in_dns_header) {
>>          VLOG_WARN_RL(&rl, "truncated dns packet");
>> +        COVERAGE_INC(dns_error_truncated);
>>          goto exit;
>>      }
>>
>>      /* Check if it is DNS request or not */
>>      if (in_dns_header->lo_flag & 0x80) {
>>          /* It's a DNS response packet which we are not interested in */
>> +        COVERAGE_INC(dns_skipped_not_request);
>>          goto exit;
>>      }
>>
>>      /* Check if at least one query request is present */
>>      if (!in_dns_header->qdcount) {
>> +        COVERAGE_INC(dns_error_no_query);
>>          goto exit;
>>      }
>>
>> @@ -3431,6 +3454,7 @@ pinctrl_handle_dns_lookup(
>>          uint8_t label_len = in_dns_data[idx++];
>>          if (in_dns_data + idx + label_len > end) {
>>              ds_destroy(&query_name);
>> +            COVERAGE_INC(dns_error_parse_failure);
>>              goto exit;
>>          }
>>          ds_put_buffer(&query_name, (const char *) in_dns_data + idx,
>> label_len);
>> @@ -3449,6 +3473,26 @@ pinctrl_handle_dns_lookup(
>>      }
>>
>>      uint16_t query_type = ntohs(get_unaligned_be16((void *)
>> in_dns_data));
>> +
>> +    /* Track query type statistics */
>> +    switch (query_type) {
>> +    case DNS_QUERY_TYPE_A:
>> +        COVERAGE_INC(dns_query_type_a);
>> +        break;
>> +    case DNS_QUERY_TYPE_AAAA:
>> +        COVERAGE_INC(dns_query_type_aaaa);
>> +        break;
>> +    case DNS_QUERY_TYPE_PTR:
>> +        COVERAGE_INC(dns_query_type_ptr);
>> +        break;
>> +    case DNS_QUERY_TYPE_ANY:
>> +        COVERAGE_INC(dns_query_type_any);
>> +        break;
>> +    default:
>> +        COVERAGE_INC(dns_query_type_other);
>> +        break;
>> +    }
>> +
>>      /* Supported query types - A, AAAA, ANY and PTR */
>>      if (!(query_type == DNS_QUERY_TYPE_A || query_type ==
>> DNS_QUERY_TYPE_AAAA
>>            || query_type == DNS_QUERY_TYPE_ANY
>> @@ -3467,8 +3511,10 @@ pinctrl_handle_dns_lookup(
>>                                               &ovn_owned);
>>      ds_destroy(&query_name);
>>      if (!answer_data) {
>> +        COVERAGE_INC(dns_cache_miss);
>>          goto exit;
>>      }
>> +    COVERAGE_INC(dns_cache_hit);
>>
>>
>>      uint16_t ancount = 0;
>> @@ -3511,6 +3557,7 @@ pinctrl_handle_dns_lookup(
>>          if (ovn_owned && (query_type == DNS_QUERY_TYPE_AAAA ||
>>              query_type == DNS_QUERY_TYPE_A) && !ancount) {
>>              send_refuse = true;
>> +            COVERAGE_INC(dns_unsupported_ovn_owned);
>>          }
>>
>>          destroy_lport_addresses(&ip_addrs);
>> @@ -3595,6 +3642,7 @@ pinctrl_handle_dns_lookup(
>>      pin->packet_len = dp_packet_size(&pkt_out);
>>
>>      success = 1;
>> +    COVERAGE_INC(dns_response_sent);
>>  exit:
>>      if (!ofperr) {
>>          union mf_subvalue sv;
>> diff --git a/tests/ovn.at b/tests/ovn.at
>> index 445a74ce5..d657bd2e1 100644
>> --- a/tests/ovn.at
>> +++ b/tests/ovn.at
>> @@ -12136,6 +12136,34 @@ reset_pcap_file hv1-vif1 hv1/vif1
>>  reset_pcap_file hv1-vif2 hv1/vif2
>>  rm -f 1.expected
>>  rm -f 2.expected
>> +
>> +AS_BOX([Verify DNS coverage counters])
>> +# The test has sent multiple DNS queries of various types, verify
>> counters
>> +# Total queries should be > 0
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_query_total | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# A record queries (multiple A queries sent)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_query_type_a | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# AAAA record queries (IPv6 queries sent)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_query_type_aaaa | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# PTR record queries (reverse DNS lookups sent)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_query_type_ptr | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# ANY record queries (vm1_ipv4_v6 queries sent)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_query_type_any | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# Cache hits (repeated queries)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_cache_hit | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# Cache misses (queries for non-existent records)
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_cache_miss | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +# Responses sent
>> +AT_CHECK([as hv1 ovn-appctl -t ovn-controller coverage/read-counter
>> dns_response_sent | awk '{if ($1 > 0) print "ok"}'], [0], [ok
>> +])
>> +
>>
>
> The test was a bit flaky, so I have adjusted it to use OVS_WAIT_FOR_OUTPUT
> instead.
>
>  OVN_CLEANUP([hv1])
>>
>>  AT_CLEANUP
>> --
>> 2.52.0
>>
>> _______________________________________________
>> dev mailing list
>> [email protected]
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>>
> With that change I went ahead and merged this into main, I have also added
> you into the AUTHORS file.
>
> Regards,
> Ales
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to