// No match in domains, use original source
useMapped = false;
}
+ else {
+ ++it->second.stats.suffixMatches;
+ }
}
// No suffix match node defined, use mapped address
}
DNSName& qname, uint16_t& qtype, uint16_t& qclass,
const struct timeval& now,
string& response, uint32_t& qhash,
- RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source)
+ RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
{
if (!t_packetCache) {
return false;
}
}
+ // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
+ if (t_proxyMapping && source != mappedSource) {
+ if (auto it = t_proxyMapping->lookup(source)) {
+ if (it->second.suffixMatchNode) {
+ if (it->second.suffixMatchNode->check(qname)) {
+ ++it->second.stats.suffixMatches;
+ }
+ }
+ }
+ }
+
g_stats.packetCacheHits++;
SyncRes::s_queries++;
ageDNSPacket(response, age);
but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
as cacheable we would cache it with a wrong tag, so better safe than sorry. */
eventTrace.add(RecEventTrace::PCacheCheck);
- bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source);
+ bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false);
if (cacheHit) {
if (!g_quiet) {
if (t_proxyMapping) {
if (auto it = t_proxyMapping->lookup(source)) {
mappedSource = it->second.address;
+ ++it->second.stats.netmaskMatches;
}
}
if (t_remotes) {
ResolveDeferred
};
+struct ProxyMappingCounts
+{
+ uint64_t netmaskMatches{};
+ uint64_t suffixMatches{};
+};
+
struct ProxyByTableValue
{
ComboAddress address;
boost::optional<SuffixMatchNode> suffixMatchNode;
+ mutable ProxyMappingCounts stats{};
};
using ProxyMapping = NetmaskTree<ProxyByTableValue, Netmask>;
return tt.times.at(n);
}
+static ProxyMappingStats_t* pleaseGetProxyMappingStats()
+{
+ auto ret = new ProxyMappingStats_t;
+ if (t_proxyMapping) {
+ for (const auto& [key, entry] : *t_proxyMapping) {
+ ret->emplace(std::make_pair(key, ProxyMappingCounts{entry.stats.netmaskMatches, entry.stats.suffixMatches}));
+ }
+ }
+ return ret;
+}
+
+static string doGetProxyMappingStats()
+{
+ ostringstream ret;
+ auto m = broadcastAccFunction<ProxyMappingStats_t>(pleaseGetProxyMappingStats);
+ for (const auto& [key, entry] : m) {
+ ret << key.toString() << '\t' << entry.netmaskMatches << '\t' << entry.suffixMatches << endl;
+ }
+ return ret.str();
+}
+
static uint64_t calculateUptime()
{
return time(nullptr) - g_stats.startupTime;
return entries;
}
+static StatsMap toProxyMappingStatsMap(const string& name)
+{
+ const string pbasename = getPrometheusName(name);
+ StatsMap entries;
+
+ auto m = broadcastAccFunction<ProxyMappingStats_t>(pleaseGetProxyMappingStats);
+ size_t count = 0;
+ for (const auto& [key, entry] : m) {
+ auto keyname = pbasename + "{netmask=\"" + key.toString() + "\",count=\"";
+ auto sname1 = name + "-n-" + std::to_string(count);
+ auto pname1 = keyname + "netmaskmatches\"}";
+ entries.emplace(sname1, StatsMapEntry{pname1, std::to_string(entry.netmaskMatches)});
+ auto sname2 = name + "-s-" + std::to_string(count);
+ auto pname2 = keyname + "suffixmatches\"}";
+ entries.emplace(sname2, StatsMapEntry{pname2, std::to_string(entry.suffixMatches)});
+ count++;
+ }
+ return entries;
+}
+
static void registerAllStats1()
{
addGetStat("questions", &g_stats.qcounter);
addGetStat("policy-hits", []() {
return toRPZStatsMap("policy-hits", g_stats.policyHits);
});
+ addGetStat("proxy-mapping-total", []() {
+ return toProxyMappingStatsMap("proxy-mapping-total");
+ });
}
void registerAllStats()
}
}
-static void* pleaseSupplantProxyMapping(std::shared_ptr<ProxyMapping> pm)
+static void* pleaseSupplantProxyMapping(const ProxyMapping& pm)
{
- t_proxyMapping = pm;
+ if (pm.empty()) {
+ t_proxyMapping = nullptr;
+ }
+ else {
+ // Copy the existing stats values, for the new config items also present in the old
+ auto newmapping = make_unique<ProxyMapping>();
+ for (const auto& [nm, entry] : pm) {
+ auto& newentry = newmapping->insert(nm);
+ newentry.second = entry;
+ if (t_proxyMapping) {
+ if (auto existing = t_proxyMapping->lookup(nm); existing != nullptr) {
+ newentry.second.stats = existing->second.stats;
+ }
+ }
+ }
+ t_proxyMapping = std::move(newmapping);
+ }
return nullptr;
}
"get-ntas get all configured Negative Trust Anchors\n"
"get-tas get all configured Trust Anchors\n"
"get-parameter [key1] [key2] .. get configuration parameters\n"
+ "get-proxymapping-stats get proxy mapping statistics\n"
"get-qtypelist get QType statistics\n"
" notice: queries from cache aren't being counted yet\n"
"hash-password [work-factor] ask for a password then return the hashed version\n"
ProxyMapping proxyMapping;
loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
- std::shared_ptr<ProxyMapping> ptr = proxyMapping.empty() ? nullptr : std::make_shared<ProxyMapping>(proxyMapping);
- broadcastFunction([=] { return pleaseSupplantProxyMapping(ptr); });
+ broadcastFunction([=] { return pleaseSupplantProxyMapping(proxyMapping); });
g_log << Logger::Warning << "Reloaded Lua configuration file '" << ::arg()["lua-config-file"] << "', requested via control channel" << endl;
return {0, "Reloaded Lua configuration file '" + ::arg()["lua-config-file"] + "'\n"};
}
if (cmd == "set-event-trace-enabled") {
return {0, setEventTracing(begin, end)};
}
+ if (cmd == "get-proxymapping-stats") {
+ return {0, doGetProxyMappingStats()};
+ }
return {1, "Unknown command '" + cmd + "', try 'help'\n"};
}
get-parameter *KEY* [*KEY*]...
Retrieves the specified configuration parameter(s).
+get-proxymapping-stats
+ Get the list of proxy-mapped subnets and associated counters.
+
get-qtypelist
Retrieves QType statistics. Queries from cache aren't being counted yet.
and finally the workers */
std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
-std::shared_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
-thread_local std::shared_ptr<ProxyMapping> t_proxyMapping;
+std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
+thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
unsigned int RecThreadInfo::s_numDistributorThreads;
return a;
}
+static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& a, const ProxyMappingStats_t& b)
+{
+ for (const auto& [key, entry] : b) {
+ a[key].netmaskMatches += entry.netmaskMatches;
+ a[key].suffixMatches += entry.suffixMatches;
+ }
+ return a;
+}
+
// This function should only be called by the handler to gather
// metrics, wipe the cache, reload the Lua script (not the Lua config)
// or change the current trace regex, and by the SNMP thread to gather
template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
+template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
static int serviceMain(int argc, char* argv[], Logr::log_t log)
{
ProxyMapping proxyMapping;
loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
// Initial proxy mapping
- g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_shared<ProxyMapping>(proxyMapping);
+ g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
}
catch (PDNSException& e) {
SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
t_allowNotifyFor = g_initialAllowNotifyFor;
t_udpclientsocks = std::make_unique<UDPClientSocks>();
t_tcpClientCounts = std::make_unique<tcpClientCounts_t>();
- t_proxyMapping = g_proxyMapping;
+ if (g_proxyMapping) {
+ t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
+ }
+ else {
+ t_proxyMapping = nullptr;
+ }
if (threadInfo.isHandler()) {
if (!primeHints()) {
for (size_t idx = 0; idx < 128; idx++) {
defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1);
}
- std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits";
+ std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total";
::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats;
::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats;
extern string g_pidfname;
extern RecursorControlChannel g_rcc; // only active in the handler thread
-extern thread_local std::shared_ptr<ProxyMapping> t_proxyMapping;
+extern thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
+using ProxyMappingStats_t = std::unordered_map<Netmask, ProxyMappingCounts>;
#ifdef NOD_ENABLED
extern bool g_nodEnabled;
DNSName& qname, uint16_t& qtype, uint16_t& qclass,
const struct timeval& now,
string& response, uint32_t& qhash,
- RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source);
+ RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource);
void protobufLogResponse(pdns::ProtoZero::RecMessage& message);
void protobufLogResponse(const struct dnsheader* dh, LocalStateHolder<LuaConfigItems>& luaconfsLocal,
const RecursorPacketCache::OptPBData& pbData, const struct timeval& tv,
if (t_proxyMapping) {
if (auto it = t_proxyMapping->lookup(conn->d_source)) {
conn->d_mappedSource = it->second.address;
+ ++it->second.stats.netmaskMatches;
}
}
if (t_allowFrom && !t_allowFrom->match(&conn->d_mappedSource)) {
but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
as cacheable we would cache it with a wrong tag, so better safe than sorry. */
dc->d_eventTrace.add(RecEventTrace::PCacheCheck);
- bool cacheHit = checkForCacheHit(qnameParsed, dc->d_tag, conn->data, qname, qtype, qclass, g_now, response, dc->d_qhash, pbData, true, dc->d_source);
+ bool cacheHit = checkForCacheHit(qnameParsed, dc->d_tag, conn->data, qname, qtype, qclass, g_now, response, dc->d_qhash, pbData, true, dc->d_source, dc->d_mappedSource);
dc->d_eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false);
if (cacheHit) {
if (!fromProxyProtocolSource && t_proxyMapping) {
if (auto it = t_proxyMapping->lookup(addr)) {
mappedSource = it->second.address;
+ ++it->second.stats.netmaskMatches;
}
}
if (!fromProxyProtocolSource && t_allowFrom && !t_allowFrom->match(&mappedSource)) {
{"maintenance-calls",
MetricDefinition(PrometheusMetricType::counter,
"Number of times internal maintenance has been called, including Lua maintenance")},
+
+ // For multicounters, state the first
+ {"proxy-mapping-total-n-0",
+ MetricDefinition(PrometheusMetricType::multicounter,
+ "Number of queries matching proxyMappings")},
};
#define CHECK_PROMETHEUS_METRICS 0
if (!s_metricDefinitions.getMetricDetails(metricName, metricDetails)) {
SLOG(g_log << Logger::Debug << "{ \"" << metricName << "\", MetricDefinition(PrometheusMetricType::counter, \"\")}," << endl,
- g_slog->info(Logr::Debug, "{ \"" << metricName << "\", MetricDefinition(PrometheusMetricType::counter, \"\")},"));
+ g_slog->info(Logr::Debug, "{ \"" + metricName + "\", MetricDefinition(PrometheusMetricType::counter, \"\")},"));
}
}
}