]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Add statistics about ECS response sizes
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 20 Feb 2019 10:59:37 +0000 (11:59 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 5 Mar 2019 08:36:03 +0000 (09:36 +0100)
pdns/pdns_recursor.cc
pdns/rec_channel_rec.cc
pdns/recursordist/docs/metrics.rst
pdns/recursordist/test-syncres_cc.cc
pdns/syncres.cc
pdns/syncres.hh

index 64bc3a751118ed3e5c4d46ed3bb5e6e5076f172c..80d30e5d117ef43558d325069cac5a25cba2c108 100644 (file)
@@ -3652,6 +3652,7 @@ static int serviceMain(int argc, char*argv[])
 
   SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
   SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
+  SyncRes::clearECSStats();
 
   if (!::arg().isEmpty("ecs-scope-zero-address")) {
     ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
index 43de415fd29b76d8028b40c0bc1266df40fe8fe2..488697b19f0cc7838df4ad3e2b90756db9b2138c 100644 (file)
@@ -45,10 +45,16 @@ static map<string, function< uint64_t() > >  d_get64bitmembers;
 static pthread_mutex_t d_dynmetricslock = PTHREAD_MUTEX_INITIALIZER;
 static map<string, std::atomic<unsigned long>* > d_dynmetrics;
 
+static std::set<std::string> s_expensiveStats = { "cache-bytes", "packetcache-bytes", "special-memory-usage" };
+
 bool isStatExpensive(const string& name)
 {
-  static const std::set<std::string> expensiveStats = { "cache-bytes", "packetcache-bytes", "special-memory-usage" };
-  return expensiveStats.count(name) != 0;
+  return s_expensiveStats.count(name) != 0;
+}
+
+void markStatAsExpensive(const string& name)
+{
+  s_expensiveStats.insert(name);
 }
 
 static void addGetStat(const string& name, const uint32_t* place)
@@ -1049,6 +1055,19 @@ void registerAllStats()
   addGetStat("policy-result-nodata", &g_stats.policyResults[DNSFilterEngine::PolicyKind::NODATA]);
   addGetStat("policy-result-truncate", &g_stats.policyResults[DNSFilterEngine::PolicyKind::Truncate]);
   addGetStat("policy-result-custom", &g_stats.policyResults[DNSFilterEngine::PolicyKind::Custom]);
+
+  /* make sure that the ECS stats are properly initialized */
+  SyncRes::clearECSStats();
+  for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize4.size(); idx++) {
+    const std::string name = "ecs-v4-response-bits-" + std::to_string(idx + 1);
+    addGetStat(name, &(SyncRes::s_ecsResponsesBySubnetSize4.at(idx)));
+    markStatAsExpensive(name);
+  }
+  for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize6.size(); idx++) {
+    const std::string name = "ecs-v6-response-bits-" + std::to_string(idx + 1);
+    addGetStat(name, &(SyncRes::s_ecsResponsesBySubnetSize6.at(idx)));
+    markStatAsExpensive(name);
+  }
 }
 
 static void doExitGeneric(bool nicely)
index 6e004f5de8f9ff609960f301832ab33d6045f516..895f61b8b868ede25a3484fce0d50e80257350d3 100644 (file)
@@ -236,6 +236,14 @@ ecs-responses
 ^^^^^^^^^^^^^
 number of responses received from authoritative servers with an EDNS Client Subnet option we used (since 4.1)
 
+ecs-v4-response-bits-*
+^^^^^^^^^^^^^^^^^^^^^^
+number of responses received from authoritative servers with an IPv4 EDNS Client Subnet option we used, of this subnet size (1 to 32). Added in 4.2.0.
+
+ecs-v6-response-bits-*
+^^^^^^^^^^^^^^^^^^^^^^
+number of responses received from authoritative servers with an IPv6 EDNS Client Subnet option we used, of this subnet size (1 to 128). Added in 4.2.0.
+
 edns-ping-matches
 ^^^^^^^^^^^^^^^^^
 number of servers that sent a valid EDNS PING   response
index 8736f32aee43dac397c1f50c1e6487d43c2a8dbb..f66682bacfde1981c5b2be44ab8fa0737a43683d 100644 (file)
@@ -151,6 +151,8 @@ static void init(bool debug=false)
   SyncRes::clearFailedServers();
   BOOST_CHECK_EQUAL(SyncRes::getFailedServersSize(), 0);
 
+  SyncRes::clearECSStats();
+
   auto luaconfsCopy = g_luaconfs.getCopy();
   luaconfsCopy.dfe.clear();
   luaconfsCopy.dsAnchors.clear();
@@ -1079,7 +1081,7 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) {
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
 }
 
-BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) {
+BOOST_AUTO_TEST_CASE(test_edns_subnet_by_domain) {
   std::unique_ptr<SyncRes> sr;
   initSR(sr);
 
@@ -1096,15 +1098,49 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) {
 
       BOOST_REQUIRE(srcmask);
       BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
+
+      if (isRootServer(ip)) {
+        setLWResult(res, 0, false, false, true);
+        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+
+        /* this one did not use the ECS info */
+        srcmask = boost::none;
+
+        return 1;
+      } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+
+        /* this one did, but only up to a precision of /16, not the full /24 */
+        srcmask = Netmask("192.0.0.0/16");
+
+        return 1;
+      }
+
       return 0;
     });
 
+  SyncRes::s_ecsqueries = 0;
+  SyncRes::s_ecsresponses = 0;
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
-  BOOST_CHECK_EQUAL(res, RCode::ServFail);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(ret[0].d_name, target);
+  BOOST_CHECK_EQUAL(SyncRes::s_ecsqueries, 2);
+  BOOST_CHECK_EQUAL(SyncRes::s_ecsresponses, 1);
+  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize4) {
+    BOOST_CHECK_EQUAL(entry.second, entry.first == 15 ? 1 : 0);
+  }
+  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize6) {
+    BOOST_CHECK_EQUAL(entry.second, 0);
+  }
 }
 
-BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
+BOOST_AUTO_TEST_CASE(test_edns_subnet_by_addr) {
   std::unique_ptr<SyncRes> sr;
   initSR(sr);
 
@@ -1139,12 +1175,22 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
       return 0;
     });
 
+  SyncRes::s_ecsqueries = 0;
+  SyncRes::s_ecsresponses = 0;
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   BOOST_REQUIRE_EQUAL(ret.size(), 1);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
+  BOOST_CHECK_EQUAL(SyncRes::s_ecsqueries, 1);
+  BOOST_CHECK_EQUAL(SyncRes::s_ecsresponses, 1);
+  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize4) {
+    BOOST_CHECK_EQUAL(entry.second, 0);
+  }
+  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize6) {
+    BOOST_CHECK_EQUAL(entry.second, entry.first == 55 ? 1 : 0);
+  }
 }
 
 BOOST_AUTO_TEST_CASE(test_ecs_use_requestor) {
index 3cf44f16a37127c762b864f709fe2e8894691d76..933d32c6826f8bf04c9ce1a3e542ddff4f247aa9 100644 (file)
@@ -72,6 +72,9 @@ std::atomic<uint64_t> SyncRes::s_nodelegated;
 std::atomic<uint64_t> SyncRes::s_unreachables;
 std::atomic<uint64_t> SyncRes::s_ecsqueries;
 std::atomic<uint64_t> SyncRes::s_ecsresponses;
+std::map<uint8_t, std::atomic<uint64_t>> SyncRes::s_ecsResponsesBySubnetSize4;
+std::map<uint8_t, std::atomic<uint64_t>> SyncRes::s_ecsResponsesBySubnetSize6;
+
 uint8_t SyncRes::s_ecsipv4limit;
 uint8_t SyncRes::s_ecsipv6limit;
 bool SyncRes::s_doIPv6;
@@ -2700,6 +2703,14 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
     if(ednsmask) {
       s_ecsresponses++;
       LOG(prefix<<qname<<": Received EDNS Client Subnet Mask "<<ednsmask->toString()<<" on response"<<endl);
+      if (ednsmask->getBits() > 0) {
+        if (ednsmask->isIpv4()) {
+          ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits()-1);
+        }
+        else {
+          ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits()-1);
+        }
+      }
     }
   }
 
index a19f21e54e6d9ead23d52b2238e582f1690e5f08..f177f47b5b0e586b07d95310d68c2acd34148246 100644 (file)
@@ -558,6 +558,20 @@ public:
     s_ecsScopeZero.source = scopeZeroMask;
   }
 
+  static void clearECSStats()
+  {
+    s_ecsqueries.store(0);
+    s_ecsresponses.store(0);
+
+    for (size_t idx = 0; idx < 32; idx++) {
+      SyncRes::s_ecsResponsesBySubnetSize4[idx].store(0);
+    }
+
+    for (size_t idx = 0; idx < 128; idx++) {
+      SyncRes::s_ecsResponsesBySubnetSize6[idx].store(0);
+    }
+  }
+
   explicit SyncRes(const struct timeval& now);
 
   int beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret);
@@ -686,6 +700,8 @@ public:
   static std::atomic<uint64_t> s_unreachables;
   static std::atomic<uint64_t> s_ecsqueries;
   static std::atomic<uint64_t> s_ecsresponses;
+  static std::map<uint8_t, std::atomic<uint64_t>> s_ecsResponsesBySubnetSize4;
+  static std::map<uint8_t, std::atomic<uint64_t>> s_ecsResponsesBySubnetSize6;
 
   static string s_serverID;
   static unsigned int s_minimumTTL;