@@ -121,6 +121,7 @@ static unixctl_cb_func debug_dump_peer_ports;
static unixctl_cb_func debug_dump_lflow_conj_ids;
static unixctl_cb_func lflow_cache_flush_cmd;
static unixctl_cb_func lflow_cache_show_stats_cmd;
+static unixctl_cb_func dns_show_stats_cmd;
static unixctl_cb_func debug_delay_nb_cfg_report;
#define DEFAULT_BRIDGE_NAME "br-int"
@@ -7377,6 +7378,9 @@ main(int argc, char *argv[])
lflow_cache_show_stats_cmd,
&lflow_output_data->pd);
+ unixctl_command_register("dns/show-stats", "", 0, 0,
+ dns_show_stats_cmd, NULL);
+
bool reset_ovnsb_idl_min_index = false;
unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
cluster_state_reset_cmd,
@@ -8401,6 +8405,17 @@ lflow_cache_show_stats_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
ds_destroy(&ds);
}
+static void
+dns_show_stats_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *arg OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ pinctrl_get_dns_stats(&ds);
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
static void
cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[] OVS_UNUSED, void *idl_reset_)
@@ -184,6 +184,9 @@ struct pinctrl {
static struct pinctrl pinctrl;
+/* DNS query statistics */
+static struct dns_stats dns_statistics = {0};
+
static bool pinctrl_is_sb_commited(int64_t commit_cfg, int64_t cur_cfg);
static void init_buffered_packets_ctx(void);
static void destroy_buffered_packets_ctx(void);
@@ -3366,6 +3369,9 @@ pinctrl_handle_dns_lookup(
uint32_t success = 0;
bool send_refuse = false;
+ /* Track total DNS queries received */
+ dns_statistics.total_queries++;
+
/* Parse result field. */
const struct mf_field *f;
enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
@@ -3392,6 +3398,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");
+ dns_statistics.error_truncated++;
goto exit;
}
@@ -3399,17 +3406,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");
+ dns_statistics.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 */
+ dns_statistics.skipped_not_request++;
goto exit;
}
/* Check if at least one query request is present */
if (!in_dns_header->qdcount) {
+ dns_statistics.error_no_query++;
goto exit;
}
@@ -3431,6 +3441,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);
+ dns_statistics.error_parse_failure++;
goto exit;
}
ds_put_buffer(&query_name, (const char *) in_dns_data + idx, label_len);
@@ -3449,6 +3460,20 @@ pinctrl_handle_dns_lookup(
}
uint16_t query_type = ntohs(get_unaligned_be16((void *) in_dns_data));
+
+ /* Track query type statistics */
+ if (query_type == DNS_QUERY_TYPE_A) {
+ dns_statistics.query_type_a++;
+ } else if (query_type == DNS_QUERY_TYPE_AAAA) {
+ dns_statistics.query_type_aaaa++;
+ } else if (query_type == DNS_QUERY_TYPE_PTR) {
+ dns_statistics.query_type_ptr++;
+ } else if (query_type == DNS_QUERY_TYPE_ANY) {
+ dns_statistics.query_type_any++;
+ } else {
+ dns_statistics.query_type_other++;
+ }
+
/* 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 +3492,10 @@ pinctrl_handle_dns_lookup(
&ovn_owned);
ds_destroy(&query_name);
if (!answer_data) {
+ dns_statistics.cache_misses++;
goto exit;
}
+ dns_statistics.cache_hits++;
uint16_t ancount = 0;
@@ -3511,6 +3538,7 @@ pinctrl_handle_dns_lookup(
if (ovn_owned && (query_type == DNS_QUERY_TYPE_AAAA ||
query_type == DNS_QUERY_TYPE_A) && !ancount) {
send_refuse = true;
+ dns_statistics.unsupported_ovn_owned++;
}
destroy_lport_addresses(&ip_addrs);
@@ -3595,6 +3623,7 @@ pinctrl_handle_dns_lookup(
pin->packet_len = dp_packet_size(&pkt_out);
success = 1;
+ dns_statistics.responses_sent++;
exit:
if (!ofperr) {
union mf_subvalue sv;
@@ -8863,3 +8892,49 @@ set_from_ctrl_flag_in_pkt_metadata(struct ofputil_packet_in *pin)
sv.u8_val = 1;
mf_write_subfield(&dst, &sv, &pin->flow_metadata);
}
+
+/* DNS Statistics functions */
+void
+pinctrl_get_dns_stats(struct ds *output)
+{
+ ds_put_format(output, "DNS Query Statistics\n");
+ ds_put_format(output, "====================\n\n");
+
+ ds_put_format(output, "Total queries received: %"PRIu64"\n\n",
+ dns_statistics.total_queries);
+
+ ds_put_format(output, "Query Types:\n");
+ ds_put_format(output, " A (IPv4): %"PRIu64"\n",
+ dns_statistics.query_type_a);
+ ds_put_format(output, " AAAA (IPv6): %"PRIu64"\n",
+ dns_statistics.query_type_aaaa);
+ ds_put_format(output, " PTR: %"PRIu64"\n",
+ dns_statistics.query_type_ptr);
+ ds_put_format(output, " ANY: %"PRIu64"\n",
+ dns_statistics.query_type_any);
+ ds_put_format(output, " Other: %"PRIu64"\n\n",
+ dns_statistics.query_type_other);
+
+ ds_put_format(output, "Cache Performance:\n");
+ ds_put_format(output, " Cache hits: %"PRIu64"\n",
+ dns_statistics.cache_hits);
+ ds_put_format(output, " Cache misses: %"PRIu64"\n\n",
+ dns_statistics.cache_misses);
+
+ ds_put_format(output,
+ "Processing Errors (packets reinjected to pipeline):\n");
+ ds_put_format(output, " Truncated packets: %"PRIu64"\n",
+ dns_statistics.error_truncated);
+ ds_put_format(output, " Skipped (not request): %"PRIu64"\n",
+ dns_statistics.skipped_not_request);
+ ds_put_format(output, " No query section: %"PRIu64"\n",
+ dns_statistics.error_no_query);
+ ds_put_format(output, " Parse failures: %"PRIu64"\n",
+ dns_statistics.error_parse_failure);
+ ds_put_format(output, " Unsupported OVN-owned: %"PRIu64"\n\n",
+ dns_statistics.unsupported_ovn_owned);
+
+ ds_put_format(output, "Responses:\n");
+ ds_put_format(output, " Responses sent: %"PRIu64"\n",
+ dns_statistics.responses_sent);
+}
@@ -83,4 +83,35 @@ void send_self_originated_neigh_packet(struct rconn *swconn,
struct in6_addr *local,
struct in6_addr *target,
uint8_t table_id);
+
+/* DNS Statistics */
+struct dns_stats {
+ /* Total queries received */
+ uint64_t total_queries;
+
+ /* Queries by type */
+ uint64_t query_type_a; /* IPv4 address lookups */
+ uint64_t query_type_aaaa; /* IPv6 address lookups */
+ uint64_t query_type_ptr; /* Reverse DNS lookups */
+ uint64_t query_type_any; /* ANY type queries */
+ uint64_t query_type_other; /* Other/unsupported types */
+
+ /* Cache performance */
+ uint64_t cache_hits; /* Queries with answers found */
+ uint64_t cache_misses; /* Queries without answers */
+
+ /* Processing errors (all packets reinjected to pipeline) */
+ uint64_t error_truncated; /* Malformed/truncated packets */
+ uint64_t skipped_not_request; /* DNS responses (not queries) */
+ uint64_t error_no_query; /* No query section present */
+ uint64_t error_parse_failure; /* Query name parsing failure */
+ uint64_t unsupported_ovn_owned; /* Unsupported query on OVN-owned record */
+
+ /* Responses sent */
+ uint64_t responses_sent; /* Successfully generated responses */
+};
+
+struct ds;
+void pinctrl_get_dns_stats(struct ds *output);
+
#endif /* controller/pinctrl.h */
Add comprehensive DNS query statistics tracking to ovn-controller for observability into DNS query processing. The statistics track query types (A, AAAA, PTR, ANY, Other), cache performance (hits and misses), processing errors, and responses sent. A new unixctl command 'dns/show-stats' displays the statistics. Signed-off-by: Ketan Supanekar <ksupanekar@crusoe.ai> --- controller/ovn-controller.c | 15 ++++++++ controller/pinctrl.c | 75 +++++++++++++++++++++++++++++++++++++ controller/pinctrl.h | 31 +++++++++++++++ 3 files changed, 121 insertions(+)