]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4969: dns: add counters for different DNS flavors
authorShibin K V (shikv) <shikv@cisco.com>
Fri, 7 Nov 2025 12:26:07 +0000 (12:26 +0000)
committerShanmugam S (shanms) <shanms@cisco.com>
Fri, 7 Nov 2025 12:26:07 +0000 (12:26 +0000)
Merge in SNORT/snort3 from ~SHIKV/snort3:doh_counters to master

Squashed commit of the following:

commit a09815a683072f0a63125f114a7ba8ae639bbf2f
Author: shibin k v <shikv@cisco.com>
Date:   Mon Nov 3 01:58:24 2025 -0600

    dns: add counters for different DNS flavors

src/flow/stream_flow.h
src/network_inspectors/appid/detector_plugins/test/detector_dns_test.cc
src/service_inspectors/dns/dns.cc
src/service_inspectors/dns/dns.h
src/service_inspectors/dns/dns_module.h
src/service_inspectors/dns/dns_payload_event_handler.cc

index 65af57f21b28a38192585c38a69c3003a3ee73b2..4617affcdacdc43eef55f5bba9ec7f5a8f612b83 100644 (file)
@@ -40,7 +40,7 @@ public:
     virtual void* get_hi_msg_section(const Flow*)
     { return nullptr; }
     virtual void set_hi_msg_section(Flow*, void*) { }
-    virtual AppId get_appid_from_stream(const Flow*) { return APP_ID_NONE; }
+    virtual AppId get_appid_from_stream(const Flow*) = 0;
     // Stream based flows should override this interface to return parent flow
     // when child flow is passed as input
     virtual Flow* get_stream_parent_flow(Flow* cflow) { return cflow; }
index 5427de3dee832cf91d456ba165b38d63793960b4..6150046137c0726fe18ec35e1eab17acc534055d 100644 (file)
@@ -43,6 +43,10 @@ public:
     {
         stream_id = 0;
     }
+    AppId get_appid_from_stream(const Flow*) override
+    {
+        return APP_ID_QUIC;
+    }
 };
 static QuicStreamIntf quic_stream_intf;
 
@@ -65,7 +69,7 @@ int ServiceDetector::incompatible_data(AppIdSession&, const snort::Packet*, Appi
     return 0;
 }
 // Stubs for AppIdInspector
-static ServiceDNSData dd;
+static ServiceDNSData dns_data;
 static bool return_null_data = false;
 ServiceDNSDoQData* dns_doq_data  = nullptr;
 AppIdConfig test_app_config;
@@ -86,24 +90,27 @@ int AppIdDetector::data_add(AppIdSession&, AppIdFlowData* dns_data)
 
 AppIdFlowData* AppIdDetector::data_get(const AppIdSession&)
 {
-    return return_null_data ? nullptr : &dd;
+    return return_null_data ? nullptr : &dns_data;
 }
 
-int ServiceDetector::fail_service(AppIdSession& asd, const Packet* pkt, AppidSessionDirection dir) { return 1; }
+int ServiceDetector::fail_service(AppIdSession& asd, const Packet* pkt, AppidSessionDirection dir)
+{
+    return 1;
+}
 
 TEST_GROUP(detector_dns_doq_tests)
 {
-    DnsTcpServiceDetector* test_detector = nullptr;
+    DnsTcpServiceDetector* const test_detector = nullptr;
     void setup() override
     {
-        test_detector = new DnsTcpServiceDetector(&test_discovery);
+        const_cast<DnsTcpServiceDetector*&>(test_detector) = new DnsTcpServiceDetector(&test_discovery);
     }
 
     void teardown() override
     {   
         delete test_detector;
-        test_detector = nullptr;
-        dd.free_dns_cache();
+        const_cast<DnsTcpServiceDetector*&>(test_detector) = nullptr;
+        dns_data.free_dns_cache();
     }
 };
 
@@ -112,7 +119,7 @@ TEST(detector_dns_doq_tests, doq_validator_match_full_session)
     OdpContext test_odp_ctxt(test_app_config, nullptr);
     AppIdModule test_module;
     AppIdInspector test_inspector(test_module);
-    dd.state = DNS_STATE_QUERY;
+    dns_data.state = DNS_STATE_QUERY;
     AppIdSession test_asd(IpProtocol::TCP, nullptr, (uint16_t)0, test_inspector, test_odp_ctxt, (uint32_t)0, 0);
     Flow* flow = new Flow;
     test_asd.flow = flow;
@@ -132,12 +139,12 @@ TEST(detector_dns_doq_tests, doq_validator_match_full_session)
     AppidChangeBits change_bits;
     AppIdDiscoveryArgs args(dns_tcp_packet, sizeof(dns_tcp_packet), APP_ID_FROM_INITIATOR, test_asd, nullptr, change_bits);
     auto result = test_detector->validate_doq(args);
-    dd.free_dns_cache();
+    dns_data.free_dns_cache();
     CHECK_EQUAL(APPID_INPROCESS, result);
 
     // Now send a response packet
-    dd.state = DNS_STATE_RESPONSE;
-    dd.id = 13330;
+    dns_data.state = DNS_STATE_RESPONSE;
+    dns_data.id = 13330;
     uint8_t dns_tcp_response[] = {
         0x00, 0x25,             // TCP length (37 bytes)
         0x12, 0x34,             // id
@@ -170,7 +177,7 @@ TEST(detector_dns_doq_tests, doq_validator_in_process_cached)
     OdpContext test_odp_ctxt(test_app_config, nullptr);
     AppIdModule test_module;
     AppIdInspector test_inspector(test_module);
-    dd.state = DNS_STATE_QUERY;
+    dns_data.state = DNS_STATE_QUERY;
     AppIdSession test_asd(IpProtocol::TCP, nullptr, (uint16_t)0, test_inspector, test_odp_ctxt, (uint32_t)0, 0);
     Flow* flow = new Flow;
     test_asd.flow = flow;
@@ -184,7 +191,7 @@ TEST(detector_dns_doq_tests, doq_validator_in_process_cached)
     AppidChangeBits change_bits;
     AppIdDiscoveryArgs args(dns_tcp_packet_1, sizeof(dns_tcp_packet_1), APP_ID_FROM_INITIATOR, test_asd, nullptr, change_bits);
     auto result = test_detector->validate_doq(args);
-    CHECK_EQUAL(6, dd.cached_len);
+    CHECK_EQUAL(6, dns_data.cached_len);
     CHECK_EQUAL(APPID_INPROCESS, result);
 
     uint8_t dns_tcp_packet_2[]= {
@@ -207,7 +214,7 @@ TEST(detector_dns_doq_tests, doq_validator_not_compatible)
     OdpContext test_odp_ctxt(test_app_config, nullptr);
     AppIdModule test_module;
     AppIdInspector test_inspector(test_module);
-    dd.state = DNS_STATE_QUERY;
+    dns_data.state = DNS_STATE_QUERY;
     AppIdSession test_asd(IpProtocol::TCP, nullptr, (uint16_t)0, test_inspector, test_odp_ctxt, (uint32_t)0, 0);
     Flow* flow = new Flow;
     test_asd.flow = flow;
@@ -238,7 +245,7 @@ TEST(detector_dns_doq_tests, doq_validator_no_match)
     OdpContext test_odp_ctxt(test_app_config, nullptr);
     AppIdModule test_module;
     AppIdInspector test_inspector(test_module);
-    dd.state = DNS_STATE_QUERY;
+    dns_data.state = DNS_STATE_QUERY;
     AppIdSession test_asd(IpProtocol::TCP, nullptr, (uint16_t)0, test_inspector, test_odp_ctxt, (uint32_t)0, 0);
     Flow* flow = new Flow;
     test_asd.flow = flow;
@@ -267,7 +274,7 @@ TEST(detector_dns_doq_tests, doq_validator_stream_intf)
     OdpContext test_odp_ctxt(test_app_config, nullptr);
     AppIdModule test_module;
     AppIdInspector test_inspector(test_module);
-    dd.state = DNS_STATE_QUERY;
+    dns_data.state = DNS_STATE_QUERY;
     AppIdSession test_asd(IpProtocol::TCP, nullptr, (uint16_t)0, test_inspector, test_odp_ctxt, (uint32_t)0, 0);
     Flow* flow = new Flow;
     test_asd.flow = flow;
index c86e71521b2aa3c7c11b91de2893baa9dc65117f..9a3439edfe80d02b30ca22fe77502661b8c9d3d7 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "dns.h"
 
+#include "appid/application_ids.h"
 #include "detection/detection_engine.h"
+#include "flow/stream_flow.h"
 #include "log/messages.h"
 #include "profiler/profiler.h"
 #include "pub_sub/dns_events.h"
@@ -51,10 +53,17 @@ const PegInfo dns_peg_names[] =
     { CountType::SUM, "packets", "total packets processed" },
     { CountType::SUM, "requests", "total dns requests" },
     { CountType::SUM, "responses", "total dns responses" },
+    { CountType::SUM, "dns_over_udp", "total dns packets over udp" },
+    { CountType::SUM, "dns_over_tcp", "total dns packets over tcp" },
+    { CountType::SUM, "dns_over_http1", "total dns packets over http/1.1" },
+    { CountType::SUM, "dns_over_http2", "total dns packets over http/2" },
+    { CountType::SUM, "dns_over_http3", "total dns packets over http/3" },
+    { CountType::SUM, "dns_over_quic", "total dns packets over quic" },
     { CountType::NOW, "concurrent_sessions", "total concurrent dns sessions" },
     { CountType::MAX, "max_concurrent_sessions", "maximum concurrent dns sessions" },
     { CountType::SUM, "aborted_sessions", "total dns sessions aborted" },
 
+
     { CountType::END, nullptr, nullptr }
 };
 
@@ -145,7 +154,7 @@ DNSData* get_dns_session_data(Packet* p, bool from_server, DNSData& udpSessionDa
         return &udpSessionData;
     }
 
-    fd = (DnsFlowData*)((p->flow)->get_flow_data(DnsFlowData::inspector_id));
+    fd = static_cast<DnsFlowData*>((p->flow)->get_flow_data(DnsFlowData::inspector_id));
     if (fd)
     {
         fd->session.dns_events.set_packet(p);
@@ -393,7 +402,7 @@ static uint16_t ParseDNSName(
                     if (!dnsSessionData->curr_txt.dns_name.empty())
                         dnsSessionData->curr_txt.dns_name += ".";
 
-                    dnsSessionData->curr_txt.dns_name.append((const char*)data, bytes_required);
+                    dnsSessionData->curr_txt.dns_name.append(reinterpret_cast<const char*>(data), bytes_required);
                 }
 
                 data += bytes_required;
@@ -630,7 +639,7 @@ static inline uint16_t get_udp_trans_id(Packet* p)
 static bool is_in_udp_flow(Packet* p, uint16_t trans_id)
 {
     bool found = false;
-    DnsUdpFlowData* udp_flow_data = (DnsUdpFlowData*)((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
+    DnsUdpFlowData* udp_flow_data = static_cast<DnsUdpFlowData*>((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
     if (udp_flow_data)
         found = udp_flow_data->trans_ids.find(trans_id) != udp_flow_data->trans_ids.end();
     return found;
@@ -639,7 +648,7 @@ static bool is_in_udp_flow(Packet* p, uint16_t trans_id)
 // Add DNS transaction ID to the UDP packet's flow data object
 static void add_to_udp_flow(Packet* p, uint16_t trans_id)
 {
-    DnsUdpFlowData* udp_flow_data = (DnsUdpFlowData*)((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
+    DnsUdpFlowData* udp_flow_data = static_cast<DnsUdpFlowData*>((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
     if (!udp_flow_data)
     {
         udp_flow_data = new DnsUdpFlowData();
@@ -649,16 +658,16 @@ static void add_to_udp_flow(Packet* p, uint16_t trans_id)
 }
 
 // Remove DNS transaction ID from the UDP packet's flow data object
-static void rm_from_udp_flow(Packet* p, uint16_t trans_id)
+static void rm_from_udp_flow(Packet* p, uint16_t trans_id, bool is_payload)
 {
-    DnsUdpFlowData* udp_flow_data = (DnsUdpFlowData*)((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
+    DnsUdpFlowData* udp_flow_data = static_cast<DnsUdpFlowData*>((p->flow)->get_flow_data(DnsUdpFlowData::inspector_id));
     bool should_close = true;
     if (udp_flow_data)
     {
         udp_flow_data->trans_ids.erase(trans_id);
         should_close = udp_flow_data->trans_ids.empty();
     }
-    if (should_close && !p->flow->is_proxied())
+    if (should_close && !is_payload)
     {
         // Mark the UDP flow as "closed" only when all trans_ids are matched
         // and removed by DNS-reply packets, or if the flow data object is not found
@@ -1144,7 +1153,7 @@ void Dns::show(const SnortConfig*) const
     config->show();
 }
 
-void Dns::snort_dns(Packet* p, bool udp)
+void Dns::snort_dns(Packet* p, bool udp, bool is_payload)
 {
     // cppcheck-suppress unreadVariable
     Profile profile(dnsPerfStats);
@@ -1184,6 +1193,37 @@ void Dns::snort_dns(Packet* p, bool udp)
     if (dnsSessionData->flags & DNS_FLAG_NOT_DNS)
         return;
 
+    if (is_payload)
+    {
+        if (p->flow->stream_intf)
+        {
+            AppId appid = p->flow->stream_intf->get_appid_from_stream(p->flow);
+            switch (appid)
+            {
+            case APP_ID_QUIC:
+                dnsstats.dns_over_quic++;
+                break;
+            case APP_ID_HTTP3:
+                dnsstats.dns_over_http3++;
+                break;
+            case APP_ID_HTTP2:
+                dnsstats.dns_over_http2++;
+                break;
+            default:
+                break;
+            }
+        }
+        else
+            dnsstats.dns_over_http1++;
+    }
+    else
+    {
+        if (udp)
+            dnsstats.dns_over_udp++;
+        else
+            dnsstats.dns_over_tcp++;
+    }
+
     dnsSessionData->dns_config = config;
     if ( from_server )
     {
@@ -1218,7 +1258,7 @@ void Dns::snort_dns(Packet* p, bool udp)
         }
 
         if (udp)
-            rm_from_udp_flow(p, trans_id);
+            rm_from_udp_flow(p, trans_id, is_payload);
     }
     else
     {
@@ -1234,7 +1274,7 @@ void Dns::eval(Packet* p)
     assert((p->is_udp() and p->dsize and p->data) or p->has_tcp_data() or p->has_udp_quic_data());
     assert(p->flow);
     if (p->has_udp_quic_data()) // DNS over QUIC follows DNS over TCP
-        snort_dns(p, false);
+        snort_dns(p, false, true);
     else
         snort_dns(p, p->is_udp());
 }
@@ -1271,7 +1311,7 @@ static void dns_init()
 
 static Inspector* dns_ctor(Module* m)
 {
-    DnsModule* mod = (DnsModule*)m;
+    DnsModule* mod = static_cast<DnsModule*>(m);
     return new Dns(mod);
 }
 
index 916a29c044eba7796d641e2a564cf814316a091f..d0cb9741528780777dcec5d8cca1c9dc66b659f4 100644 (file)
@@ -312,7 +312,7 @@ public:
     bool supports_no_ips() const override
     { return true; }
 
-    void snort_dns(snort::Packet* p, bool udp);
+    void snort_dns(snort::Packet* p, bool udp, bool is_payload = false);
 
     const DnsConfig* get_config() const
     { return config; }
index 9512a092dc2637c6e8e990d3f0243fe84acb45a2..1e5057d4f8e8c09e506dbaebb032ec5e3b03e599 100644 (file)
@@ -46,6 +46,12 @@ struct DnsStats
     PegCount packets;
     PegCount requests;
     PegCount responses;
+    PegCount dns_over_udp;
+    PegCount dns_over_tcp;
+    PegCount dns_over_http1;
+    PegCount dns_over_http2;
+    PegCount dns_over_http3;
+    PegCount dns_over_quic;
     PegCount concurrent_sessions;
     PegCount max_concurrent_sessions;
     PegCount aborted_sessions;
index 65e2c58712941b015d918ebb5e5d44baa7b50045..3cc09eaec9fb439b07e73e6e3c2f6cffb446adeb 100644 (file)
@@ -57,7 +57,7 @@ void DnsPayloadEventHandler::handle(DataEvent& event, Flow* flow)
     }
 
     if (is_udp)
-        static_cast<Dns&>(inspector).snort_dns(p, true);
+        static_cast<Dns&>(inspector).snort_dns(p, true, true);
 
     p->data = old_data;
     p->dsize = old_dsize;