]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add metrics for cookies
authorOtto Moerbeek <otto@drijf.net>
Wed, 26 Mar 2025 09:57:07 +0000 (10:57 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 4 Sep 2025 09:05:16 +0000 (11:05 +0200)
pdns/recursordist/RECURSOR-MIB.txt
pdns/recursordist/lwres.cc
pdns/recursordist/metrics_table.py
pdns/recursordist/rec-tcounters.hh
regression-tests.recursor-dnssec/test_SNMP.py

index e96ce2760e770d347ea85814a7ae24ae7a224913..716da33ddef6d51f186939e5a67b5e09b72fabd7 100644 (file)
@@ -1302,6 +1302,70 @@ ecsMissing OBJECT-TYPE
         "Number of answers where ECS info was missing"
     ::= { stats 153 }
 
+cookieMalformed OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Malformed cookies received"
+    ::= { stats 154 }
+
+cookieMatched OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Matching cookies recieved"
+    ::= { stats 155 }
+
+cookieMismatchTcp OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Mismatched cookies received over TCP"
+    ::= { stats 156 }
+
+cookieMismatchUdp OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Mismatched cookies received over UDP"
+    ::= { stats 157 }
+
+cookieNotInReply OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Authoritative serve sent a reply bnack without cookie"
+    ::= { stats 158 }
+
+cookieRetry OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Retries because authoritative server sent a BADCOOKIE reply"
+    ::= { stats 159 }
+
+cookiesSupported OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Number of authoritative server IPs marked as supporting cookies"
+    ::= { stats 160 }
+
+cookiesUnsupported OBJECT-TYPE
+    SYNTAX Counter64
+    MAX-ACCESS read-only
+    STATUS current
+    DESCRIPTION
+        "Number of authoritative server IPs marked as not supporting cookies"
+    ::= { stats 161 }
+
 ---
 --- Traps / Notifications
 ---
@@ -1501,7 +1565,15 @@ recGroup OBJECT-GROUP
         maxChainWeight,
         chainLimits,
         tcpOverflow,
-        ecsMissing
+        ecsMissing,
+        cookieMalformed,
+        cookieMatched,
+        cookieMismatchTcp,
+        cookieMismatchUdp,
+        cookieNotInReply,
+        cookieRetry,
+        cookiesSupported,
+        cookiesUnsupported
     }
     STATUS current
     DESCRIPTION "Objects conformance group for PowerDNS Recursor"
index c724fa389af02043c9194f6a273f634d77cbc184..432ca1e56a8645a990c4a3a4f08726c267b26c5b 100644 (file)
@@ -489,13 +489,18 @@ static std::pair<bool, LWResult::Result> incomingCookie(const OptLog& log, const
         VLOG(log, "Received cookie info back from " << address.toString() << ": " << received.toDisplayString() << endl);
         if (received.getClient() == cookieSentOut->getClient()) {
           VLOG(log, "Client cookie from " << address.toString() << " matched! Storing with localAddress " << localip.toString() << endl);
+          ++t_Counters.at(rec::Counter::cookieMatched);
           found->d_localaddress = localip;
           found->d_cookie = received;
+          if (found->getSupport() == CookieEntry::Support::Probing) {
+            ++t_Counters.at(rec::Counter::cookiesSupported);
+          }
           found->setSupport(CookieEntry::Support::Supported, now.tv_sec);
           // check extended error code
           uint16_t ercode = (edo.d_extRCode << 4) | lwr.d_rcode;
           if (ercode == ERCode::BADCOOKIE) {
             lwr.d_validpacket = true;
+            ++t_Counters.at(rec::Counter::cookieRetry);
             VLOG(log, "Server " << localip.toString() << " returned BADCOOKIE " << endl);
             return {true, LWResult::Result::BadCookie}; // We did update the entry, retry should succeed
           }
@@ -505,16 +510,19 @@ static std::pair<bool, LWResult::Result> incomingCookie(const OptLog& log, const
             // Server responded with a wrong client cookie, fall back to TCP, RFC 7873 5.3
             VLOG(log, "Server " << localip.toString() << " responded with wrong client cookie, fall back to TCP" << endl);
             lwr.d_validpacket = true;
+            ++t_Counters.at(rec::Counter::cookieMismatchedOverUDP);
             return {true, LWResult::Result::Spoofed};
           }
           // mismatched cookie when already doing TCP, ignore that
           VLOG(log, "Server " << localip.toString() << " responded with wrong client cookie over TCP, ignoring that" << endl);
+          ++t_Counters.at(rec::Counter::cookieMismatchedOverTCP);
         }
       }
       else {
         VLOG(log, "Malformed cookie in reply from " << address.toString() << ", dropping as if was a timeout" << endl);
         // Do something special if we get malformed repeatedly? And or consider current status?
         lwr.d_validpacket = false;
+        ++t_Counters.at(rec::Counter::cookieMalformed);
         return {true, LWResult::Result::Timeout};
       }
       break; // only consider first cookie option found, RFC 7873 5.3
@@ -796,6 +804,7 @@ static LWResult::Result asyncresolve(const OptLog& log, const ComboAddress& addr
 
     // Case: we sent out a cookie but did not get one back
     if (cookieSentOut && !cookieFoundInReply && !*chained) {
+      ++t_Counters.at(rec::Counter::cookieNotInReply);
       auto lock = s_cookiestore.lock();
       auto found = lock->find(address);
       if (found != lock->end()) {
@@ -803,13 +812,14 @@ static LWResult::Result asyncresolve(const OptLog& log, const ComboAddress& addr
         case CookieEntry::Support::Probing:
           VLOG(log, "No cookie in repy from " << address.toString() << ", was probing, setting support to Unsupported" << endl);
           found->setSupport(CookieEntry::Support::Unsupported, now->tv_sec);
+          ++t_Counters.at(rec::Counter::cookiesUnsupported);
           break;
         case CookieEntry::Support::Unsupported:
           // We could have detected the server does not support cookies in the meantime
           VLOG(log, "No cookie in repy from " << address.toString() << ", cookie state is Unsupported, fine" << endl);
           break;
         case CookieEntry::Support::Supported:
-          // RFC says: ignore rpolies not containing any cookie info, equivalent to timeout
+          // RFC says: ignore replies not containing any cookie info, equivalent to timeout
           VLOG(log, "No cookie in repy from " << address.toString() << ", cookie state is Supported, dropping packet as if it timed out)" << endl);
           return LWResult::Result::Timeout;
           break;
index 9bd117049a0e05711a344a928bffb16f58fa37d4..42029702859d4de1af427cbcc108fca77b5cd839 100644 (file)
         'desc': 'Incoming TCP limits reached',
         'snmp': 152,
     },
+    {
+        'name': 'ecs-missing',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::ecsMissingCount); }',
+        'desc': 'Number of answers where ECS info was missing',
+        'snmp': 153,
+    },
+    {
+        'name': 'cookie-malformed',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMalformed); }',
+        'desc': 'Malformed cookies received',
+        'snmp': 154,
+    },
+    {
+        'name': 'cookie-matched',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMatched); }',
+        'desc': 'Matching cookies recieved',
+        'snmp': 155,
+    },
+    {
+        'name': 'cookie-mismatch-tcp',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverTCP); }',
+        'desc': 'Mismatched cookies received over TCP',
+        'snmp': 156,
+    },
+    {
+        'name': 'cookie-mismatch-udp',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverUDP); }',
+        'desc': 'Mismatched cookies received over UDP',
+        'snmp': 157,
+    },
+    {
+        'name': 'cookie-not-in-reply',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieNotInReply); }',
+        'desc': 'Authoritative serve sent a reply bnack without cookie',
+        'snmp': 158,
+    },
+    {
+        'name': 'cookie-retry',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookieRetry); }',
+        'desc': 'Retries because authoritative server sent a BADCOOKIE reply',
+        'snmp': 159,
+    },
+    {
+        'name': 'cookies-supported',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookiesSupported); }',
+        'desc': 'Number of authoritative server IPs marked as supporting cookies',
+        'snmp': 160,
+    },
+    {
+        'name': 'cookies-unsupported',
+        'lambda': '[] { return g_Counters.sum(rec::Counter::cookiesUnsupported); }',
+        'desc': 'Number of authoritative server IPs marked as not supporting cookies',
+        'snmp': 161,
+    },
     {
         'name': 'remote-logger-count',
         'lambda':  '''[]() {
         'pname': 'proxy-mapping-total-n-0', # For multicounters, state the first
         # No SNMP
     },
-    {
-        'name': 'ecs-missing',
-        'lambda': '[] { return g_Counters.sum(rec::Counter::ecsMissingCount); }',
-        'desc': 'Number of answers where ECS info was missing',
-        'snmp': 153,
-    },
 ]
index 9f637971aa468d9f608743b9255c877902c167c2..171a286cb4e6eeecd7d08aa6b131e62c0be91034 100644 (file)
@@ -99,6 +99,14 @@ enum class Counter : uint8_t
   maxChainWeight,
   chainLimits,
   ecsMissingCount,
+  cookieMalformed,
+  cookieMatched,
+  cookieMismatchedOverTCP,
+  cookieMismatchedOverUDP,
+  cookieNotInReply,
+  cookieRetry,
+  cookiesSupported,
+  cookiesUnsupported,
 
   numberOfCounters
 };
index c55c5818ef3429bf1bb12462d9146c13cda2c0bb..34088ba8d3718737add87680f2170b91efa058e9 100644 (file)
@@ -21,7 +21,7 @@ class SNMPTest(RecursorTest):
     """
 
     def _checkStatsValues(self, results):
-        count = 153
+        count = 161
         for i in list(range(1, count)):
             oid = self._snmpOID + '.1.' + str(i) + '.0'
             self.assertTrue(oid in results)