From 4fc57a40ae1313fd328c1351097b667f9ef1db4d Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Tue, 9 Jan 2024 15:33:30 +0100 Subject: [PATCH 01/65] Refactor RPZ download thread code --- pdns/recursordist/rec-lua-conf.cc | 6 +- pdns/recursordist/rec-lua-conf.hh | 4 +- pdns/recursordist/rpzloader.cc | 357 +++++++++++++++--------------- pdns/recursordist/rpzloader.hh | 18 +- 4 files changed, 206 insertions(+), 179 deletions(-) diff --git a/pdns/recursordist/rec-lua-conf.cc b/pdns/recursordist/rec-lua-conf.cc index 071271f61a62..615f8b9621ce 100644 --- a/pdns/recursordist/rec-lua-conf.cc +++ b/pdns/recursordist/rec-lua-conf.cc @@ -387,7 +387,7 @@ static void rpzPrimary(LuaConfigItems& lci, luaConfigDelayedThreads& delayedThre lci.d_slog->error(Logr::Error, e.reason, "Exception configuring 'rpzPrimary'", Logging::Loggable("PDNSException"))); } - delayedThreads.rpzPrimaryThreads.emplace_back(primaries, defpol, defpolOverrideLocal, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes, localAddress, axfrTimeout, refresh, sr, dumpFile); + delayedThreads.rpzPrimaryThreads.emplace_back(RPZTrackerParams{primaries, defpol, defpolOverrideLocal, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes, localAddress, axfrTimeout, refresh, sr, dumpFile}); } // A wrapper class that loads the standard Lua defintions into the context, so that we can use things like pdns.A @@ -904,8 +904,8 @@ void startLuaConfigDelayedThreads(const luaConfigDelayedThreads& delayedThreads, try { // The get calls all return a value object here. That is essential, since we want copies so that RPZIXFRTracker gets values // with the proper lifetime. - std::thread t(RPZIXFRTracker, std::get<0>(rpzPrimary), std::get<1>(rpzPrimary), std::get<2>(rpzPrimary), std::get<3>(rpzPrimary), std::get<4>(rpzPrimary), std::get<5>(rpzPrimary), std::get<6>(rpzPrimary) * 1024 * 1024, std::get<7>(rpzPrimary), std::get<8>(rpzPrimary), std::get<9>(rpzPrimary), std::get<10>(rpzPrimary), std::get<11>(rpzPrimary), generation); - t.detach(); + std::thread theThread(RPZIXFRTracker, rpzPrimary, generation); + theThread.detach(); } catch (const std::exception& e) { SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.what() << endl, diff --git a/pdns/recursordist/rec-lua-conf.hh b/pdns/recursordist/rec-lua-conf.hh index 29a18b00f61a..440068ec6ea6 100644 --- a/pdns/recursordist/rec-lua-conf.hh +++ b/pdns/recursordist/rec-lua-conf.hh @@ -29,6 +29,7 @@ #include "rec-zonetocache.hh" #include "logging.hh" #include "fstrm_logger.hh" +#include "rpzloader.hh" struct ProtobufExportConfig { @@ -125,8 +126,7 @@ extern GlobalStateHolder g_luaconfs; struct luaConfigDelayedThreads { - // Please make sure that the tuple below only contains value types since they are used as parameters in a thread ct - std::vector, boost::optional, bool, uint32_t, size_t, TSIGTriplet, size_t, ComboAddress, uint16_t, uint32_t, std::shared_ptr, std::string>> rpzPrimaryThreads; + std::vector rpzPrimaryThreads; }; void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads, ProxyMapping&); diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index 549902c0586d..45c3576fae93 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -386,47 +386,27 @@ static bool dumpZoneToDisk(Logr::log_t logger, const DNSName& zoneName, const st return true; } -void RPZIXFRTracker(const std::vector& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t xfrTimeout, const uint32_t refreshFromConf, std::shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration) +static void preloadRPZFIle(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, Logr::log_t logger) { - setThreadName("rec/rpzixfr"); - bool isPreloaded = sr != nullptr; - auto logger = g_slog->withName("rpz"); - - /* we can _never_ modify this zone directly, we need to do a full copy then replace the existing zone */ - std::shared_ptr oldZone = g_luaconfs.getLocal()->dfe.getZone(zoneIdx); - if (!oldZone) { - SLOG(g_log << Logger::Error << "Unable to retrieve RPZ zone with index " << zoneIdx << " from the configuration, exiting" << endl, - logger->error(Logr::Error, "Unable to retrieve RPZ zone from configuration", "index", Logging::Loggable(zoneIdx))); - return; - } - - // If oldZone failed to load its getRefresh() returns 0, protect against that - uint32_t refresh = std::max(refreshFromConf ? refreshFromConf : oldZone->getRefresh(), 10U); - DNSName zoneName = oldZone->getDomain(); - std::string polName = !oldZone->getName().empty() ? oldZone->getName() : zoneName.toStringNoDot(); - - // Now that we know the name, set it in the logger - logger = logger->withValues("zone", Logging::Loggable(zoneName)); - - while (!sr) { + while (!params.soaRecordContent) { /* if we received an empty sr, the zone was not really preloaded */ /* full copy, as promised */ std::shared_ptr newZone = std::make_shared(*oldZone); - for (const auto& primary : primaries) { + for (const auto& primary : params.primaries) { try { - sr = loadRPZFromServer(logger, primary, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, xfrTimeout); - newZone->setSerial(sr->d_st.serial); - newZone->setRefresh(sr->d_st.refresh); - refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U); - setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), false, true); + params.soaRecordContent = loadRPZFromServer(logger, primary, zoneName, newZone, params.defpol, params.defpolOverrideLocal, params.maxTTL, params.tsigtriplet, params.maxReceivedBytes, params.localAddress, params.xfrTimeout); + newZone->setSerial(params.soaRecordContent->d_st.serial); + newZone->setRefresh(params.soaRecordContent->d_st.refresh); + refresh = std::max(params.refreshFromConf != 0 ? params.refreshFromConf : newZone->getRefresh(), 1U); + setRPZZoneNewState(polName, params.soaRecordContent->d_st.serial, newZone->size(), false, true); - g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) { + g_luaconfs.modify([zoneIdx = params.zoneIdx, &newZone](LuaConfigItems& lci) { lci.dfe.setZone(zoneIdx, newZone); }); - if (!dumpZoneFileName.empty()) { - dumpZoneToDisk(logger, zoneName, newZone, dumpZoneFileName); + if (!params.dumpZoneFileName.empty()) { + dumpZoneToDisk(logger, zoneName, newZone, params.dumpZoneFileName); } /* no need to try another primary */ @@ -445,176 +425,207 @@ void RPZIXFRTracker(const std::vector& primaries, const boost::opt } // Release newZone before (long) sleep to reduce memory usage newZone = nullptr; - if (!sr) { + if (!params.soaRecordContent) { sleep(refresh); } } +} - bool skipRefreshDelay = isPreloaded; - - for (;;) { - // Don't hold on to oldZone, it well be re-assigned after sleep in the try block - oldZone = nullptr; - DNSRecord dr; - dr.setContent(sr); +static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, bool skipRefreshDelay, uint64_t configGeneration, Logr::log_t logger) +{ + // Don't hold on to oldZone, it well be re-assigned after sleep in the try block + oldZone = nullptr; + DNSRecord dr; + dr.setContent(params.soaRecordContent); - if (skipRefreshDelay) { - skipRefreshDelay = false; - } - else { - sleep(refresh); - } - auto luaconfsLocal = g_luaconfs.getLocal(); + if (skipRefreshDelay) { + skipRefreshDelay = false; + } + else { + sleep(refresh); + } + auto luaconfsLocal = g_luaconfs.getLocal(); - if (luaconfsLocal->generation != configGeneration) { - /* the configuration has been reloaded, meaning that a new thread - has been started to handle that zone and we are now obsolete. - */ - SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, - logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; - } + if (luaconfsLocal->generation != configGeneration) { + /* the configuration has been reloaded, meaning that a new thread + has been started to handle that zone and we are now obsolete. + */ + SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, + logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); + return; + } - vector, vector>> deltas; - for (const auto& primary : primaries) { - auto soa = getRR(dr); - auto serial = soa ? soa->d_st.serial : 0; - SLOG(g_log << Logger::Info << "Getting IXFR deltas for " << zoneName << " from " << primary.toStringWithPort() << ", our serial: " << serial << endl, - logger->info(Logr::Info, "Getting IXFR deltas", "address", Logging::Loggable(primary), "ourserial", Logging::Loggable(serial))); + vector, vector>> deltas; + for (const auto& primary : params.primaries) { + auto soa = getRR(dr); + auto serial = soa ? soa->d_st.serial : 0; + SLOG(g_log << Logger::Info << "Getting IXFR deltas for " << zoneName << " from " << primary.toStringWithPort() << ", our serial: " << serial << endl, + logger->info(Logr::Info, "Getting IXFR deltas", "address", Logging::Loggable(primary), "ourserial", Logging::Loggable(serial))); - ComboAddress local(localAddress); - if (local == ComboAddress()) { - local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0); - } + ComboAddress local(params.localAddress); + if (local == ComboAddress()) { + local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0); + } - try { - deltas = getIXFRDeltas(primary, zoneName, dr, xfrTimeout, true, tt, &local, maxReceivedBytes); + try { + deltas = getIXFRDeltas(primary, zoneName, dr, params.xfrTimeout, true, params.tsigtriplet, &local, params.maxReceivedBytes); - /* no need to try another primary */ - break; - } - catch (const std::runtime_error& e) { - SLOG(g_log << Logger::Warning << e.what() << endl, - logger->error(Logr::Warning, e.what(), "Exception during retrieval of delta", "exception", Logging::Loggable("std::runtime_error"))); - incRPZFailedTransfers(polName); - continue; - } + /* no need to try another primary */ + break; } - - if (deltas.empty()) { + catch (const std::runtime_error& e) { + SLOG(g_log << Logger::Warning << e.what() << endl, + logger->error(Logr::Warning, e.what(), "Exception during retrieval of delta", "exception", Logging::Loggable("std::runtime_error"))); + incRPZFailedTransfers(polName); continue; } + } - try { - SLOG(g_log << Logger::Info << "Processing " << deltas.size() << " delta" << addS(deltas) << " for RPZ " << zoneName << endl, - logger->info(Logr::Info, "Processing deltas", "size", Logging::Loggable(deltas.size()))); + if (deltas.empty()) { + return; + } - if (luaconfsLocal->generation != configGeneration) { - SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, - logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; - } - oldZone = luaconfsLocal->dfe.getZone(zoneIdx); - if (!oldZone || oldZone->getDomain() != zoneName) { - SLOG(g_log << Logger::Info << "This policy is no more, stopping the existing RPZ update thread for " << zoneName << endl, - logger->info(Logr::Info, "This policy is no more, stopping the existing RPZ update thread")); - return; + try { + SLOG(g_log << Logger::Info << "Processing " << deltas.size() << " delta" << addS(deltas) << " for RPZ " << zoneName << endl, + logger->info(Logr::Info, "Processing deltas", "size", Logging::Loggable(deltas.size()))); + + if (luaconfsLocal->generation != configGeneration) { + SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, + logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); + return; + } + oldZone = luaconfsLocal->dfe.getZone(params.zoneIdx); + if (!oldZone || oldZone->getDomain() != zoneName) { + SLOG(g_log << Logger::Info << "This policy is no more, stopping the existing RPZ update thread for " << zoneName << endl, + logger->info(Logr::Info, "This policy is no more, stopping the existing RPZ update thread")); + return; + } + /* we need to make a _full copy_ of the zone we are going to work on */ + std::shared_ptr newZone = std::make_shared(*oldZone); + /* initialize the current serial to the last one */ + std::shared_ptr currentSR = params.soaRecordContent; + + int totremove = 0; + int totadd = 0; + bool fullUpdate = false; + for (const auto& delta : deltas) { + const auto& remove = delta.first; + const auto& add = delta.second; + if (remove.empty()) { + SLOG(g_log << Logger::Warning << "IXFR update is a whole new zone" << endl, + logger->info(Logr::Warning, "IXFR update is a whole new zone")); + newZone->clear(); + fullUpdate = true; } - /* we need to make a _full copy_ of the zone we are going to work on */ - std::shared_ptr newZone = std::make_shared(*oldZone); - /* initialize the current serial to the last one */ - std::shared_ptr currentSR = sr; - - int totremove = 0, totadd = 0; - bool fullUpdate = false; - for (const auto& delta : deltas) { - const auto& remove = delta.first; - const auto& add = delta.second; - if (remove.empty()) { - SLOG(g_log << Logger::Warning << "IXFR update is a whole new zone" << endl, - logger->info(Logr::Warning, "IXFR update is a whole new zone")); - newZone->clear(); - fullUpdate = true; + for (const auto& resourceRecord : remove) { // should always contain the SOA + if (resourceRecord.d_type == QType::NS) { + continue; } - for (const auto& rr : remove) { // should always contain the SOA - if (rr.d_type == QType::NS) - continue; - if (rr.d_type == QType::SOA) { - auto oldsr = getRR(rr); - if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) { - // cout<<"Got good removal of SOA serial "<d_st.serial<d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update"); - } - } + if (resourceRecord.d_type == QType::SOA) { + auto oldsr = getRR(resourceRecord); + if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) { + // cout<<"Got good removal of SOA serial "<d_st.serial<info(g_logRPZChanges ? Logr::Info : Logr::Debug, "Remove from RPZ zone", "name", Logging::Loggable(rr.d_name))); - RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL, logger); - } - } - - for (const auto& rr : add) { // should always contain the new SOA - if (rr.d_type == QType::NS) - continue; - if (rr.d_type == QType::SOA) { - auto tempSR = getRR(rr); - // g_log<d_st.serial<info(g_logRPZChanges ? Logr::Info : Logr::Debug, "Addition to RPZ zone", "name", Logging::Loggable(rr.d_name))); - RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL, logger); + throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update"); } } + else { + totremove++; + SLOG(g_log << (g_logRPZChanges ? Logger::Info : Logger::Debug) << "Had removal of " << resourceRecord.d_name << " from RPZ zone " << zoneName << endl, + logger->info(g_logRPZChanges ? Logr::Info : Logr::Debug, "Remove from RPZ zone", "name", Logging::Loggable(resourceRecord.d_name))); + RPZRecordToPolicy(resourceRecord, newZone, false, params.defpol, params.defpolOverrideLocal, params.maxTTL, logger); + } } - /* only update sr now that all changes have been converted */ - if (currentSR) { - sr = std::move(currentSR); - } - SLOG(g_log << Logger::Info << "Had " << totremove << " RPZ removal" << addS(totremove) << ", " << totadd << " addition" << addS(totadd) << " for " << zoneName << " New serial: " << sr->d_st.serial << endl, - logger->info(Logr::Info, "RPZ mutations", "removals", Logging::Loggable(totremove), "additions", Logging::Loggable(totadd), "newserial", Logging::Loggable(sr->d_st.serial))); - newZone->setSerial(sr->d_st.serial); - newZone->setRefresh(sr->d_st.refresh); - setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), false, fullUpdate); - - /* we need to replace the existing zone with the new one, - but we don't want to touch anything else, especially other zones, - since they might have been updated by another RPZ IXFR tracker thread. - */ - if (luaconfsLocal->generation != configGeneration) { - SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, - logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; + for (const auto& resourceRecord : add) { // should always contain the new SOA + if (resourceRecord.d_type == QType::NS) { + continue; + } + if (resourceRecord.d_type == QType::SOA) { + auto tempSR = getRR(resourceRecord); + // g_log<d_st.serial<info(g_logRPZChanges ? Logr::Info : Logr::Debug, "Addition to RPZ zone", "name", Logging::Loggable(resourceRecord.d_name))); + RPZRecordToPolicy(resourceRecord, newZone, true, params.defpol, params.defpolOverrideLocal, params.maxTTL, logger); + } } - g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) { - lci.dfe.setZone(zoneIdx, newZone); - }); + } - if (!dumpZoneFileName.empty()) { - dumpZoneToDisk(logger, zoneName, newZone, dumpZoneFileName); - } - refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U); + /* only update sr now that all changes have been converted */ + if (currentSR) { + params.soaRecordContent = std::move(currentSR); } - catch (const std::exception& e) { - SLOG(g_log << Logger::Error << "Error while applying the update received over XFR for " << zoneName << ", skipping the update: " << e.what() << endl, - logger->error(Logr::Error, e.what(), "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("std::exception"))); + SLOG(g_log << Logger::Info << "Had " << totremove << " RPZ removal" << addS(totremove) << ", " << totadd << " addition" << addS(totadd) << " for " << zoneName << " New serial: " << params.soaRecordContent->d_st.serial << endl, + logger->info(Logr::Info, "RPZ mutations", "removals", Logging::Loggable(totremove), "additions", Logging::Loggable(totadd), "newserial", Logging::Loggable(params.soaRecordContent->d_st.serial))); + newZone->setSerial(params.soaRecordContent->d_st.serial); + newZone->setRefresh(params.soaRecordContent->d_st.refresh); + setRPZZoneNewState(polName, params.soaRecordContent->d_st.serial, newZone->size(), false, fullUpdate); + + /* we need to replace the existing zone with the new one, + but we don't want to touch anything else, especially other zones, + since they might have been updated by another RPZ IXFR tracker thread. + */ + if (luaconfsLocal->generation != configGeneration) { + SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, + logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); + return; } - catch (const PDNSException& e) { - SLOG(g_log << Logger::Error << "Error while applying the update received over XFR for " << zoneName << ", skipping the update: " << e.reason << endl, - logger->error(Logr::Error, e.reason, "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("PDNSException"))); + g_luaconfs.modify([zoneIdx = params.zoneIdx, &newZone](LuaConfigItems& lci) { + lci.dfe.setZone(zoneIdx, newZone); + }); + + if (!params.dumpZoneFileName.empty()) { + dumpZoneToDisk(logger, zoneName, newZone, params.dumpZoneFileName); } + refresh = std::max(params.refreshFromConf != 0 ? params.refreshFromConf : newZone->getRefresh(), 1U); + } + catch (const std::exception& e) { + SLOG(g_log << Logger::Error << "Error while applying the update received over XFR for " << zoneName << ", skipping the update: " << e.what() << endl, + logger->error(Logr::Error, e.what(), "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("std::exception"))); + } + catch (const PDNSException& e) { + SLOG(g_log << Logger::Error << "Error while applying the update received over XFR for " << zoneName << ", skipping the update: " << e.reason << endl, + logger->error(Logr::Error, e.reason, "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("PDNSException"))); + } +} + +void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration) +{ + setThreadName("rec/rpzixfr"); + bool isPreloaded = params.soaRecordContent != nullptr; + auto logger = g_slog->withName("rpz"); + + /* we can _never_ modify this zone directly, we need to do a full copy then replace the existing zone */ + std::shared_ptr oldZone = g_luaconfs.getLocal()->dfe.getZone(params.zoneIdx); + if (!oldZone) { + SLOG(g_log << Logger::Error << "Unable to retrieve RPZ zone with index " << params.zoneIdx << " from the configuration, exiting" << endl, + logger->error(Logr::Error, "Unable to retrieve RPZ zone from configuration", "index", Logging::Loggable(params.zoneIdx))); + return; + } + + // If oldZone failed to load its getRefresh() returns 0, protect against that + uint32_t refresh = std::max(params.refreshFromConf != 0 ? params.refreshFromConf : oldZone->getRefresh(), 10U); + DNSName zoneName = oldZone->getDomain(); + std::string polName = !oldZone->getName().empty() ? oldZone->getName() : zoneName.toStringNoDot(); + + // Now that we know the name, set it in the logger + logger = logger->withValues("zone", Logging::Loggable(zoneName)); + + preloadRPZFIle(params, zoneName, oldZone, refresh, polName, logger); + + bool skipRefreshDelay = isPreloaded; + + for (;;) { + RPZTrackerIteration(params, zoneName, oldZone, refresh, polName, skipRefreshDelay, configGeneration, logger); } } diff --git a/pdns/recursordist/rpzloader.hh b/pdns/recursordist/rpzloader.hh index 8f37a0b4c0f7..c8811ee0b3d9 100644 --- a/pdns/recursordist/rpzloader.hh +++ b/pdns/recursordist/rpzloader.hh @@ -26,8 +26,24 @@ extern bool g_logRPZChanges; +// Please make sure that the struct below only contains value types since they are used as parameters in a thread ct +struct RPZTrackerParams { + std::vector primaries; + boost::optional defpol; + bool defpolOverrideLocal; + uint32_t maxTTL; + size_t zoneIdx; + TSIGTriplet tsigtriplet; + size_t maxReceivedBytes; + ComboAddress localAddress; + uint16_t xfrTimeout; + uint32_t refreshFromConf; + std::shared_ptr soaRecordContent; + std::string dumpZoneFileName; +}; + std::shared_ptr loadRPZFromFile(const std::string& fname, std::shared_ptr zone, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL); -void RPZIXFRTracker(const std::vector& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t xfrTimeout, const uint32_t reloadFromConf, shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration); +void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration); struct rpzStats { From b59c7fc35b29bfde8a5a34230e1d0eb22923e5c9 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Wed, 10 Jan 2024 09:55:11 +0100 Subject: [PATCH 02/65] RPZ notify --- pdns/recursordist/pdns_recursor.cc | 2 +- pdns/recursordist/rpzloader.cc | 88 ++++++++++++++++++++++++------ pdns/recursordist/rpzloader.hh | 1 + 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/pdns/recursordist/pdns_recursor.cc b/pdns/recursordist/pdns_recursor.cc index b10234786eef..ad6c83c70cab 100644 --- a/pdns/recursordist/pdns_recursor.cc +++ b/pdns/recursordist/pdns_recursor.cc @@ -2318,7 +2318,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl, g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname))); } - + notifyRPZTracker(qname); requestWipeCaches(qname); // the operation will now be treated as a Query, generating diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index 45c3576fae93..02be12566288 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -386,7 +386,17 @@ static bool dumpZoneToDisk(Logr::log_t logger, const DNSName& zoneName, const st return true; } -static void preloadRPZFIle(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, Logr::log_t logger) +// A struct that holds the condition var and related stuff to allow notifies to be sent to the tread owning +// the struct. +struct RPZWaiter { + RPZWaiter(std::thread::id arg) : id(arg) {} + std::thread::id id; + std::mutex mutex; + std::condition_variable condVar; + std::atomic stop{false}; +}; + +static void preloadRPZFIle(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, RPZWaiter& rpzwaiter, Logr::log_t logger) { while (!params.soaRecordContent) { /* if we received an empty sr, the zone was not really preloaded */ @@ -426,23 +436,29 @@ static void preloadRPZFIle(RPZTrackerParams& params, const DNSName& zoneName, st // Release newZone before (long) sleep to reduce memory usage newZone = nullptr; if (!params.soaRecordContent) { - sleep(refresh); + std::unique_lock lock(rpzwaiter.mutex); + rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(refresh), + [&stop = rpzwaiter.stop] { return stop.load(); }); } + rpzwaiter.stop = false; } } -static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, bool skipRefreshDelay, uint64_t configGeneration, Logr::log_t logger) +static bool RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneName, std::shared_ptr& oldZone, uint32_t& refresh, const string& polName, bool& skipRefreshDelay, uint64_t configGeneration, RPZWaiter& rpzwaiter, Logr::log_t logger) { // Don't hold on to oldZone, it well be re-assigned after sleep in the try block oldZone = nullptr; - DNSRecord dr; - dr.setContent(params.soaRecordContent); + DNSRecord dnsRecord; + dnsRecord.setContent(params.soaRecordContent); if (skipRefreshDelay) { skipRefreshDelay = false; } else { - sleep(refresh); + std::unique_lock lock(rpzwaiter.mutex); + rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(refresh), + [&stop = rpzwaiter.stop] { return stop.load(); }); + rpzwaiter.stop = false; } auto luaconfsLocal = g_luaconfs.getLocal(); @@ -452,12 +468,12 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam */ SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; + return false; } vector, vector>> deltas; for (const auto& primary : params.primaries) { - auto soa = getRR(dr); + auto soa = getRR(dnsRecord); auto serial = soa ? soa->d_st.serial : 0; SLOG(g_log << Logger::Info << "Getting IXFR deltas for " << zoneName << " from " << primary.toStringWithPort() << ", our serial: " << serial << endl, logger->info(Logr::Info, "Getting IXFR deltas", "address", Logging::Loggable(primary), "ourserial", Logging::Loggable(serial))); @@ -468,7 +484,7 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam } try { - deltas = getIXFRDeltas(primary, zoneName, dr, params.xfrTimeout, true, params.tsigtriplet, &local, params.maxReceivedBytes); + deltas = getIXFRDeltas(primary, zoneName, dnsRecord, params.xfrTimeout, true, params.tsigtriplet, &local, params.maxReceivedBytes); /* no need to try another primary */ break; @@ -482,7 +498,7 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam } if (deltas.empty()) { - return; + return true; } try { @@ -492,13 +508,13 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam if (luaconfsLocal->generation != configGeneration) { SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; + return false; } oldZone = luaconfsLocal->dfe.getZone(params.zoneIdx); if (!oldZone || oldZone->getDomain() != zoneName) { SLOG(g_log << Logger::Info << "This policy is no more, stopping the existing RPZ update thread for " << zoneName << endl, logger->info(Logr::Info, "This policy is no more, stopping the existing RPZ update thread")); - return; + return false; } /* we need to make a _full copy_ of the zone we are going to work on */ std::shared_ptr newZone = std::make_shared(*oldZone); @@ -578,7 +594,7 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam if (luaconfsLocal->generation != configGeneration) { SLOG(g_log << Logger::Info << "A more recent configuration has been found, stopping the existing RPZ update thread for " << zoneName << endl, logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing RPZ update thread")); - return; + return false; } g_luaconfs.modify([zoneIdx = params.zoneIdx, &newZone](LuaConfigItems& lci) { lci.dfe.setZone(zoneIdx, newZone); @@ -597,6 +613,29 @@ static void RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam SLOG(g_log << Logger::Error << "Error while applying the update received over XFR for " << zoneName << ", skipping the update: " << e.reason << endl, logger->error(Logr::Error, e.reason, "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("PDNSException"))); } + return true; +} + +// As there can be multiple threads doing updates (due to config reloads), we use a multimap. +// The value contains the actual thread id that owns the struct. + +static LockGuarded> condVars; + +// Notify all threads trakcing the RPZ name +bool notifyRPZTracker(const DNSName& name) +{ + auto lock = condVars.lock(); + auto [start, end] = lock->equal_range(name); + if (start == end) { + // Did not find any thread tracking that RPZ name + return false; + } + while (start != end) { + start->second.stop = true; + start->second.condVar.notify_one(); + ++start; + } + return true; } void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration) @@ -604,6 +643,7 @@ void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration) setThreadName("rec/rpzixfr"); bool isPreloaded = params.soaRecordContent != nullptr; auto logger = g_slog->withName("rpz"); + RPZWaiter waiter(std::this_thread::get_id()); /* we can _never_ modify this zone directly, we need to do a full copy then replace the existing zone */ std::shared_ptr oldZone = g_luaconfs.getLocal()->dfe.getZone(params.zoneIdx); @@ -621,11 +661,27 @@ void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration) // Now that we know the name, set it in the logger logger = logger->withValues("zone", Logging::Loggable(zoneName)); - preloadRPZFIle(params, zoneName, oldZone, refresh, polName, logger); + { + auto lock = condVars.lock(); + lock->emplace(zoneName, waiter); + } + preloadRPZFIle(params, zoneName, oldZone, refresh, polName, waiter, logger); bool skipRefreshDelay = isPreloaded; - for (;;) { - RPZTrackerIteration(params, zoneName, oldZone, refresh, polName, skipRefreshDelay, configGeneration, logger); + while (RPZTrackerIteration(params, zoneName, oldZone, refresh, polName, skipRefreshDelay, configGeneration, waiter, logger)) { + // empty + } + + // Zap our (and only our) RPZWaiter struct out of the multimap + auto lock = condVars.lock(); + auto [start, end] = lock->equal_range(zoneName); + while (start != end) { + if (start->second.id == std::this_thread::get_id()) { + start = lock->erase(start); + } + else { + ++start; + } } } diff --git a/pdns/recursordist/rpzloader.hh b/pdns/recursordist/rpzloader.hh index c8811ee0b3d9..99fd749945ed 100644 --- a/pdns/recursordist/rpzloader.hh +++ b/pdns/recursordist/rpzloader.hh @@ -44,6 +44,7 @@ struct RPZTrackerParams { std::shared_ptr loadRPZFromFile(const std::string& fname, std::shared_ptr zone, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL); void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration); +bool notifyRPZTracker(const DNSName& name); struct rpzStats { From 29691be2a61262292bbf006663a231a3c0224c3d Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Fri, 12 Jan 2024 09:07:22 +0100 Subject: [PATCH 03/65] Tidy --- pdns/recursordist/rpzloader.cc | 32 ++++++++++++++++++++++++++++---- pdns/recursordist/rpzloader.hh | 3 ++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index 02be12566288..5f835969c61c 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -1,3 +1,25 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include #include "arguments.hh" #include "dnsparser.hh" #include "dnsrecords.hh" @@ -388,8 +410,10 @@ static bool dumpZoneToDisk(Logr::log_t logger, const DNSName& zoneName, const st // A struct that holds the condition var and related stuff to allow notifies to be sent to the tread owning // the struct. -struct RPZWaiter { - RPZWaiter(std::thread::id arg) : id(arg) {} +struct RPZWaiter +{ + RPZWaiter(std::thread::id arg) : + id(arg) {} std::thread::id id; std::mutex mutex; std::condition_variable condVar; @@ -438,7 +462,7 @@ static void preloadRPZFIle(RPZTrackerParams& params, const DNSName& zoneName, st if (!params.soaRecordContent) { std::unique_lock lock(rpzwaiter.mutex); rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(refresh), - [&stop = rpzwaiter.stop] { return stop.load(); }); + [&stop = rpzwaiter.stop] { return stop.load(); }); } rpzwaiter.stop = false; } @@ -457,7 +481,7 @@ static bool RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam else { std::unique_lock lock(rpzwaiter.mutex); rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(refresh), - [&stop = rpzwaiter.stop] { return stop.load(); }); + [&stop = rpzwaiter.stop] { return stop.load(); }); rpzwaiter.stop = false; } auto luaconfsLocal = g_luaconfs.getLocal(); diff --git a/pdns/recursordist/rpzloader.hh b/pdns/recursordist/rpzloader.hh index 99fd749945ed..2933d9a1d0fa 100644 --- a/pdns/recursordist/rpzloader.hh +++ b/pdns/recursordist/rpzloader.hh @@ -27,7 +27,8 @@ extern bool g_logRPZChanges; // Please make sure that the struct below only contains value types since they are used as parameters in a thread ct -struct RPZTrackerParams { +struct RPZTrackerParams +{ std::vector primaries; boost::optional defpol; bool defpolOverrideLocal; From 046b69a59455054542cdc4f836f4227f14c54f5d Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Fri, 12 Jan 2024 09:50:00 +0100 Subject: [PATCH 04/65] Add test, only clear cache if the notify wasn't for an RPZ --- pdns/recursordist/pdns_recursor.cc | 6 ++-- pdns/recursordist/rpzloader.cc | 3 +- regression-tests.recursor-dnssec/test_RPZ.py | 30 +++++++++++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/pdns/recursordist/pdns_recursor.cc b/pdns/recursordist/pdns_recursor.cc index ad6c83c70cab..e31cd7d31b24 100644 --- a/pdns/recursordist/pdns_recursor.cc +++ b/pdns/recursordist/pdns_recursor.cc @@ -2318,8 +2318,10 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl, g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname))); } - notifyRPZTracker(qname); - requestWipeCaches(qname); + if (!notifyRPZTracker(qname)) { + // It wasn't an RPZ + requestWipeCaches(qname); + } // the operation will now be treated as a Query, generating // a normal response, as the rest of the code does not diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index 5f835969c61c..c5e1036a9e09 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -564,7 +564,7 @@ static bool RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam if (resourceRecord.d_type == QType::SOA) { auto oldsr = getRR(resourceRecord); if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) { - // cout<<"Got good removal of SOA serial "<d_st.serial<(resourceRecord); - // g_log<d_st.serial< Date: Fri, 12 Jan 2024 10:23:41 +0100 Subject: [PATCH 05/65] Docs --- pdns/recursordist/docs/lua-config/rpz.rst | 3 ++- pdns/recursordist/settings/table.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pdns/recursordist/docs/lua-config/rpz.rst b/pdns/recursordist/docs/lua-config/rpz.rst index 34c77e06d08c..f09809892b83 100644 --- a/pdns/recursordist/docs/lua-config/rpz.rst +++ b/pdns/recursordist/docs/lua-config/rpz.rst @@ -214,7 +214,8 @@ Base64 encoded TSIG secret refresh ^^^^^^^ An integer describing the interval between checks for updates. -By default, the RPZ zone's default is used +By default, the RPZ zone's default is used. +If allowed by :ref:`setting-allow-notify-for` and :ref:`setting-allow-notify-from`, a ``notify`` for an RPZ zone will initiate an freshness check. maxReceivedMBytes ^^^^^^^^^^^^^^^^^ diff --git a/pdns/recursordist/settings/table.py b/pdns/recursordist/settings/table.py index 96cd063427d5..a9de993c3976 100644 --- a/pdns/recursordist/settings/table.py +++ b/pdns/recursordist/settings/table.py @@ -171,7 +171,7 @@ netmask of /32 or /128. NOTIFY operations received from a client listed in one of these netmasks -will be accepted and used to wipe any cache entries whose zones match +will be accepted and used to initiate a freshness check for an RPZ zone or wipe any cache entries whose zones match the zone specified in the NOTIFY operation, but only if that zone (or one of its parents) is included in :ref:`setting-allow-notify-for`, :ref:`setting-allow-notify-for-file`, or :ref:`setting-forward-zones-file` with a ``allow_notify`` set to ``true``. From cce0983c9fca654ae785a32184deb15d0f8f3296 Mon Sep 17 00:00:00 2001 From: Charles-Henri Bruyand Date: Thu, 18 Jan 2024 13:50:44 +0100 Subject: [PATCH 06/65] dnsname: move len and offset from int to size_t --- pdns/dnsname.cc | 11 +++++------ pdns/dnsname.hh | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pdns/dnsname.cc b/pdns/dnsname.cc index 25a90078a890..78ac49abe7e0 100644 --- a/pdns/dnsname.cc +++ b/pdns/dnsname.cc @@ -99,8 +99,7 @@ DNSName::DNSName(const std::string_view sw) } } - -DNSName::DNSName(const char* pos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, uint16_t minOffset) +DNSName::DNSName(const char* pos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, uint16_t minOffset) { if (offset >= len) throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")"); @@ -115,7 +114,7 @@ DNSName::DNSName(const char* pos, int len, int offset, bool uncompress, uint16_t } // this should be the __only__ dns name parser in PowerDNS. -void DNSName::packetParser(const char* qpos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset) +void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset) { const unsigned char* pos=(const unsigned char*)qpos; unsigned char labellen; @@ -123,7 +122,7 @@ void DNSName::packetParser(const char* qpos, int len, int offset, bool uncompres if (offset >= len) throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")"); - if (offset < (int) minOffset) + if (offset < static_cast(minOffset)) throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")"); const unsigned char* end = pos + len; @@ -134,10 +133,10 @@ void DNSName::packetParser(const char* qpos, int len, int offset, bool uncompres throw std::range_error("Found compressed label, instructed not to follow"); labellen &= (~0xc0); - int newpos = (labellen << 8) + *(const unsigned char*)pos; + size_t newpos = (labellen << 8) + *(const unsigned char*)pos; if(newpos < offset) { - if(newpos < (int) minOffset) + if(newpos < static_cast(minOffset)) throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")"); if (++depth > 100) throw std::range_error("Abort label decompression after 100 redirects"); diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index 2350d2bd3b8a..b338a532958d 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -100,7 +100,7 @@ public: DNSName(DNSName&& a) = default; explicit DNSName(std::string_view sw); //!< Constructs from a human formatted, escaped presentation - DNSName(const char* p, int len, int offset, bool uncompress, uint16_t* qtype = nullptr, uint16_t* qclass = nullptr, unsigned int* consumed = nullptr, uint16_t minOffset = 0); //!< Construct from a DNS Packet, taking the first question if offset=12. If supplied, consumed is set to the number of bytes consumed from the packet, which will not be equal to the wire length of the resulting name in case of compression. + DNSName(const char* p, size_t len, size_t offset, bool uncompress, uint16_t* qtype = nullptr, uint16_t* qclass = nullptr, unsigned int* consumed = nullptr, uint16_t minOffset = 0); //!< Construct from a DNS Packet, taking the first question if offset=12. If supplied, consumed is set to the number of bytes consumed from the packet, which will not be equal to the wire length of the resulting name in case of compression. bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name? Note that name.isPartOf(name). inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty @@ -216,7 +216,7 @@ public: private: string_t d_storage; - void packetParser(const char* p, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset); + void packetParser(const char* p, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset); static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len); static std::string unescapeLabel(const std::string& orig); static void throwSafeRangeError(const std::string& msg, const char* buf, size_t length); From 05fe7452e75e4600c37eafdac09a65ef9e0c9e86 Mon Sep 17 00:00:00 2001 From: Charles-Henri Bruyand Date: Fri, 19 Jan 2024 10:24:01 +0100 Subject: [PATCH 07/65] dnsname: clang-tidy fix a few missing braces --- pdns/dnsname.cc | 89 ++++++++++++++++++++++++++++++++----------------- pdns/dnsname.hh | 2 +- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/pdns/dnsname.cc b/pdns/dnsname.cc index 78ac49abe7e0..e02f6d4015e6 100644 --- a/pdns/dnsname.cc +++ b/pdns/dnsname.cc @@ -120,10 +120,12 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc unsigned char labellen; const unsigned char *opos = pos; - if (offset >= len) + if (offset >= len) { throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")"); - if (offset < static_cast(minOffset)) + } + if (offset < static_cast(minOffset)) { throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")"); + } const unsigned char* end = pos + len; pos += offset; @@ -136,13 +138,16 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc size_t newpos = (labellen << 8) + *(const unsigned char*)pos; if(newpos < offset) { - if(newpos < static_cast(minOffset)) + if(newpos < static_cast(minOffset)) { throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")"); - if (++depth > 100) + } + if (++depth > 100) { throw std::range_error("Abort label decompression after 100 redirects"); + } packetParser((const char*)opos, len, newpos, true, nullptr, nullptr, nullptr, depth, minOffset); - } else + } else { throw std::range_error("Found a forward reference during label decompression"); + } pos++; break; } else if(labellen & 0xc0) { @@ -151,14 +156,17 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc if (pos + labellen < end) { appendRawLabel((const char*)pos, labellen); } - else + else { throw std::range_error("Found an invalid label length in qname"); + } pos+=labellen; } - if(d_storage.empty()) + if(d_storage.empty()) { d_storage.append(1, (char)0); // we just parsed the root - if(consumed) + } + if(consumed) { *consumed = pos - opos - offset; + } if(qtype) { if (pos + 2 > end) { throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string((pos - opos) + 2)+ " > "+std::to_string(len)+")"); @@ -224,8 +232,9 @@ std::string DNSName::toLogString() const std::string DNSName::toDNSString() const { - if (empty()) + if (empty()) { throw std::out_of_range("Attempt to DNSString an unset dnsname"); + } return std::string(d_storage.c_str(), d_storage.length()); } @@ -249,11 +258,13 @@ size_t DNSName::wirelength() const { // Are WE part of parent bool DNSName::isPartOf(const DNSName& parent) const { - if(parent.empty() || empty()) + if(parent.empty() || empty()) { throw std::out_of_range("empty dnsnames aren't part of anything"); + } - if(parent.d_storage.size() > d_storage.size()) + if(parent.d_storage.size() > d_storage.size()) { return false; + } // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then for(auto us=d_storage.cbegin(); us l=getRawLabels(); @@ -342,14 +355,17 @@ void DNSName::appendRawLabel(const std::string& label) void DNSName::appendRawLabel(const char* start, unsigned int length) { - if(length==0) + if (length==0) { throw std::range_error("no such thing as an empty label to append"); - if(length > 63) + } + if (length > 63) { throw std::range_error("label too long to append"); - if(d_storage.size() + length > s_maxDNSNameLength - 1) // reserve one byte for the label length + } + if (d_storage.size() + length > s_maxDNSNameLength - 1) { // reserve one byte for the label length throw std::range_error("name too long to append"); + } - if(d_storage.empty()) { + if (d_storage.empty()) { d_storage.append(1, (char)length); } else { @@ -361,15 +377,19 @@ void DNSName::appendRawLabel(const char* start, unsigned int length) void DNSName::prependRawLabel(const std::string& label) { - if(label.empty()) + if (label.empty()) { throw std::range_error("no such thing as an empty label to prepend"); - if(label.size() > 63) + } + if (label.size() > 63) { throw std::range_error("label too long to prepend"); - if(d_storage.size() + label.size() > s_maxDNSNameLength - 1) // reserve one byte for the label length + } + if (d_storage.size() + label.size() > s_maxDNSNameLength - 1) { // reserve one byte for the label length throw std::range_error("name too long to prepend"); + } - if(d_storage.empty()) + if (d_storage.empty()) { d_storage.append(1, (char)0); + } string_t prep(1, (char)label.size()); prep.append(label.c_str(), label.size()); @@ -414,16 +434,18 @@ DNSName DNSName::getLastLabel() const bool DNSName::chopOff() { - if(d_storage.empty() || d_storage[0]==0) + if (d_storage.empty() || d_storage[0]==0) { return false; + } d_storage.erase(0, (unsigned int)d_storage[0]+1); return true; } bool DNSName::isWildcard() const { - if(d_storage.size() < 2) + if (d_storage.size() < 2) { return false; + } auto p = d_storage.begin(); return (*p == 0x01 && *++p == '*'); } @@ -453,8 +475,9 @@ unsigned int DNSName::countLabels() const void DNSName::trimToLabels(unsigned int to) { - while(countLabels() > to && chopOff()) + while(countLabels() > to && chopOff()) { ; + } } @@ -469,12 +492,15 @@ void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t while (pos < len) { auto p = static_cast(orig[pos]); - if(p=='.') + if (p=='.') { appendTo+="\\."; - else if(p=='\\') + } + else if (p=='\\') { appendTo+="\\\\"; - else if(p > 0x20 && p < 0x7f) + } + else if (p > 0x20 && p < 0x7f) { appendTo.append(1, (char)p); + } else { char buf[] = "000"; auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p); @@ -497,11 +523,12 @@ bool DNSName::has8bitBytes() const for (size_t idx = 0; idx < length; idx++) { ++pos; char c = s.at(pos); - if(!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) { return true; + } } ++pos; length = s.at(pos); diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index b338a532958d..64c14732fe7c 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -216,7 +216,7 @@ public: private: string_t d_storage; - void packetParser(const char* p, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset); + void packetParser(const char* qpos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset); static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len); static std::string unescapeLabel(const std::string& orig); static void throwSafeRangeError(const std::string& msg, const char* buf, size_t length); From 7cd99b086bc1ed191d7feef63d1e51b96a9664ec Mon Sep 17 00:00:00 2001 From: Charles-Henri Bruyand Date: Fri, 19 Jan 2024 11:47:16 +0100 Subject: [PATCH 08/65] dnsname: tidy some implicit conversion --- pdns/dnsname.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pdns/dnsname.cc b/pdns/dnsname.cc index e02f6d4015e6..0c45c96c51fb 100644 --- a/pdns/dnsname.cc +++ b/pdns/dnsname.cc @@ -161,13 +161,13 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc } pos+=labellen; } - if(d_storage.empty()) { + if (d_storage.empty()) { d_storage.append(1, (char)0); // we just parsed the root } - if(consumed) { + if (consumed != nullptr) { *consumed = pos - opos - offset; } - if(qtype) { + if (qtype != nullptr) { if (pos + 2 > end) { throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string((pos - opos) + 2)+ " > "+std::to_string(len)+")"); } From 1440623f6c5984f85389f9a9ab679e7f707adb35 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Mon, 22 Jan 2024 13:33:32 +0100 Subject: [PATCH 09/65] Process comments from review by @rgacogne, thanks! --- pdns/recursordist/docs/lua-config/rpz.rst | 2 +- pdns/recursordist/rpzloader.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pdns/recursordist/docs/lua-config/rpz.rst b/pdns/recursordist/docs/lua-config/rpz.rst index f09809892b83..dfae45125614 100644 --- a/pdns/recursordist/docs/lua-config/rpz.rst +++ b/pdns/recursordist/docs/lua-config/rpz.rst @@ -215,7 +215,7 @@ refresh ^^^^^^^ An integer describing the interval between checks for updates. By default, the RPZ zone's default is used. -If allowed by :ref:`setting-allow-notify-for` and :ref:`setting-allow-notify-from`, a ``notify`` for an RPZ zone will initiate an freshness check. +If allowed by :ref:`setting-allow-notify-for` and :ref:`setting-allow-notify-from`, a ``notify`` for an RPZ zone will initiate a freshness check. maxReceivedMBytes ^^^^^^^^^^^^^^^^^ diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index c5e1036a9e09..f04f9ce30292 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -644,7 +644,7 @@ static bool RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam static LockGuarded> condVars; -// Notify all threads trakcing the RPZ name +// Notify all threads tracking the RPZ name bool notifyRPZTracker(const DNSName& name) { auto lock = condVars.lock(); From c33b0247c08a370a508f26494507af054ce2bb10 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Mon, 22 Jan 2024 15:13:35 +0100 Subject: [PATCH 10/65] Rate limit the notifies per zone to max 1 per 5 sec (less if refresh is lower) --- pdns/recursordist/rpzloader.cc | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/pdns/recursordist/rpzloader.cc b/pdns/recursordist/rpzloader.cc index f04f9ce30292..5eb5f315d7c7 100644 --- a/pdns/recursordist/rpzloader.cc +++ b/pdns/recursordist/rpzloader.cc @@ -479,10 +479,20 @@ static bool RPZTrackerIteration(RPZTrackerParams& params, const DNSName& zoneNam skipRefreshDelay = false; } else { - std::unique_lock lock(rpzwaiter.mutex); - rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(refresh), - [&stop = rpzwaiter.stop] { return stop.load(); }); - rpzwaiter.stop = false; + const time_t minimumTimeBetweenRefreshes = std::min(refresh, 5U); + const time_t startTime = time(nullptr); + time_t wakeTime = startTime; + while (wakeTime - startTime < minimumTimeBetweenRefreshes) { + std::unique_lock lock(rpzwaiter.mutex); + time_t remaining = refresh - (wakeTime - startTime); + if (remaining <= 0) { + break; + } + rpzwaiter.condVar.wait_for(lock, std::chrono::seconds(remaining), + [&stop = rpzwaiter.stop] { return stop.load(); }); + rpzwaiter.stop = false; + wakeTime = time(nullptr); + } } auto luaconfsLocal = g_luaconfs.getLocal(); @@ -701,10 +711,9 @@ void RPZIXFRTracker(RPZTrackerParams params, uint64_t configGeneration) auto [start, end] = lock->equal_range(zoneName); while (start != end) { if (start->second.id == std::this_thread::get_id()) { - start = lock->erase(start); - } - else { - ++start; + lock->erase(start); + break; } + ++start; } } From 8e9a1388b99efecdef9a10479f52a5fcae193f3f Mon Sep 17 00:00:00 2001 From: Charles-Henri Bruyand Date: Tue, 23 Jan 2024 09:42:01 +0100 Subject: [PATCH 11/65] dnsname: remove unnecessary cast as suggested by Otto --- pdns/dnsname.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdns/dnsname.cc b/pdns/dnsname.cc index 0c45c96c51fb..4534788b0da3 100644 --- a/pdns/dnsname.cc +++ b/pdns/dnsname.cc @@ -137,8 +137,8 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc labellen &= (~0xc0); size_t newpos = (labellen << 8) + *(const unsigned char*)pos; - if(newpos < offset) { - if(newpos < static_cast(minOffset)) { + if (newpos < offset) { + if (newpos < minOffset) { throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")"); } if (++depth > 100) { From 71ada8e045af192eeaf41e85353d78fcf46fa17f Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Tue, 23 Jan 2024 10:22:11 +0100 Subject: [PATCH 12/65] Make the refresh 1 again, to work around the new rate limiting --- regression-tests.recursor-dnssec/test_RPZ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/regression-tests.recursor-dnssec/test_RPZ.py b/regression-tests.recursor-dnssec/test_RPZ.py index d71953268d83..ca7292d39c75 100644 --- a/regression-tests.recursor-dnssec/test_RPZ.py +++ b/regression-tests.recursor-dnssec/test_RPZ.py @@ -385,7 +385,7 @@ class RPZXFRRecursorTest(RPZRecursorTest): global rpzServerPort _lua_config_file = """ -- The first server is a bogus one, to test that we correctly fail over to the second one - rpzMaster({'127.0.0.1:9999', '127.0.0.1:%d'}, 'zone.rpz.', { refresh=3600, includeSOA=true}) + rpzMaster({'127.0.0.1:9999', '127.0.0.1:%d'}, 'zone.rpz.', { refresh=1, includeSOA=true}) """ % (rpzServerPort) _confdir = 'RPZXFR' _wsPort = 8042 From a61dd3f3b6fed8cc1233b101cdb538593f850aeb Mon Sep 17 00:00:00 2001 From: Y7n05h Date: Wed, 17 Aug 2022 22:18:11 +0800 Subject: [PATCH 13/65] dnsdist: add AF_XDP support for udp Signed-off-by: Y7n05h --- contrib/xdp-filter.ebpf.src | 352 +++++----- contrib/xdp.py | 10 +- ext/libbpf/libbpf.h | 13 - pdns/bpf-filter.cc | 14 + pdns/dnsdist-idstate.hh | 1 + pdns/dnsdist-lua-bindings.cc | 49 +- pdns/dnsdist-lua.cc | 57 +- pdns/dnsdist-tcp.cc | 3 +- pdns/dnsdist.cc | 491 ++++++++++++-- pdns/dnsdist.hh | 31 +- pdns/dnsdistdist/Makefile.am | 13 +- pdns/dnsdistdist/configure.ac | 1 + pdns/dnsdistdist/dnsdist-backend.cc | 8 +- pdns/dnsdistdist/dnsdist-healthchecks.cc | 116 ++-- pdns/dnsdistdist/dnsdist-healthchecks.hh | 39 ++ pdns/dnsdistdist/m4/pdns_with_xsk.m4 | 22 + pdns/dnsdistdist/test-dnsdisttcp_cc.cc | 2 +- pdns/dnsdistdist/xsk.cc | 1 + pdns/dnsdistdist/xsk.hh | 1 + pdns/iputils.hh | 28 +- pdns/test-dnsdist_cc.cc | 4 +- pdns/xsk.cc | 828 +++++++++++++++++++++++ pdns/xsk.hh | 243 +++++++ 23 files changed, 2031 insertions(+), 296 deletions(-) create mode 100644 pdns/dnsdistdist/m4/pdns_with_xsk.m4 create mode 120000 pdns/dnsdistdist/xsk.cc create mode 120000 pdns/dnsdistdist/xsk.hh create mode 100644 pdns/xsk.cc create mode 100644 pdns/xsk.hh diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index 3ead6e151b96..8577b08c08e1 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -17,6 +17,24 @@ BPF_TABLE_PINNED("prog", int, int, progsarray, 2, "/sys/fs/bpf/dnsdist/progs"); BPF_TABLE_PINNED7("lpm_trie", struct CIDR4, struct map_value, cidr4filter, 1024, "/sys/fs/bpf/dnsdist/cidr4", BPF_F_NO_PREALLOC); BPF_TABLE_PINNED7("lpm_trie", struct CIDR6, struct map_value, cidr6filter, 1024, "/sys/fs/bpf/dnsdist/cidr6", BPF_F_NO_PREALLOC); +#ifdef UseXsk +#define BPF_XSKMAP_PIN(_name, _max_entries, _pinned) \ + struct _name##_table_t \ + { \ + u32 key; \ + int leaf; \ + int* (*lookup)(int*); \ + /* xdp_act = map.redirect_map(index, flag) */ \ + u64 (*redirect_map)(int, int); \ + u32 max_entries; \ + }; \ + __attribute__((section("maps/xskmap:" _pinned))) struct _name##_table_t _name = {.max_entries = (_max_entries)} + +BPF_XSKMAP_PIN(xsk_map, 16, "/sys/fs/bpf/dnsdist/xskmap"); +#endif /* UseXsk */ + +#define COMPARE_PORT(x, p) ((x) == bpf_htons(p)) + /* * Recalculate the checksum * Copyright 2020, NLnet Labs, All rights reserved. @@ -39,7 +57,7 @@ static inline void update_checksum(uint16_t *csum, uint16_t old_val, uint16_t ne * Set the TC bit and swap UDP ports * Copyright 2020, NLnet Labs, All rights reserved. */ -static inline enum dns_action set_tc_bit(struct udphdr *udp, struct dnshdr *dns) +static inline void set_tc_bit(struct udphdr* udp, struct dnshdr* dns) { uint16_t old_val = dns->flags.as_value; @@ -49,14 +67,12 @@ static inline enum dns_action set_tc_bit(struct udphdr *udp, struct dnshdr *dns) dns->flags.as_bits_and_pieces.tc = 1; // change the UDP destination to the source - udp->dest = udp->source; - udp->source = bpf_htons(DNS_PORT); + uint16_t tmp = udp->dest; + udp->dest = udp->source; + udp->source = tmp; // calculate and write the new checksum update_checksum(&udp->check, old_val, dns->flags.as_value); - - // bounce - return TC; } /* @@ -65,41 +81,43 @@ static inline enum dns_action set_tc_bit(struct udphdr *udp, struct dnshdr *dns) * TC if (modified) message needs to be replied * DROP if message needs to be blocke */ -static inline enum dns_action check_qname(struct cursor *c) +static inline struct map_value* check_qname(struct cursor* c) { struct dns_qname qkey = {0}; uint8_t qname_byte; uint16_t qtype; int length = 0; - for(int i = 0; i<255; i++) { - if (bpf_probe_read_kernel(&qname_byte, sizeof(qname_byte), c->pos)) { - return PASS; - } - c->pos += 1; - if (length == 0) { - if (qname_byte == 0 || qname_byte > 63 ) { - break; + for (int i = 0; i < 255; i++) { + if (bpf_probe_read_kernel(&qname_byte, sizeof(qname_byte), c->pos)) { + return NULL; + } + c->pos += 1; + if (length == 0) { + if (qname_byte == 0 || qname_byte > 63) { + break; } length += qname_byte; - } else { + } + else { length--; } - if (qname_byte >= 'A' && qname_byte <= 'Z') { - qkey.qname[i] = qname_byte + ('a' - 'A'); - } else { - qkey.qname[i] = qname_byte; - } + if (qname_byte >= 'A' && qname_byte <= 'Z') { + qkey.qname[i] = qname_byte + ('a' - 'A'); + } + else { + qkey.qname[i] = qname_byte; + } } // if the last read qbyte is not 0 incorrect QName format), return PASS if (qname_byte != 0) { - return PASS; + return NULL; } // get QType - if(bpf_probe_read_kernel(&qtype, sizeof(qtype), c->pos)) { - return PASS; + if (bpf_probe_read_kernel(&qtype, sizeof(qtype), c->pos)) { + return NULL; } struct map_value* value; @@ -108,125 +126,184 @@ static inline enum dns_action check_qname(struct cursor *c) qkey.qtype = bpf_htons(qtype); value = qnamefilter.lookup(&qkey); if (value) { - __sync_fetch_and_add(&value->counter, 1); - return value->action; + return value; } // check with Qtype 255 (*) qkey.qtype = 255; - value = qnamefilter.lookup(&qkey); - if (value) { - __sync_fetch_and_add(&value->counter, 1); - return value->action; - } - - return PASS; + return qnamefilter.lookup(&qkey); } /* * Parse IPv4 DNS mesage. - * Returns PASS if message needs to go through (i.e. pass) - * TC if (modified) message needs to be replied - * DROP if message needs to be blocked + * Returns XDP_PASS if message needs to go through (i.e. pass) + * XDP_REDIRECT if message needs to be redirected (for AF_XDP, which needs to be translated to the caller into XDP_PASS outside of the AF_XDP) + * XDP_TX if (modified) message needs to be replied + * XDP_DROP if message needs to be blocked */ -static inline enum dns_action udp_dns_reply_v4(struct cursor *c, struct CIDR4 *key) +static inline enum xdp_action parseIPV4(struct xdp_md* ctx, struct cursor* c) { - struct udphdr *udp; - struct dnshdr *dns; + struct iphdr* ipv4; + struct udphdr* udp = NULL; + struct dnshdr* dns = NULL; + if (!(ipv4 = parse_iphdr(c))) { + return XDP_PASS; + } + switch (ipv4->protocol) { + case IPPROTO_UDP: { + if (!(udp = parse_udphdr(c))) { + return XDP_PASS; + } + if (!IN_DNS_PORT_SET(udp->dest)) { + return XDP_PASS; + } + if (!(dns = parse_dnshdr(c))) { + return XDP_DROP; + } + break; + } - if (!(udp = parse_udphdr(c)) || udp->dest != bpf_htons(DNS_PORT)) { - return PASS; +#ifdef UseXsk + case IPPROTO_TCP: { + struct tcphdr* tcp; + if (!(tcp = parse_tcphdr(c))) { + return XDP_PASS; + } + if (!IN_DNS_PORT_SET(tcp->dest)) { + return XDP_PASS; + } + } +#endif /* UseXsk */ + + default: + return XDP_PASS; } - // check that we have a DNS packet - if (!(dns = parse_dnshdr(c))) { - return PASS; - } + struct CIDR4 key; + key.addr = bpf_htonl(ipv4->saddr); // if the address is blocked, perform the corresponding action - struct map_value* value = v4filter.lookup(&key->addr); + struct map_value* value = v4filter.lookup(&key.addr); if (value) { - __sync_fetch_and_add(&value->counter, 1); - if (value->action == TC) { - return set_tc_bit(udp, dns); - } else { - return value->action; - } + goto res; + } + + key.cidr = 32; + key.addr = bpf_htonl(key.addr); + value = cidr4filter.lookup(&key); + if (value) { + goto res; } - key->cidr = 32; - key->addr = bpf_htonl(key->addr); - value = cidr4filter.lookup(key); + if (dns) { + value = check_qname(c); + } if (value) { + res: __sync_fetch_and_add(&value->counter, 1); - if (value->action == TC) { - return set_tc_bit(udp, dns); + if (value->action == TC && udp && dns) { + set_tc_bit(udp, dns); + // swap src/dest IP addresses + uint32_t swap_ipv4 = ipv4->daddr; + ipv4->daddr = ipv4->saddr; + ipv4->saddr = swap_ipv4; + + progsarray.call(ctx, 1); + return XDP_TX; } - else { - return value->action; + + if (value->action == DROP) { + progsarray.call(ctx, 0); + return XDP_DROP; } } - enum dns_action action = check_qname(c); - if (action == TC) { - return set_tc_bit(udp, dns); - } - return action; + return XDP_REDIRECT; } /* * Parse IPv6 DNS mesage. - * Returns PASS if message needs to go through (i.e. pass) - * TC if (modified) message needs to be replied - * DROP if message needs to be blocked + * Returns XDP_PASS if message needs to go through (i.e. pass) + * XDP_REDIRECT if message needs to be redirected (for AF_XDP, which needs to be translated to the caller into XDP_PASS outside of the AF_XDP) + * XDP_TX if (modified) message needs to be replied + * XDP_DROP if message needs to be blocked */ -static inline enum dns_action udp_dns_reply_v6(struct cursor *c, struct CIDR6* key) +static inline enum xdp_action parseIPV6(struct xdp_md* ctx, struct cursor* c) { - struct udphdr *udp; - struct dnshdr *dns; + struct ipv6hdr* ipv6; + struct udphdr* udp = NULL; + struct dnshdr* dns = NULL; + if (!(ipv6 = parse_ipv6hdr(c))) { + return XDP_PASS; + } + switch (ipv6->nexthdr) { + case IPPROTO_UDP: { + if (!(udp = parse_udphdr(c))) { + return XDP_PASS; + } + if (!IN_DNS_PORT_SET(udp->dest)) { + return XDP_PASS; + } + if (!(dns = parse_dnshdr(c))) { + return XDP_DROP; + } + break; + } - - if (!(udp = parse_udphdr(c)) || udp->dest != bpf_htons(DNS_PORT)) { - return PASS; +#ifdef UseXsk + case IPPROTO_TCP: { + struct tcphdr* tcp; + if (!(tcp = parse_tcphdr(c))) { + return XDP_PASS; + } + if (!IN_DNS_PORT_SET(tcp->dest)) { + return XDP_PASS; + } } +#endif /* UseXsk */ - // check that we have a DNS packet - ; - if (!(dns = parse_dnshdr(c))) { - return PASS; + default: + return XDP_PASS; } + struct CIDR6 key; + key.addr = ipv6->saddr; + // if the address is blocked, perform the corresponding action - struct map_value* value = v6filter.lookup(&key->addr); + struct map_value* value = v6filter.lookup(&key.addr); + if (value) { + goto res; + } + key.cidr = 128; + value = cidr6filter.lookup(&key); if (value) { - __sync_fetch_and_add(&value->counter, 1); - if (value->action == TC) { - return set_tc_bit(udp, dns); - } else { - return value->action; - } + goto res; } - key->cidr = 128; - value = cidr6filter.lookup(key); + if (dns) { + value = check_qname(c); + } if (value) { + res: __sync_fetch_and_add(&value->counter, 1); - if (value->action == TC) { - return set_tc_bit(udp, dns); + if (value->action == TC && udp && dns) { + set_tc_bit(udp, dns); + // swap src/dest IP addresses + struct in6_addr swap_ipv6 = ipv6->daddr; + ipv6->daddr = ipv6->saddr; + ipv6->saddr = swap_ipv6; + progsarray.call(ctx, 1); + return XDP_TX; } - else { - return value->action; + if (value->action == DROP) { + progsarray.call(ctx, 0); + return XDP_DROP; } } - - enum dns_action action = check_qname(c); - if (action == TC) { - return set_tc_bit(udp, dns); - } - return action; + return XDP_REDIRECT; } int xdp_dns_filter(struct xdp_md* ctx) @@ -235,9 +312,7 @@ int xdp_dns_filter(struct xdp_md* ctx) struct cursor c; struct ethhdr *eth; uint16_t eth_proto; - struct iphdr *ipv4; - struct ipv6hdr *ipv6; - int r = 0; + enum xdp_action r; // initialise the cursor cursor_init(&c, ctx); @@ -245,67 +320,36 @@ int xdp_dns_filter(struct xdp_md* ctx) // pass the packet if it is not an ethernet one if ((eth = parse_eth(&c, ð_proto))) { // IPv4 packets - if (eth_proto == bpf_htons(ETH_P_IP)) - { - if (!(ipv4 = parse_iphdr(&c)) || bpf_htons(ipv4->protocol != IPPROTO_UDP)) { - return XDP_PASS; - } - - struct CIDR4 key; - key.addr = bpf_htonl(ipv4->saddr); - // if TC bit must not be set, apply the action - if ((r = udp_dns_reply_v4(&c, &key)) != TC) { - if (r == DROP) { - progsarray.call(ctx, 0); - return XDP_DROP; - } - return XDP_PASS; - } - - // swap src/dest IP addresses - uint32_t swap_ipv4 = ipv4->daddr; - ipv4->daddr = ipv4->saddr; - ipv4->saddr = swap_ipv4; + if (eth_proto == bpf_htons(ETH_P_IP)) { + r = parseIPV4(ctx, &c); + goto res; } // IPv6 packets else if (eth_proto == bpf_htons(ETH_P_IPV6)) { - if (!(ipv6 = parse_ipv6hdr(&c)) || bpf_htons(ipv6->nexthdr != IPPROTO_UDP)) { - return XDP_PASS; - } - struct CIDR6 key; - key.addr = ipv6->saddr; - - // if TC bit must not be set, apply the action - if ((r = udp_dns_reply_v6(&c, &key)) != TC) { - if (r == DROP) { - progsarray.call(ctx, 0); - return XDP_DROP; - } - return XDP_PASS; - } - - // swap src/dest IP addresses - struct in6_addr swap_ipv6 = ipv6->daddr; - ipv6->daddr = ipv6->saddr; - ipv6->saddr = swap_ipv6; + r = parseIPV6(ctx, &c); + goto res; } // pass all non-IP packets - else { - return XDP_PASS; - } + return XDP_PASS; } - else { + return XDP_PASS; +res: + switch (r) { + case XDP_REDIRECT: +#ifdef UseXsk + return xsk_map.redirect_map(ctx->rx_queue_index, 0); +#else return XDP_PASS; +#endif /* UseXsk */ + case XDP_TX: { // swap MAC addresses + uint8_t swap_eth[ETH_ALEN]; + memcpy(swap_eth, eth->h_dest, ETH_ALEN); + memcpy(eth->h_dest, eth->h_source, ETH_ALEN); + memcpy(eth->h_source, swap_eth, ETH_ALEN); + // bounce the request + return XDP_TX; + } + default: + return r; } - - // swap MAC addresses - uint8_t swap_eth[ETH_ALEN]; - memcpy(swap_eth, eth->h_dest, ETH_ALEN); - memcpy(eth->h_dest, eth->h_source, ETH_ALEN); - memcpy(eth->h_source, swap_eth, ETH_ALEN); - - progsarray.call(ctx, 1); - - // bounce the request - return XDP_TX; } diff --git a/contrib/xdp.py b/contrib/xdp.py index 6384c3f8b85d..bd96ddb1ce58 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -27,7 +27,15 @@ blocked_qnames = [("localhost", "A", DROP_ACTION), ("test.com", "*", TC_ACTION)] # Main -xdp = BPF(src_file="xdp-filter.ebpf.src") +useXsk = True +Ports = [53] +cflag = [] +if useXsk: + cflag.append("-DUseXsk") +IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x),"+str(i)+")" for i in Ports) +cflag.append(r"-DIN_DNS_PORT_SET(x)=(" + IN_DNS_PORT_SET + r")") + +xdp = BPF(src_file="xdp-filter.ebpf.src", cflags=cflag) fn = xdp.load_func("xdp_dns_filter", BPF.XDP) xdp.attach_xdp(DEV, fn, 0) diff --git a/ext/libbpf/libbpf.h b/ext/libbpf/libbpf.h index 2fc728190966..f429545a0beb 100644 --- a/ext/libbpf/libbpf.h +++ b/ext/libbpf/libbpf.h @@ -8,19 +8,6 @@ extern "C" { struct bpf_insn; -int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, - int max_entries, int map_flags); -int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); -int bpf_lookup_elem(int fd, void *key, void *value); -int bpf_delete_elem(int fd, void *key); -int bpf_get_next_key(int fd, void *key, void *next_key); - -int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int insn_len, - const char *license, int kern_version); - -int bpf_obj_pin(int fd, const char *pathname); -int bpf_obj_get(const char *pathname); #define LOG_BUF_SIZE 65536 extern char bpf_log_buf[LOG_BUF_SIZE]; diff --git a/pdns/bpf-filter.cc b/pdns/bpf-filter.cc index ec6bd05c5528..19343955c81f 100644 --- a/pdns/bpf-filter.cc +++ b/pdns/bpf-filter.cc @@ -33,6 +33,20 @@ #include "misc.hh" +int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, + int max_entries, int map_flags); +int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); +int bpf_lookup_elem(int fd, void *key, void *value); +int bpf_delete_elem(int fd, void *key); +int bpf_get_next_key(int fd, void *key, void *next_key); + +int bpf_prog_load(enum bpf_prog_type prog_type, + const struct bpf_insn *insns, int insn_len, + const char *license, int kern_version); + +int bpf_obj_pin(int fd, const char *pathname); +int bpf_obj_get(const char *pathname); + static __u64 ptr_to_u64(void *ptr) { return (__u64) (unsigned long) ptr; diff --git a/pdns/dnsdist-idstate.hh b/pdns/dnsdist-idstate.hh index 73d5f6e5e32f..49248a08c786 100644 --- a/pdns/dnsdist-idstate.hh +++ b/pdns/dnsdist-idstate.hh @@ -134,6 +134,7 @@ struct InternalQueryState std::unique_ptr d_packet{nullptr}; // Initial packet, so we can restart the query from the response path if needed // 8 std::unique_ptr d_protoBufData{nullptr}; std::unique_ptr d_extendedError{nullptr}; + std::unique_ptr xskPacketHeader; // 8 boost::optional tempFailureTTL{boost::none}; // 8 ClientState* cs{nullptr}; // 8 std::unique_ptr du; // 8 diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 79ba4ec57b33..130a71153ddf 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -28,6 +28,7 @@ #include "dnsdist-svc.hh" #include "dolog.hh" +#include "xsk.hh" // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) @@ -715,7 +716,53 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) } }); #endif /* HAVE_EBPF */ - +#ifdef HAVE_XSK + using xskopt_t = LuaAssociativeTable>; + luaCtx.writeFunction("newXsk", [client](xskopt_t opts) { + if (g_configurationDone) { + throw std::runtime_error("newXsk() only can be used at configuration time!"); + } + if (client) { + return std::shared_ptr(nullptr); + } + uint32_t queue_id; + uint32_t frameNums; + std::string ifName; + std::string path; + std::string poolName; + if (opts.count("NIC_queue_id") == 1) { + queue_id = boost::get(opts.at("NIC_queue_id")); + } + else { + throw std::runtime_error("NIC_queue_id field is required!"); + } + if (opts.count("frameNums") == 1) { + frameNums = boost::get(opts.at("frameNums")); + } + else { + throw std::runtime_error("frameNums field is required!"); + } + if (opts.count("ifName") == 1) { + ifName = boost::get(opts.at("ifName")); + } + else { + throw std::runtime_error("ifName field is required!"); + } + if (opts.count("xskMapPath") == 1) { + path = boost::get(opts.at("xskMapPath")); + } + else { + throw std::runtime_error("xskMapPath field is required!"); + } + if (opts.count("pool") == 1) { + poolName = boost::get(opts.at("pool")); + } + extern std::vector> g_xsk; + auto socket = std::make_shared(frameNums, ifName, queue_id, path, poolName); + g_xsk.push_back(socket); + return socket; + }); +#endif /* HAVE_XSK */ /* EDNSOptionView */ luaCtx.registerFunction("count", [](const EDNSOptionView& option) { return option.values.size(); diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 54b7109b1968..ac6e6e84e8d5 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -45,6 +46,7 @@ #include "dnsdist-ecs.hh" #include "dnsdist-healthchecks.hh" #include "dnsdist-lua.hh" +#include "xsk.hh" #ifdef LUAJIT_VERSION #include "dnsdist-lua-ffi.hh" #endif /* LUAJIT_VERSION */ @@ -110,7 +112,7 @@ void resetLuaSideEffect() g_noLuaSideEffect = boost::logic::indeterminate; } -using localbind_t = LuaAssociativeTable, LuaArray, LuaAssociativeTable>>; +using localbind_t = LuaAssociativeTable, LuaArray, LuaAssociativeTable, std::shared_ptr>>; static void parseLocalBindVars(boost::optional& vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set& cpus, int& tcpListenQueueSize, uint64_t& maxInFlightQueriesPerConnection, uint64_t& tcpMaxConcurrentConnections, bool& enableProxyProtocol) { @@ -131,6 +133,16 @@ static void parseLocalBindVars(boost::optional& vars, bool& reusePo } } } +#ifdef HAVE_XSK +static void parseXskVars(boost::optional& vars, std::shared_ptr& socket) +{ + if (!vars) { + return; + } + + getOptionalValue>(vars, "xskSocket", socket); +} +#endif /* HAVE_XSK */ #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) || defined(HAVE_DNS_OVER_QUIC) static bool loadTLSCertificateAndKeys(const std::string& context, std::vector& pairs, const boost::variant, LuaArray, LuaArray>>& certFiles, const LuaTypeOrArrayOf& keyFiles) @@ -298,7 +310,7 @@ static bool checkConfigurationTime(const std::string& name) // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) { - typedef LuaAssociativeTable, DownstreamState::checkfunc_t>> newserver_t; + typedef LuaAssociativeTable, std::shared_ptr, DownstreamState::checkfunc_t>> newserver_t; luaCtx.writeFunction("inClientStartup", [client]() { return client && !g_configurationDone; }); @@ -621,7 +633,18 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (!(client || configCheck)) { infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); } - +#ifdef HAVE_XSK + std::shared_ptr xskSocket; + if (getOptionalValue>(vars, "xskSocket", xskSocket) > 0) { + ret->registerXsk(xskSocket); + std::string mac; + if (getOptionalValue(vars, "MACAddr", mac) != 1) { + throw runtime_error("field MACAddr is required!"); + } + auto* addr = &ret->d_config.destMACAddr[0]; + sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", addr, addr + 1, addr + 2, addr + 3, addr + 4, addr + 5); + } +#endif /* HAVE_XSK */ if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) { dnsdist::ServiceDiscovery::addUpgradeableServer(ret, upgradeInterval, upgradePool, upgradeDoHKey, keepAfterUpgrade); } @@ -744,7 +767,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } // only works pre-startup, so no sync necessary - g_frontends.push_back(std::make_unique(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol)); + auto udpCS = std::make_unique(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); auto tcpCS = std::make_unique(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; @@ -756,6 +779,18 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } +#ifdef HAVE_XSK + std::shared_ptr socket; + parseXskVars(vars, socket); + if (socket) { + udpCS->xskInfo = XskWorker::create(); + udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; + socket->addWorker(udpCS->xskInfo, loc, false); + // tcpCS->xskInfo=XskWorker::create(); + // TODO: socket->addWorker(tcpCS->xskInfo, loc, true); + } +#endif /* HAVE_XSK */ + g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); } catch (const std::exception& e) { @@ -786,7 +821,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) try { ComboAddress loc(addr, 53); // only works pre-startup, so no sync necessary - g_frontends.push_back(std::make_unique(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol)); + auto udpCS = std::make_unique(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); auto tcpCS = std::make_unique(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; @@ -797,6 +832,18 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (tcpMaxConcurrentConnections > 0) { tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } +#ifdef HAVE_XSK + std::shared_ptr socket; + parseXskVars(vars, socket); + if (socket) { + udpCS->xskInfo = XskWorker::create(); + udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; + socket->addWorker(udpCS->xskInfo, loc, false); + // TODO tcpCS->xskInfo=XskWorker::create(); + // TODO socket->addWorker(tcpCS->xskInfo, loc, true); + } +#endif /* HAVE_XSK */ + g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); } catch (std::exception& e) { diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index c15a14484db5..3baf478ea22b 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -495,9 +495,8 @@ void IncomingTCPConnectionState::handleResponse(const struct timeval& now, TCPRe if (!response.isAsync()) { try { auto& ids = response.d_idstate; - unsigned int qnameWireLength{0}; std::shared_ptr backend = response.d_ds ? response.d_ds : (response.d_connection ? response.d_connection->getDS() : nullptr); - if (backend == nullptr || !responseContentMatches(response.d_buffer, ids.qname, ids.qtype, ids.qclass, backend, qnameWireLength)) { + if (backend == nullptr || !responseContentMatches(response.d_buffer, ids.qname, ids.qtype, ids.qclass, backend)) { state->terminateClientConnection(); return; } diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 3beef62b1234..304cd0f99b69 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -29,9 +29,15 @@ #include #include #include +#include #include #include +#ifdef HAVE_XSK +#include +#include +#endif /* HAVE_XSK */ + #ifdef HAVE_LIBEDIT #if defined (__OpenBSD__) || defined(__NetBSD__) // If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h @@ -111,6 +117,7 @@ std::vector> g_dohlocals; std::vector> g_doqlocals; std::vector> g_doh3locals; std::vector> g_dnsCryptLocals; +std::vector> g_xsk; shared_ptr g_defaultBPFFilter{nullptr}; std::vector > g_dynBPFFilters; @@ -332,7 +339,7 @@ static void doLatencyStats(dnsdist::Protocol protocol, double udiff) } } -bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote, unsigned int& qnameWireLength) +bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote) { if (response.size() < sizeof(dnsheader)) { return false; @@ -363,7 +370,7 @@ bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, uint16_t rqtype, rqclass; DNSName rqname; try { - rqname = DNSName(reinterpret_cast(response.data()), response.size(), sizeof(dnsheader), false, &rqtype, &rqclass, &qnameWireLength); + rqname = DNSName(reinterpret_cast(response.data()), response.size(), sizeof(dnsheader), false, &rqtype, &rqclass); } catch (const std::exception& e) { if (remote && response.size() > 0 && static_cast(response.size()) > sizeof(dnsheader)) { @@ -743,9 +750,7 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } bool muted = true; - if (ids.cs && !ids.cs->muted) { - ComboAddress empty; - empty.sin4.sin_family = 0; + if (ids.cs && !ids.cs->muted && !ids.xskPacketHeader) { sendUDPResponse(ids.cs->udpFD, response, dr.ids.delayMsec, ids.hopLocal, ids.hopRemote); muted = false; } @@ -766,6 +771,61 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } } +#ifdef HAVE_XSK +static void XskHealthCheck(std::shared_ptr& dss, std::unordered_map>& map, bool initial = false) +{ + auto& xskInfo = dss->xskInfo; + std::shared_ptr data; + auto packet = getHealthCheckPacket(dss, nullptr, data); + data->d_initial = initial; + setHealthCheckTime(dss, data); + auto* frame = xskInfo->getEmptyframe(); + auto *xskPacket = new XskPacket(frame, 0, xskInfo->frameSize); + xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); + xskPacket->setPayload(packet); + xskPacket->rewrite(); + xskInfo->sq.push(xskPacket); + const auto queryId = data->d_queryID; + map[queryId] = std::move(data); +} +#endif /* HAVE_XSK */ + +static bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) +{ + + const dnsheader_aligned dh(response.data()); + auto queryId = dh->id; + + if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss)) { + dss->restoreState(queryId, std::move(ids)); + return false; + } + + auto du = std::move(ids.du); + dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [&ids](dnsheader& header) { + header.id = ids.origID; + return true; + }); + ++dss->responses; + + double udiff = ids.queryRealTime.udiff(); + // do that _before_ the processing, otherwise it's not fair to the backend + dss->latencyUsec = (127.0 * dss->latencyUsec / 128.0) + udiff / 128.0; + dss->reportResponse(dh->rcode); + + /* don't call processResponse for DOH */ + if (du) { +#ifdef HAVE_DNS_OVER_HTTPS + // DoH query, we cannot touch du after that + DOHUnitInterface::handleUDPResponse(std::move(du), std::move(response), std::move(ids), dss); +#endif + return false; + } + + handleResponseForUDPClient(ids, response, localRespRuleActions, cacheInsertedRespRuleActions, dss, false, false); + return true; +} + // listens on a dedicated socket, lobs answers from downstream servers to original requestors void responderThread(std::shared_ptr dss) { @@ -773,6 +833,103 @@ void responderThread(std::shared_ptr dss) setThreadName("dnsdist/respond"); auto localRespRuleActions = g_respruleactions.getLocal(); auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); +#ifdef HAVE_XSK + if (dss->xskInfo) { + auto xskInfo = dss->xskInfo; + auto pollfds = getPollFdsForWorker(*xskInfo); + std::unordered_map> healthCheckMap; + XskHealthCheck(dss, healthCheckMap, true); + itimerspec tm; + tm.it_value.tv_sec = dss->d_config.checkTimeout / 1000; + tm.it_value.tv_nsec = (dss->d_config.checkTimeout % 1000) * 1000000; + tm.it_interval = tm.it_value; + auto res = timerfd_settime(pollfds[1].fd, 0, &tm, nullptr); + if (res) { + throw std::runtime_error("timerfd_settime failed:" + stringerror(errno)); + } + const auto xskFd = xskInfo->workerWaker.getHandle(); + while (!dss->isStopped()) { + poll(pollfds.data(), pollfds.size(), -1); + bool needNotify = false; + if (pollfds[0].revents & POLLIN) { + needNotify = true; + xskInfo->cq.consume_all([&](XskPacket* packet) { + if (packet->dataLen() < sizeof(dnsheader)) { + xskInfo->sq.push(packet); + return; + } + const auto* dh = reinterpret_cast(packet->payloadData()); + const auto queryId = dh->id; + auto ids = dss->getState(queryId); + if (ids) { + if (xskFd != ids->backendFD || !ids->xskPacketHeader) { + dss->restoreState(queryId, std::move(*ids)); + ids = std::nullopt; + } + } + if (!ids) { + // this has to go before we can refactor the duplicated response handling code + auto iter = healthCheckMap.find(queryId); + if (iter != healthCheckMap.end()) { + auto data = std::move(iter->second); + healthCheckMap.erase(iter); + packet->cloneIntoPacketBuffer(data->d_buffer); + data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); + } + xskInfo->sq.push(packet); + return; + } + auto response = packet->clonePacketBuffer(); + if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { + xskInfo->sq.push(packet); + return; + } + packet->setHeader(*ids->xskPacketHeader); + packet->setPayload(response); + if (ids->delayMsec > 0) { + packet->addDelay(ids->delayMsec); + } + packet->updatePacket(); + xskInfo->sq.push(packet); + }); + xskInfo->cleanSocketNotification(); + } + if (pollfds[1].revents & POLLIN) { + timeval now; + gettimeofday(&now, nullptr); + for (auto i = healthCheckMap.begin(); i != healthCheckMap.end();) { + auto& ttd = i->second->d_ttd; + if (ttd < now) { + dss->submitHealthCheckResult(i->second->d_initial, false); + i = healthCheckMap.erase(i); + } + else { + ++i; + } + } + needNotify = true; + dss->updateStatisticsInfo(); + dss->handleUDPTimeouts(); + if (dss->d_nextCheck <= 1) { + dss->d_nextCheck = dss->d_config.checkInterval; + if (dss->d_config.availability == DownstreamState::Availability::Auto) { + XskHealthCheck(dss, healthCheckMap); + } + } + else { + --dss->d_nextCheck; + } + + uint64_t tmp; + res = read(pollfds[1].fd, &tmp, sizeof(tmp)); + } + if (needNotify) { + xskInfo->notifyXskSocket(); + } + } + } + else { +#endif /* HAVE_XSK */ const size_t initialBufferSize = getInitialUDPPacketBufferSize(false); /* allocate one more byte so we can detect truncation */ PacketBuffer response(initialBufferSize + 1); @@ -805,7 +962,7 @@ void responderThread(std::shared_ptr dss) for (const auto& fd : sockets) { /* allocate one more byte so we can detect truncation */ - // NOLINTNEXTLINE(bugprone-use-after-move): resizing a vector has no preconditions so it is valid to do so after moving it + // NOLINTNEXTLINE(bugprone-use-after-move): resizing a vector has no preconditions so it is valid to do so after moving it response.resize(initialBufferSize + 1); ssize_t got = recv(fd, response.data(), response.size(), 0); @@ -826,41 +983,32 @@ void responderThread(std::shared_ptr dss) continue; } - unsigned int qnameWireLength = 0; - if (fd != ids->backendFD || !responseContentMatches(response, ids->qname, ids->qtype, ids->qclass, dss, qnameWireLength)) { + if (fd != ids->backendFD) { dss->restoreState(queryId, std::move(*ids)); continue; } - auto du = std::move(ids->du); - - dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [&ids](dnsheader& header) { - header.id = ids->origID; - return true; - }); - ++dss->responses; - - double udiff = ids->queryRealTime.udiff(); - // do that _before_ the processing, otherwise it's not fair to the backend - dss->latencyUsec = (127.0 * dss->latencyUsec / 128.0) + udiff / 128.0; - dss->reportResponse(dh->rcode); - - /* don't call processResponse for DOH */ - if (du) { -#ifdef HAVE_DNS_OVER_HTTPS - // DoH query, we cannot touch du after that - DOHUnitInterface::handleUDPResponse(std::move(du), std::move(response), std::move(*ids), dss); -#endif - continue; + if (processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids)) && ids->xskPacketHeader && ids->cs->xskInfo) { +#ifdef HAVE_XSK + auto& xskInfo = ids->cs->xskInfo; + auto* frame = xskInfo->getEmptyframe(); + auto xskPacket = std::make_unique(frame, 0, xskInfo->frameSize); + xskPacket->setHeader(*ids->xskPacketHeader); + xskPacket->setPayload(response); + xskPacket->updatePacket(); + xskInfo->sq.push(xskPacket.release()); + xskInfo->notifyXskSocket(); +#endif /* HAVE_XSK */ } - - handleResponseForUDPClient(*ids, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, dss, false, false); } } catch (const std::exception& e) { vinfolog("Got an error in UDP responder thread while parsing a response from %s, id %d: %s", dss->d_config.remote.toStringWithPort(), queryId, e.what()); } } +#ifdef HAVE_XSK + } +#endif /* HAVE_XSK */ } catch (const std::exception& e) { errlog("UDP responder thread died because of exception: %s", e.what()); @@ -1280,6 +1428,23 @@ static bool isUDPQueryAcceptable(ClientState& cs, LocalHolders& holders, const s return true; } +#ifdef HAVE_XSK +static bool isXskQueryAcceptable(const XskPacket& packet, ClientState& cs, LocalHolders& holders, bool& expectProxyProtocol) noexcept +{ + const auto& from = packet.getFromAddr(); + expectProxyProtocol = expectProxyProtocolFrom(from); + if (!holders.acl->match(from) && !expectProxyProtocol) { + vinfolog("Query from %s dropped because of ACL", from.toStringWithPort()); + ++dnsdist::metrics::g_stats.aclDrops; + return false; + } + cs.queries++; + ++dnsdist::metrics::g_stats.queries; + + return true; +} +#endif /* HAVE_XSK */ + bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr& dnsCryptQuery, time_t now, bool tcp) { if (cs.dnscryptCtx) { @@ -1408,7 +1573,11 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders ++dq.ids.cs->responses; return ProcessQueryResult::SendAnswer; } - +#ifdef HAVE_XSK + if (dq.ids.cs->xskInfo) { + dq.ids.poolName = dq.ids.cs->xskInfo->poolName; + } +#endif /* HAVE_XSK */ std::shared_ptr serverPool = getPool(*holders.pools, dq.ids.poolName); std::shared_ptr poolPolicy = serverPool->policy; dq.ids.packetCache = serverPool->packetCache; @@ -1649,7 +1818,7 @@ ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::sha return ProcessQueryResult::Drop; } -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query) +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool actuallySend) { bool doh = dq.ids.du != nullptr; @@ -1670,7 +1839,9 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint1 try { int fd = ds->pickSocketForSending(); - dq.ids.backendFD = fd; + if (actuallySend) { + dq.ids.backendFD = fd; + } dq.ids.origID = queryID; dq.ids.forwardedOverUDP = true; @@ -1682,6 +1853,10 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint1 /* set the correct ID */ memcpy(&query.at(proxyProtocolPayloadSize), &idOffset, sizeof(idOffset)); + if (!actuallySend) { + return true; + } + /* you can't touch ids or du after this line, unless the call returned a non-negative value, because it might already have been freed */ ssize_t ret = udpClientSendRequestToBackend(ds, fd, query); @@ -1839,6 +2014,127 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct } } +#ifdef HAVE_XSK +static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) +{ + uint16_t queryId = 0; + const auto& remote = packet.getFromAddr(); + const auto& dest = packet.getToAddr(); + InternalQueryState ids; + ids.cs = &cs; + ids.origRemote = remote; + ids.hopRemote = remote; + ids.origDest = dest; + ids.hopLocal = dest; + ids.protocol = dnsdist::Protocol::DoUDP; + ids.xskPacketHeader = packet.cloneHeadertoPacketBuffer(); + + try { + bool expectProxyProtocol = false; + if (!isXskQueryAcceptable(packet, cs, holders, expectProxyProtocol)) { + return; + } + + auto query = packet.clonePacketBuffer(); + std::vector proxyProtocolValues; + if (expectProxyProtocol && !handleProxyProtocol(remote, false, *holders.acl, query, ids.origRemote, ids.origDest, proxyProtocolValues)) { + return; + } + + ids.queryRealTime.start(); + + auto dnsCryptResponse = checkDNSCryptQuery(cs, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false); + if (dnsCryptResponse) { + packet.setPayload(query); + return; + } + + { + /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */ + dnsheader_aligned dnsHeader(query.data()); + queryId = ntohs(dnsHeader->id); + + if (!checkQueryHeaders(dnsHeader.get(), cs)) { + return; + } + + if (dnsHeader->qdcount == 0) { + dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) { + header.rcode = RCode::NotImp; + header.qr = true; + return true; + }); + packet.setPayload(query); + return; + } + } + + ids.qname = DNSName(reinterpret_cast(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass); + if (ids.origDest.sin4.sin_family == 0) { + ids.origDest = cs.local; + } + if (ids.dnsCryptQuery) { + ids.protocol = dnsdist::Protocol::DNSCryptUDP; + } + DNSQuestion dq(ids, query); + if (!proxyProtocolValues.empty()) { + dq.proxyProtocolValues = make_unique>(std::move(proxyProtocolValues)); + } + std::shared_ptr ss{nullptr}; + auto result = processQuery(dq, holders, ss); + + if (result == ProcessQueryResult::Drop) { + return; + } + + if (result == ProcessQueryResult::SendAnswer) { + packet.setPayload(query); + if (dq.ids.delayMsec > 0) { + packet.addDelay(dq.ids.delayMsec); + } + return; + } + + if (result != ProcessQueryResult::PassToBackend || ss == nullptr) { + return; + } + + // the buffer might have been invalidated by now (resized) + const auto dh = dq.getHeader(); + if (ss->isTCPOnly()) { + std::string proxyProtocolPayload; + /* we need to do this _before_ creating the cross protocol query because + after that the buffer will have been moved */ + if (ss->d_config.useProxyProtocol) { + proxyProtocolPayload = getProxyProtocolPayload(dq); + } + + ids.origID = dh->id; + auto cpq = std::make_unique(std::move(query), std::move(ids), ss); + cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload); + + ss->passCrossProtocolQuery(std::move(cpq)); + return; + } + + if (!ss->xskInfo) { + assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); + } + else { + int fd = ss->xskInfo->workerWaker; + ids.backendFD = fd; + assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, false); + packet.setAddr(ss->d_config.sourceAddr,ss->d_config.sourceMACAddr, ss->d_config.remote,ss->d_config.destMACAddr); + packet.setPayload(query); + packet.rewrite(); + } + } + catch (const std::exception& e) { + vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what()); + } +} +#endif /* HAVE_XSK */ + #ifndef DISABLE_RECVMMSG #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) static void MultipleMessagesUDPClientThread(ClientState* cs, LocalHolders& holders) @@ -1931,6 +2227,27 @@ static void MultipleMessagesUDPClientThread(ClientState* cs, LocalHolders& holde #endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */ #endif /* DISABLE_RECVMMSG */ +#ifdef HAVE_XSK +static void xskClientThread(ClientState* cs) +{ + setThreadName("dnsdist/xskClient"); + auto xskInfo = cs->xskInfo; + LocalHolders holders; + + for (;;) { + while (!xskInfo->cq.read_available()) { + xskInfo->waitForXskSocket(); + } + xskInfo->cq.consume_all([&](XskPacket* packet) { + ProcessXskQuery(*cs, holders, *packet); + packet->updatePacket(); + xskInfo->sq.push(packet); + }); + xskInfo->notifyXskSocket(); + } +} +#endif /* HAVE_XSK */ + // listens to incoming queries, sends out to downstream servers, noting the intended return path static void udpClientThread(std::vector states) { @@ -2177,11 +2494,12 @@ static void healthChecksThread() std::unique_ptr mplexer{nullptr}; for (auto& dss : *states) { - auto delta = dss->sw.udiffAndSet()/1000000.0; - dss->queryLoad.store(1.0*(dss->queries.load() - dss->prev.queries.load())/delta); - dss->dropRate.store(1.0*(dss->reuseds.load() - dss->prev.reuseds.load())/delta); - dss->prev.queries.store(dss->queries.load()); - dss->prev.reuseds.store(dss->reuseds.load()); +#ifdef HAVE_XSK + if (dss->xskInfo) { + continue; + } +#endif /* HAVE_XSK */ + dss->updateStatisticsInfo(); dss->handleUDPTimeouts(); @@ -2909,13 +3227,35 @@ static void initFrontends() } } +#ifdef HAVE_XSK +void XskRouter(std::shared_ptr xsk); +#endif /* HAVE_XSK */ + namespace dnsdist { static void startFrontends() { +#ifdef HAVE_XSK + for (auto& xskContext : g_xsk) { + std::thread xskThread(XskRouter, std::move(xskContext)); + xskThread.detach(); + } +#endif /* HAVE_XSK */ + std::vector tcpStates; std::vector udpStates; for (auto& clientState : g_frontends) { +#ifdef HAVE_XSK + if (clientState->xskInfo) { + std::thread xskCT(xskClientThread, clientState.get()); + if (!clientState->cpus.empty()) { + mapThreadToCPUList(xskCT.native_handle(), clientState->cpus); + } + xskCT.detach(); + continue; + } +#endif /* HAVE_XSK */ + if (clientState->dohFrontend != nullptr && clientState->dohFrontend->d_library == "h2o") { #ifdef HAVE_DNS_OVER_HTTPS #ifdef HAVE_LIBH2OEVLOOP @@ -3175,6 +3515,12 @@ int main(int argc, char** argv) auto states = g_dstates.getCopy(); // it is a copy, but the internal shared_ptrs are the real deal auto mplexer = std::unique_ptr(FDMultiplexer::getMultiplexerSilent(states.size())); for (auto& dss : states) { +#ifdef HAVE_XSK + if (dss->xskInfo) { + continue; + } +#endif /* HAVE_XSK */ + if (dss->d_config.availability == DownstreamState::Availability::Auto || dss->d_config.availability == DownstreamState::Availability::Lazy) { if (dss->d_config.availability == DownstreamState::Availability::Auto) { dss->d_nextCheck = dss->d_config.checkInterval; @@ -3270,3 +3616,72 @@ int main(int argc, char** argv) #endif } } + +#ifdef HAVE_XSK +void XskRouter(std::shared_ptr xsk) +{ + setThreadName("dnsdist/XskRouter"); + uint32_t failed; + // packets to be submitted for sending + vector fillInTx; + const auto size = xsk->fds.size(); + // list of workers that need to be notified + std::set needNotify; + const auto& xskWakerIdx = xsk->workers.get<0>(); + const auto& destIdx = xsk->workers.get<1>(); + while (true) { + auto ready = xsk->wait(-1); + // descriptor 0 gets incoming AF_XDP packets + if (xsk->fds[0].revents & POLLIN) { + auto packets = xsk->recv(64, &failed); + dnsdist::metrics::g_stats.nonCompliantQueries += failed; + for (auto &packet : packets) { + const auto dest = packet->getToAddr(); + auto res = destIdx.find(dest); + if (res == destIdx.end()) { + xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*packet)); + continue; + } + res->worker->cq.push(packet.release()); + needNotify.insert(res->workerWaker); + } + for (auto i : needNotify) { + uint64_t x = 1; + auto written = write(i, &x, sizeof(x)); + if (written != sizeof(x)) { + // oh, well, the worker is clearly overloaded + // but there is nothing we can do about it, + // and hopefully the queue will be processed eventually + } + } + needNotify.clear(); + ready--; + } + const auto backup = ready; + for (size_t i = 1; i < size && ready > 0; i++) { + if (xsk->fds[i].revents & POLLIN) { + ready--; + auto& info = xskWakerIdx.find(xsk->fds[i].fd)->worker; + info->sq.consume_all([&](XskPacket* x) { + if (!(x->getFlags() & XskPacket::UPDATE)) { + xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*x)); + return; + } + auto ptr = std::unique_ptr(x); + if (x->getFlags() & XskPacket::DELAY) { + xsk->waitForDelay.push(std::move(ptr)); + return; + } + fillInTx.push_back(std::move(ptr)); + }); + info->cleanWorkerNotification(); + } + } + xsk->pickUpReadyPacket(fillInTx); + xsk->recycle(64); + xsk->fillFq(); + xsk->send(fillInTx); + ready = backup; + } +} +#endif /* HAVE_XSK */ diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 9d5d06f59296..34d4600dac1d 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -56,6 +56,7 @@ #include "uuid-utils.hh" #include "proxy-protocol.hh" #include "stat_t.hh" +#include "xsk.hh" uint64_t uptimeOfProcess(const std::string& str); @@ -511,6 +512,7 @@ struct ClientState std::shared_ptr doqFrontend{nullptr}; std::shared_ptr doh3Frontend{nullptr}; std::shared_ptr d_filter{nullptr}; + std::shared_ptr xskInfo{nullptr}; size_t d_maxInFlightQueriesPerConn{1}; size_t d_tcpConcurrentConnectionsLimit{0}; int udpFD{-1}; @@ -702,6 +704,8 @@ struct DownstreamState: public std::enable_shared_from_this std::string d_dohPath; std::string name; std::string nameWithAddr; + MACAddr sourceMACAddr; + MACAddr destMACAddr; size_t d_numberOfSockets{1}; size_t d_maxInFlightQueriesPerConn{1}; size_t d_tcpConcurrentConnectionsLimit{0}; @@ -815,6 +819,7 @@ public: std::vector sockets; StopWatch sw; QPSLimiter qps; + std::shared_ptr xskInfo{nullptr}; std::atomic idOffset{0}; size_t socketsOffset{0}; double latencyUsec{0.0}; @@ -837,7 +842,14 @@ private: uint8_t consecutiveSuccessfulChecks{0}; bool d_stopped{false}; public: - + void updateStatisticsInfo() + { + auto delta = sw.udiffAndSet() / 1000000.0; + queryLoad.store(1.0 * (queries.load() - prev.queries.load()) / delta); + dropRate.store(1.0 * (reuseds.load() - prev.reuseds.load()) / delta); + prev.queries.store(queries.load()); + prev.reuseds.store(reuseds.load()); + } void start(); bool isUp() const @@ -966,6 +978,19 @@ public: void restoreState(uint16_t id, InternalQueryState&&); std::optional getState(uint16_t id); +#ifdef HAVE_XSK + void registerXsk(std::shared_ptr& xsk) + { + xskInfo = XskWorker::create(); + if (d_config.sourceAddr.sin4.sin_family == 0) { + throw runtime_error("invalid source addr"); + } + xsk->addWorker(xskInfo, d_config.sourceAddr, getProtocol() != dnsdist::Protocol::DoUDP); + memcpy(d_config.sourceMACAddr, xsk->source, sizeof(MACAddr)); + xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; + } +#endif /* HAVE_XSK */ + dnsdist::Protocol getProtocol() const { if (isDoH()) { @@ -1138,7 +1163,7 @@ void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ s bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect void resetLuaSideEffect(); // reset to indeterminate state -bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote, unsigned int& qnameWireLength); +bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote); bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs); @@ -1163,7 +1188,7 @@ bool processResponse(PacketBuffer& response, const std::vector& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted); -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query); +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool actuallySend = true); ssize_t udpClientSendRequestToBackend(const std::shared_ptr& ss, const int sd, const PacketBuffer& request, bool healthCheck = false); bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index 9ed171375a9e..c95629daac97 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -254,7 +254,8 @@ dnsdist_SOURCES = \ tcpiohandler.cc tcpiohandler.hh \ threadname.hh threadname.cc \ uuid-utils.hh uuid-utils.cc \ - xpf.cc xpf.hh + xpf.cc xpf.hh \ + xsk.cc xsk.hh testrunner_SOURCES = \ base64.hh \ @@ -361,7 +362,8 @@ testrunner_SOURCES = \ testrunner.cc \ threadname.hh threadname.cc \ uuid-utils.hh uuid-utils.cc \ - xpf.cc xpf.hh + xpf.cc xpf.hh \ + xsk.cc xsk.hh dnsdist_LDFLAGS = \ $(AM_LDFLAGS) \ @@ -411,6 +413,13 @@ if HAVE_LIBSSL dnsdist_LDADD += $(LIBSSL_LIBS) endif +if HAVE_XSK +dnsdist_LDADD += -lbpf +dnsdist_LDADD += -lxdp +testrunner_LDADD += -lbpf +testrunner_LDADD += -lxdp +endif + if HAVE_LIBCRYPTO dnsdist_LDADD += $(LIBCRYPTO_LDFLAGS) $(LIBCRYPTO_LIBS) testrunner_LDADD += $(LIBCRYPTO_LDFLAGS) $(LIBCRYPTO_LIBS) diff --git a/pdns/dnsdistdist/configure.ac b/pdns/dnsdistdist/configure.ac index d9429c93f61f..d9f6c719ddc3 100644 --- a/pdns/dnsdistdist/configure.ac +++ b/pdns/dnsdistdist/configure.ac @@ -40,6 +40,7 @@ PDNS_ENABLE_FUZZ_TARGETS PDNS_WITH_RE2 DNSDIST_ENABLE_DNSCRYPT PDNS_WITH_EBPF +PDNS_WITH_XSK PDNS_WITH_NET_SNMP PDNS_WITH_LIBCAP diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 8c3eefc2399e..bd7592545a49 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -714,9 +714,11 @@ void DownstreamState::submitHealthCheckResult(bool initial, bool newResult) setUpStatus(newResult); if (newResult == false) { currentCheckFailures++; - auto stats = d_lazyHealthCheckStats.lock(); - stats->d_status = LazyHealthCheckStats::LazyStatus::Failed; - updateNextLazyHealthCheck(*stats, false); + if (d_config.availability == DownstreamState::Availability::Lazy) { + auto stats = d_lazyHealthCheckStats.lock(); + stats->d_status = LazyHealthCheckStats::LazyStatus::Failed; + updateNextLazyHealthCheck(*stats, false); + } } return; } diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.cc b/pdns/dnsdistdist/dnsdist-healthchecks.cc index 36805573e784..d60c9dc58410 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.cc +++ b/pdns/dnsdistdist/dnsdist-healthchecks.cc @@ -31,40 +31,7 @@ bool g_verboseHealthChecks{false}; -struct HealthCheckData -{ - enum class TCPState : uint8_t - { - WritingQuery, - ReadingResponseSize, - ReadingResponse - }; - - HealthCheckData(FDMultiplexer& mplexer, std::shared_ptr downstream, DNSName&& checkName, uint16_t checkType, uint16_t checkClass, uint16_t queryID) : - d_ds(std::move(downstream)), d_mplexer(mplexer), d_udpSocket(-1), d_checkName(std::move(checkName)), d_checkType(checkType), d_checkClass(checkClass), d_queryID(queryID) - { - } - - const std::shared_ptr d_ds; - FDMultiplexer& d_mplexer; - std::unique_ptr d_tcpHandler{nullptr}; - std::unique_ptr d_ioState{nullptr}; - PacketBuffer d_buffer; - Socket d_udpSocket; - DNSName d_checkName; - struct timeval d_ttd - { - 0, 0 - }; - size_t d_bufferPos{0}; - uint16_t d_checkType; - uint16_t d_checkClass; - uint16_t d_queryID; - TCPState d_tcpState{TCPState::WritingQuery}; - bool d_initial{false}; -}; - -static bool handleResponse(std::shared_ptr& data) +bool handleResponse(std::shared_ptr& data) { const auto& downstream = data->d_ds; try { @@ -207,7 +174,7 @@ static void healthCheckUDPCallback(int descriptor, FDMultiplexer::funcparam_t& p } ++data->d_ds->d_healthCheckMetrics.d_networkErrors; data->d_ds->submitHealthCheckResult(data->d_initial, false); - data->d_mplexer.removeReadFD(descriptor); + data->d_mplexer->removeReadFD(descriptor); return; } } while (got < 0); @@ -224,7 +191,7 @@ static void healthCheckUDPCallback(int descriptor, FDMultiplexer::funcparam_t& p return; } - data->d_mplexer.removeReadFD(descriptor); + data->d_mplexer->removeReadFD(descriptor); data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } @@ -300,36 +267,51 @@ static void healthCheckTCPCallback(int descriptor, FDMultiplexer::funcparam_t& p } } -bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared_ptr& downstream, bool initialCheck) +PacketBuffer getHealthCheckPacket(const std::shared_ptr& downstream, FDMultiplexer* mplexer, std::shared_ptr& data) { - try { - uint16_t queryID = dnsdist::getRandomDNSID(); - DNSName checkName = downstream->d_config.checkName; - uint16_t checkType = downstream->d_config.checkType.getCode(); - uint16_t checkClass = downstream->d_config.checkClass; - dnsheader checkHeader{}; - memset(&checkHeader, 0, sizeof(checkHeader)); - - checkHeader.qdcount = htons(1); - checkHeader.id = queryID; - - checkHeader.rd = true; - if (downstream->d_config.setCD) { - checkHeader.cd = true; - } + uint16_t queryID = dnsdist::getRandomDNSID(); + DNSName checkName = downstream->d_config.checkName; + uint16_t checkType = downstream->d_config.checkType.getCode(); + uint16_t checkClass = downstream->d_config.checkClass; + dnsheader checkHeader{}; + memset(&checkHeader, 0, sizeof(checkHeader)); + + checkHeader.qdcount = htons(1); + checkHeader.id = queryID; + + checkHeader.rd = true; + if (downstream->d_config.setCD) { + checkHeader.cd = true; + } - if (downstream->d_config.checkFunction) { - auto lock = g_lua.lock(); - auto ret = downstream->d_config.checkFunction(checkName, checkType, checkClass, &checkHeader); - checkName = std::get<0>(ret); - checkType = std::get<1>(ret); - checkClass = std::get<2>(ret); - } + if (downstream->d_config.checkFunction) { + auto lock = g_lua.lock(); + auto ret = downstream->d_config.checkFunction(checkName, checkType, checkClass, &checkHeader); + checkName = std::get<0>(ret); + checkType = std::get<1>(ret); + checkClass = std::get<2>(ret); + } + PacketBuffer packet; + GenericDNSPacketWriter dpw(packet, checkName, checkType, checkClass); + dnsheader* requestHeader = dpw.getHeader(); + *requestHeader = checkHeader; + data = std::make_shared(mplexer, downstream, std::move(checkName), checkType, checkClass, queryID); + return packet; +} - PacketBuffer packet; - GenericDNSPacketWriter dpw(packet, checkName, checkType, checkClass); - dnsheader* requestHeader = dpw.getHeader(); - *requestHeader = checkHeader; +void setHealthCheckTime(const std::shared_ptr& downstream, const std::shared_ptr& data) +{ + gettimeofday(&data->d_ttd, nullptr); + data->d_ttd.tv_sec += static_castd_ttd.tv_sec)>(downstream->d_config.checkTimeout / 1000); /* ms to seconds */ + data->d_ttd.tv_usec += static_castd_ttd.tv_usec)>((downstream->d_config.checkTimeout % 1000) * 1000); /* remaining ms to us */ + normalizeTV(data->d_ttd); +} + +bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared_ptr& downstream, bool initialCheck) +{ + try { + std::shared_ptr data; + PacketBuffer packet = getHealthCheckPacket(downstream, mplexer.get(), data); /* we need to compute that _before_ adding the proxy protocol payload */ uint16_t packetSize = packet.size(); @@ -368,13 +350,9 @@ bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared sock.bind(downstream->d_config.sourceAddr, false); } - auto data = std::make_shared(*mplexer, downstream, std::move(checkName), checkType, checkClass, queryID); data->d_initial = initialCheck; - gettimeofday(&data->d_ttd, nullptr); - data->d_ttd.tv_sec += static_castd_ttd.tv_sec)>(downstream->d_config.checkTimeout / 1000); /* ms to seconds */ - data->d_ttd.tv_usec += static_castd_ttd.tv_usec)>((downstream->d_config.checkTimeout % 1000) * 1000); /* remaining ms to us */ - normalizeTV(data->d_ttd); + setHealthCheckTime(downstream, data); if (!downstream->doHealthcheckOverTCP()) { sock.connect(downstream->d_config.remote); @@ -383,7 +361,7 @@ bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared if (sent < 0) { int ret = errno; if (g_verboseHealthChecks) { - infolog("Error while sending a health check query (ID %d) to backend %s: %d", queryID, downstream->getNameWithAddr(), ret); + infolog("Error while sending a health check query (ID %d) to backend %s: %d", data->d_queryID, downstream->getNameWithAddr(), ret); } return false; } diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.hh b/pdns/dnsdistdist/dnsdist-healthchecks.hh index e9da6c66de8b..4f1940643e33 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.hh +++ b/pdns/dnsdistdist/dnsdist-healthchecks.hh @@ -24,8 +24,47 @@ #include "dnsdist.hh" #include "mplexer.hh" #include "sstuff.hh" +#include "tcpiohandler-mplexer.hh" extern bool g_verboseHealthChecks; bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared_ptr& downstream, bool initial = false); void handleQueuedHealthChecks(FDMultiplexer& mplexer, bool initial = false); + +struct HealthCheckData +{ + enum class TCPState : uint8_t + { + WritingQuery, + ReadingResponseSize, + ReadingResponse + }; + + HealthCheckData(FDMultiplexer* mplexer, std::shared_ptr downstream, DNSName&& checkName, uint16_t checkType, uint16_t checkClass, uint16_t queryID) : + d_ds(std::move(downstream)), d_mplexer(mplexer), d_udpSocket(-1), d_checkName(std::move(checkName)), d_checkType(checkType), d_checkClass(checkClass), d_queryID(queryID) + { + } + + const std::shared_ptr d_ds; + FDMultiplexer* d_mplexer{nullptr}; + std::unique_ptr d_tcpHandler{nullptr}; + std::unique_ptr d_ioState{nullptr}; + PacketBuffer d_buffer; + Socket d_udpSocket; + DNSName d_checkName; + struct timeval d_ttd + { + 0, 0 + }; + size_t d_bufferPos{0}; + uint16_t d_checkType; + uint16_t d_checkClass; + uint16_t d_queryID; + TCPState d_tcpState{TCPState::WritingQuery}; + bool d_initial{false}; +}; + +PacketBuffer getHealthCheckPacket(const std::shared_ptr& ds, FDMultiplexer* mplexer, std::shared_ptr& data); +void setHealthCheckTime(const std::shared_ptr& ds, const std::shared_ptr& data); +bool handleResponse(std::shared_ptr& data); + diff --git a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 new file mode 100644 index 000000000000..b45c9f30f1ce --- /dev/null +++ b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([PDNS_WITH_XSK],[ + AC_MSG_CHECKING([if we have xsk support]) + AC_ARG_WITH([xsk], + AS_HELP_STRING([--with-xsk],[enable xsk support @<:@default=auto@:>@]), + [with_xsk=$withval], + [with_xsk=auto], + ) + AC_MSG_RESULT([$with_xsk]) + + AS_IF([test "x$with_xsk" != "xno"], [ + AS_IF([test "x$with_xsk" = "xyes" -o "x$with_xsk" = "xauto"], [ + AC_CHECK_HEADERS([xdp/xsk.h], xsk_headers=yes, xsk_headers=no) + ]) + ]) + AS_IF([test "x$with_xsk" = "xyes"], [ + AS_IF([test x"$xsk_headers" = "no"], [ + AC_MSG_ERROR([XSK support requested but required libxdp were not found]) + ]) + ]) + AS_IF([test x"$xsk_headers" = "xyes" ], [ AC_DEFINE([HAVE_XSK], [1], [Define if using eBPF.]) ]) + AM_CONDITIONAL([HAVE_XSK], [test x"$xsk_headers" = "xyes" ]) +]) diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index a4c887aeef4d..fd37dda3196d 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -76,7 +76,7 @@ ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::sha return ProcessQueryResult::Drop; } -bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote, unsigned int& qnameWireLength) +bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote) { return true; } diff --git a/pdns/dnsdistdist/xsk.cc b/pdns/dnsdistdist/xsk.cc new file mode 120000 index 000000000000..3258a93cb142 --- /dev/null +++ b/pdns/dnsdistdist/xsk.cc @@ -0,0 +1 @@ +../xsk.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/xsk.hh b/pdns/dnsdistdist/xsk.hh new file mode 120000 index 000000000000..4b1bba7374b3 --- /dev/null +++ b/pdns/dnsdistdist/xsk.hh @@ -0,0 +1 @@ +../xsk.hh \ No newline at end of file diff --git a/pdns/iputils.hh b/pdns/iputils.hh index 4ef0b8f764a3..7d8b2e4c2d6a 100644 --- a/pdns/iputils.hh +++ b/pdns/iputils.hh @@ -83,6 +83,7 @@ #undef IP_PKTINFO #endif +using MACAddr = uint8_t[6]; union ComboAddress { struct sockaddr_in sin4; struct sockaddr_in6 sin6; @@ -123,6 +124,24 @@ union ComboAddress { return rhs.operator<(*this); } + struct addressPortOnlyHash + { + uint32_t operator()(const ComboAddress& ca) const + { + const unsigned char* start = nullptr; + if (ca.sin4.sin_family == AF_INET) { + start = reinterpret_cast(&ca.sin4.sin_addr.s_addr); + auto tmp = burtle(start, 4, 0); + return burtle(reinterpret_cast(&ca.sin4.sin_port), 2, tmp); + } + { + start = reinterpret_cast(&ca.sin6.sin6_addr.s6_addr); + auto tmp = burtle(start, 16, 0); + return burtle(reinterpret_cast(&ca.sin6.sin6_port), 2, tmp); + } + } + }; + struct addressOnlyHash { uint32_t operator()(const ComboAddress& ca) const @@ -347,11 +366,14 @@ union ComboAddress { void truncate(unsigned int bits) noexcept; - uint16_t getPort() const + uint16_t getNetworkOrderPort() const noexcept { - return ntohs(sin4.sin_port); + return sin4.sin_port; + } + uint16_t getPort() const noexcept + { + return ntohs(getNetworkOrderPort()); } - void setPort(uint16_t port) { sin4.sin_port = htons(port); diff --git a/pdns/test-dnsdist_cc.cc b/pdns/test-dnsdist_cc.cc index 56f31d2348b1..c29fdabba5e9 100644 --- a/pdns/test-dnsdist_cc.cc +++ b/pdns/test-dnsdist_cc.cc @@ -57,7 +57,7 @@ bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMs return false; } -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query) +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool) { return true; } @@ -74,6 +74,8 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(DownstreamState const&) return false; } +std::vector> g_xsk; + BOOST_AUTO_TEST_SUITE(test_dnsdist_cc) static const uint16_t ECSSourcePrefixV4 = 24; diff --git a/pdns/xsk.cc b/pdns/xsk.cc new file mode 100644 index 000000000000..b7659317e5c1 --- /dev/null +++ b/pdns/xsk.cc @@ -0,0 +1,828 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "gettime.hh" +#include "xsk.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_XSK +#include +#include +extern "C" +{ +#include +} + +constexpr bool XskSocket::isPowOfTwo(uint32_t value) noexcept +{ + return value != 0 && (value & (value - 1)) == 0; +} +int XskSocket::firstTimeout() +{ + if (waitForDelay.empty()) { + return -1; + } + timespec now; + gettime(&now); + const auto& firstTime = waitForDelay.top()->sendTime; + const auto res = timeDifference(now, firstTime); + if (res <= 0) { + return 0; + } + return res; +} +XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_) : + frameNum(frameNum_), queueId(queue_id), ifName(ifName_), poolName(poolName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) +{ + if (!isPowOfTwo(frameNum_) || !isPowOfTwo(frameSize) + || !isPowOfTwo(fqCapacity) || !isPowOfTwo(cqCapacity) || !isPowOfTwo(rxCapacity) || !isPowOfTwo(txCapacity)) { + throw std::runtime_error("The number of frame , the size of frame and the capacity of rings must is a pow of 2"); + } + getMACFromIfName(); + + memset(&cq, 0, sizeof(cq)); + memset(&fq, 0, sizeof(fq)); + memset(&tx, 0, sizeof(tx)); + memset(&rx, 0, sizeof(rx)); + xsk_umem_config umemCfg; + umemCfg.fill_size = fqCapacity; + umemCfg.comp_size = cqCapacity; + umemCfg.frame_size = frameSize; + umemCfg.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM; + umemCfg.flags = 0; + umem.umemInit(frameNum_ * frameSize, &cq, &fq, &umemCfg); + { + xsk_socket_config socketCfg; + socketCfg.rx_size = rxCapacity; + socketCfg.tx_size = txCapacity; + socketCfg.bind_flags = XDP_USE_NEED_WAKEUP; + socketCfg.xdp_flags = XDP_FLAGS_SKB_MODE; + socketCfg.libxdp_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD; + xsk_socket* tmp = nullptr; + auto ret = xsk_socket__create(&tmp, ifName.c_str(), queue_id, umem.umem, &rx, &tx, &socketCfg); + if (ret != 0) { + throw std::runtime_error("Error creating a xsk socket of if_name" + ifName + stringerror(ret)); + } + socket = std::unique_ptr(tmp, xsk_socket__delete); + } + for (uint64_t i = 0; i < frameNum; i++) { + uniqueEmptyFrameOffset.push_back(i * frameSize + XDP_PACKET_HEADROOM); + } + fillFq(fqCapacity); + const auto xskfd = xskFd(); + fds.push_back(pollfd{ + .fd = xskfd, + .events = POLLIN, + .revents = 0}); + const auto xskMapFd = FDWrapper(bpf_obj_get(xskMapPath.c_str())); + if (xskMapFd.getHandle() < 0) { + throw std::runtime_error("Error get BPF map from path"); + } + auto ret = bpf_map_update_elem(xskMapFd.getHandle(), &queue_id, &xskfd, 0); + if (ret) { + throw std::runtime_error("Error insert into xsk_map"); + } +} +void XskSocket::fillFq(uint32_t fillSize) noexcept +{ + { + auto frames = sharedEmptyFrameOffset->lock(); + if (frames->size() < holdThreshold) { + const auto moveSize = std::min(holdThreshold - frames->size(), uniqueEmptyFrameOffset.size()); + if (moveSize > 0) { + frames->insert(frames->end(), std::make_move_iterator(uniqueEmptyFrameOffset.end() - moveSize), std::make_move_iterator(uniqueEmptyFrameOffset.end())); + } + } + } + if (uniqueEmptyFrameOffset.size() < fillSize) { + return; + } + uint32_t idx; + if (xsk_ring_prod__reserve(&fq, fillSize, &idx) != fillSize) { + return; + } + for (uint32_t i = 0; i < fillSize; i++) { + *xsk_ring_prod__fill_addr(&fq, idx++) = uniqueEmptyFrameOffset.back(); + uniqueEmptyFrameOffset.pop_back(); + } + xsk_ring_prod__submit(&fq, idx); +} +int XskSocket::wait(int timeout) +{ + return poll(fds.data(), fds.size(), static_cast(std::min(static_cast(timeout), static_cast(firstTimeout())))); +} +[[nodiscard]] uint64_t XskSocket::frameOffset(const XskPacket& packet) const noexcept +{ + return reinterpret_cast(packet.frame) - reinterpret_cast(umem.bufBase); +} + +int XskSocket::xskFd() const noexcept { return xsk_socket__fd(socket.get()); } + +void XskSocket::send(std::vector& packets) +{ + const auto packetSize = packets.size(); + if (packetSize == 0) { + return; + } + uint32_t idx; + if (xsk_ring_prod__reserve(&tx, packetSize, &idx) != packets.size()) { + return; + } + + for (const auto& i : packets) { + *xsk_ring_prod__tx_desc(&tx, idx++) = { + .addr = frameOffset(*i), + .len = i->FrameLen(), + .options = 0}; + } + xsk_ring_prod__submit(&tx, packetSize); + packets.clear(); +} +std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCount) +{ + uint32_t idx; + std::vector res; + const auto recvSize = xsk_ring_cons__peek(&rx, recvSizeMax, &idx); + if (recvSize <= 0) { + return res; + } + const auto baseAddr = reinterpret_cast(umem.bufBase); + uint32_t count = 0; + for (uint32_t i = 0; i < recvSize; i++) { + const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); + auto ptr = std::make_unique(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); + if (!ptr->parse()) { + ++count; + uniqueEmptyFrameOffset.push_back(frameOffset(*ptr)); + } + else { + res.push_back(std::move(ptr)); + } + } + xsk_ring_cons__release(&rx, recvSize); + if (failedCount) { + *failedCount = count; + } + return res; +} +void XskSocket::pickUpReadyPacket(std::vector& packets) +{ + timespec now; + gettime(&now); + while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top()->sendTime) <= 0) { + auto& top = const_cast(waitForDelay.top()); + packets.push_back(std::move(top)); + waitForDelay.pop(); + } +} +void XskSocket::recycle(size_t size) noexcept +{ + uint32_t idx; + const auto completeSize = xsk_ring_cons__peek(&cq, size, &idx); + if (completeSize <= 0) { + return; + } + for (uint32_t i = 0; i < completeSize; ++i) { + uniqueEmptyFrameOffset.push_back(*xsk_ring_cons__comp_addr(&cq, idx++)); + } + xsk_ring_cons__release(&cq, completeSize); +} + +void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_prod* fq, xsk_umem_config* config) +{ + size = memSize; + bufBase = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + if (bufBase == MAP_FAILED) { + throw std::runtime_error("mmap failed"); + } + auto ret = xsk_umem__create(&umem, bufBase, size, fq, cq, config); + if (ret != 0) { + munmap(bufBase, size); + throw std::runtime_error("Error creating a umem of size" + std::to_string(size) + stringerror(ret)); + } +} + +XskSocket::XskUmem::~XskUmem() +{ + if (umem) { + xsk_umem__delete(umem); + } + if (bufBase) { + munmap(bufBase, size); + } +} + +bool XskPacket::parse() +{ + // payloadEnd must bigger than payload + sizeof(ethhdr) + sizoef(iphdr) + sizeof(udphdr) + auto* eth = reinterpret_cast(frame); + uint8_t l4Protocol; + if (eth->h_proto == htons(ETH_P_IP)) { + auto* ip = reinterpret_cast(eth + 1); + if (ip->ihl != static_cast(sizeof(iphdr) >> 2)) { + // ip->ihl*4 != sizeof(iphdr) + // ip options is not supported now! + return false; + } + // check ip.check == ipv4Checksum() is not needed! + // We check it in BPF program + from = makeComboAddressFromRaw(4, reinterpret_cast(&ip->saddr), sizeof(ip->saddr)); + to = makeComboAddressFromRaw(4, reinterpret_cast(&ip->daddr), sizeof(ip->daddr)); + l4Protocol = ip->protocol; + l4Header = reinterpret_cast(ip + 1); + payloadEnd = std::min(reinterpret_cast(ip) + ntohs(ip->tot_len), payloadEnd); + } + else if (eth->h_proto == htons(ETH_P_IPV6)) { + auto* ipv6 = reinterpret_cast(eth + 1); + l4Header = reinterpret_cast(ipv6 + 1); + if (l4Header >= payloadEnd) { + return false; + } + from = makeComboAddressFromRaw(6, reinterpret_cast(&ipv6->saddr), sizeof(ipv6->saddr)); + to = makeComboAddressFromRaw(6, reinterpret_cast(&ipv6->daddr), sizeof(ipv6->daddr)); + l4Protocol = ipv6->nexthdr; + payloadEnd = std::min(l4Header + ntohs(ipv6->payload_len), payloadEnd); + } + else { + return false; + } + if (l4Protocol == IPPROTO_UDP) { + // check udp.check == ipv4Checksum() is not needed! + // We check it in BPF program + auto* udp = reinterpret_cast(l4Header); + payload = l4Header + sizeof(udphdr); + // Because of XskPacket::setHeader + // payload = payloadEnd should be allow + if (payload > payloadEnd) { + return false; + } + payloadEnd = std::min(l4Header + ntohs(udp->len), payloadEnd); + from.setPort(ntohs(udp->source)); + to.setPort(ntohs(udp->dest)); + return true; + } + if (l4Protocol == IPPROTO_TCP) { + // check tcp.check == ipv4Checksum() is not needed! + // We check it in BPF program + auto* tcp = reinterpret_cast(l4Header); + if (tcp->doff != static_cast(sizeof(tcphdr) >> 2)) { + // tcp is not supported now! + return false; + } + payload = l4Header + sizeof(tcphdr); + // + if (payload > payloadEnd) { + return false; + } + from.setPort(ntohs(tcp->source)); + to.setPort(ntohs(tcp->dest)); + flags |= TCP; + return true; + } + // ipv6 extension header is not supported now! + return false; +} + +uint32_t XskPacket::dataLen() const noexcept +{ + return payloadEnd - payload; +} +uint32_t XskPacket::FrameLen() const noexcept +{ + return payloadEnd - frame; +} +size_t XskPacket::capacity() const noexcept +{ + return frameEnd - payloadEnd; +} + +void XskPacket::changeDirectAndUpdateChecksum() noexcept +{ + auto* eth = reinterpret_cast(frame); + { + uint8_t tmp[ETH_ALEN]; + static_assert(sizeof(tmp) == sizeof(eth->h_dest), "Size Error"); + static_assert(sizeof(tmp) == sizeof(eth->h_source), "Size Error"); + memcpy(tmp, eth->h_dest, sizeof(tmp)); + memcpy(eth->h_dest, eth->h_source, sizeof(tmp)); + memcpy(eth->h_source, tmp, sizeof(tmp)); + } + if (eth->h_proto == htons(ETH_P_IPV6)) { + // IPV6 + auto* ipv6 = reinterpret_cast(eth + 1); + std::swap(ipv6->daddr, ipv6->saddr); + if (ipv6->nexthdr == IPPROTO_UDP) { + // UDP + auto* udp = reinterpret_cast(ipv6 + 1); + std::swap(udp->dest, udp->source); + udp->len = htons(payloadEnd - reinterpret_cast(udp)); + udp->check = 0; + udp->check = tcp_udp_v6_checksum(); + } + else { + // TCP + auto* tcp = reinterpret_cast(ipv6 + 1); + std::swap(tcp->dest, tcp->source); + // TODO + } + rewriteIpv6Header(ipv6); + } + else { + // IPV4 + auto* ipv4 = reinterpret_cast(eth + 1); + std::swap(ipv4->daddr, ipv4->saddr); + if (ipv4->protocol == IPPROTO_UDP) { + // UDP + auto* udp = reinterpret_cast(ipv4 + 1); + std::swap(udp->dest, udp->source); + udp->len = htons(payloadEnd - reinterpret_cast(udp)); + udp->check = 0; + udp->check = tcp_udp_v4_checksum(); + } + else { + // TCP + auto* tcp = reinterpret_cast(ipv4 + 1); + std::swap(tcp->dest, tcp->source); + // TODO + } + rewriteIpv4Header(ipv4); + } +} +void XskPacket::rewriteIpv4Header(void* ipv4header) noexcept +{ + auto* ipv4 = static_cast(ipv4header); + ipv4->version = 4; + ipv4->ihl = sizeof(iphdr) / 4; + ipv4->tos = 0; + ipv4->tot_len = htons(payloadEnd - reinterpret_cast(ipv4)); + ipv4->id = 0; + ipv4->frag_off = 0; + ipv4->ttl = DefaultTTL; + ipv4->check = 0; + ipv4->check = ipv4Checksum(); +} +void XskPacket::rewriteIpv6Header(void* ipv6header) noexcept +{ + auto* ipv6 = static_cast(ipv6header); + ipv6->version = 6; + ipv6->priority = 0; + ipv6->payload_len = htons(payloadEnd - reinterpret_cast(ipv6 + 1)); + ipv6->hop_limit = DefaultTTL; + memset(&ipv6->flow_lbl, 0, sizeof(ipv6->flow_lbl)); +} + +bool XskPacket::isIPV6() const noexcept +{ + const auto* eth = reinterpret_cast(frame); + return eth->h_proto == htons(ETH_P_IPV6); +} +XskPacket::XskPacket(void* frame_, size_t dataSize, size_t frameSize) : + frame(static_cast(frame_)), payloadEnd(static_cast(frame) + dataSize), frameEnd(static_cast(frame) + frameSize - XDP_PACKET_HEADROOM) +{ +} +PacketBuffer XskPacket::clonePacketBuffer() const +{ + const auto size = dataLen(); + PacketBuffer tmp(size); + memcpy(tmp.data(), payload, size); + return tmp; +} +void XskPacket::cloneIntoPacketBuffer(PacketBuffer& buffer) const +{ + const auto size = dataLen(); + buffer.resize(size); + memcpy(buffer.data(), payload, size); +} +bool XskPacket::setPayload(const PacketBuffer& buf) +{ + const auto bufSize = buf.size(); + if (bufSize == 0 || bufSize > capacity()) { + return false; + } + flags |= UPDATE; + memcpy(payload, buf.data(), bufSize); + payloadEnd = payload + bufSize; + return true; +} +void XskPacket::addDelay(const int relativeMilliseconds) noexcept +{ + gettime(&sendTime); + sendTime.tv_nsec += static_cast(relativeMilliseconds) * 1000000L; + sendTime.tv_sec += sendTime.tv_nsec / 1000000000L; + sendTime.tv_nsec %= 1000000000L; +} +bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept +{ + return s1->sendTime < s2->sendTime; +} +const ComboAddress& XskPacket::getFromAddr() const noexcept +{ + return from; +} +const ComboAddress& XskPacket::getToAddr() const noexcept +{ + return to; +} +void XskWorker::notify(int fd) +{ + uint64_t value = 1; + ssize_t res = 0; + while ((res = write(fd, &value, sizeof(value))) == EINTR) { + } + if (res != sizeof(value)) { + throw runtime_error("Unable Wake Up XskSocket Failed"); + } +} +XskWorker::XskWorker() : + workerWaker(createEventfd()), xskSocketWaker(createEventfd()) +{ +} +void* XskPacket::payloadData() +{ + return reinterpret_cast(payload); +} +const void* XskPacket::payloadData() const +{ + return reinterpret_cast(payload); +} +void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC, bool tcp) noexcept +{ + auto* eth = reinterpret_cast(frame); + memcpy(eth->h_dest, &toMAC[0], sizeof(MACAddr)); + memcpy(eth->h_source, &fromMAC[0], sizeof(MACAddr)); + to = to_; + from = from_; + l4Header = frame + sizeof(ethhdr) + (to.isIPv4() ? sizeof(iphdr) : sizeof(ipv6hdr)); + if (tcp) { + flags = TCP; + payload = l4Header + sizeof(tcphdr); + } + else { + flags = 0; + payload = l4Header + sizeof(udphdr); + } +} +void XskPacket::rewrite() noexcept +{ + flags |= REWRITE; + auto* eth = reinterpret_cast(frame); + if (to.isIPv4()) { + eth->h_proto = htons(ETH_P_IP); + auto* ipv4 = reinterpret_cast(eth + 1); + + ipv4->daddr = to.sin4.sin_addr.s_addr; + ipv4->saddr = from.sin4.sin_addr.s_addr; + if (flags & XskPacket::TCP) { + auto* tcp = reinterpret_cast(ipv4 + 1); + ipv4->protocol = IPPROTO_TCP; + tcp->source = from.sin4.sin_port; + tcp->dest = to.sin4.sin_port; + // TODO + } + else { + auto* udp = reinterpret_cast(ipv4 + 1); + ipv4->protocol = IPPROTO_UDP; + udp->source = from.sin4.sin_port; + udp->dest = to.sin4.sin_port; + udp->len = htons(payloadEnd - reinterpret_cast(udp)); + udp->check = 0; + udp->check = tcp_udp_v4_checksum(); + } + rewriteIpv4Header(ipv4); + } + else { + auto* ipv6 = reinterpret_cast(eth + 1); + memcpy(&ipv6->daddr, &to.sin6.sin6_addr, sizeof(ipv6->daddr)); + memcpy(&ipv6->saddr, &from.sin6.sin6_addr, sizeof(ipv6->saddr)); + if (flags & XskPacket::TCP) { + auto* tcp = reinterpret_cast(ipv6 + 1); + ipv6->nexthdr = IPPROTO_TCP; + tcp->source = from.sin6.sin6_port; + tcp->dest = to.sin6.sin6_port; + // TODO + } + else { + auto* udp = reinterpret_cast(ipv6 + 1); + ipv6->nexthdr = IPPROTO_UDP; + udp->source = from.sin6.sin6_port; + udp->dest = to.sin6.sin6_port; + udp->len = htons(payloadEnd - reinterpret_cast(udp)); + udp->check = 0; + udp->check = tcp_udp_v6_checksum(); + } + } +} + +[[nodiscard]] __be16 XskPacket::ipv4Checksum() const noexcept +{ + auto* ip = reinterpret_cast(frame + sizeof(ethhdr)); + return ip_checksum_fold(ip_checksum_partial(ip, sizeof(iphdr), 0)); +} +[[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum() const noexcept +{ + const auto* ip = reinterpret_cast(frame + sizeof(ethhdr)); + // ip options is not supported !!! + const auto l4Length = static_cast(payloadEnd - l4Header); + auto sum = tcp_udp_v4_header_checksum_partial(ip->saddr, ip->daddr, ip->protocol, l4Length); + sum = ip_checksum_partial(l4Header, l4Length, sum); + return ip_checksum_fold(sum); +} +[[nodiscard]] __be16 XskPacket::tcp_udp_v6_checksum() const noexcept +{ + const auto* ipv6 = reinterpret_cast(frame + sizeof(ethhdr)); + const auto l4Length = static_cast(payloadEnd - l4Header); + uint64_t sum = tcp_udp_v6_header_checksum_partial(&ipv6->saddr, &ipv6->daddr, ipv6->nexthdr, l4Length); + sum = ip_checksum_partial(l4Header, l4Length, sum); + return ip_checksum_fold(sum); +} + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif +[[nodiscard]] uint64_t XskPacket::ip_checksum_partial(const void* p, size_t len, uint64_t sum) noexcept +{ + /* Main loop: 32 bits at a time. + * We take advantage of intel's ability to do unaligned memory + * accesses with minimal additional cost. Other architectures + * probably want to be more careful here. + */ + const uint32_t* p32 = (const uint32_t*)(p); + for (; len >= sizeof(*p32); len -= sizeof(*p32)) + sum += *p32++; + + /* Handle un-32bit-aligned trailing bytes */ + const uint16_t* p16 = (const uint16_t*)(p32); + if (len >= 2) { + sum += *p16++; + len -= sizeof(*p16); + } + if (len > 0) { + const uint8_t* p8 = (const uint8_t*)(p16); + sum += ntohs(*p8 << 8); /* RFC says pad last byte */ + } + + return sum; +} +[[nodiscard]] __be16 XskPacket::ip_checksum_fold(uint64_t sum) noexcept +{ + while (sum & ~0xffffffffULL) + sum = (sum >> 32) + (sum & 0xffffffffULL); + while (sum & 0xffff0000ULL) + sum = (sum >> 16) + (sum & 0xffffULL); + + return ~sum; +} +[[nodiscard]] uint64_t XskPacket::tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept +{ + struct header + { + __be32 src_ip; + __be32 dst_ip; + __uint8_t mbz; + __uint8_t protocol; + __be16 length; + }; + /* The IPv4 pseudo-header is defined in RFC 793, Section 3.1. */ + struct ipv4_pseudo_header_t + { + /* We use a union here to avoid aliasing issues with gcc -O2 */ + union + { + header __packed fields; + uint32_t words[3]; + }; + }; + struct ipv4_pseudo_header_t pseudo_header; + assert(sizeof(pseudo_header) == 12); + + /* Fill in the pseudo-header. */ + pseudo_header.fields.src_ip = src_ip; + pseudo_header.fields.dst_ip = dst_ip; + pseudo_header.fields.mbz = 0; + pseudo_header.fields.protocol = protocol; + pseudo_header.fields.length = htons(len); + return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); +} +[[nodiscard]] uint64_t XskPacket::tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept +{ + struct header + { + struct in6_addr src_ip; + struct in6_addr dst_ip; + __be32 length; + __uint8_t mbz[3]; + __uint8_t next_header; + }; + /* The IPv6 pseudo-header is defined in RFC 2460, Section 8.1. */ + struct ipv6_pseudo_header_t + { + /* We use a union here to avoid aliasing issues with gcc -O2 */ + union + { + header __packed fields; + uint32_t words[10]; + }; + }; + struct ipv6_pseudo_header_t pseudo_header; + assert(sizeof(pseudo_header) == 40); + + /* Fill in the pseudo-header. */ + pseudo_header.fields.src_ip = *src_ip; + pseudo_header.fields.dst_ip = *dst_ip; + pseudo_header.fields.length = htonl(len); + memset(pseudo_header.fields.mbz, 0, sizeof(pseudo_header.fields.mbz)); + pseudo_header.fields.next_header = protocol; + return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); +} +void XskPacket::setHeader(const PacketBuffer& buf) noexcept +{ + memcpy(frame, buf.data(), buf.size()); + payloadEnd = frame + buf.size(); + flags = 0; + const auto res = parse(); + assert(res); +} +std::unique_ptr XskPacket::cloneHeadertoPacketBuffer() const +{ + const auto size = payload - frame; + auto tmp = std::make_unique(size); + memcpy(tmp->data(), frame, size); + return tmp; +} +int XskWorker::createEventfd() +{ + auto fd = ::eventfd(0, EFD_CLOEXEC); + if (fd < 0) { + throw runtime_error("Unable create eventfd"); + } + return fd; +} +void XskWorker::waitForXskSocket() noexcept +{ + uint64_t x = read(workerWaker, &x, sizeof(x)); +} +void XskWorker::notifyXskSocket() noexcept +{ + notify(xskSocketWaker); +} + +std::shared_ptr XskWorker::create() +{ + return std::make_shared(); +} +void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP) +{ + extern std::atomic g_configurationDone; + if (g_configurationDone) { + throw runtime_error("Adding a server with xsk at runtime is not supported"); + } + s->poolName = poolName; + const auto socketWaker = s->xskSocketWaker.getHandle(); + const auto workerWaker = s->workerWaker.getHandle(); + const auto& socketWakerIdx = workers.get<0>(); + if (socketWakerIdx.contains(socketWaker)) { + throw runtime_error("Server already exist"); + } + s->umemBufBase = umem.bufBase; + workers.insert(XskRouteInfo{ + .worker = std::move(s), + .dest = dest, + .xskSocketWaker = socketWaker, + .workerWaker = workerWaker, + }); + fds.push_back(pollfd{ + .fd = socketWaker, + .events = POLLIN, + .revents = 0}); +}; +uint64_t XskWorker::frameOffset(const XskPacket& s) const noexcept +{ + return s.frame - umemBufBase; +} +void XskWorker::notifyWorker() noexcept +{ + notify(workerWaker); +} +void XskSocket::getMACFromIfName() +{ + ifreq ifr; + auto fd = ::socket(AF_INET, SOCK_DGRAM, 0); + strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + throw runtime_error("Error get MAC addr"); + } + memcpy(source, ifr.ifr_hwaddr.sa_data, sizeof(source)); + close(fd); +} +[[nodiscard]] int XskSocket::timeDifference(const timespec& t1, const timespec& t2) noexcept +{ + const auto res = t1.tv_sec * 1000 + t1.tv_nsec / 1000000L - (t2.tv_sec * 1000 + t2.tv_nsec / 1000000L); + return static_cast(res); +} +void XskWorker::cleanWorkerNotification() noexcept +{ + uint64_t x = read(xskSocketWaker, &x, sizeof(x)); +} +void XskWorker::cleanSocketNotification() noexcept +{ + uint64_t x = read(workerWaker, &x, sizeof(x)); +} +std::vector getPollFdsForWorker(XskWorker& info) +{ + std::vector fds; + int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (timerfd < 0) { + throw std::runtime_error("create_timerfd failed"); + } + fds.push_back(pollfd{ + .fd = info.workerWaker, + .events = POLLIN, + .revents = 0, + }); + fds.push_back(pollfd{ + .fd = timerfd, + .events = POLLIN, + .revents = 0, + }); + return fds; +} +void XskWorker::fillUniqueEmptyOffset() +{ + auto frames = sharedEmptyFrameOffset->lock(); + const auto moveSize = std::min(static_cast(32), frames->size()); + if (moveSize > 0) { + uniqueEmptyFrameOffset.insert(uniqueEmptyFrameOffset.end(), std::make_move_iterator(frames->end() - moveSize), std::make_move_iterator(frames->end())); + } +} +void* XskWorker::getEmptyframe() +{ + if (!uniqueEmptyFrameOffset.empty()) { + auto offset = uniqueEmptyFrameOffset.back(); + uniqueEmptyFrameOffset.pop_back(); + return offset + umemBufBase; + } + fillUniqueEmptyOffset(); + if (!uniqueEmptyFrameOffset.empty()) { + auto offset = uniqueEmptyFrameOffset.back(); + uniqueEmptyFrameOffset.pop_back(); + return offset + umemBufBase; + } + return nullptr; +} +uint32_t XskPacket::getFlags() const noexcept +{ + return flags; +} +void XskPacket::updatePacket() noexcept +{ + if (!(flags & UPDATE)) { + return; + } + if (!(flags & REWRITE)) { + changeDirectAndUpdateChecksum(); + } +} +#endif /* HAVE_XSK */ diff --git a/pdns/xsk.hh b/pdns/xsk.hh new file mode 100644 index 000000000000..83c957dc320f --- /dev/null +++ b/pdns/xsk.hh @@ -0,0 +1,243 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once +#include "iputils.hh" +#include "misc.hh" +#include "noinitvector.hh" +#include "lock.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_XSK +#include +#endif /* HAVE_XSK */ + +class XskPacket; +class XskWorker; +class XskSocket; + +#ifdef HAVE_XSK +using XskPacketPtr = std::unique_ptr; + +// We use an XskSocket to manage an AF_XDP Socket corresponding to a NIC queue. +// The XDP program running in the kernel redirects the data to the XskSocket in userspace. +// XskSocket routes packets to multiple worker threads registered on XskSocket via XskSocket::addWorker based on the destination port number of the packet. +// The kernel and the worker thread holding XskWorker will wake up the XskSocket through XskFd and the Eventfd corresponding to each worker thread, respectively. +class XskSocket +{ + struct XskRouteInfo + { + std::shared_ptr worker; + ComboAddress dest; + int xskSocketWaker; + int workerWaker; + }; + struct XskUmem + { + xsk_umem* umem{nullptr}; + uint8_t* bufBase{nullptr}; + size_t size; + void umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_prod* fq, xsk_umem_config* config); + ~XskUmem(); + XskUmem() = default; + }; + boost::multi_index_container< + XskRouteInfo, + boost::multi_index::indexed_by< + boost::multi_index::hashed_unique>, + boost::multi_index::hashed_unique, ComboAddress::addressPortOnlyHash>>> + workers; + static constexpr size_t holdThreshold = 256; + static constexpr size_t fillThreshold = 128; + static constexpr size_t frameSize = 2048; + const size_t frameNum; + const uint32_t queueId; + std::priority_queue waitForDelay; + const std::string ifName; + const std::string poolName; + vector fds; + vector uniqueEmptyFrameOffset; + xsk_ring_cons cq; + xsk_ring_cons rx; + xsk_ring_prod fq; + xsk_ring_prod tx; + std::unique_ptr socket; + XskUmem umem; + bpf_object* prog; + + static constexpr uint32_t fqCapacity = XSK_RING_PROD__DEFAULT_NUM_DESCS * 4; + static constexpr uint32_t cqCapacity = XSK_RING_CONS__DEFAULT_NUM_DESCS * 4; + static constexpr uint32_t rxCapacity = XSK_RING_CONS__DEFAULT_NUM_DESCS * 2; + static constexpr uint32_t txCapacity = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2; + + constexpr static bool isPowOfTwo(uint32_t value) noexcept; + [[nodiscard]] static int timeDifference(const timespec& t1, const timespec& t2) noexcept; + friend void XskRouter(std::shared_ptr xsk); + + [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; + int firstTimeout(); + void fillFq(uint32_t fillSize = fillThreshold) noexcept; + void recycle(size_t size) noexcept; + void getMACFromIfName(); + void pickUpReadyPacket(std::vector& packets); + +public: + std::shared_ptr>> sharedEmptyFrameOffset; + XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_); + MACAddr source; + [[nodiscard]] int xskFd() const noexcept; + int wait(int timeout); + void send(std::vector& packets); + std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); + void addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP); +}; +class XskPacket +{ +public: + enum Flags : uint32_t + { + TCP = 1 << 0, + UPDATE = 1 << 1, + DELAY = 1 << 3, + REWRITE = 1 << 4 + }; + +private: + ComboAddress from; + ComboAddress to; + timespec sendTime; + uint8_t* frame; + uint8_t* l4Header; + uint8_t* payload; + uint8_t* payloadEnd; + uint8_t* frameEnd; + uint32_t flags{0}; + + friend XskSocket; + friend XskWorker; + friend bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; + + constexpr static uint8_t DefaultTTL = 64; + bool parse(); + void changeDirectAndUpdateChecksum() noexcept; + + // You must set ipHeader.check = 0 before call this method + [[nodiscard]] __be16 ipv4Checksum() const noexcept; + // You must set l4Header.check = 0 before call this method + // ip options is not supported + [[nodiscard]] __be16 tcp_udp_v4_checksum() const noexcept; + // You must set l4Header.check = 0 before call this method + [[nodiscard]] __be16 tcp_udp_v6_checksum() const noexcept; + [[nodiscard]] static uint64_t ip_checksum_partial(const void* p, size_t len, uint64_t sum) noexcept; + [[nodiscard]] static __be16 ip_checksum_fold(uint64_t sum) noexcept; + [[nodiscard]] static uint64_t tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept; + [[nodiscard]] static uint64_t tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept; + void rewriteIpv4Header(void* ipv4header) noexcept; + void rewriteIpv6Header(void* ipv6header) noexcept; + +public: + [[nodiscard]] const ComboAddress& getFromAddr() const noexcept; + [[nodiscard]] const ComboAddress& getToAddr() const noexcept; + [[nodiscard]] const void* payloadData() const; + [[nodiscard]] bool isIPV6() const noexcept; + [[nodiscard]] size_t capacity() const noexcept; + [[nodiscard]] uint32_t dataLen() const noexcept; + [[nodiscard]] uint32_t FrameLen() const noexcept; + [[nodiscard]] PacketBuffer clonePacketBuffer() const; + void cloneIntoPacketBuffer(PacketBuffer& buffer) const; + [[nodiscard]] std::unique_ptr cloneHeadertoPacketBuffer() const; + [[nodiscard]] void* payloadData(); + void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC, bool tcp = false) noexcept; + bool setPayload(const PacketBuffer& buf); + void rewrite() noexcept; + void setHeader(const PacketBuffer& buf) noexcept; + XskPacket() = default; + XskPacket(void* frame, size_t dataSize, size_t frameSize); + void addDelay(int relativeMilliseconds) noexcept; + void updatePacket() noexcept; + [[nodiscard]] uint32_t getFlags() const noexcept; +}; +bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; + +// XskWorker obtains XskPackets of specific ports in the NIC from XskSocket through cq. +// After finishing processing the packet, XskWorker puts the packet into sq so that XskSocket decides whether to send it through the network card according to XskPacket::flags. +// XskWorker wakes up XskSocket via xskSocketWaker after putting the packets in sq. +class XskWorker +{ + using XskPacketRing = boost::lockfree::spsc_queue>; + +public: + uint8_t* umemBufBase; + std::shared_ptr>> sharedEmptyFrameOffset; + vector uniqueEmptyFrameOffset; + XskPacketRing cq; + XskPacketRing sq; + std::string poolName; + size_t frameSize; + FDWrapper workerWaker; + FDWrapper xskSocketWaker; + + XskWorker(); + static int createEventfd(); + static void notify(int fd); + static std::shared_ptr create(); + void notifyWorker() noexcept; + void notifyXskSocket() noexcept; + void waitForXskSocket() noexcept; + void cleanWorkerNotification() noexcept; + void cleanSocketNotification() noexcept; + [[nodiscard]] uint64_t frameOffset(const XskPacket& s) const noexcept; + void fillUniqueEmptyOffset(); + void* getEmptyframe(); +}; +std::vector getPollFdsForWorker(XskWorker& info); +#else +class XskSocket +{ +}; +class XskPacket +{ +}; +class XskWorker +{ +}; + +#endif /* HAVE_XSK */ From bbb193ae199b7ab472311626266a443b8fc53925 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 1 Mar 2023 11:33:41 +0100 Subject: [PATCH 14/65] dnsdist: Better error messages when failing to load the XDP map --- pdns/xsk.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index b7659317e5c1..f43d4a3c8555 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -119,11 +119,11 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu .revents = 0}); const auto xskMapFd = FDWrapper(bpf_obj_get(xskMapPath.c_str())); if (xskMapFd.getHandle() < 0) { - throw std::runtime_error("Error get BPF map from path"); + throw std::runtime_error("Error getting BPF map from path '" + xskMapPath + "'"); } auto ret = bpf_map_update_elem(xskMapFd.getHandle(), &queue_id, &xskfd, 0); if (ret) { - throw std::runtime_error("Error insert into xsk_map"); + throw std::runtime_error("Error inserting into xsk_map '" + xskMapPath + "': " + std::to_string(ret)); } } void XskSocket::fillFq(uint32_t fillSize) noexcept From d7a4f1fd253facecdd529d255fec713da2bd3bd3 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 1 Mar 2023 11:34:02 +0100 Subject: [PATCH 15/65] dnsdist: Prevent a false-positive warning from the compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` In file included from xsk.hh:50, from xsk.cc:23: In function ‘xdp_desc* xsk_ring_prod__tx_desc(xsk_ring_prod*, __u32)’, inlined from ‘void XskSocket::send(std::vector, std::allocator > >&)’ at xsk.cc:176:28: /usr/include/xdp/xsk.h:76:27: warning: ‘idx’ may be used uninitialized [-Wmaybe-uninitialized] 76 | return &descs[idx & tx->mask]; | ~~~~^~~~~~~~~~ ``` --- pdns/xsk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index f43d4a3c8555..778d3cf4ae44 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -167,7 +167,7 @@ void XskSocket::send(std::vector& packets) if (packetSize == 0) { return; } - uint32_t idx; + uint32_t idx{0}; if (xsk_ring_prod__reserve(&tx, packetSize, &idx) != packets.size()) { return; } From f3eb5247672a5ba1ee6f67757d0961ec715a0247 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 1 Mar 2023 14:03:22 +0100 Subject: [PATCH 16/65] dnsdist: Add an option to easily disable XDP logging (default) --- contrib/xdp-filter.ebpf.src | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index 8577b08c08e1..aa435f8c9850 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -1,9 +1,13 @@ #include "xdp.h" +#define DISABLE_LOGGING 1 + BPF_TABLE_PINNED("hash", uint32_t, struct map_value, v4filter, 1024, "/sys/fs/bpf/dnsdist/addr-v4"); BPF_TABLE_PINNED("hash", struct in6_addr, struct map_value, v6filter, 1024, "/sys/fs/bpf/dnsdist/addr-v6"); BPF_TABLE_PINNED("hash", struct dns_qname, struct map_value, qnamefilter, 1024, "/sys/fs/bpf/dnsdist/qnames"); +#ifndef DISABLE_LOGGING BPF_TABLE_PINNED("prog", int, int, progsarray, 2, "/sys/fs/bpf/dnsdist/progs"); +#endif /* DISABLE_LOGGING */ /* * bcc has added BPF_TABLE_PINNED7 to the latest commit of the master branch, but it has not yet been released. @@ -210,12 +214,16 @@ static inline enum xdp_action parseIPV4(struct xdp_md* ctx, struct cursor* c) ipv4->daddr = ipv4->saddr; ipv4->saddr = swap_ipv4; +#ifndef DISABLE_LOGGING progsarray.call(ctx, 1); +#endif /* DISABLE_LOGGING */ return XDP_TX; } if (value->action == DROP) { +#ifndef DISABLE_LOGGING progsarray.call(ctx, 0); +#endif /* DISABLE_LOGGING */ return XDP_DROP; } } @@ -295,11 +303,15 @@ static inline enum xdp_action parseIPV6(struct xdp_md* ctx, struct cursor* c) struct in6_addr swap_ipv6 = ipv6->daddr; ipv6->daddr = ipv6->saddr; ipv6->saddr = swap_ipv6; +#ifndef DISABLE_LOGGING progsarray.call(ctx, 1); +#endif /* DISABLE_LOGGING */ return XDP_TX; } if (value->action == DROP) { +#ifndef DISABLE_LOGGING progsarray.call(ctx, 0); +#endif /* DISABLE_LOGGING */ return XDP_DROP; } } From c3cad015511c9df7b8a748a4302401572a6153c6 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 1 Mar 2023 14:04:08 +0100 Subject: [PATCH 17/65] dnsdist: Pass-through TCP packets from XDP --- contrib/xdp-filter.ebpf.src | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index aa435f8c9850..8de35a0445e0 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -170,13 +170,7 @@ static inline enum xdp_action parseIPV4(struct xdp_md* ctx, struct cursor* c) #ifdef UseXsk case IPPROTO_TCP: { - struct tcphdr* tcp; - if (!(tcp = parse_tcphdr(c))) { - return XDP_PASS; - } - if (!IN_DNS_PORT_SET(tcp->dest)) { - return XDP_PASS; - } + return XDP_PASS; } #endif /* UseXsk */ @@ -262,13 +256,7 @@ static inline enum xdp_action parseIPV6(struct xdp_md* ctx, struct cursor* c) #ifdef UseXsk case IPPROTO_TCP: { - struct tcphdr* tcp; - if (!(tcp = parse_tcphdr(c))) { - return XDP_PASS; - } - if (!IN_DNS_PORT_SET(tcp->dest)) { - return XDP_PASS; - } + return XDP_PASS; } #endif /* UseXsk */ From 7d0d8ed6816816cf1193aee9147a1cf5bb45ddb3 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 2 Mar 2023 15:04:33 +0100 Subject: [PATCH 18/65] dnsdist: Punt fragmented UDP dgrams to the kernel in xdp-filter --- contrib/xdp-filter.ebpf.src | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index 8de35a0445e0..786d9d75a355 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -195,6 +195,14 @@ static inline enum xdp_action parseIPV4(struct xdp_md* ctx, struct cursor* c) goto res; } + // ignore the DF flag + const uint16_t fragMask = htons(~(1 << 14)); + uint16_t frag = ipv4->frag_off & fragMask; + if (frag != 0) { + // MF flag is set, or Fragment Offset is != 0 + return XDP_PASS; + } + if (dns) { value = check_qname(c); } From bb8ab11ec3dc4bbd31aaaf5d8f696a6591c16ed5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 2 Mar 2023 15:04:57 +0100 Subject: [PATCH 19/65] dnsdist: Fix parameter validation with XSK --- pdns/dnsdist-lua.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index ac6e6e84e8d5..3338ceea3120 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -752,8 +752,6 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); - checkAllParametersConsumed("setLocal", vars); - try { ComboAddress loc(addr, 53); for (auto it = g_frontends.begin(); it != g_frontends.end();) { @@ -792,6 +790,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); + + checkAllParametersConsumed("setLocal", vars); } catch (const std::exception& e) { g_outputBuffer = "Error: " + string(e.what()) + "\n"; @@ -816,7 +816,6 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) bool enableProxyProtocol = true; parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); - checkAllParametersConsumed("addLocal", vars); try { ComboAddress loc(addr, 53); @@ -845,6 +844,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); + + checkAllParametersConsumed("addLocal", vars); } catch (std::exception& e) { g_outputBuffer = "Error: " + string(e.what()) + "\n"; From b1dd62ee62d3c0da1815d9e1d5ec87d76354946e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 2 Mar 2023 15:06:59 +0100 Subject: [PATCH 20/65] dnsdist: Add comments to the XSK code --- pdns/xsk.cc | 15 +++++++++++++-- pdns/xsk.hh | 44 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 778d3cf4ae44..722d823ae220 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -19,6 +19,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + +#include "config.h" + +#ifdef HAVE_XSK + #include "gettime.hh" #include "xsk.hh" @@ -48,7 +53,6 @@ #include #include -#ifdef HAVE_XSK #include #include extern "C" @@ -185,10 +189,12 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed { uint32_t idx; std::vector res; + // how many descriptors to packets have been filled const auto recvSize = xsk_ring_cons__peek(&rx, recvSizeMax, &idx); if (recvSize <= 0) { return res; } + const auto baseAddr = reinterpret_cast(umem.bufBase); uint32_t count = 0; for (uint32_t i = 0; i < recvSize; i++) { @@ -202,10 +208,15 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed res.push_back(std::move(ptr)); } } + + // this releases the descriptor, but not the packet (umem entries) + // which will only be made available again when pushed into the fill + // queue xsk_ring_cons__release(&rx, recvSize); if (failedCount) { *failedCount = count; } + return res; } void XskSocket::pickUpReadyPacket(std::vector& packets) @@ -752,7 +763,7 @@ void XskSocket::getMACFromIfName() auto fd = ::socket(AF_INET, SOCK_DGRAM, 0); strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - throw runtime_error("Error get MAC addr"); + throw runtime_error("Error getting MAC addr"); } memcpy(source, ifr.ifr_hwaddr.sa_data, sizeof(source)); close(fd); diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 83c957dc320f..4f9d5cc389c6 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -21,21 +21,14 @@ */ #pragma once -#include "iputils.hh" -#include "misc.hh" -#include "noinitvector.hh" -#include "lock.hh" +#ifdef HAVE_XSK #include #include #include #include -#include #include #include -#include -#include -#include #include #include #include @@ -45,9 +38,14 @@ #include #include #include +#include -#ifdef HAVE_XSK #include + +#include "iputils.hh" +#include "lock.hh" +#include "misc.hh" +#include "noinitvector.hh" #endif /* HAVE_XSK */ class XskPacket; @@ -74,7 +72,7 @@ class XskSocket { xsk_umem* umem{nullptr}; uint8_t* bufBase{nullptr}; - size_t size; + size_t size{0}; void umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_prod* fq, xsk_umem_config* config); ~XskUmem(); XskUmem() = default; @@ -88,16 +86,25 @@ class XskSocket static constexpr size_t holdThreshold = 256; static constexpr size_t fillThreshold = 128; static constexpr size_t frameSize = 2048; + // number of entries (frames) in the umem const size_t frameNum; + // ID of the network queue const uint32_t queueId; + // responses that have been delayed std::priority_queue waitForDelay; const std::string ifName; const std::string poolName; + // AF_XDP socket then worker waker sockets vector fds; + // list of (indexes of) umem entries that can be reused vector uniqueEmptyFrameOffset; + // completion ring: queue where sent packets are stored by the kernel xsk_ring_cons cq; + // rx ring: queue where the incoming packets are stored, read by XskRouter xsk_ring_cons rx; + // fill ring: queue where umem entries available to be filled (put into rx) are stored xsk_ring_prod fq; + // tx ring: queue where outgoing packets are stored xsk_ring_prod tx; std::unique_ptr socket; XskUmem umem; @@ -114,18 +121,27 @@ class XskSocket [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; int firstTimeout(); + // pick ups as many available frames as possible from uniqueEmptyFrameOffset + // and put them into sharedEmptyFrameOffset + // then insert them into fq void fillFq(uint32_t fillSize = fillThreshold) noexcept; + // picks up entries that have been processed (sent) and push them into uniqueEmptyFrameOffset void recycle(size_t size) noexcept; void getMACFromIfName(); + // look at delayed packets, and send the ones that are ready void pickUpReadyPacket(std::vector& packets); public: + // list of free umem entries that can be reused std::shared_ptr>> sharedEmptyFrameOffset; XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_); MACAddr source; [[nodiscard]] int xskFd() const noexcept; + // wait until one event has occurred int wait(int timeout); + // add as many packets as possible to the rx queue for sending */ void send(std::vector& packets); + // look at incoming packets in rx, return them if parsing succeeeded std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); void addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP); }; @@ -156,6 +172,7 @@ private: friend bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; constexpr static uint8_t DefaultTTL = 64; + // parse IP and UDP/TCP payloads bool parse(); void changeDirectAndUpdateChecksum() noexcept; @@ -208,7 +225,9 @@ public: uint8_t* umemBufBase; std::shared_ptr>> sharedEmptyFrameOffset; vector uniqueEmptyFrameOffset; + // queue of packets to be processed by this worker XskPacketRing cq; + // queue of packets processed by this worker (to be sent, or discarded) XskPacketRing sq; std::string poolName; size_t frameSize; @@ -219,13 +238,18 @@ public: static int createEventfd(); static void notify(int fd); static std::shared_ptr create(); + // notify worker that at least one packet is available for processing void notifyWorker() noexcept; + // notify the router that packets are ready to be sent void notifyXskSocket() noexcept; void waitForXskSocket() noexcept; void cleanWorkerNotification() noexcept; void cleanSocketNotification() noexcept; [[nodiscard]] uint64_t frameOffset(const XskPacket& s) const noexcept; + // reap empty umeme entry from sharedEmptyFrameOffset into uniqueEmptyFrameOffset void fillUniqueEmptyOffset(); + // look for an empty umem entry in uniqueEmptyFrameOffset + // then sharedEmptyFrameOffset if needed void* getEmptyframe(); }; std::vector getPollFdsForWorker(XskWorker& info); From 57131ee948ed9bef99470bd0bf598f863f0a95c0 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 3 Mar 2023 14:01:43 +0100 Subject: [PATCH 21/65] dnsdist: Add a `XskSocket::getMetrics()` binding --- pdns/dnsdist-lua-bindings.cc | 7 +++++++ pdns/xsk.cc | 22 ++++++++++++++++++++++ pdns/xsk.hh | 1 + 3 files changed, 30 insertions(+) diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 130a71153ddf..6a3644d672f0 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -762,6 +762,13 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) g_xsk.push_back(socket); return socket; }); + luaCtx.registerFunction::*)()const>("getMetrics", [](const std::shared_ptr& xsk) { + std::string result; + if (!xsk) { + return result; + } + return xsk->getMetrics(); + }); #endif /* HAVE_XSK */ /* EDNSOptionView */ luaCtx.registerFunction("count", [](const EDNSOptionView& option) { diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 722d823ae220..f8c73d8c8815 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -256,6 +256,28 @@ void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_pr } } +std::string XskSocket::getMetrics() const +{ + struct xdp_statistics stats; + socklen_t optlen = sizeof(stats); + int err = getsockopt(xskFd(), SOL_XDP, XDP_STATISTICS, &stats, &optlen); + if (err) { + return ""; + } + if (optlen != sizeof(struct xdp_statistics)) { + return ""; + } + + ostringstream ret; + ret << "RX dropped: " << std::to_string(stats.rx_dropped) << std::endl; + ret << "RX invalid descs: " << std::to_string(stats.rx_invalid_descs) << std::endl; + ret << "TX invalid descs: " << std::to_string(stats.tx_invalid_descs) << std::endl; + ret << "RX ring full: " << std::to_string(stats.rx_ring_full) << std::endl; + ret << "RX fill ring empty descs: " << std::to_string(stats.rx_fill_ring_empty_descs) << std::endl; + ret << "RX ring empty descs: " << std::to_string(stats.tx_ring_empty_descs) << std::endl; + return ret.str(); +} + XskSocket::XskUmem::~XskUmem() { if (umem) { diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 4f9d5cc389c6..026417d6f340 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -144,6 +144,7 @@ public: // look at incoming packets in rx, return them if parsing succeeeded std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); void addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP); + std::string getMetrics() const; }; class XskPacket { From 953dc1ee667d747add03a7eb038c9805b18a9478 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 3 Mar 2023 14:04:16 +0100 Subject: [PATCH 22/65] dnsdist: Fall-back to non-XSK for too large responses --- pdns/dnsdist.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 304cd0f99b69..82ea4fc8494f 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -880,10 +880,20 @@ void responderThread(std::shared_ptr dss) return; } auto response = packet->clonePacketBuffer(); + if (response.size() > packet->capacity()) { + /* fallback to sending the packet via normal socket */ + ids->xskPacketHeader.reset(); + } if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { xskInfo->sq.push(packet); return; } + if (response.size() > packet->capacity()) { + /* fallback to sending the packet via normal socket */ + sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); + xskInfo->sq.push(packet); + return; + } packet->setHeader(*ids->xskPacketHeader); packet->setPayload(response); if (ids->delayMsec > 0) { From ce2e13280ae62de8f93602738bac12968ee313fa Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 18 Sep 2023 14:22:07 +0200 Subject: [PATCH 23/65] dnsdist: Small cleanup of the XSK code --- pdns/dnsdist-lua-bindings.cc | 2 +- pdns/dnsdist.hh | 2 +- pdns/dnsdistdist/dnsdist-healthchecks.hh | 1 - pdns/iputils.hh | 1 - pdns/xsk.cc | 21 +++++++++++++++------ pdns/xsk.hh | 2 ++ 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 6a3644d672f0..5a6d8f4e95d8 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -759,7 +759,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) } extern std::vector> g_xsk; auto socket = std::make_shared(frameNums, ifName, queue_id, path, poolName); - g_xsk.push_back(socket); + g_xsk.push_back(socket); return socket; }); luaCtx.registerFunction::*)()const>("getMetrics", [](const std::shared_ptr& xsk) { diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 34d4600dac1d..9b5d33e38c59 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -986,7 +986,7 @@ public: throw runtime_error("invalid source addr"); } xsk->addWorker(xskInfo, d_config.sourceAddr, getProtocol() != dnsdist::Protocol::DoUDP); - memcpy(d_config.sourceMACAddr, xsk->source, sizeof(MACAddr)); + d_config.sourceMACAddr = xsk->source; xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; } #endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.hh b/pdns/dnsdistdist/dnsdist-healthchecks.hh index 4f1940643e33..2c5fc6015c80 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.hh +++ b/pdns/dnsdistdist/dnsdist-healthchecks.hh @@ -67,4 +67,3 @@ struct HealthCheckData PacketBuffer getHealthCheckPacket(const std::shared_ptr& ds, FDMultiplexer* mplexer, std::shared_ptr& data); void setHealthCheckTime(const std::shared_ptr& ds, const std::shared_ptr& data); bool handleResponse(std::shared_ptr& data); - diff --git a/pdns/iputils.hh b/pdns/iputils.hh index 7d8b2e4c2d6a..6bcac3256bfb 100644 --- a/pdns/iputils.hh +++ b/pdns/iputils.hh @@ -83,7 +83,6 @@ #undef IP_PKTINFO #endif -using MACAddr = uint8_t[6]; union ComboAddress { struct sockaddr_in sin4; struct sockaddr_in6 sin6; diff --git a/pdns/xsk.cc b/pdns/xsk.cc index f8c73d8c8815..c57f97f3679e 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -781,14 +782,22 @@ void XskWorker::notifyWorker() noexcept } void XskSocket::getMACFromIfName() { - ifreq ifr; - auto fd = ::socket(AF_INET, SOCK_DGRAM, 0); + ifreq ifr{}; + auto fd = FDWrapper(::socket(AF_INET, SOCK_DGRAM, 0)); + if (fd < 0) { + throw std::runtime_error("Error creating a socket to get the MAC address of interface " + ifName); + } + + if (ifName.size() >= IFNAMSIZ) { + throw std::runtime_error("Unable to get MAC address for interface " + ifName + ": name too long"); + } + strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - throw runtime_error("Error getting MAC addr"); + if (ioctl(fd.getHandle(), SIOCGIFHWADDR, &ifr) < 0 || ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + throw std::runtime_error("Error getting MAC address for interface " + ifName); } - memcpy(source, ifr.ifr_hwaddr.sa_data, sizeof(source)); - close(fd); + static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= std::tuple_size{}, "The size of an ARPHRD_ETHER MAC address is smaller than expected"); + memcpy(source.data(), ifr.ifr_hwaddr.sa_data, source.size()); } [[nodiscard]] int XskSocket::timeDifference(const timespec& t1, const timespec& t2) noexcept { diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 026417d6f340..b4e10c6eeeff 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -52,6 +52,8 @@ class XskPacket; class XskWorker; class XskSocket; +using MACAddr = std::array; + #ifdef HAVE_XSK using XskPacketPtr = std::unique_ptr; From 961e2a7ef00b4f26a2cd4603f2fb7be75298b868 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 19 Sep 2023 16:45:46 +0200 Subject: [PATCH 24/65] dnsdist: Better detection of libbpf and libxdp --- pdns/dnsdistdist/m4/pdns_with_xsk.m4 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 index b45c9f30f1ce..a5f61dd5249e 100644 --- a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 +++ b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 @@ -1,7 +1,7 @@ AC_DEFUN([PDNS_WITH_XSK],[ - AC_MSG_CHECKING([if we have xsk support]) + AC_MSG_CHECKING([if we have AF_XDP (XSK) support]) AC_ARG_WITH([xsk], - AS_HELP_STRING([--with-xsk],[enable xsk support @<:@default=auto@:>@]), + AS_HELP_STRING([--with-xsk],[enable AF_XDP (XDK) support @<:@default=auto@:>@]), [with_xsk=$withval], [with_xsk=auto], ) @@ -9,14 +9,18 @@ AC_DEFUN([PDNS_WITH_XSK],[ AS_IF([test "x$with_xsk" != "xno"], [ AS_IF([test "x$with_xsk" = "xyes" -o "x$with_xsk" = "xauto"], [ - AC_CHECK_HEADERS([xdp/xsk.h], xsk_headers=yes, xsk_headers=no) + PKG_CHECK_MODULES([XDP], [libxdp], [ + AC_DEFINE([HAVE_XDP], [1], [Define to 1 if you have the XDP library]) + ], [:]) + PKG_CHECK_MODULES([BPF], [libbpf], [ + AC_DEFINE([HAVE_BPF], [1], [Define to 1 if you have the BPF library]) + ], [:]) ]) ]) + AM_CONDITIONAL([HAVE_XSK], [test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"]) AS_IF([test "x$with_xsk" = "xyes"], [ - AS_IF([test x"$xsk_headers" = "no"], [ - AC_MSG_ERROR([XSK support requested but required libxdp were not found]) + AS_IF([test x"$BPF_LIBS" = "x" -o x"$XDP_LIBS" = "x" ], [ + AC_MSG_ERROR([AF_XDP (XSK) support requested but required libbpf and/or libxdp were not found]) ]) ]) - AS_IF([test x"$xsk_headers" = "xyes" ], [ AC_DEFINE([HAVE_XSK], [1], [Define if using eBPF.]) ]) - AM_CONDITIONAL([HAVE_XSK], [test x"$xsk_headers" = "xyes" ]) ]) From cad1ba8ccd06a6acd0ec603a07a716283921d7ab Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 26 Sep 2023 12:35:09 +0200 Subject: [PATCH 25/65] dnsdist: Fix AF_XDP (XSK) detection --- pdns/dnsdistdist/m4/pdns_with_xsk.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 index a5f61dd5249e..655d410f808f 100644 --- a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 +++ b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 @@ -17,6 +17,7 @@ AC_DEFUN([PDNS_WITH_XSK],[ ], [:]) ]) ]) + AC_DEFINE([HAVE_XSK], [test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"], [Define to 1 if you have AF_XDP (XSK) support enabled]) AM_CONDITIONAL([HAVE_XSK], [test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"]) AS_IF([test "x$with_xsk" = "xyes"], [ AS_IF([test x"$BPF_LIBS" = "x" -o x"$XDP_LIBS" = "x" ], [ From 4dd6b4b46314a0b9e7eab9a9b74104456f71fde3 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 26 Sep 2023 12:35:50 +0200 Subject: [PATCH 26/65] dnsdist: Minor clean ups in the XSK code --- pdns/dnsdist.cc | 41 ++++++++++++++++++++++------------------- pdns/xsk.cc | 38 ++++++++++++++++++++++++++++---------- pdns/xsk.hh | 11 +++++++---- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 82ea4fc8494f..ae1d00aa1ff6 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -780,11 +780,11 @@ static void XskHealthCheck(std::shared_ptr& dss, std::unordered data->d_initial = initial; setHealthCheckTime(dss, data); auto* frame = xskInfo->getEmptyframe(); - auto *xskPacket = new XskPacket(frame, 0, xskInfo->frameSize); + auto xskPacket = std::make_unique(frame, 0, xskInfo->frameSize); xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); xskPacket->setPayload(packet); xskPacket->rewrite(); - xskInfo->sq.push(xskPacket); + xskInfo->pushToSendQueue(std::move(xskPacket)); const auto queryId = data->d_queryID; map[queryId] = std::move(data); } @@ -853,9 +853,10 @@ void responderThread(std::shared_ptr dss) bool needNotify = false; if (pollfds[0].revents & POLLIN) { needNotify = true; - xskInfo->cq.consume_all([&](XskPacket* packet) { + xskInfo->cq.consume_all([&](XskPacket* packetRaw) { + auto packet = XskPacketPtr(packetRaw); if (packet->dataLen() < sizeof(dnsheader)) { - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); return; } const auto* dh = reinterpret_cast(packet->payloadData()); @@ -876,7 +877,7 @@ void responderThread(std::shared_ptr dss) packet->cloneIntoPacketBuffer(data->d_buffer); data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); return; } auto response = packet->clonePacketBuffer(); @@ -885,13 +886,13 @@ void responderThread(std::shared_ptr dss) ids->xskPacketHeader.reset(); } if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); return; } if (response.size() > packet->capacity()) { /* fallback to sending the packet via normal socket */ sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); return; } packet->setHeader(*ids->xskPacketHeader); @@ -900,7 +901,7 @@ void responderThread(std::shared_ptr dss) packet->addDelay(ids->delayMsec); } packet->updatePacket(); - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); }); xskInfo->cleanSocketNotification(); } @@ -1006,7 +1007,7 @@ void responderThread(std::shared_ptr dss) xskPacket->setHeader(*ids->xskPacketHeader); xskPacket->setPayload(response); xskPacket->updatePacket(); - xskInfo->sq.push(xskPacket.release()); + xskInfo->pushToSendQueue(std::move(xskPacket)); xskInfo->notifyXskSocket(); #endif /* HAVE_XSK */ } @@ -2248,10 +2249,11 @@ static void xskClientThread(ClientState* cs) while (!xskInfo->cq.read_available()) { xskInfo->waitForXskSocket(); } - xskInfo->cq.consume_all([&](XskPacket* packet) { + xskInfo->cq.consume_all([&](XskPacket* packetRaw) { + auto packet = XskPacketPtr(packetRaw); ProcessXskQuery(*cs, holders, *packet); packet->updatePacket(); - xskInfo->sq.push(packet); + xskInfo->pushToSendQueue(std::move(packet)); }); xskInfo->notifyXskSocket(); } @@ -3652,7 +3654,7 @@ void XskRouter(std::shared_ptr xsk) xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*packet)); continue; } - res->worker->cq.push(packet.release()); + res->worker->pushToProcessingQueue(std::move(packet)); needNotify.insert(res->workerWaker); } for (auto i : needNotify) { @@ -3672,17 +3674,18 @@ void XskRouter(std::shared_ptr xsk) if (xsk->fds[i].revents & POLLIN) { ready--; auto& info = xskWakerIdx.find(xsk->fds[i].fd)->worker; - info->sq.consume_all([&](XskPacket* x) { - if (!(x->getFlags() & XskPacket::UPDATE)) { - xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*x)); + info->sq.consume_all([&](XskPacket* packetRaw) { + auto packet = XskPacketPtr(packetRaw); + if (!(packet->getFlags() & XskPacket::UPDATE)) { + xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*packet)); + packet.release(); return; } - auto ptr = std::unique_ptr(x); - if (x->getFlags() & XskPacket::DELAY) { - xsk->waitForDelay.push(std::move(ptr)); + if (packet->getFlags() & XskPacket::DELAY) { + xsk->waitForDelay.push(std::move(packet)); return; } - fillInTx.push_back(std::move(ptr)); + fillInTx.push_back(std::move(packet)); }); info->cleanWorkerNotification(); } diff --git a/pdns/xsk.cc b/pdns/xsk.cc index c57f97f3679e..4f055d626c4d 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -28,7 +28,6 @@ #include "xsk.hh" #include -#include #include #include #include @@ -65,6 +64,7 @@ constexpr bool XskSocket::isPowOfTwo(uint32_t value) noexcept { return value != 0 && (value & (value - 1)) == 0; } + int XskSocket::firstTimeout() { if (waitForDelay.empty()) { @@ -243,14 +243,14 @@ void XskSocket::recycle(size_t size) noexcept xsk_ring_cons__release(&cq, completeSize); } -void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_prod* fq, xsk_umem_config* config) +void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* completionQueue, xsk_ring_prod* fillQueue, xsk_umem_config* config) { size = memSize; bufBase = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); if (bufBase == MAP_FAILED) { throw std::runtime_error("mmap failed"); } - auto ret = xsk_umem__create(&umem, bufBase, size, fq, cq, config); + auto ret = xsk_umem__create(&umem, bufBase, size, fillQueue, completionQueue, config); if (ret != 0) { munmap(bufBase, size); throw std::runtime_error("Error creating a umem of size" + std::to_string(size) + stringerror(ret)); @@ -326,7 +326,7 @@ bool XskPacket::parse() if (l4Protocol == IPPROTO_UDP) { // check udp.check == ipv4Checksum() is not needed! // We check it in BPF program - auto* udp = reinterpret_cast(l4Header); + const auto* udp = reinterpret_cast(l4Header); payload = l4Header + sizeof(udphdr); // Because of XskPacket::setHeader // payload = payloadEnd should be allow @@ -341,7 +341,7 @@ bool XskPacket::parse() if (l4Protocol == IPPROTO_TCP) { // check tcp.check == ipv4Checksum() is not needed! // We check it in BPF program - auto* tcp = reinterpret_cast(l4Header); + const auto* tcp = reinterpret_cast(l4Header); if (tcp->doff != static_cast(sizeof(tcphdr) >> 2)) { // tcp is not supported now! return false; @@ -514,6 +514,23 @@ XskWorker::XskWorker() : workerWaker(createEventfd()), xskSocketWaker(createEventfd()) { } + +void XskWorker::pushToProcessingQueue(XskPacketPtr&& packet) +{ + auto raw = packet.release(); + if (!cq.push(raw)) { + delete raw; + } +} + +void XskWorker::pushToSendQueue(XskPacketPtr&& packet) +{ + auto raw = packet.release(); + if (!sq.push(raw)) { + delete raw; + } +} + void* XskPacket::payloadData() { return reinterpret_cast(payload); @@ -670,7 +687,7 @@ void XskPacket::rewrite() noexcept }; }; struct ipv4_pseudo_header_t pseudo_header; - assert(sizeof(pseudo_header) == 12); + static_assert(sizeof(pseudo_header) == 12, "IPv4 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ pseudo_header.fields.src_ip = src_ip; @@ -701,7 +718,7 @@ void XskPacket::rewrite() noexcept }; }; struct ipv6_pseudo_header_t pseudo_header; - assert(sizeof(pseudo_header) == 40); + static_assert(sizeof(pseudo_header) == 40, "IPv6 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ pseudo_header.fields.src_ip = *src_ip; @@ -711,13 +728,14 @@ void XskPacket::rewrite() noexcept pseudo_header.fields.next_header = protocol; return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); } -void XskPacket::setHeader(const PacketBuffer& buf) noexcept +void XskPacket::setHeader(const PacketBuffer& buf) { memcpy(frame, buf.data(), buf.size()); payloadEnd = frame + buf.size(); flags = 0; - const auto res = parse(); - assert(res); + if (!parse()) { + throw std::runtime_error("Error setting the XSK frame header"); + } } std::unique_ptr XskPacket::cloneHeadertoPacketBuffer() const { diff --git a/pdns/xsk.hh b/pdns/xsk.hh index b4e10c6eeeff..671557109112 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -208,7 +208,7 @@ public: void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC, bool tcp = false) noexcept; bool setPayload(const PacketBuffer& buf); void rewrite() noexcept; - void setHeader(const PacketBuffer& buf) noexcept; + void setHeader(const PacketBuffer& buf); XskPacket() = default; XskPacket(void* frame, size_t dataSize, size_t frameSize); void addDelay(int relativeMilliseconds) noexcept; @@ -225,13 +225,14 @@ class XskWorker using XskPacketRing = boost::lockfree::spsc_queue>; public: - uint8_t* umemBufBase; - std::shared_ptr>> sharedEmptyFrameOffset; - vector uniqueEmptyFrameOffset; // queue of packets to be processed by this worker XskPacketRing cq; // queue of packets processed by this worker (to be sent, or discarded) XskPacketRing sq; + + uint8_t* umemBufBase; + std::shared_ptr>> sharedEmptyFrameOffset; + vector uniqueEmptyFrameOffset; std::string poolName; size_t frameSize; FDWrapper workerWaker; @@ -241,6 +242,8 @@ public: static int createEventfd(); static void notify(int fd); static std::shared_ptr create(); + void pushToProcessingQueue(XskPacketPtr&& packet); + void pushToSendQueue(XskPacketPtr&& packet); // notify worker that at least one packet is available for processing void notifyWorker() noexcept; // notify the router that packets are ready to be sent From 954a9898c2e1791d8eb62300c7e3c3c0b48612b4 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 28 Dec 2023 11:51:04 +0100 Subject: [PATCH 27/65] dnsdist: Fix a UMEM corruption in XSK The second parameter to `xsk_ring_prod__submit` is the number of processed items, not the final index. --- pdns/xsk.cc | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 4f055d626c4d..f1fa45fbf18f 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -149,11 +149,12 @@ void XskSocket::fillFq(uint32_t fillSize) noexcept if (xsk_ring_prod__reserve(&fq, fillSize, &idx) != fillSize) { return; } - for (uint32_t i = 0; i < fillSize; i++) { + uint32_t processed = 0; + for (; processed < fillSize; processed++) { *xsk_ring_prod__fill_addr(&fq, idx++) = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); } - xsk_ring_prod__submit(&fq, idx); + xsk_ring_prod__submit(&fq, processed); } int XskSocket::wait(int timeout) { @@ -177,10 +178,10 @@ void XskSocket::send(std::vector& packets) return; } - for (const auto& i : packets) { + for (const auto& packet : packets) { *xsk_ring_prod__tx_desc(&tx, idx++) = { - .addr = frameOffset(*i), - .len = i->FrameLen(), + .addr = frameOffset(*packet), + .len = packet->FrameLen(), .options = 0}; } xsk_ring_prod__submit(&tx, packetSize); @@ -197,12 +198,13 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed } const auto baseAddr = reinterpret_cast(umem.bufBase); - uint32_t count = 0; - for (uint32_t i = 0; i < recvSize; i++) { + uint32_t failed = 0; + uint32_t processed = 0; + for (; processed < recvSize; processed++) { const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); auto ptr = std::make_unique(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); if (!ptr->parse()) { - ++count; + ++failed; uniqueEmptyFrameOffset.push_back(frameOffset(*ptr)); } else { @@ -213,9 +215,9 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed // this releases the descriptor, but not the packet (umem entries) // which will only be made available again when pushed into the fill // queue - xsk_ring_cons__release(&rx, recvSize); + xsk_ring_cons__release(&rx, processed); if (failedCount) { - *failedCount = count; + *failedCount = failed; } return res; @@ -237,7 +239,7 @@ void XskSocket::recycle(size_t size) noexcept if (completeSize <= 0) { return; } - for (uint32_t i = 0; i < completeSize; ++i) { + for (uint32_t processed = 0; processed < completeSize; ++processed) { uniqueEmptyFrameOffset.push_back(*xsk_ring_cons__comp_addr(&cq, idx++)); } xsk_ring_cons__release(&cq, completeSize); From 677f9d2b7da2e1c5833eb26003be307487b8099c Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 28 Dec 2023 11:53:49 +0100 Subject: [PATCH 28/65] dnsdist: Cleanup of the XSK code, fixing alignment issues Also add UMEM checks for debugging. --- contrib/xdp.py | 2 +- pdns/dnsdist-lua.cc | 8 +- pdns/dnsdist.cc | 209 +++++++----- pdns/dnsdist.hh | 2 +- pdns/xsk.cc | 776 ++++++++++++++++++++++++++++---------------- pdns/xsk.hh | 151 ++++++--- 6 files changed, 732 insertions(+), 416 deletions(-) diff --git a/contrib/xdp.py b/contrib/xdp.py index bd96ddb1ce58..1b9187007ff2 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -14,7 +14,7 @@ TC_ACTION = 2 # The interface on wich the filter will be attached -DEV = "eth0" +DEV = "eth1" # The list of blocked IPv4, IPv6 and QNames # IP format : (IPAddress, Action) diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 3338ceea3120..c4ee51962812 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -783,9 +783,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (socket) { udpCS->xskInfo = XskWorker::create(); udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; - socket->addWorker(udpCS->xskInfo, loc, false); - // tcpCS->xskInfo=XskWorker::create(); - // TODO: socket->addWorker(tcpCS->xskInfo, loc, true); + socket->addWorker(udpCS->xskInfo, loc); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); @@ -837,9 +835,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (socket) { udpCS->xskInfo = XskWorker::create(); udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; - socket->addWorker(udpCS->xskInfo, loc, false); - // TODO tcpCS->xskInfo=XskWorker::create(); - // TODO socket->addWorker(tcpCS->xskInfo, loc, true); + socket->addWorker(udpCS->xskInfo, loc); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index ae1d00aa1ff6..f3e840814364 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -761,7 +761,12 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re vinfolog("Got answer from %s, relayed to %s (UDP), took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); } else { - vinfolog("Got answer from %s, NOT relayed to %s (UDP) since that frontend is muted, took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); + if (!ids.xskPacketHeader) { + vinfolog("Got answer from %s, NOT relayed to %s (UDP) since that frontend is muted, took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); + } + else { + vinfolog("Got answer from %s, relayed to %s (UDP via XSK), took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); + } } handleResponseSent(ids, udiff, dr.ids.origRemote, ds->d_config.remote, response.size(), cleartextDH, ds->getProtocol(), true); @@ -779,8 +784,10 @@ static void XskHealthCheck(std::shared_ptr& dss, std::unordered auto packet = getHealthCheckPacket(dss, nullptr, data); data->d_initial = initial; setHealthCheckTime(dss, data); - auto* frame = xskInfo->getEmptyframe(); - auto xskPacket = std::make_unique(frame, 0, xskInfo->frameSize); + auto xskPacket = xskInfo->getEmptyFrame(); + if (!xskPacket) { + return; + } xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); xskPacket->setPayload(packet); xskPacket->rewrite(); @@ -853,14 +860,18 @@ void responderThread(std::shared_ptr dss) bool needNotify = false; if (pollfds[0].revents & POLLIN) { needNotify = true; - xskInfo->cq.consume_all([&](XskPacket* packetRaw) { +#if defined(__SANITIZE_THREAD__) + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { +#else + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { +#endif auto packet = XskPacketPtr(packetRaw); - if (packet->dataLen() < sizeof(dnsheader)) { - xskInfo->pushToSendQueue(std::move(packet)); + if (packet->getDataLen() < sizeof(dnsheader)) { + xskInfo->markAsFree(std::move(packet)); return; } - const auto* dh = reinterpret_cast(packet->payloadData()); - const auto queryId = dh->id; + const dnsheader_aligned dnsHeader(packet->getPayloadData()); + const auto queryId = dnsHeader->id; auto ids = dss->getState(queryId); if (ids) { if (xskFd != ids->backendFD || !ids->xskPacketHeader) { @@ -877,30 +888,40 @@ void responderThread(std::shared_ptr dss) packet->cloneIntoPacketBuffer(data->d_buffer); data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } - xskInfo->pushToSendQueue(std::move(packet)); + xskInfo->markAsFree(std::move(packet)); return; } auto response = packet->clonePacketBuffer(); - if (response.size() > packet->capacity()) { + if (response.size() > packet->getCapacity()) { /* fallback to sending the packet via normal socket */ ids->xskPacketHeader.reset(); } if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { - xskInfo->pushToSendQueue(std::move(packet)); + xskInfo->markAsFree(std::move(packet)); + vinfolog("XSK packet pushed to queue because processResponderPacket failed"); return; } - if (response.size() > packet->capacity()) { + vinfolog("XSK packet - processResponderPacket OK"); + if (response.size() > packet->getCapacity()) { /* fallback to sending the packet via normal socket */ sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); - xskInfo->pushToSendQueue(std::move(packet)); + vinfolog("XSK packet falling back because packet is too large"); + xskInfo->markAsFree(std::move(packet)); return; } + //vinfolog("XSK packet - set header"); packet->setHeader(*ids->xskPacketHeader); - packet->setPayload(response); + //vinfolog("XSK packet - set payload"); + if (!packet->setPayload(response)) { + vinfolog("Unable to set payload !"); + } if (ids->delayMsec > 0) { + vinfolog("XSK packet - adding delay"); packet->addDelay(ids->delayMsec); } + //vinfolog("XSK packet - update packet"); packet->updatePacket(); + //vinfolog("XSK packet pushed to send queue"); xskInfo->pushToSendQueue(std::move(packet)); }); xskInfo->cleanSocketNotification(); @@ -1001,12 +1022,19 @@ void responderThread(std::shared_ptr dss) if (processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids)) && ids->xskPacketHeader && ids->cs->xskInfo) { #ifdef HAVE_XSK + //vinfolog("processResponderPacket OK"); auto& xskInfo = ids->cs->xskInfo; - auto* frame = xskInfo->getEmptyframe(); - auto xskPacket = std::make_unique(frame, 0, xskInfo->frameSize); + auto xskPacket = xskInfo->getEmptyFrame(); + if (!xskPacket) { + continue; + } + //vinfolog("XSK setHeader"); xskPacket->setHeader(*ids->xskPacketHeader); + //vinfolog("XSK payload"); xskPacket->setPayload(response); + //vinfolog("XSK update packet"); xskPacket->updatePacket(); + //vinfolog("XSK pushed to send queue"); xskInfo->pushToSendQueue(std::move(xskPacket)); xskInfo->notifyXskSocket(); #endif /* HAVE_XSK */ @@ -2026,7 +2054,7 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct } #ifdef HAVE_XSK -static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) +static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) { uint16_t queryId = 0; const auto& remote = packet.getFromAddr(); @@ -2043,13 +2071,13 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p try { bool expectProxyProtocol = false; if (!isXskQueryAcceptable(packet, cs, holders, expectProxyProtocol)) { - return; + return false; } auto query = packet.clonePacketBuffer(); std::vector proxyProtocolValues; if (expectProxyProtocol && !handleProxyProtocol(remote, false, *holders.acl, query, ids.origRemote, ids.origDest, proxyProtocolValues)) { - return; + return false; } ids.queryRealTime.start(); @@ -2057,7 +2085,7 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p auto dnsCryptResponse = checkDNSCryptQuery(cs, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false); if (dnsCryptResponse) { packet.setPayload(query); - return; + return true; } { @@ -2066,7 +2094,7 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p queryId = ntohs(dnsHeader->id); if (!checkQueryHeaders(dnsHeader.get(), cs)) { - return; + return false; } if (dnsHeader->qdcount == 0) { @@ -2076,7 +2104,7 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p return true; }); packet.setPayload(query); - return; + return true; } } @@ -2095,7 +2123,7 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p auto result = processQuery(dq, holders, ss); if (result == ProcessQueryResult::Drop) { - return; + return false; } if (result == ProcessQueryResult::SendAnswer) { @@ -2103,11 +2131,11 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p if (dq.ids.delayMsec > 0) { packet.addDelay(dq.ids.delayMsec); } - return; + return true; } if (result != ProcessQueryResult::PassToBackend || ss == nullptr) { - return; + return false; } // the buffer might have been invalidated by now (resized) @@ -2125,11 +2153,12 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload); ss->passCrossProtocolQuery(std::move(cpq)); - return; + return false; } if (!ss->xskInfo) { assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); + return false; } else { int fd = ss->xskInfo->workerWaker; @@ -2138,11 +2167,13 @@ static void ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p packet.setAddr(ss->d_config.sourceAddr,ss->d_config.sourceMACAddr, ss->d_config.remote,ss->d_config.destMACAddr); packet.setPayload(query); packet.rewrite(); + return true; } } catch (const std::exception& e) { vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what()); } + return false; } #endif /* HAVE_XSK */ @@ -2246,14 +2277,26 @@ static void xskClientThread(ClientState* cs) LocalHolders holders; for (;;) { - while (!xskInfo->cq.read_available()) { +#if defined(__SANITIZE_THREAD__) + while (!xskInfo->incomingPacketsQueue.lock()->read_available()) { +#else + while (!xskInfo->incomingPacketsQueue.read_available()) { +#endif xskInfo->waitForXskSocket(); } - xskInfo->cq.consume_all([&](XskPacket* packetRaw) { +#if defined(__SANITIZE_THREAD__) + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { +#else + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { +#endif auto packet = XskPacketPtr(packetRaw); - ProcessXskQuery(*cs, holders, *packet); - packet->updatePacket(); - xskInfo->pushToSendQueue(std::move(packet)); + if (ProcessXskQuery(*cs, holders, *packet)) { + packet->updatePacket(); + xskInfo->pushToSendQueue(std::move(packet)); + } + else { + xskInfo->markAsFree(std::move(packet)); + } }); xskInfo->notifyXskSocket(); } @@ -3642,59 +3685,67 @@ void XskRouter(std::shared_ptr xsk) const auto& xskWakerIdx = xsk->workers.get<0>(); const auto& destIdx = xsk->workers.get<1>(); while (true) { - auto ready = xsk->wait(-1); - // descriptor 0 gets incoming AF_XDP packets - if (xsk->fds[0].revents & POLLIN) { - auto packets = xsk->recv(64, &failed); - dnsdist::metrics::g_stats.nonCompliantQueries += failed; - for (auto &packet : packets) { - const auto dest = packet->getToAddr(); - auto res = destIdx.find(dest); - if (res == destIdx.end()) { - xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*packet)); - continue; + try { + auto ready = xsk->wait(-1); + // descriptor 0 gets incoming AF_XDP packets + if (xsk->fds[0].revents & POLLIN) { + auto packets = xsk->recv(64, &failed); + dnsdist::metrics::g_stats.nonCompliantQueries += failed; + for (auto &packet : packets) { + const auto dest = packet->getToAddr(); + auto res = destIdx.find(dest); + if (res == destIdx.end()) { + xsk->markAsFree(std::move(packet)); + continue; + } + res->worker->pushToProcessingQueue(std::move(packet)); + needNotify.insert(res->workerWaker); } - res->worker->pushToProcessingQueue(std::move(packet)); - needNotify.insert(res->workerWaker); - } - for (auto i : needNotify) { - uint64_t x = 1; - auto written = write(i, &x, sizeof(x)); - if (written != sizeof(x)) { - // oh, well, the worker is clearly overloaded - // but there is nothing we can do about it, - // and hopefully the queue will be processed eventually + for (auto i : needNotify) { + uint64_t x = 1; + auto written = write(i, &x, sizeof(x)); + if (written != sizeof(x)) { + // oh, well, the worker is clearly overloaded + // but there is nothing we can do about it, + // and hopefully the queue will be processed eventually + } } - } - needNotify.clear(); - ready--; - } - const auto backup = ready; - for (size_t i = 1; i < size && ready > 0; i++) { - if (xsk->fds[i].revents & POLLIN) { + needNotify.clear(); ready--; - auto& info = xskWakerIdx.find(xsk->fds[i].fd)->worker; - info->sq.consume_all([&](XskPacket* packetRaw) { - auto packet = XskPacketPtr(packetRaw); - if (!(packet->getFlags() & XskPacket::UPDATE)) { - xsk->uniqueEmptyFrameOffset.push_back(xsk->frameOffset(*packet)); - packet.release(); - return; - } - if (packet->getFlags() & XskPacket::DELAY) { - xsk->waitForDelay.push(std::move(packet)); - return; - } - fillInTx.push_back(std::move(packet)); - }); - info->cleanWorkerNotification(); } + const auto backup = ready; + for (size_t i = 1; i < size && ready > 0; i++) { + if (xsk->fds[i].revents & POLLIN) { + ready--; + auto& info = xskWakerIdx.find(xsk->fds[i].fd)->worker; +#if defined(__SANITIZE_THREAD__) + info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { +#else + info->outgoingPacketsQueue.consume_all([&](XskPacket* packetRaw) { +#endif + auto packet = XskPacketPtr(packetRaw); + if (!(packet->getFlags() & XskPacket::UPDATE)) { + xsk->markAsFree(std::move(packet)); + return; + } + if (packet->getFlags() & XskPacket::DELAY) { + xsk->waitForDelay.push(std::move(packet)); + return; + } + fillInTx.push_back(std::move(packet)); + }); + info->cleanWorkerNotification(); + } + } + xsk->pickUpReadyPacket(fillInTx); + xsk->recycle(4096); + xsk->fillFq(); + xsk->send(fillInTx); + ready = backup; + } + catch (...) { + vinfolog("Exception in XSK router loop"); } - xsk->pickUpReadyPacket(fillInTx); - xsk->recycle(64); - xsk->fillFq(); - xsk->send(fillInTx); - ready = backup; } } #endif /* HAVE_XSK */ diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 9b5d33e38c59..0081c492a625 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -985,7 +985,7 @@ public: if (d_config.sourceAddr.sin4.sin_family == 0) { throw runtime_error("invalid source addr"); } - xsk->addWorker(xskInfo, d_config.sourceAddr, getProtocol() != dnsdist::Protocol::DoUDP); + xsk->addWorker(xskInfo, d_config.sourceAddr); d_config.sourceMACAddr = xsk->source; xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; } diff --git a/pdns/xsk.cc b/pdns/xsk.cc index f1fa45fbf18f..2f438f9fd9c1 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -24,22 +24,17 @@ #ifdef HAVE_XSK -#include "gettime.hh" -#include "xsk.hh" - #include #include #include #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -60,6 +55,36 @@ extern "C" #include } +#include "gettime.hh" +#include "xsk.hh" + +#define DEBUG_UMEM 0 +#ifdef DEBUG_UMEM +namespace { +struct UmemEntryStatus +{ + enum class Status: uint8_t { Free, FillQueue, Received, TXQueue }; + Status status{Status::Free}; +}; + +LockGuarded> s_umems; + +void checkUmemIntegrity(const char* function, int line, uint64_t offset, const std::set& validStatuses, UmemEntryStatus::Status newStatus) +{ + auto umems = s_umems.lock(); + if (validStatuses.count(umems->at(offset).status) == 0) { + std::cerr << "UMEM integrity check failed at " << function << ": " << line << ": status is " << static_cast(umems->at(offset).status) << ", expected: "; + for (const auto status : validStatuses) { + std::cerr << static_cast(status) << " "; + } + std::cerr << std::endl; + abort(); + } + (*umems)[offset].status = newStatus; +} +} +#endif /* DEBUG_UMEM */ + constexpr bool XskSocket::isPowOfTwo(uint32_t value) noexcept { return value != 0 && (value & (value - 1)) == 0; @@ -79,6 +104,7 @@ int XskSocket::firstTimeout() } return res; } + XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_) : frameNum(frameNum_), queueId(queue_id), ifName(ifName_), poolName(poolName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) { @@ -92,6 +118,7 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu memset(&fq, 0, sizeof(fq)); memset(&tx, 0, sizeof(tx)); memset(&rx, 0, sizeof(rx)); + xsk_umem_config umemCfg; umemCfg.fill_size = fqCapacity; umemCfg.comp_size = cqCapacity; @@ -99,6 +126,7 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu umemCfg.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM; umemCfg.flags = 0; umem.umemInit(frameNum_ * frameSize, &cq, &fq, &umemCfg); + { xsk_socket_config socketCfg; socketCfg.rx_size = rxCapacity; @@ -109,106 +137,165 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu xsk_socket* tmp = nullptr; auto ret = xsk_socket__create(&tmp, ifName.c_str(), queue_id, umem.umem, &rx, &tx, &socketCfg); if (ret != 0) { - throw std::runtime_error("Error creating a xsk socket of if_name" + ifName + stringerror(ret)); + throw std::runtime_error("Error creating a xsk socket of if_name " + ifName + ": " + stringerror(ret)); } - socket = std::unique_ptr(tmp, xsk_socket__delete); + socket = std::unique_ptr(tmp, xsk_socket__delete); } - for (uint64_t i = 0; i < frameNum; i++) { - uniqueEmptyFrameOffset.push_back(i * frameSize + XDP_PACKET_HEADROOM); + + uniqueEmptyFrameOffset.reserve(frameNum); + { + for (uint64_t i = 0; i < frameNum; i++) { + //uniqueEmptyFrameOffset.push_back(i * frameSize); + uniqueEmptyFrameOffset.push_back(i * frameSize + XDP_PACKET_HEADROOM); +#ifdef DEBUG_UMEM + { + auto umems = s_umems.lock(); + (*umems)[i * frameSize + XDP_PACKET_HEADROOM] = UmemEntryStatus(); + } +#endif /* DEBUG_UMEM */ + } } + fillFq(fqCapacity); + const auto xskfd = xskFd(); fds.push_back(pollfd{ .fd = xskfd, .events = POLLIN, .revents = 0}); + const auto xskMapFd = FDWrapper(bpf_obj_get(xskMapPath.c_str())); + if (xskMapFd.getHandle() < 0) { throw std::runtime_error("Error getting BPF map from path '" + xskMapPath + "'"); } + auto ret = bpf_map_update_elem(xskMapFd.getHandle(), &queue_id, &xskfd, 0); if (ret) { throw std::runtime_error("Error inserting into xsk_map '" + xskMapPath + "': " + std::to_string(ret)); } } + void XskSocket::fillFq(uint32_t fillSize) noexcept { { +#warning why are we collecting frames from unique into shared here, even though we need unique ones? auto frames = sharedEmptyFrameOffset->lock(); if (frames->size() < holdThreshold) { const auto moveSize = std::min(holdThreshold - frames->size(), uniqueEmptyFrameOffset.size()); if (moveSize > 0) { frames->insert(frames->end(), std::make_move_iterator(uniqueEmptyFrameOffset.end() - moveSize), std::make_move_iterator(uniqueEmptyFrameOffset.end())); + uniqueEmptyFrameOffset.resize(uniqueEmptyFrameOffset.size() - moveSize); } } } + if (uniqueEmptyFrameOffset.size() < fillSize) { return; } - uint32_t idx; - if (xsk_ring_prod__reserve(&fq, fillSize, &idx) != fillSize) { + + uint32_t idx{0}; + auto toFill = xsk_ring_prod__reserve(&fq, fillSize, &idx); + if (toFill == 0) { return; } uint32_t processed = 0; - for (; processed < fillSize; processed++) { + for (; processed < toFill; processed++) { *xsk_ring_prod__fill_addr(&fq, idx++) = uniqueEmptyFrameOffset.back(); +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, uniqueEmptyFrameOffset.back(), {UmemEntryStatus::Status::Free}, UmemEntryStatus::Status::FillQueue); +#endif /* DEBUG_UMEM */ uniqueEmptyFrameOffset.pop_back(); } + xsk_ring_prod__submit(&fq, processed); } + int XskSocket::wait(int timeout) { - return poll(fds.data(), fds.size(), static_cast(std::min(static_cast(timeout), static_cast(firstTimeout())))); + auto waitAtMost = std::min(timeout, firstTimeout()); + return poll(fds.data(), fds.size(), waitAtMost); } + [[nodiscard]] uint64_t XskSocket::frameOffset(const XskPacket& packet) const noexcept { - return reinterpret_cast(packet.frame) - reinterpret_cast(umem.bufBase); + return packet.frame - umem.bufBase; } -int XskSocket::xskFd() const noexcept { return xsk_socket__fd(socket.get()); } +[[nodiscard]] int XskSocket::xskFd() const noexcept { + return xsk_socket__fd(socket.get()); +} void XskSocket::send(std::vector& packets) { - const auto packetSize = packets.size(); - if (packetSize == 0) { - return; - } - uint32_t idx{0}; - if (xsk_ring_prod__reserve(&tx, packetSize, &idx) != packets.size()) { - return; - } + while (packets.size() > 0) { + auto packetSize = packets.size(); + if (packetSize > std::numeric_limits::max()) { + packetSize = std::numeric_limits::max(); + } + size_t toSend = std::min(static_cast(packetSize), txCapacity); + uint32_t idx{0}; + auto toFill = xsk_ring_prod__reserve(&tx, toSend, &idx); + if (toFill == 0) { + return; + } - for (const auto& packet : packets) { - *xsk_ring_prod__tx_desc(&tx, idx++) = { - .addr = frameOffset(*packet), - .len = packet->FrameLen(), - .options = 0}; + size_t queued = 0; + for (const auto& packet : packets) { + if (queued == toFill) { + break; + } + *xsk_ring_prod__tx_desc(&tx, idx++) = { + .addr = frameOffset(*packet), + .len = packet->getFrameLen(), + .options = 0}; +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(*packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::Received}, UmemEntryStatus::Status::TXQueue); +#endif /* DEBUG_UMEM */ + queued++; + } + xsk_ring_prod__submit(&tx, toFill); + packets.erase(packets.begin(), packets.begin() + toFill); } - xsk_ring_prod__submit(&tx, packetSize); - packets.clear(); } + std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCount) { - uint32_t idx; + uint32_t idx{0}; std::vector res; // how many descriptors to packets have been filled const auto recvSize = xsk_ring_cons__peek(&rx, recvSizeMax, &idx); - if (recvSize <= 0) { + if (recvSize == 0) { return res; } const auto baseAddr = reinterpret_cast(umem.bufBase); uint32_t failed = 0; uint32_t processed = 0; + res.reserve(recvSize); for (; processed < recvSize; processed++) { - const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); - auto ptr = std::make_unique(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); - if (!ptr->parse()) { - ++failed; - uniqueEmptyFrameOffset.push_back(frameOffset(*ptr)); + try { + const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); + auto ptr = std::make_unique(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(*ptr), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); +#endif /* DEBUG_UMEM */ + + if (!ptr->parse(false)) { + ++failed; + markAsFree(std::move(ptr)); + } + else { + res.push_back(std::move(ptr)); + } + } + catch (const std::exception& exp) { + std::cerr << "Exception while processing the XSK RX queue: " << exp.what() << std::endl; + break; } - else { - res.push_back(std::move(ptr)); + catch (...) { + std::cerr << "Exception while processing the XSK RX queue" << std::endl; + break; } } @@ -222,6 +309,7 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed return res; } + void XskSocket::pickUpReadyPacket(std::vector& packets) { timespec now; @@ -232,17 +320,23 @@ void XskSocket::pickUpReadyPacket(std::vector& packets) waitForDelay.pop(); } } + void XskSocket::recycle(size_t size) noexcept { - uint32_t idx; + uint32_t idx{0}; const auto completeSize = xsk_ring_cons__peek(&cq, size, &idx); - if (completeSize <= 0) { + if (completeSize == 0) { return; } - for (uint32_t processed = 0; processed < completeSize; ++processed) { + uniqueEmptyFrameOffset.reserve(uniqueEmptyFrameOffset.size() + completeSize); + uint32_t processed = 0; + for (; processed < completeSize; ++processed) { uniqueEmptyFrameOffset.push_back(*xsk_ring_cons__comp_addr(&cq, idx++)); +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, uniqueEmptyFrameOffset.back(), {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); +#endif /* DEBUG_UMEM */ } - xsk_ring_cons__release(&cq, completeSize); + xsk_ring_cons__release(&cq, processed); } void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* completionQueue, xsk_ring_prod* fillQueue, xsk_umem_config* config) @@ -255,7 +349,7 @@ void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* completionQueue auto ret = xsk_umem__create(&umem, bufBase, size, fillQueue, completionQueue, config); if (ret != 0) { munmap(bufBase, size); - throw std::runtime_error("Error creating a umem of size" + std::to_string(size) + stringerror(ret)); + throw std::runtime_error("Error creating a umem of size " + std::to_string(size) + ": " + stringerror(ret)); } } @@ -277,10 +371,21 @@ std::string XskSocket::getMetrics() const ret << "TX invalid descs: " << std::to_string(stats.tx_invalid_descs) << std::endl; ret << "RX ring full: " << std::to_string(stats.rx_ring_full) << std::endl; ret << "RX fill ring empty descs: " << std::to_string(stats.rx_fill_ring_empty_descs) << std::endl; - ret << "RX ring empty descs: " << std::to_string(stats.tx_ring_empty_descs) << std::endl; + ret << "TX ring empty descs: " << std::to_string(stats.tx_ring_empty_descs) << std::endl; return ret.str(); } +void XskSocket::markAsFree(XskPacketPtr&& packet) +{ + auto offset = frameOffset(*packet); +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); +#endif /* DEBUG_UMEM */ + + uniqueEmptyFrameOffset.push_back(offset); + packet.release(); +} + XskSocket::XskUmem::~XskUmem() { if (umem) { @@ -291,198 +396,276 @@ XskSocket::XskUmem::~XskUmem() } } -bool XskPacket::parse() +[[nodiscard]] size_t XskPacket::getL4HeaderOffset() const noexcept +{ + return sizeof(ethhdr) + (v6 ? (sizeof(ipv6hdr)) : sizeof(iphdr)); +} + +[[nodiscard]] size_t XskPacket::getDataOffset() const noexcept +{ + return getL4HeaderOffset() + sizeof(udphdr); +} + +[[nodiscard]] size_t XskPacket::getDataSize() const noexcept +{ + return frameLength - getDataOffset(); +} + +[[nodiscard]] ethhdr XskPacket::getEthernetHeader() const noexcept +{ + ethhdr ethHeader{}; + assert(frameLength >= sizeof(ethHeader)); + memcpy(ðHeader, frame, sizeof(ethHeader)); + return ethHeader; +} + +void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept { - // payloadEnd must bigger than payload + sizeof(ethhdr) + sizoef(iphdr) + sizeof(udphdr) - auto* eth = reinterpret_cast(frame); - uint8_t l4Protocol; - if (eth->h_proto == htons(ETH_P_IP)) { - auto* ip = reinterpret_cast(eth + 1); - if (ip->ihl != static_cast(sizeof(iphdr) >> 2)) { - // ip->ihl*4 != sizeof(iphdr) + assert(frameLength >= sizeof(ethHeader)); + memcpy(frame, ðHeader, sizeof(ethHeader)); +} + +[[nodiscard]] iphdr XskPacket::getIPv4Header() const noexcept +{ + iphdr ipv4Header{}; + assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv4Header))); + assert(!v6); + memcpy(&ipv4Header, frame + sizeof(ethhdr), sizeof(ipv4Header)); + return ipv4Header; +} + +void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept +{ + assert(frameLength >= (sizeof(ethhdr) + sizeof(iphdr))); + assert(!v6); + memcpy(frame + sizeof(ethhdr), &ipv4Header, sizeof(ipv4Header)); +} + +[[nodiscard]] ipv6hdr XskPacket::getIPv6Header() const noexcept +{ + ipv6hdr ipv6Header{}; + assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); + assert(v6); + memcpy(&ipv6Header, frame + sizeof(ethhdr), sizeof(ipv6Header)); + return ipv6Header; +} + +void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept +{ + assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); + assert(v6); + memcpy(frame + sizeof(ethhdr), &ipv6Header, sizeof(ipv6Header)); +} + +[[nodiscard]] udphdr XskPacket::getUDPHeader() const noexcept +{ + udphdr udpHeader{}; + assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); + memcpy(&udpHeader, frame + getL4HeaderOffset(), sizeof(udpHeader)); + return udpHeader; +} + +void XskPacket::setUDPHeader(const udphdr& udpHeader) noexcept +{ + assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); + memcpy(frame + getL4HeaderOffset(), &udpHeader, sizeof(udpHeader)); +} + +bool XskPacket::parse(bool fromSetHeader) +{ + if (frameLength <= sizeof(ethhdr)) { + return false; + } + + auto ethHeader = getEthernetHeader(); + uint8_t l4Protocol{0}; + if (ethHeader.h_proto == htons(ETH_P_IP)) { + if (frameLength < (sizeof(ethhdr) + sizeof(iphdr) + sizeof(udphdr))) { + return false; + } + v6 = false; + auto ipHeader = getIPv4Header(); + if (ipHeader.ihl != (static_cast(sizeof(iphdr) / 4))) { // ip options is not supported now! return false; } // check ip.check == ipv4Checksum() is not needed! // We check it in BPF program - from = makeComboAddressFromRaw(4, reinterpret_cast(&ip->saddr), sizeof(ip->saddr)); - to = makeComboAddressFromRaw(4, reinterpret_cast(&ip->daddr), sizeof(ip->daddr)); - l4Protocol = ip->protocol; - l4Header = reinterpret_cast(ip + 1); - payloadEnd = std::min(reinterpret_cast(ip) + ntohs(ip->tot_len), payloadEnd); - } - else if (eth->h_proto == htons(ETH_P_IPV6)) { - auto* ipv6 = reinterpret_cast(eth + 1); - l4Header = reinterpret_cast(ipv6 + 1); - if (l4Header >= payloadEnd) { + // we don't, actually. + from = makeComboAddressFromRaw(4, reinterpret_cast(&ipHeader.saddr), sizeof(ipHeader.saddr)); + to = makeComboAddressFromRaw(4, reinterpret_cast(&ipHeader.daddr), sizeof(ipHeader.daddr)); + l4Protocol = ipHeader.protocol; + if (!fromSetHeader && (frameLength - sizeof(ethhdr)) != ntohs(ipHeader.tot_len)) { + // too small, or too large (trailing data), go away + return false; + } + } + else if (ethHeader.h_proto == htons(ETH_P_IPV6)) { + if (frameLength < (sizeof(ethhdr) + sizeof(ipv6hdr) + sizeof(udphdr))) { + return false; + } + v6 = true; + auto ipHeader = getIPv6Header(); + from = makeComboAddressFromRaw(6, reinterpret_cast(&ipHeader.saddr), sizeof(ipHeader.saddr)); + to = makeComboAddressFromRaw(6, reinterpret_cast(&ipHeader.daddr), sizeof(ipHeader.daddr)); + l4Protocol = ipHeader.nexthdr; + if (!fromSetHeader && (frameLength - (sizeof(ethhdr) + sizeof(ipv6hdr))) != ntohs(ipHeader.payload_len)) { return false; } - from = makeComboAddressFromRaw(6, reinterpret_cast(&ipv6->saddr), sizeof(ipv6->saddr)); - to = makeComboAddressFromRaw(6, reinterpret_cast(&ipv6->daddr), sizeof(ipv6->daddr)); - l4Protocol = ipv6->nexthdr; - payloadEnd = std::min(l4Header + ntohs(ipv6->payload_len), payloadEnd); } else { return false; } - if (l4Protocol == IPPROTO_UDP) { - // check udp.check == ipv4Checksum() is not needed! - // We check it in BPF program - const auto* udp = reinterpret_cast(l4Header); - payload = l4Header + sizeof(udphdr); - // Because of XskPacket::setHeader - // payload = payloadEnd should be allow - if (payload > payloadEnd) { - return false; - } - payloadEnd = std::min(l4Header + ntohs(udp->len), payloadEnd); - from.setPort(ntohs(udp->source)); - to.setPort(ntohs(udp->dest)); - return true; + + if (l4Protocol != IPPROTO_UDP) { + return false; } - if (l4Protocol == IPPROTO_TCP) { - // check tcp.check == ipv4Checksum() is not needed! - // We check it in BPF program - const auto* tcp = reinterpret_cast(l4Header); - if (tcp->doff != static_cast(sizeof(tcphdr) >> 2)) { - // tcp is not supported now! + + // check udp.check == ipv4Checksum() is not needed! + // We check it in BPF program + // we don't, actually. + auto udpHeader = getUDPHeader(); + if (!fromSetHeader) { + // Because of XskPacket::setHeader + if (getDataOffset() > frameLength) { return false; } - payload = l4Header + sizeof(tcphdr); - // - if (payload > payloadEnd) { + + if (getDataSize() + sizeof(udphdr) != ntohs(udpHeader.len)) { return false; } - from.setPort(ntohs(tcp->source)); - to.setPort(ntohs(tcp->dest)); - flags |= TCP; - return true; } - // ipv6 extension header is not supported now! - return false; + + from.setPort(ntohs(udpHeader.source)); + to.setPort(ntohs(udpHeader.dest)); + return true; } -uint32_t XskPacket::dataLen() const noexcept +uint32_t XskPacket::getDataLen() const noexcept { - return payloadEnd - payload; + return getDataSize(); } -uint32_t XskPacket::FrameLen() const noexcept + +uint32_t XskPacket::getFrameLen() const noexcept { - return payloadEnd - frame; + return frameLength; } -size_t XskPacket::capacity() const noexcept + +size_t XskPacket::getCapacity() const noexcept { - return frameEnd - payloadEnd; + return frameSize; } void XskPacket::changeDirectAndUpdateChecksum() noexcept { - auto* eth = reinterpret_cast(frame); + auto ethHeader = getEthernetHeader(); { uint8_t tmp[ETH_ALEN]; - static_assert(sizeof(tmp) == sizeof(eth->h_dest), "Size Error"); - static_assert(sizeof(tmp) == sizeof(eth->h_source), "Size Error"); - memcpy(tmp, eth->h_dest, sizeof(tmp)); - memcpy(eth->h_dest, eth->h_source, sizeof(tmp)); - memcpy(eth->h_source, tmp, sizeof(tmp)); + static_assert(sizeof(tmp) == sizeof(ethHeader.h_dest), "Size Error"); + static_assert(sizeof(tmp) == sizeof(ethHeader.h_source), "Size Error"); + memcpy(tmp, ethHeader.h_dest, sizeof(tmp)); + memcpy(ethHeader.h_dest, ethHeader.h_source, sizeof(tmp)); + memcpy(ethHeader.h_source, tmp, sizeof(tmp)); } - if (eth->h_proto == htons(ETH_P_IPV6)) { + if (ethHeader.h_proto == htons(ETH_P_IPV6)) { // IPV6 - auto* ipv6 = reinterpret_cast(eth + 1); - std::swap(ipv6->daddr, ipv6->saddr); - if (ipv6->nexthdr == IPPROTO_UDP) { - // UDP - auto* udp = reinterpret_cast(ipv6 + 1); - std::swap(udp->dest, udp->source); - udp->len = htons(payloadEnd - reinterpret_cast(udp)); - udp->check = 0; - udp->check = tcp_udp_v6_checksum(); - } - else { - // TCP - auto* tcp = reinterpret_cast(ipv6 + 1); - std::swap(tcp->dest, tcp->source); - // TODO - } - rewriteIpv6Header(ipv6); + auto ipv6 = getIPv6Header(); + std::swap(ipv6.daddr, ipv6.saddr); + assert(ipv6.nexthdr == IPPROTO_UDP); + + auto udp = getUDPHeader(); + std::swap(udp.dest, udp.source); + udp.len = htons(getDataSize() + sizeof(udp)); + udp.check = 0; + /* needed to get the correct checksum */ + setIPv6Header(ipv6); + setUDPHeader(udp); + udp.check = tcp_udp_v6_checksum(&ipv6); + rewriteIpv6Header(&ipv6, getFrameLen()); + setIPv6Header(ipv6); + setUDPHeader(udp); } else { // IPV4 - auto* ipv4 = reinterpret_cast(eth + 1); - std::swap(ipv4->daddr, ipv4->saddr); - if (ipv4->protocol == IPPROTO_UDP) { - // UDP - auto* udp = reinterpret_cast(ipv4 + 1); - std::swap(udp->dest, udp->source); - udp->len = htons(payloadEnd - reinterpret_cast(udp)); - udp->check = 0; - udp->check = tcp_udp_v4_checksum(); - } - else { - // TCP - auto* tcp = reinterpret_cast(ipv4 + 1); - std::swap(tcp->dest, tcp->source); - // TODO - } - rewriteIpv4Header(ipv4); + auto ipv4 = getIPv4Header(); + std::swap(ipv4.daddr, ipv4.saddr); + assert(ipv4.protocol == IPPROTO_UDP); + + auto udp = getUDPHeader(); + std::swap(udp.dest, udp.source); + udp.len = htons(getDataSize() + sizeof(udp)); + udp.check = 0; + /* needed to get the correct checksum */ + setIPv4Header(ipv4); + setUDPHeader(udp); + udp.check = tcp_udp_v4_checksum(&ipv4); + rewriteIpv4Header(&ipv4, getFrameLen()); + setIPv4Header(ipv4); + setUDPHeader(udp); } + setEthernetHeader(ethHeader); } -void XskPacket::rewriteIpv4Header(void* ipv4header) noexcept + +void XskPacket::rewriteIpv4Header(struct iphdr* ipv4header, size_t frameLen) noexcept { - auto* ipv4 = static_cast(ipv4header); - ipv4->version = 4; - ipv4->ihl = sizeof(iphdr) / 4; - ipv4->tos = 0; - ipv4->tot_len = htons(payloadEnd - reinterpret_cast(ipv4)); - ipv4->id = 0; - ipv4->frag_off = 0; - ipv4->ttl = DefaultTTL; - ipv4->check = 0; - ipv4->check = ipv4Checksum(); + ipv4header->version = 4; + ipv4header->ihl = sizeof(iphdr) / 4; + ipv4header->tos = 0; + ipv4header->tot_len = htons(frameLen - sizeof(ethhdr)); + ipv4header->id = 0; + ipv4header->frag_off = 0; + ipv4header->ttl = DefaultTTL; + ipv4header->check = 0; + ipv4header->check = ipv4Checksum(ipv4header); } -void XskPacket::rewriteIpv6Header(void* ipv6header) noexcept + +void XskPacket::rewriteIpv6Header(struct ipv6hdr* ipv6header, size_t frameLen) noexcept { - auto* ipv6 = static_cast(ipv6header); - ipv6->version = 6; - ipv6->priority = 0; - ipv6->payload_len = htons(payloadEnd - reinterpret_cast(ipv6 + 1)); - ipv6->hop_limit = DefaultTTL; - memset(&ipv6->flow_lbl, 0, sizeof(ipv6->flow_lbl)); + ipv6header->version = 6; + ipv6header->priority = 0; + ipv6header->payload_len = htons(frameLen - sizeof(ethhdr) - sizeof(ipv6hdr)); + ipv6header->hop_limit = DefaultTTL; + memset(&ipv6header->flow_lbl, 0, sizeof(ipv6header->flow_lbl)); } bool XskPacket::isIPV6() const noexcept { - const auto* eth = reinterpret_cast(frame); - return eth->h_proto == htons(ETH_P_IPV6); + return v6; } -XskPacket::XskPacket(void* frame_, size_t dataSize, size_t frameSize) : - frame(static_cast(frame_)), payloadEnd(static_cast(frame) + dataSize), frameEnd(static_cast(frame) + frameSize - XDP_PACKET_HEADROOM) + +XskPacket::XskPacket(uint8_t* frame_, size_t dataSize, size_t frameSize) : + frame(frame_), frameLength(dataSize), frameSize(frameSize - XDP_PACKET_HEADROOM) { } + PacketBuffer XskPacket::clonePacketBuffer() const { - const auto size = dataLen(); + const auto size = getDataSize(); PacketBuffer tmp(size); - memcpy(tmp.data(), payload, size); + memcpy(tmp.data(), frame + getDataOffset(), size); return tmp; } + void XskPacket::cloneIntoPacketBuffer(PacketBuffer& buffer) const { - const auto size = dataLen(); + const auto size = getDataSize(); buffer.resize(size); - memcpy(buffer.data(), payload, size); + memcpy(buffer.data(), frame + getDataOffset(), size); } + bool XskPacket::setPayload(const PacketBuffer& buf) { const auto bufSize = buf.size(); - if (bufSize == 0 || bufSize > capacity()) { + const auto currentCapacity = getCapacity(); + if (bufSize == 0 || bufSize > currentCapacity) { return false; } flags |= UPDATE; - memcpy(payload, buf.data(), bufSize); - payloadEnd = payload + bufSize; + memcpy(frame + getDataOffset(), buf.data(), bufSize); + frameLength = getDataOffset() + bufSize; return true; } + void XskPacket::addDelay(const int relativeMilliseconds) noexcept { gettime(&sendTime); @@ -490,18 +673,22 @@ void XskPacket::addDelay(const int relativeMilliseconds) noexcept sendTime.tv_sec += sendTime.tv_nsec / 1000000000L; sendTime.tv_nsec %= 1000000000L; } + bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept { return s1->sendTime < s2->sendTime; } + const ComboAddress& XskPacket::getFromAddr() const noexcept { return from; } + const ComboAddress& XskPacket::getToAddr() const noexcept { return to; } + void XskWorker::notify(int fd) { uint64_t value = 1; @@ -512,6 +699,7 @@ void XskWorker::notify(int fd) throw runtime_error("Unable Wake Up XskSocket Failed"); } } + XskWorker::XskWorker() : workerWaker(createEventfd()), xskSocketWaker(createEventfd()) { @@ -520,154 +708,158 @@ XskWorker::XskWorker() : void XskWorker::pushToProcessingQueue(XskPacketPtr&& packet) { auto raw = packet.release(); - if (!cq.push(raw)) { - delete raw; +#if defined(__SANITIZE_THREAD__) + if (!incomingPacketsQueue.lock()->push(std::move(raw))) { +#else + if (!incomingPacketsQueue.push(std::move(raw))) { +#endif + markAsFree(XskPacketPtr(raw)); } } void XskWorker::pushToSendQueue(XskPacketPtr&& packet) { auto raw = packet.release(); - if (!sq.push(raw)) { - delete raw; +#if defined(__SANITIZE_THREAD__) + if (!outgoingPacketsQueue.lock()->push(raw)) { +#else + if (!outgoingPacketsQueue.push(raw)) { +#endif + markAsFree(XskPacketPtr(raw)); } } -void* XskPacket::payloadData() -{ - return reinterpret_cast(payload); -} -const void* XskPacket::payloadData() const +const void* XskPacket::getPayloadData() const { - return reinterpret_cast(payload); + return frame + getDataOffset(); } -void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC, bool tcp) noexcept + +void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept { - auto* eth = reinterpret_cast(frame); - memcpy(eth->h_dest, &toMAC[0], sizeof(MACAddr)); - memcpy(eth->h_source, &fromMAC[0], sizeof(MACAddr)); + auto ethHeader = getEthernetHeader(); + memcpy(ethHeader.h_dest, &toMAC[0], sizeof(MACAddr)); + memcpy(ethHeader.h_source, &fromMAC[0], sizeof(MACAddr)); + setEthernetHeader(ethHeader); to = to_; from = from_; - l4Header = frame + sizeof(ethhdr) + (to.isIPv4() ? sizeof(iphdr) : sizeof(ipv6hdr)); - if (tcp) { - flags = TCP; - payload = l4Header + sizeof(tcphdr); - } - else { - flags = 0; - payload = l4Header + sizeof(udphdr); - } + v6 = !to.isIPv4(); + flags = 0; } + void XskPacket::rewrite() noexcept { flags |= REWRITE; - auto* eth = reinterpret_cast(frame); - if (to.isIPv4()) { - eth->h_proto = htons(ETH_P_IP); - auto* ipv4 = reinterpret_cast(eth + 1); - - ipv4->daddr = to.sin4.sin_addr.s_addr; - ipv4->saddr = from.sin4.sin_addr.s_addr; - if (flags & XskPacket::TCP) { - auto* tcp = reinterpret_cast(ipv4 + 1); - ipv4->protocol = IPPROTO_TCP; - tcp->source = from.sin4.sin_port; - tcp->dest = to.sin4.sin_port; - // TODO - } - else { - auto* udp = reinterpret_cast(ipv4 + 1); - ipv4->protocol = IPPROTO_UDP; - udp->source = from.sin4.sin_port; - udp->dest = to.sin4.sin_port; - udp->len = htons(payloadEnd - reinterpret_cast(udp)); - udp->check = 0; - udp->check = tcp_udp_v4_checksum(); - } - rewriteIpv4Header(ipv4); + auto ethHeader = getEthernetHeader(); + if (!v6) { + ethHeader.h_proto = htons(ETH_P_IP); + + auto ipHeader = getIPv4Header(); + ipHeader.daddr = to.sin4.sin_addr.s_addr; + ipHeader.saddr = from.sin4.sin_addr.s_addr; + + auto udpHeader = getUDPHeader(); + ipHeader.protocol = IPPROTO_UDP; + udpHeader.source = from.sin4.sin_port; + udpHeader.dest = to.sin4.sin_port; + udpHeader.len = htons(getDataSize()); + udpHeader.check = 0; + /* needed to get the correct checksum */ + setIPv4Header(ipHeader); + setUDPHeader(udpHeader); + udpHeader.check = tcp_udp_v4_checksum(&ipHeader); + rewriteIpv4Header(&ipHeader, getFrameLen()); + setIPv4Header(ipHeader); + setUDPHeader(udpHeader); } else { - auto* ipv6 = reinterpret_cast(eth + 1); - memcpy(&ipv6->daddr, &to.sin6.sin6_addr, sizeof(ipv6->daddr)); - memcpy(&ipv6->saddr, &from.sin6.sin6_addr, sizeof(ipv6->saddr)); - if (flags & XskPacket::TCP) { - auto* tcp = reinterpret_cast(ipv6 + 1); - ipv6->nexthdr = IPPROTO_TCP; - tcp->source = from.sin6.sin6_port; - tcp->dest = to.sin6.sin6_port; - // TODO - } - else { - auto* udp = reinterpret_cast(ipv6 + 1); - ipv6->nexthdr = IPPROTO_UDP; - udp->source = from.sin6.sin6_port; - udp->dest = to.sin6.sin6_port; - udp->len = htons(payloadEnd - reinterpret_cast(udp)); - udp->check = 0; - udp->check = tcp_udp_v6_checksum(); - } + ethHeader.h_proto = htons(ETH_P_IPV6); + + auto ipHeader = getIPv6Header(); + memcpy(&ipHeader.daddr, &to.sin6.sin6_addr, sizeof(ipHeader.daddr)); + memcpy(&ipHeader.saddr, &from.sin6.sin6_addr, sizeof(ipHeader.saddr)); + + auto udpHeader = getUDPHeader(); + ipHeader.nexthdr = IPPROTO_UDP; + udpHeader.source = from.sin6.sin6_port; + udpHeader.dest = to.sin6.sin6_port; + udpHeader.len = htons(getDataSize()); + udpHeader.check = 0; + /* needed to get the correct checksum */ + setIPv6Header(ipHeader); + setUDPHeader(udpHeader); + udpHeader.check = tcp_udp_v6_checksum(&ipHeader); + setIPv6Header(ipHeader); + setUDPHeader(udpHeader); } + + setEthernetHeader(ethHeader); } -[[nodiscard]] __be16 XskPacket::ipv4Checksum() const noexcept +[[nodiscard]] __be16 XskPacket::ipv4Checksum(const struct iphdr* ip) noexcept { - auto* ip = reinterpret_cast(frame + sizeof(ethhdr)); - return ip_checksum_fold(ip_checksum_partial(ip, sizeof(iphdr), 0)); + auto partial = ip_checksum_partial(ip, sizeof(iphdr), 0); + return ip_checksum_fold(partial); } -[[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum() const noexcept + +[[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum(const struct iphdr* ip) const noexcept { - const auto* ip = reinterpret_cast(frame + sizeof(ethhdr)); // ip options is not supported !!! - const auto l4Length = static_cast(payloadEnd - l4Header); + const auto l4Length = static_cast(getDataSize() + sizeof(udphdr)); auto sum = tcp_udp_v4_header_checksum_partial(ip->saddr, ip->daddr, ip->protocol, l4Length); - sum = ip_checksum_partial(l4Header, l4Length, sum); + sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); return ip_checksum_fold(sum); } -[[nodiscard]] __be16 XskPacket::tcp_udp_v6_checksum() const noexcept + +[[nodiscard]] __be16 XskPacket::tcp_udp_v6_checksum(const struct ipv6hdr* ipv6) const noexcept { - const auto* ipv6 = reinterpret_cast(frame + sizeof(ethhdr)); - const auto l4Length = static_cast(payloadEnd - l4Header); + const auto l4Length = static_cast(getDataSize() + sizeof(udphdr)); uint64_t sum = tcp_udp_v6_header_checksum_partial(&ipv6->saddr, &ipv6->daddr, ipv6->nexthdr, l4Length); - sum = ip_checksum_partial(l4Header, l4Length, sum); + sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); return ip_checksum_fold(sum); } -#ifndef __packed -#define __packed __attribute__((packed)) -#endif -[[nodiscard]] uint64_t XskPacket::ip_checksum_partial(const void* p, size_t len, uint64_t sum) noexcept +[[nodiscard]] uint64_t XskPacket::ip_checksum_partial(const void* ptr, const size_t len, uint64_t sum) noexcept { - /* Main loop: 32 bits at a time. - * We take advantage of intel's ability to do unaligned memory - * accesses with minimal additional cost. Other architectures - * probably want to be more careful here. - */ - const uint32_t* p32 = (const uint32_t*)(p); - for (; len >= sizeof(*p32); len -= sizeof(*p32)) - sum += *p32++; + size_t position{0}; + /* Main loop: 32 bits at a time */ + for (position = 0; position < len; position += sizeof(uint32_t)) { + uint32_t value{}; + memcpy(&value, reinterpret_cast(ptr) + position, sizeof(value)); + sum += value; + } /* Handle un-32bit-aligned trailing bytes */ - const uint16_t* p16 = (const uint16_t*)(p32); - if (len >= 2) { - sum += *p16++; - len -= sizeof(*p16); + if ((len - position) >= 2) { + uint16_t value{}; + memcpy(&value, reinterpret_cast(ptr) + position, sizeof(value)); + sum += value; + position += sizeof(value); } - if (len > 0) { - const uint8_t* p8 = (const uint8_t*)(p16); + + if ((len - position) > 0) { + const auto* p8 = static_cast(ptr) + position; sum += ntohs(*p8 << 8); /* RFC says pad last byte */ } return sum; } + [[nodiscard]] __be16 XskPacket::ip_checksum_fold(uint64_t sum) noexcept { - while (sum & ~0xffffffffULL) + while (sum & ~0xffffffffULL) { sum = (sum >> 32) + (sum & 0xffffffffULL); - while (sum & 0xffff0000ULL) + } + while (sum & 0xffff0000ULL) { sum = (sum >> 16) + (sum & 0xffffULL); + } - return ~sum; + return static_cast<__be16>(~sum); } + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + [[nodiscard]] uint64_t XskPacket::tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept { struct header @@ -699,6 +891,7 @@ void XskPacket::rewrite() noexcept pseudo_header.fields.length = htons(len); return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); } + [[nodiscard]] uint64_t XskPacket::tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept { struct header @@ -730,22 +923,25 @@ void XskPacket::rewrite() noexcept pseudo_header.fields.next_header = protocol; return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); } + void XskPacket::setHeader(const PacketBuffer& buf) { memcpy(frame, buf.data(), buf.size()); - payloadEnd = frame + buf.size(); + frameLength = buf.size(); flags = 0; - if (!parse()) { + if (!parse(true)) { throw std::runtime_error("Error setting the XSK frame header"); } } + std::unique_ptr XskPacket::cloneHeadertoPacketBuffer() const { - const auto size = payload - frame; + const auto size = getFrameLen() - getDataSize(); auto tmp = std::make_unique(size); memcpy(tmp->data(), frame, size); return tmp; } + int XskWorker::createEventfd() { auto fd = ::eventfd(0, EFD_CLOEXEC); @@ -754,10 +950,12 @@ int XskWorker::createEventfd() } return fd; } + void XskWorker::waitForXskSocket() noexcept { uint64_t x = read(workerWaker, &x, sizeof(x)); } + void XskWorker::notifyXskSocket() noexcept { notify(xskSocketWaker); @@ -767,7 +965,8 @@ std::shared_ptr XskWorker::create() { return std::make_shared(); } -void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP) + +void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest) { extern std::atomic g_configurationDone; if (g_configurationDone) { @@ -792,14 +991,17 @@ void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest .events = POLLIN, .revents = 0}); }; + uint64_t XskWorker::frameOffset(const XskPacket& s) const noexcept { return s.frame - umemBufBase; } + void XskWorker::notifyWorker() noexcept { notify(workerWaker); } + void XskSocket::getMACFromIfName() { ifreq ifr{}; @@ -819,19 +1021,23 @@ void XskSocket::getMACFromIfName() static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= std::tuple_size{}, "The size of an ARPHRD_ETHER MAC address is smaller than expected"); memcpy(source.data(), ifr.ifr_hwaddr.sa_data, source.size()); } + [[nodiscard]] int XskSocket::timeDifference(const timespec& t1, const timespec& t2) noexcept { const auto res = t1.tv_sec * 1000 + t1.tv_nsec / 1000000L - (t2.tv_sec * 1000 + t2.tv_nsec / 1000000L); return static_cast(res); } + void XskWorker::cleanWorkerNotification() noexcept { uint64_t x = read(xskSocketWaker, &x, sizeof(x)); } + void XskWorker::cleanSocketNotification() noexcept { uint64_t x = read(workerWaker, &x, sizeof(x)); } + std::vector getPollFdsForWorker(XskWorker& info) { std::vector fds; @@ -851,33 +1057,49 @@ std::vector getPollFdsForWorker(XskWorker& info) }); return fds; } + void XskWorker::fillUniqueEmptyOffset() { auto frames = sharedEmptyFrameOffset->lock(); const auto moveSize = std::min(static_cast(32), frames->size()); if (moveSize > 0) { uniqueEmptyFrameOffset.insert(uniqueEmptyFrameOffset.end(), std::make_move_iterator(frames->end() - moveSize), std::make_move_iterator(frames->end())); + frames->resize(frames->size() - moveSize); } } -void* XskWorker::getEmptyframe() + +XskPacketPtr XskWorker::getEmptyFrame() { if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); - return offset + umemBufBase; + return std::make_unique(offset + umemBufBase, 0, frameSize); } fillUniqueEmptyOffset(); if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); - return offset + umemBufBase; + return std::make_unique(offset + umemBufBase, 0, frameSize); } return nullptr; } + +void XskWorker::markAsFree(XskPacketPtr&& packet) +{ + + auto offset = frameOffset(*packet); +#ifdef DEBUG_UMEM + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); +#endif /* DEBUG_UMEM */ + uniqueEmptyFrameOffset.push_back(offset); + packet.release(); +} + uint32_t XskPacket::getFlags() const noexcept { return flags; } + void XskPacket::updatePacket() noexcept { if (!(flags & UPDATE)) { diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 671557109112..551c23074c94 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -30,15 +30,17 @@ #include #include #include +#include #include #include #include -#include -#include +//#include #include #include #include +#include #include +#include #include @@ -59,8 +61,14 @@ using XskPacketPtr = std::unique_ptr; // We use an XskSocket to manage an AF_XDP Socket corresponding to a NIC queue. // The XDP program running in the kernel redirects the data to the XskSocket in userspace. +// We allocate frames that are placed into the descriptors in the fill queue, allowing the kernel to put incoming packets into the frames and place descriptors into the rx queue. +// Once we have read the descriptors from the rx queue we release them, but we own the frames. +// After we are done with the frame, we place them into descriptors of either the fill queue (empty frames) or tx queues (packets to be sent). +// Once the kernel is done, it places descriptors referencing these frames into the cq where we can recycle them (packets destined to the tx queue or empty frame to the fill queue queue). + // XskSocket routes packets to multiple worker threads registered on XskSocket via XskSocket::addWorker based on the destination port number of the packet. // The kernel and the worker thread holding XskWorker will wake up the XskSocket through XskFd and the Eventfd corresponding to each worker thread, respectively. + class XskSocket { struct XskRouteInfo @@ -85,7 +93,9 @@ class XskSocket boost::multi_index::hashed_unique>, boost::multi_index::hashed_unique, ComboAddress::addressPortOnlyHash>>> workers; + // number of frames to keep in sharedEmptyFrameOffset static constexpr size_t holdThreshold = 256; + // number of frames to insert into the fill queue static constexpr size_t fillThreshold = 128; static constexpr size_t frameSize = 2048; // number of entries (frames) in the umem @@ -98,7 +108,10 @@ class XskSocket const std::string poolName; // AF_XDP socket then worker waker sockets vector fds; - // list of (indexes of) umem entries that can be reused + // list of frames, aka (indexes of) umem entries that can be reused to fill fq, + // collected from packets that we could not route (unknown destination), + // could not parse, were dropped during processing (!UPDATE), or + // simply recycled from cq after being processed by the kernel vector uniqueEmptyFrameOffset; // completion ring: queue where sent packets are stored by the kernel xsk_ring_cons cq; @@ -122,119 +135,152 @@ class XskSocket friend void XskRouter(std::shared_ptr xsk); [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; - int firstTimeout(); - // pick ups as many available frames as possible from uniqueEmptyFrameOffset - // and put them into sharedEmptyFrameOffset - // then insert them into fq + [[nodiscard]] int firstTimeout(); + // pick ups available frames from uniqueEmptyFrameOffset + // insert entries from uniqueEmptyFrameOffset into fq void fillFq(uint32_t fillSize = fillThreshold) noexcept; - // picks up entries that have been processed (sent) and push them into uniqueEmptyFrameOffset + // picks up entries that have been processed (sent) from cq and push them into uniqueEmptyFrameOffset void recycle(size_t size) noexcept; void getMACFromIfName(); // look at delayed packets, and send the ones that are ready void pickUpReadyPacket(std::vector& packets); public: + static constexpr size_t getFrameSize() + { + return frameSize; + } // list of free umem entries that can be reused std::shared_ptr>> sharedEmptyFrameOffset; XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_); MACAddr source; [[nodiscard]] int xskFd() const noexcept; // wait until one event has occurred - int wait(int timeout); + [[nodiscard]] int wait(int timeout); // add as many packets as possible to the rx queue for sending */ void send(std::vector& packets); // look at incoming packets in rx, return them if parsing succeeeded - std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); - void addWorker(std::shared_ptr s, const ComboAddress& dest, bool isTCP); - std::string getMetrics() const; + [[nodiscard]] std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); + void addWorker(std::shared_ptr s, const ComboAddress& dest); + [[nodiscard]] std::string getMetrics() const; + void markAsFree(XskPacketPtr&& packet); }; + +struct iphdr; +struct ipv6hdr; + class XskPacket { public: enum Flags : uint32_t { - TCP = 1 << 0, - UPDATE = 1 << 1, - DELAY = 1 << 3, - REWRITE = 1 << 4 + UPDATE = 1 << 0, + DELAY = 1 << 1, + REWRITE = 1 << 2 }; private: ComboAddress from; ComboAddress to; timespec sendTime; - uint8_t* frame; - uint8_t* l4Header; - uint8_t* payload; - uint8_t* payloadEnd; - uint8_t* frameEnd; + uint8_t* frame{nullptr}; + size_t frameLength{0}; + size_t frameSize{0}; uint32_t flags{0}; + bool v6{false}; + + // You must set ipHeader.check = 0 before calling this method + [[nodiscard]] static __be16 ipv4Checksum(const struct iphdr*) noexcept; + [[nodiscard]] static uint64_t ip_checksum_partial(const void* p, size_t len, uint64_t sum) noexcept; + [[nodiscard]] static __be16 ip_checksum_fold(uint64_t sum) noexcept; + [[nodiscard]] static uint64_t tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept; + [[nodiscard]] static uint64_t tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept; + static void rewriteIpv4Header(struct iphdr* ipv4header, size_t frameLen) noexcept; + static void rewriteIpv6Header(struct ipv6hdr* ipv6header, size_t frameLen) noexcept; + + // You must set l4Header.check = 0 before calling this method + // ip options is not supported + [[nodiscard]] __be16 tcp_udp_v4_checksum(const struct iphdr*) const noexcept; + // You must set l4Header.check = 0 before calling this method + [[nodiscard]] __be16 tcp_udp_v6_checksum(const struct ipv6hdr*) const noexcept; + /* offset of the L4 (udphdr) header (after ethhdr and iphdr/ipv6hdr) */ + [[nodiscard]] size_t getL4HeaderOffset() const noexcept; + /* offset of the data after the UDP header */ + [[nodiscard]] size_t getDataOffset() const noexcept; + [[nodiscard]] size_t getDataSize() const noexcept; + [[nodiscard]] ethhdr getEthernetHeader() const noexcept; + void setEthernetHeader(const ethhdr& ethHeader) noexcept; + [[nodiscard]] iphdr getIPv4Header() const noexcept; + void setIPv4Header(const iphdr& ipv4Header) noexcept; + [[nodiscard]] ipv6hdr getIPv6Header() const noexcept; + void setIPv6Header(const ipv6hdr& ipv6Header) noexcept; + [[nodiscard]] udphdr getUDPHeader() const noexcept; + void setUDPHeader(const udphdr& udpHeader) noexcept; + // parse IP and UDP payloads + bool parse(bool fromSetHeader); + void changeDirectAndUpdateChecksum() noexcept; friend XskSocket; friend XskWorker; friend bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; constexpr static uint8_t DefaultTTL = 64; - // parse IP and UDP/TCP payloads - bool parse(); - void changeDirectAndUpdateChecksum() noexcept; - - // You must set ipHeader.check = 0 before call this method - [[nodiscard]] __be16 ipv4Checksum() const noexcept; - // You must set l4Header.check = 0 before call this method - // ip options is not supported - [[nodiscard]] __be16 tcp_udp_v4_checksum() const noexcept; - // You must set l4Header.check = 0 before call this method - [[nodiscard]] __be16 tcp_udp_v6_checksum() const noexcept; - [[nodiscard]] static uint64_t ip_checksum_partial(const void* p, size_t len, uint64_t sum) noexcept; - [[nodiscard]] static __be16 ip_checksum_fold(uint64_t sum) noexcept; - [[nodiscard]] static uint64_t tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept; - [[nodiscard]] static uint64_t tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept; - void rewriteIpv4Header(void* ipv4header) noexcept; - void rewriteIpv6Header(void* ipv6header) noexcept; public: [[nodiscard]] const ComboAddress& getFromAddr() const noexcept; [[nodiscard]] const ComboAddress& getToAddr() const noexcept; - [[nodiscard]] const void* payloadData() const; + [[nodiscard]] const void* getPayloadData() const; [[nodiscard]] bool isIPV6() const noexcept; - [[nodiscard]] size_t capacity() const noexcept; - [[nodiscard]] uint32_t dataLen() const noexcept; - [[nodiscard]] uint32_t FrameLen() const noexcept; + [[nodiscard]] size_t getCapacity() const noexcept; + [[nodiscard]] uint32_t getDataLen() const noexcept; + [[nodiscard]] uint32_t getFrameLen() const noexcept; [[nodiscard]] PacketBuffer clonePacketBuffer() const; void cloneIntoPacketBuffer(PacketBuffer& buffer) const; [[nodiscard]] std::unique_ptr cloneHeadertoPacketBuffer() const; - [[nodiscard]] void* payloadData(); - void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC, bool tcp = false) noexcept; + void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept; bool setPayload(const PacketBuffer& buf); void rewrite() noexcept; void setHeader(const PacketBuffer& buf); - XskPacket() = default; - XskPacket(void* frame, size_t dataSize, size_t frameSize); + XskPacket(uint8_t* frame, size_t dataSize, size_t frameSize); void addDelay(int relativeMilliseconds) noexcept; void updatePacket() noexcept; [[nodiscard]] uint32_t getFlags() const noexcept; }; bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; +/* g++ defines __SANITIZE_THREAD__ + clang++ supports the nice __has_feature(thread_sanitizer), + let's merge them */ +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define __SANITIZE_THREAD__ 1 +#endif +#endif + // XskWorker obtains XskPackets of specific ports in the NIC from XskSocket through cq. // After finishing processing the packet, XskWorker puts the packet into sq so that XskSocket decides whether to send it through the network card according to XskPacket::flags. // XskWorker wakes up XskSocket via xskSocketWaker after putting the packets in sq. class XskWorker { - using XskPacketRing = boost::lockfree::spsc_queue>; +#if defined(__SANITIZE_THREAD__) + using XskPacketRing = LockGuarded>>; +#else + using XskPacketRing = boost::lockfree::spsc_queue>; +#endif public: // queue of packets to be processed by this worker - XskPacketRing cq; + XskPacketRing incomingPacketsQueue; // queue of packets processed by this worker (to be sent, or discarded) - XskPacketRing sq; + XskPacketRing outgoingPacketsQueue; uint8_t* umemBufBase; + // list of frames that are shared with the XskRouter std::shared_ptr>> sharedEmptyFrameOffset; + // list of frames that we own, used to generate new packets (health-check) vector uniqueEmptyFrameOffset; std::string poolName; - size_t frameSize; + const size_t frameSize{XskSocket::getFrameSize()}; FDWrapper workerWaker; FDWrapper xskSocketWaker; @@ -244,6 +290,7 @@ public: static std::shared_ptr create(); void pushToProcessingQueue(XskPacketPtr&& packet); void pushToSendQueue(XskPacketPtr&& packet); + void markAsFree(XskPacketPtr&& packet); // notify worker that at least one packet is available for processing void notifyWorker() noexcept; // notify the router that packets are ready to be sent @@ -252,11 +299,11 @@ public: void cleanWorkerNotification() noexcept; void cleanSocketNotification() noexcept; [[nodiscard]] uint64_t frameOffset(const XskPacket& s) const noexcept; - // reap empty umeme entry from sharedEmptyFrameOffset into uniqueEmptyFrameOffset + // reap empty umem entry from sharedEmptyFrameOffset into uniqueEmptyFrameOffset void fillUniqueEmptyOffset(); // look for an empty umem entry in uniqueEmptyFrameOffset // then sharedEmptyFrameOffset if needed - void* getEmptyframe(); + XskPacketPtr getEmptyFrame(); }; std::vector getPollFdsForWorker(XskWorker& info); #else From ea247879c44e957d91f4c22595810f4bef491be7 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 28 Dec 2023 15:18:44 +0100 Subject: [PATCH 29/65] dnsdist: Refactor the XSK code into a proper namespace --- pdns/dnsdist.cc | 550 ++++++++++++++++++++++++------------------------ pdns/xsk.cc | 14 +- pdns/xsk.hh | 54 +++-- 3 files changed, 320 insertions(+), 298 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index f3e840814364..fb1c0e437975 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -776,27 +776,6 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } } -#ifdef HAVE_XSK -static void XskHealthCheck(std::shared_ptr& dss, std::unordered_map>& map, bool initial = false) -{ - auto& xskInfo = dss->xskInfo; - std::shared_ptr data; - auto packet = getHealthCheckPacket(dss, nullptr, data); - data->d_initial = initial; - setHealthCheckTime(dss, data); - auto xskPacket = xskInfo->getEmptyFrame(); - if (!xskPacket) { - return; - } - xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); - xskPacket->setPayload(packet); - xskPacket->rewrite(); - xskInfo->pushToSendQueue(std::move(xskPacket)); - const auto queryId = data->d_queryID; - map[queryId] = std::move(data); -} -#endif /* HAVE_XSK */ - static bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) { @@ -833,135 +812,267 @@ static bool processResponderPacket(std::shared_ptr& dss, Packet return true; } -// listens on a dedicated socket, lobs answers from downstream servers to original requestors +#ifdef HAVE_XSK +namespace dnsdist::xsk +{ +static void doHealthCheck(std::shared_ptr& dss, std::unordered_map>& map, bool initial = false) +{ + auto& xskInfo = dss->xskInfo; + std::shared_ptr data; + auto packet = getHealthCheckPacket(dss, nullptr, data); + data->d_initial = initial; + setHealthCheckTime(dss, data); + auto xskPacket = xskInfo->getEmptyFrame(); + if (!xskPacket) { + return; + } + xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); + xskPacket->setPayload(packet); + xskPacket->rewrite(); + xskInfo->pushToSendQueue(std::move(xskPacket)); + const auto queryId = data->d_queryID; + map[queryId] = std::move(data); +} + void responderThread(std::shared_ptr dss) { + if (dss->xskInfo == nullptr) { + throw std::runtime_error("Starting XSK responder thread for a backend without XSK!"); + } + try { - setThreadName("dnsdist/respond"); - auto localRespRuleActions = g_respruleactions.getLocal(); - auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); -#ifdef HAVE_XSK - if (dss->xskInfo) { - auto xskInfo = dss->xskInfo; - auto pollfds = getPollFdsForWorker(*xskInfo); - std::unordered_map> healthCheckMap; - XskHealthCheck(dss, healthCheckMap, true); - itimerspec tm; - tm.it_value.tv_sec = dss->d_config.checkTimeout / 1000; - tm.it_value.tv_nsec = (dss->d_config.checkTimeout % 1000) * 1000000; - tm.it_interval = tm.it_value; - auto res = timerfd_settime(pollfds[1].fd, 0, &tm, nullptr); - if (res) { - throw std::runtime_error("timerfd_settime failed:" + stringerror(errno)); - } - const auto xskFd = xskInfo->workerWaker.getHandle(); - while (!dss->isStopped()) { - poll(pollfds.data(), pollfds.size(), -1); - bool needNotify = false; - if (pollfds[0].revents & POLLIN) { - needNotify = true; + setThreadName("dnsdist/XskResp"); + auto localRespRuleActions = g_respruleactions.getLocal(); + auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); + auto xskInfo = dss->xskInfo; + auto pollfds = getPollFdsForWorker(*xskInfo); + std::unordered_map> healthCheckMap; + dnsdist::xsk::doHealthCheck(dss, healthCheckMap, true); + itimerspec tm; + tm.it_value.tv_sec = dss->d_config.checkTimeout / 1000; + tm.it_value.tv_nsec = (dss->d_config.checkTimeout % 1000) * 1000000; + tm.it_interval = tm.it_value; + auto res = timerfd_settime(pollfds[1].fd, 0, &tm, nullptr); + if (res) { + throw std::runtime_error("timerfd_settime failed:" + stringerror(errno)); + } + const auto xskFd = xskInfo->workerWaker.getHandle(); + while (!dss->isStopped()) { + poll(pollfds.data(), pollfds.size(), -1); + bool needNotify = false; + if (pollfds[0].revents & POLLIN) { + needNotify = true; #if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { #else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { #endif - auto packet = XskPacketPtr(packetRaw); - if (packet->getDataLen() < sizeof(dnsheader)) { - xskInfo->markAsFree(std::move(packet)); - return; - } - const dnsheader_aligned dnsHeader(packet->getPayloadData()); - const auto queryId = dnsHeader->id; - auto ids = dss->getState(queryId); - if (ids) { - if (xskFd != ids->backendFD || !ids->xskPacketHeader) { - dss->restoreState(queryId, std::move(*ids)); - ids = std::nullopt; - } - } - if (!ids) { - // this has to go before we can refactor the duplicated response handling code - auto iter = healthCheckMap.find(queryId); - if (iter != healthCheckMap.end()) { - auto data = std::move(iter->second); - healthCheckMap.erase(iter); - packet->cloneIntoPacketBuffer(data->d_buffer); - data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); - } - xskInfo->markAsFree(std::move(packet)); - return; - } - auto response = packet->clonePacketBuffer(); - if (response.size() > packet->getCapacity()) { - /* fallback to sending the packet via normal socket */ - ids->xskPacketHeader.reset(); - } - if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { - xskInfo->markAsFree(std::move(packet)); - vinfolog("XSK packet pushed to queue because processResponderPacket failed"); - return; - } - vinfolog("XSK packet - processResponderPacket OK"); - if (response.size() > packet->getCapacity()) { - /* fallback to sending the packet via normal socket */ - sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); - vinfolog("XSK packet falling back because packet is too large"); - xskInfo->markAsFree(std::move(packet)); - return; - } - //vinfolog("XSK packet - set header"); - packet->setHeader(*ids->xskPacketHeader); - //vinfolog("XSK packet - set payload"); - if (!packet->setPayload(response)) { - vinfolog("Unable to set payload !"); - } - if (ids->delayMsec > 0) { - vinfolog("XSK packet - adding delay"); - packet->addDelay(ids->delayMsec); - } - //vinfolog("XSK packet - update packet"); - packet->updatePacket(); - //vinfolog("XSK packet pushed to send queue"); - xskInfo->pushToSendQueue(std::move(packet)); - }); - xskInfo->cleanSocketNotification(); - } - if (pollfds[1].revents & POLLIN) { - timeval now; - gettimeofday(&now, nullptr); - for (auto i = healthCheckMap.begin(); i != healthCheckMap.end();) { - auto& ttd = i->second->d_ttd; - if (ttd < now) { - dss->submitHealthCheckResult(i->second->d_initial, false); - i = healthCheckMap.erase(i); - } - else { - ++i; + auto packet = XskPacketPtr(packetRaw); + if (packet->getDataLen() < sizeof(dnsheader)) { + xskInfo->markAsFree(std::move(packet)); + return; + } + const dnsheader_aligned dnsHeader(packet->getPayloadData()); + const auto queryId = dnsHeader->id; + auto ids = dss->getState(queryId); + if (ids) { + if (xskFd != ids->backendFD || !ids->xskPacketHeader) { + dss->restoreState(queryId, std::move(*ids)); + ids = std::nullopt; } } - needNotify = true; - dss->updateStatisticsInfo(); - dss->handleUDPTimeouts(); - if (dss->d_nextCheck <= 1) { - dss->d_nextCheck = dss->d_config.checkInterval; - if (dss->d_config.availability == DownstreamState::Availability::Auto) { - XskHealthCheck(dss, healthCheckMap); + if (!ids) { + // this has to go before we can refactor the duplicated response handling code + auto iter = healthCheckMap.find(queryId); + if (iter != healthCheckMap.end()) { + auto data = std::move(iter->second); + healthCheckMap.erase(iter); + packet->cloneIntoPacketBuffer(data->d_buffer); + data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } + xskInfo->markAsFree(std::move(packet)); + return; + } + auto response = packet->clonePacketBuffer(); + if (response.size() > packet->getCapacity()) { + /* fallback to sending the packet via normal socket */ + ids->xskPacketHeader.reset(); + } + if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { + xskInfo->markAsFree(std::move(packet)); + vinfolog("XSK packet pushed to queue because processResponderPacket failed"); + return; + } + vinfolog("XSK packet - processResponderPacket OK"); + if (response.size() > packet->getCapacity()) { + /* fallback to sending the packet via normal socket */ + sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); + vinfolog("XSK packet falling back because packet is too large"); + xskInfo->markAsFree(std::move(packet)); + return; + } + //vinfolog("XSK packet - set header"); + packet->setHeader(*ids->xskPacketHeader); + //vinfolog("XSK packet - set payload"); + if (!packet->setPayload(response)) { + vinfolog("Unable to set payload !"); + } + if (ids->delayMsec > 0) { + vinfolog("XSK packet - adding delay"); + packet->addDelay(ids->delayMsec); + } + //vinfolog("XSK packet - update packet"); + packet->updatePacket(); + //vinfolog("XSK packet pushed to send queue"); + xskInfo->pushToSendQueue(std::move(packet)); + }); + xskInfo->cleanSocketNotification(); + } + if (pollfds[1].revents & POLLIN) { + timeval now; + gettimeofday(&now, nullptr); + for (auto i = healthCheckMap.begin(); i != healthCheckMap.end();) { + auto& ttd = i->second->d_ttd; + if (ttd < now) { + dss->submitHealthCheckResult(i->second->d_initial, false); + i = healthCheckMap.erase(i); } else { - --dss->d_nextCheck; + ++i; + } + } + needNotify = true; + dss->updateStatisticsInfo(); + dss->handleUDPTimeouts(); + if (dss->d_nextCheck <= 1) { + dss->d_nextCheck = dss->d_config.checkInterval; + if (dss->d_config.availability == DownstreamState::Availability::Auto) { + doHealthCheck(dss, healthCheckMap); } + } + else { + --dss->d_nextCheck; + } + + uint64_t tmp; + res = read(pollfds[1].fd, &tmp, sizeof(tmp)); + } + if (needNotify) { + xskInfo->notifyXskSocket(); + } + } + } + catch (const std::exception& e) { + errlog("XSK responder thread died because of exception: %s", e.what()); + } + catch (const PDNSException& e) { + errlog("XSK responder thread died because of PowerDNS exception: %s", e.reason); + } + catch (...) { + errlog("XSK responder thread died because of an exception: %s", "unknown"); + } +} + +static bool isXskQueryAcceptable(const XskPacket& packet, ClientState& cs, LocalHolders& holders, bool& expectProxyProtocol) noexcept +{ + const auto& from = packet.getFromAddr(); + expectProxyProtocol = expectProxyProtocolFrom(from); + if (!holders.acl->match(from) && !expectProxyProtocol) { + vinfolog("Query from %s dropped because of ACL", from.toStringWithPort()); + ++dnsdist::metrics::g_stats.aclDrops; + return false; + } + cs.queries++; + ++dnsdist::metrics::g_stats.queries; - uint64_t tmp; - res = read(pollfds[1].fd, &tmp, sizeof(tmp)); + return true; +} + +void XskRouter(std::shared_ptr xsk) +{ + setThreadName("dnsdist/XskRouter"); + uint32_t failed; + // packets to be submitted for sending + vector fillInTx; + const auto& fds = xsk->getDescriptors(); + // list of workers that need to be notified + std::set needNotify; + const auto& xskWakerIdx = xsk->getWorkers().get<0>(); + const auto& destIdx = xsk->getWorkers().get<1>(); + while (true) { + try { + auto ready = xsk->wait(-1); + // descriptor 0 gets incoming AF_XDP packets + if (fds.at(0).revents & POLLIN) { + auto packets = xsk->recv(64, &failed); + dnsdist::metrics::g_stats.nonCompliantQueries += failed; + for (auto &packet : packets) { + const auto dest = packet->getToAddr(); + auto res = destIdx.find(dest); + if (res == destIdx.end()) { + xsk->markAsFree(std::move(packet)); + continue; + } + res->worker->pushToProcessingQueue(std::move(packet)); + needNotify.insert(res->workerWaker); } - if (needNotify) { - xskInfo->notifyXskSocket(); + for (auto i : needNotify) { + uint64_t x = 1; + auto written = write(i, &x, sizeof(x)); + if (written != sizeof(x)) { + // oh, well, the worker is clearly overloaded + // but there is nothing we can do about it, + // and hopefully the queue will be processed eventually + } + } + needNotify.clear(); + ready--; + } + const auto backup = ready; + for (size_t fdIndex = 1; fdIndex < fds.size() && ready > 0; fdIndex++) { + if (fds.at(fdIndex).revents & POLLIN) { + ready--; + auto& info = xskWakerIdx.find(fds.at(fdIndex).fd)->worker; +#if defined(__SANITIZE_THREAD__) + info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { +#else + info->outgoingPacketsQueue.consume_all([&](XskPacket* packetRaw) { +#endif + auto packet = XskPacketPtr(packetRaw); + if (!(packet->getFlags() & XskPacket::UPDATE)) { + xsk->markAsFree(std::move(packet)); + return; + } + if (packet->getFlags() & XskPacket::DELAY) { + xsk->pushDelayed(std::move(packet)); + return; + } + fillInTx.push_back(std::move(packet)); + }); + info->cleanWorkerNotification(); } } + xsk->pickUpReadyPacket(fillInTx); + xsk->recycle(4096); + xsk->fillFq(); + xsk->send(fillInTx); + ready = backup; } - else { + catch (...) { + vinfolog("Exception in XSK router loop"); + } + } +} +} #endif /* HAVE_XSK */ + +// listens on a dedicated socket, lobs answers from downstream servers to original requestors +void responderThread(std::shared_ptr dss) +{ + try { + setThreadName("dnsdist/respond"); + auto localRespRuleActions = g_respruleactions.getLocal(); + auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); const size_t initialBufferSize = getInitialUDPPacketBufferSize(false); /* allocate one more byte so we can detect truncation */ PacketBuffer response(initialBufferSize + 1); @@ -1045,9 +1156,6 @@ void responderThread(std::shared_ptr dss) vinfolog("Got an error in UDP responder thread while parsing a response from %s, id %d: %s", dss->d_config.remote.toStringWithPort(), queryId, e.what()); } } -#ifdef HAVE_XSK - } -#endif /* HAVE_XSK */ } catch (const std::exception& e) { errlog("UDP responder thread died because of exception: %s", e.what()); @@ -1467,23 +1575,6 @@ static bool isUDPQueryAcceptable(ClientState& cs, LocalHolders& holders, const s return true; } -#ifdef HAVE_XSK -static bool isXskQueryAcceptable(const XskPacket& packet, ClientState& cs, LocalHolders& holders, bool& expectProxyProtocol) noexcept -{ - const auto& from = packet.getFromAddr(); - expectProxyProtocol = expectProxyProtocolFrom(from); - if (!holders.acl->match(from) && !expectProxyProtocol) { - vinfolog("Query from %s dropped because of ACL", from.toStringWithPort()); - ++dnsdist::metrics::g_stats.aclDrops; - return false; - } - cs.queries++; - ++dnsdist::metrics::g_stats.queries; - - return true; -} -#endif /* HAVE_XSK */ - bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr& dnsCryptQuery, time_t now, bool tcp) { if (cs.dnscryptCtx) { @@ -2054,6 +2145,8 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct } #ifdef HAVE_XSK +namespace dnsdist::xsk +{ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) { uint16_t queryId = 0; @@ -2175,6 +2268,39 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p } return false; } + +static void xskClientThread(ClientState* cs) +{ + setThreadName("dnsdist/xskClient"); + auto xskInfo = cs->xskInfo; + LocalHolders holders; + + for (;;) { +#if defined(__SANITIZE_THREAD__) + while (!xskInfo->incomingPacketsQueue.lock()->read_available()) { +#else + while (!xskInfo->incomingPacketsQueue.read_available()) { +#endif + xskInfo->waitForXskSocket(); + } +#if defined(__SANITIZE_THREAD__) + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { +#else + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { +#endif + auto packet = XskPacketPtr(packetRaw); + if (ProcessXskQuery(*cs, holders, *packet)) { + packet->updatePacket(); + xskInfo->pushToSendQueue(std::move(packet)); + } + else { + xskInfo->markAsFree(std::move(packet)); + } + }); + xskInfo->notifyXskSocket(); + } +} +} #endif /* HAVE_XSK */ #ifndef DISABLE_RECVMMSG @@ -2269,40 +2395,6 @@ static void MultipleMessagesUDPClientThread(ClientState* cs, LocalHolders& holde #endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */ #endif /* DISABLE_RECVMMSG */ -#ifdef HAVE_XSK -static void xskClientThread(ClientState* cs) -{ - setThreadName("dnsdist/xskClient"); - auto xskInfo = cs->xskInfo; - LocalHolders holders; - - for (;;) { -#if defined(__SANITIZE_THREAD__) - while (!xskInfo->incomingPacketsQueue.lock()->read_available()) { -#else - while (!xskInfo->incomingPacketsQueue.read_available()) { -#endif - xskInfo->waitForXskSocket(); - } -#if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { -#else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { -#endif - auto packet = XskPacketPtr(packetRaw); - if (ProcessXskQuery(*cs, holders, *packet)) { - packet->updatePacket(); - xskInfo->pushToSendQueue(std::move(packet)); - } - else { - xskInfo->markAsFree(std::move(packet)); - } - }); - xskInfo->notifyXskSocket(); - } -} -#endif /* HAVE_XSK */ - // listens to incoming queries, sends out to downstream servers, noting the intended return path static void udpClientThread(std::vector states) { @@ -3282,17 +3374,13 @@ static void initFrontends() } } -#ifdef HAVE_XSK -void XskRouter(std::shared_ptr xsk); -#endif /* HAVE_XSK */ - namespace dnsdist { static void startFrontends() { #ifdef HAVE_XSK for (auto& xskContext : g_xsk) { - std::thread xskThread(XskRouter, std::move(xskContext)); + std::thread xskThread(dnsdist::xsk::XskRouter, std::move(xskContext)); xskThread.detach(); } #endif /* HAVE_XSK */ @@ -3302,7 +3390,7 @@ static void startFrontends() for (auto& clientState : g_frontends) { #ifdef HAVE_XSK if (clientState->xskInfo) { - std::thread xskCT(xskClientThread, clientState.get()); + std::thread xskCT(dnsdist::xsk::xskClientThread, clientState.get()); if (!clientState->cpus.empty()) { mapThreadToCPUList(xskCT.native_handle(), clientState->cpus); } @@ -3671,81 +3759,3 @@ int main(int argc, char** argv) #endif } } - -#ifdef HAVE_XSK -void XskRouter(std::shared_ptr xsk) -{ - setThreadName("dnsdist/XskRouter"); - uint32_t failed; - // packets to be submitted for sending - vector fillInTx; - const auto size = xsk->fds.size(); - // list of workers that need to be notified - std::set needNotify; - const auto& xskWakerIdx = xsk->workers.get<0>(); - const auto& destIdx = xsk->workers.get<1>(); - while (true) { - try { - auto ready = xsk->wait(-1); - // descriptor 0 gets incoming AF_XDP packets - if (xsk->fds[0].revents & POLLIN) { - auto packets = xsk->recv(64, &failed); - dnsdist::metrics::g_stats.nonCompliantQueries += failed; - for (auto &packet : packets) { - const auto dest = packet->getToAddr(); - auto res = destIdx.find(dest); - if (res == destIdx.end()) { - xsk->markAsFree(std::move(packet)); - continue; - } - res->worker->pushToProcessingQueue(std::move(packet)); - needNotify.insert(res->workerWaker); - } - for (auto i : needNotify) { - uint64_t x = 1; - auto written = write(i, &x, sizeof(x)); - if (written != sizeof(x)) { - // oh, well, the worker is clearly overloaded - // but there is nothing we can do about it, - // and hopefully the queue will be processed eventually - } - } - needNotify.clear(); - ready--; - } - const auto backup = ready; - for (size_t i = 1; i < size && ready > 0; i++) { - if (xsk->fds[i].revents & POLLIN) { - ready--; - auto& info = xskWakerIdx.find(xsk->fds[i].fd)->worker; -#if defined(__SANITIZE_THREAD__) - info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { -#else - info->outgoingPacketsQueue.consume_all([&](XskPacket* packetRaw) { -#endif - auto packet = XskPacketPtr(packetRaw); - if (!(packet->getFlags() & XskPacket::UPDATE)) { - xsk->markAsFree(std::move(packet)); - return; - } - if (packet->getFlags() & XskPacket::DELAY) { - xsk->waitForDelay.push(std::move(packet)); - return; - } - fillInTx.push_back(std::move(packet)); - }); - info->cleanWorkerNotification(); - } - } - xsk->pickUpReadyPacket(fillInTx); - xsk->recycle(4096); - xsk->fillFq(); - xsk->send(fillInTx); - ready = backup; - } - catch (...) { - vinfolog("Exception in XSK router loop"); - } - } -} -#endif /* HAVE_XSK */ diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 2f438f9fd9c1..5b3c2142dfe0 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -97,7 +97,7 @@ int XskSocket::firstTimeout() } timespec now; gettime(&now); - const auto& firstTime = waitForDelay.top()->sendTime; + const auto& firstTime = waitForDelay.top()->getSendTime(); const auto res = timeDifference(now, firstTime); if (res <= 0) { return 0; @@ -106,7 +106,7 @@ int XskSocket::firstTimeout() } XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_) : - frameNum(frameNum_), queueId(queue_id), ifName(ifName_), poolName(poolName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) + frameNum(frameNum_), ifName(ifName_), poolName(poolName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) { if (!isPowOfTwo(frameNum_) || !isPowOfTwo(frameSize) || !isPowOfTwo(fqCapacity) || !isPowOfTwo(cqCapacity) || !isPowOfTwo(rxCapacity) || !isPowOfTwo(txCapacity)) { @@ -219,7 +219,7 @@ int XskSocket::wait(int timeout) [[nodiscard]] uint64_t XskSocket::frameOffset(const XskPacket& packet) const noexcept { - return packet.frame - umem.bufBase; + return packet.getFrameOffsetFrom(umem.bufBase); } [[nodiscard]] int XskSocket::xskFd() const noexcept { @@ -314,7 +314,7 @@ void XskSocket::pickUpReadyPacket(std::vector& packets) { timespec now; gettime(&now); - while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top()->sendTime) <= 0) { + while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top()->getSendTime()) <= 0) { auto& top = const_cast(waitForDelay.top()); packets.push_back(std::move(top)); waitForDelay.pop(); @@ -676,7 +676,7 @@ void XskPacket::addDelay(const int relativeMilliseconds) noexcept bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept { - return s1->sendTime < s2->sendTime; + return s1->getSendTime() < s2->getSendTime(); } const ComboAddress& XskPacket::getFromAddr() const noexcept @@ -992,9 +992,9 @@ void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest .revents = 0}); }; -uint64_t XskWorker::frameOffset(const XskPacket& s) const noexcept +uint64_t XskWorker::frameOffset(const XskPacket& packet) const noexcept { - return s.frame - umemBufBase; + return packet.getFrameOffsetFrom(umemBufBase); } void XskWorker::notifyWorker() noexcept diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 551c23074c94..d8dc06756375 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -87,12 +87,12 @@ class XskSocket ~XskUmem(); XskUmem() = default; }; - boost::multi_index_container< + using WorkerContainer = boost::multi_index_container< XskRouteInfo, boost::multi_index::indexed_by< boost::multi_index::hashed_unique>, - boost::multi_index::hashed_unique, ComboAddress::addressPortOnlyHash>>> - workers; + boost::multi_index::hashed_unique, ComboAddress::addressPortOnlyHash>>>; + WorkerContainer workers; // number of frames to keep in sharedEmptyFrameOffset static constexpr size_t holdThreshold = 256; // number of frames to insert into the fill queue @@ -100,8 +100,6 @@ class XskSocket static constexpr size_t frameSize = 2048; // number of entries (frames) in the umem const size_t frameNum; - // ID of the network queue - const uint32_t queueId; // responses that have been delayed std::priority_queue waitForDelay; const std::string ifName; @@ -123,7 +121,6 @@ class XskSocket xsk_ring_prod tx; std::unique_ptr socket; XskUmem umem; - bpf_object* prog; static constexpr uint32_t fqCapacity = XSK_RING_PROD__DEFAULT_NUM_DESCS * 4; static constexpr uint32_t cqCapacity = XSK_RING_CONS__DEFAULT_NUM_DESCS * 4; @@ -132,18 +129,10 @@ class XskSocket constexpr static bool isPowOfTwo(uint32_t value) noexcept; [[nodiscard]] static int timeDifference(const timespec& t1, const timespec& t2) noexcept; - friend void XskRouter(std::shared_ptr xsk); [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; [[nodiscard]] int firstTimeout(); - // pick ups available frames from uniqueEmptyFrameOffset - // insert entries from uniqueEmptyFrameOffset into fq - void fillFq(uint32_t fillSize = fillThreshold) noexcept; - // picks up entries that have been processed (sent) from cq and push them into uniqueEmptyFrameOffset - void recycle(size_t size) noexcept; void getMACFromIfName(); - // look at delayed packets, and send the ones that are ready - void pickUpReadyPacket(std::vector& packets); public: static constexpr size_t getFrameSize() @@ -164,6 +153,25 @@ public: void addWorker(std::shared_ptr s, const ComboAddress& dest); [[nodiscard]] std::string getMetrics() const; void markAsFree(XskPacketPtr&& packet); + [[nodiscard]] WorkerContainer& getWorkers() + { + return workers; + } + [[nodiscard]] const std::vector& getDescriptors() const + { + return fds; + } + // pick ups available frames from uniqueEmptyFrameOffset + // insert entries from uniqueEmptyFrameOffset into fq + void fillFq(uint32_t fillSize = fillThreshold) noexcept; + // picks up entries that have been processed (sent) from cq and push them into uniqueEmptyFrameOffset + void recycle(size_t size) noexcept; + // look at delayed packets, and send the ones that are ready + void pickUpReadyPacket(std::vector& packets); + void pushDelayed(XskPacketPtr&& packet) + { + waitForDelay.push(std::move(packet)); + } }; struct iphdr; @@ -216,14 +224,8 @@ private: void setIPv6Header(const ipv6hdr& ipv6Header) noexcept; [[nodiscard]] udphdr getUDPHeader() const noexcept; void setUDPHeader(const udphdr& udpHeader) noexcept; - // parse IP and UDP payloads - bool parse(bool fromSetHeader); void changeDirectAndUpdateChecksum() noexcept; - friend XskSocket; - friend XskWorker; - friend bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; - constexpr static uint8_t DefaultTTL = 64; public: @@ -244,7 +246,17 @@ public: XskPacket(uint8_t* frame, size_t dataSize, size_t frameSize); void addDelay(int relativeMilliseconds) noexcept; void updatePacket() noexcept; + // parse IP and UDP payloads + bool parse(bool fromSetHeader); [[nodiscard]] uint32_t getFlags() const noexcept; + [[nodiscard]] timespec getSendTime() const noexcept + { + return sendTime; + } + [[nodiscard]] uint64_t getFrameOffsetFrom(const uint8_t* base) const noexcept + { + return frame - base; + } }; bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; @@ -298,7 +310,7 @@ public: void waitForXskSocket() noexcept; void cleanWorkerNotification() noexcept; void cleanSocketNotification() noexcept; - [[nodiscard]] uint64_t frameOffset(const XskPacket& s) const noexcept; + [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; // reap empty umem entry from sharedEmptyFrameOffset into uniqueEmptyFrameOffset void fillUniqueEmptyOffset(); // look for an empty umem entry in uniqueEmptyFrameOffset From ae61f00ab206e812791838ddd9fe798e146e6ac4 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 28 Dec 2023 16:20:40 +0100 Subject: [PATCH 30/65] dnsdist: Get rid of memory allocations in the XSK hot path --- pdns/dnsdist-idstate.hh | 13 ++++++- pdns/dnsdist.cc | 63 +++++++++++++++----------------- pdns/xsk.cc | 81 +++++++++++++++++++---------------------- pdns/xsk.hh | 30 +++++++-------- 4 files changed, 95 insertions(+), 92 deletions(-) diff --git a/pdns/dnsdist-idstate.hh b/pdns/dnsdist-idstate.hh index 49248a08c786..f83f9b71f520 100644 --- a/pdns/dnsdist-idstate.hh +++ b/pdns/dnsdist-idstate.hh @@ -120,6 +120,15 @@ struct InternalQueryState InternalQueryState(const InternalQueryState& orig) = delete; InternalQueryState& operator=(const InternalQueryState& orig) = delete; + bool isXSK() const noexcept + { +#ifdef HAVE_XSK + return !xskPacketHeader.empty(); +#else + return false; +#endif /* HAVE_XSK */ + } + boost::optional subnet{boost::none}; // 40 ComboAddress origRemote; // 28 ComboAddress origDest; // 28 @@ -129,12 +138,14 @@ struct InternalQueryState std::string poolName; // 24 StopWatch queryRealTime{true}; // 24 std::shared_ptr packetCache{nullptr}; // 16 +#ifdef HAVE_XSK + PacketBuffer xskPacketHeader; // 8 +#endif /* HAVE_XSK */ std::unique_ptr dnsCryptQuery{nullptr}; // 8 std::unique_ptr qTag{nullptr}; // 8 std::unique_ptr d_packet{nullptr}; // Initial packet, so we can restart the query from the response path if needed // 8 std::unique_ptr d_protoBufData{nullptr}; std::unique_ptr d_extendedError{nullptr}; - std::unique_ptr xskPacketHeader; // 8 boost::optional tempFailureTTL{boost::none}; // 8 ClientState* cs{nullptr}; // 8 std::unique_ptr du; // 8 diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index fb1c0e437975..a4dcd3f1bd18 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -750,7 +750,7 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } bool muted = true; - if (ids.cs && !ids.cs->muted && !ids.xskPacketHeader) { + if (ids.cs && !ids.cs->muted && !ids.isXSK()) { sendUDPResponse(ids.cs->udpFD, response, dr.ids.delayMsec, ids.hopLocal, ids.hopRemote); muted = false; } @@ -761,7 +761,7 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re vinfolog("Got answer from %s, relayed to %s (UDP), took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); } else { - if (!ids.xskPacketHeader) { + if (!ids.isXSK()) { vinfolog("Got answer from %s, NOT relayed to %s (UDP) since that frontend is muted, took %f us", ds->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff); } else { @@ -829,7 +829,7 @@ static void doHealthCheck(std::shared_ptr& dss, std::unordered_ xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); xskPacket->setPayload(packet); xskPacket->rewrite(); - xskInfo->pushToSendQueue(std::move(xskPacket)); + xskInfo->pushToSendQueue(std::move(*xskPacket)); const auto queryId = data->d_queryID; map[queryId] = std::move(data); } @@ -863,20 +863,19 @@ void responderThread(std::shared_ptr dss) if (pollfds[0].revents & POLLIN) { needNotify = true; #if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { #else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { #endif - auto packet = XskPacketPtr(packetRaw); - if (packet->getDataLen() < sizeof(dnsheader)) { + if (packet.getDataLen() < sizeof(dnsheader)) { xskInfo->markAsFree(std::move(packet)); return; } - const dnsheader_aligned dnsHeader(packet->getPayloadData()); + const dnsheader_aligned dnsHeader(packet.getPayloadData()); const auto queryId = dnsHeader->id; auto ids = dss->getState(queryId); if (ids) { - if (xskFd != ids->backendFD || !ids->xskPacketHeader) { + if (xskFd != ids->backendFD || !ids->isXSK()) { dss->restoreState(queryId, std::move(*ids)); ids = std::nullopt; } @@ -887,16 +886,16 @@ void responderThread(std::shared_ptr dss) if (iter != healthCheckMap.end()) { auto data = std::move(iter->second); healthCheckMap.erase(iter); - packet->cloneIntoPacketBuffer(data->d_buffer); + packet.cloneIntoPacketBuffer(data->d_buffer); data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } xskInfo->markAsFree(std::move(packet)); return; } - auto response = packet->clonePacketBuffer(); - if (response.size() > packet->getCapacity()) { + auto response = packet.clonePacketBuffer(); + if (response.size() > packet.getCapacity()) { /* fallback to sending the packet via normal socket */ - ids->xskPacketHeader.reset(); + ids->xskPacketHeader.clear(); } if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { xskInfo->markAsFree(std::move(packet)); @@ -904,7 +903,7 @@ void responderThread(std::shared_ptr dss) return; } vinfolog("XSK packet - processResponderPacket OK"); - if (response.size() > packet->getCapacity()) { + if (response.size() > packet.getCapacity()) { /* fallback to sending the packet via normal socket */ sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); vinfolog("XSK packet falling back because packet is too large"); @@ -912,17 +911,17 @@ void responderThread(std::shared_ptr dss) return; } //vinfolog("XSK packet - set header"); - packet->setHeader(*ids->xskPacketHeader); + packet.setHeader(ids->xskPacketHeader); //vinfolog("XSK packet - set payload"); - if (!packet->setPayload(response)) { + if (!packet.setPayload(response)) { vinfolog("Unable to set payload !"); } if (ids->delayMsec > 0) { vinfolog("XSK packet - adding delay"); - packet->addDelay(ids->delayMsec); + packet.addDelay(ids->delayMsec); } //vinfolog("XSK packet - update packet"); - packet->updatePacket(); + packet.updatePacket(); //vinfolog("XSK packet pushed to send queue"); xskInfo->pushToSendQueue(std::move(packet)); }); @@ -993,7 +992,7 @@ void XskRouter(std::shared_ptr xsk) setThreadName("dnsdist/XskRouter"); uint32_t failed; // packets to be submitted for sending - vector fillInTx; + vector fillInTx; const auto& fds = xsk->getDescriptors(); // list of workers that need to be notified std::set needNotify; @@ -1007,7 +1006,7 @@ void XskRouter(std::shared_ptr xsk) auto packets = xsk->recv(64, &failed); dnsdist::metrics::g_stats.nonCompliantQueries += failed; for (auto &packet : packets) { - const auto dest = packet->getToAddr(); + const auto dest = packet.getToAddr(); auto res = destIdx.find(dest); if (res == destIdx.end()) { xsk->markAsFree(std::move(packet)); @@ -1034,16 +1033,15 @@ void XskRouter(std::shared_ptr xsk) ready--; auto& info = xskWakerIdx.find(fds.at(fdIndex).fd)->worker; #if defined(__SANITIZE_THREAD__) - info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { + info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { #else - info->outgoingPacketsQueue.consume_all([&](XskPacket* packetRaw) { + info->outgoingPacketsQueue.consume_all([&](XskPacket& packet) { #endif - auto packet = XskPacketPtr(packetRaw); - if (!(packet->getFlags() & XskPacket::UPDATE)) { + if (!(packet.getFlags() & XskPacket::UPDATE)) { xsk->markAsFree(std::move(packet)); return; } - if (packet->getFlags() & XskPacket::DELAY) { + if (packet.getFlags() & XskPacket::DELAY) { xsk->pushDelayed(std::move(packet)); return; } @@ -1131,7 +1129,7 @@ void responderThread(std::shared_ptr dss) continue; } - if (processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids)) && ids->xskPacketHeader && ids->cs->xskInfo) { + if (processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfo) { #ifdef HAVE_XSK //vinfolog("processResponderPacket OK"); auto& xskInfo = ids->cs->xskInfo; @@ -1140,13 +1138,13 @@ void responderThread(std::shared_ptr dss) continue; } //vinfolog("XSK setHeader"); - xskPacket->setHeader(*ids->xskPacketHeader); + xskPacket->setHeader(ids->xskPacketHeader); //vinfolog("XSK payload"); xskPacket->setPayload(response); //vinfolog("XSK update packet"); xskPacket->updatePacket(); //vinfolog("XSK pushed to send queue"); - xskInfo->pushToSendQueue(std::move(xskPacket)); + xskInfo->pushToSendQueue(std::move(*xskPacket)); xskInfo->notifyXskSocket(); #endif /* HAVE_XSK */ } @@ -2284,13 +2282,12 @@ static void xskClientThread(ClientState* cs) xskInfo->waitForXskSocket(); } #if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { #else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket* packetRaw) { + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { #endif - auto packet = XskPacketPtr(packetRaw); - if (ProcessXskQuery(*cs, holders, *packet)) { - packet->updatePacket(); + if (ProcessXskQuery(*cs, holders, packet)) { + packet.updatePacket(); xskInfo->pushToSendQueue(std::move(packet)); } else { diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 5b3c2142dfe0..371587da5254 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -58,7 +58,6 @@ extern "C" #include "gettime.hh" #include "xsk.hh" -#define DEBUG_UMEM 0 #ifdef DEBUG_UMEM namespace { struct UmemEntryStatus @@ -97,7 +96,7 @@ int XskSocket::firstTimeout() } timespec now; gettime(&now); - const auto& firstTime = waitForDelay.top()->getSendTime(); + const auto& firstTime = waitForDelay.top().getSendTime(); const auto res = timeDifference(now, firstTime); if (res <= 0) { return 0; @@ -226,7 +225,7 @@ int XskSocket::wait(int timeout) return xsk_socket__fd(socket.get()); } -void XskSocket::send(std::vector& packets) +void XskSocket::send(std::vector& packets) { while (packets.size() > 0) { auto packetSize = packets.size(); @@ -246,11 +245,11 @@ void XskSocket::send(std::vector& packets) break; } *xsk_ring_prod__tx_desc(&tx, idx++) = { - .addr = frameOffset(*packet), - .len = packet->getFrameLen(), + .addr = frameOffset(packet), + .len = packet.getFrameLen(), .options = 0}; #ifdef DEBUG_UMEM - checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(*packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::Received}, UmemEntryStatus::Status::TXQueue); + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::Received}, UmemEntryStatus::Status::TXQueue); #endif /* DEBUG_UMEM */ queued++; } @@ -259,10 +258,10 @@ void XskSocket::send(std::vector& packets) } } -std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCount) +std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCount) { uint32_t idx{0}; - std::vector res; + std::vector res; // how many descriptors to packets have been filled const auto recvSize = xsk_ring_cons__peek(&rx, recvSizeMax, &idx); if (recvSize == 0) { @@ -276,17 +275,17 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed for (; processed < recvSize; processed++) { try { const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); - auto ptr = std::make_unique(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); + XskPacket packet = XskPacket(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); #ifdef DEBUG_UMEM - checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(*ptr), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); + checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); #endif /* DEBUG_UMEM */ - if (!ptr->parse(false)) { + if (!packet.parse(false)) { ++failed; - markAsFree(std::move(ptr)); + markAsFree(std::move(packet)); } else { - res.push_back(std::move(ptr)); + res.push_back(std::move(packet)); } } catch (const std::exception& exp) { @@ -310,12 +309,12 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failed return res; } -void XskSocket::pickUpReadyPacket(std::vector& packets) +void XskSocket::pickUpReadyPacket(std::vector& packets) { timespec now; gettime(&now); - while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top()->getSendTime()) <= 0) { - auto& top = const_cast(waitForDelay.top()); + while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top().getSendTime()) <= 0) { + auto& top = const_cast(waitForDelay.top()); packets.push_back(std::move(top)); waitForDelay.pop(); } @@ -375,15 +374,14 @@ std::string XskSocket::getMetrics() const return ret.str(); } -void XskSocket::markAsFree(XskPacketPtr&& packet) +void XskSocket::markAsFree(XskPacket&& packet) { - auto offset = frameOffset(*packet); + auto offset = frameOffset(packet); #ifdef DEBUG_UMEM checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); #endif /* DEBUG_UMEM */ uniqueEmptyFrameOffset.push_back(offset); - packet.release(); } XskSocket::XskUmem::~XskUmem() @@ -674,9 +672,9 @@ void XskPacket::addDelay(const int relativeMilliseconds) noexcept sendTime.tv_nsec %= 1000000000L; } -bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept +bool operator<(const XskPacket& s1, const XskPacket& s2) noexcept { - return s1->getSendTime() < s2->getSendTime(); + return s1.getSendTime() < s2.getSendTime(); } const ComboAddress& XskPacket::getFromAddr() const noexcept @@ -705,27 +703,25 @@ XskWorker::XskWorker() : { } -void XskWorker::pushToProcessingQueue(XskPacketPtr&& packet) +void XskWorker::pushToProcessingQueue(XskPacket&& packet) { - auto raw = packet.release(); #if defined(__SANITIZE_THREAD__) - if (!incomingPacketsQueue.lock()->push(std::move(raw))) { + if (!incomingPacketsQueue.lock()->push(std::move(packet))) { #else - if (!incomingPacketsQueue.push(std::move(raw))) { + if (!incomingPacketsQueue.push(std::move(packet))) { #endif - markAsFree(XskPacketPtr(raw)); + markAsFree(std::move(packet)); } } -void XskWorker::pushToSendQueue(XskPacketPtr&& packet) +void XskWorker::pushToSendQueue(XskPacket&& packet) { - auto raw = packet.release(); #if defined(__SANITIZE_THREAD__) - if (!outgoingPacketsQueue.lock()->push(raw)) { + if (!outgoingPacketsQueue.lock()->push(std::move(packet))) { #else - if (!outgoingPacketsQueue.push(raw)) { + if (!outgoingPacketsQueue.push(std::move(packet))) { #endif - markAsFree(XskPacketPtr(raw)); + markAsFree(std::move(packet)); } } @@ -924,21 +920,22 @@ void XskPacket::rewrite() noexcept return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); } -void XskPacket::setHeader(const PacketBuffer& buf) +void XskPacket::setHeader(PacketBuffer& buf) { memcpy(frame, buf.data(), buf.size()); frameLength = buf.size(); + buf.clear(); flags = 0; if (!parse(true)) { throw std::runtime_error("Error setting the XSK frame header"); } } -std::unique_ptr XskPacket::cloneHeadertoPacketBuffer() const +PacketBuffer XskPacket::cloneHeadertoPacketBuffer() const { const auto size = getFrameLen() - getDataSize(); - auto tmp = std::make_unique(size); - memcpy(tmp->data(), frame, size); + PacketBuffer tmp(size); + memcpy(tmp.data(), frame, size); return tmp; } @@ -1068,31 +1065,29 @@ void XskWorker::fillUniqueEmptyOffset() } } -XskPacketPtr XskWorker::getEmptyFrame() +std::optional XskWorker::getEmptyFrame() { if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); - return std::make_unique(offset + umemBufBase, 0, frameSize); + return XskPacket(offset + umemBufBase, 0, frameSize); } fillUniqueEmptyOffset(); if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); - return std::make_unique(offset + umemBufBase, 0, frameSize); + return XskPacket(offset + umemBufBase, 0, frameSize); } - return nullptr; + return std::nullopt; } -void XskWorker::markAsFree(XskPacketPtr&& packet) +void XskWorker::markAsFree(XskPacket&& packet) { - - auto offset = frameOffset(*packet); + auto offset = frameOffset(packet); #ifdef DEBUG_UMEM checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); #endif /* DEBUG_UMEM */ uniqueEmptyFrameOffset.push_back(offset); - packet.release(); } uint32_t XskPacket::getFlags() const noexcept diff --git a/pdns/xsk.hh b/pdns/xsk.hh index d8dc06756375..dc9f285751b2 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -101,7 +101,7 @@ class XskSocket // number of entries (frames) in the umem const size_t frameNum; // responses that have been delayed - std::priority_queue waitForDelay; + std::priority_queue waitForDelay; const std::string ifName; const std::string poolName; // AF_XDP socket then worker waker sockets @@ -147,12 +147,12 @@ public: // wait until one event has occurred [[nodiscard]] int wait(int timeout); // add as many packets as possible to the rx queue for sending */ - void send(std::vector& packets); + void send(std::vector& packets); // look at incoming packets in rx, return them if parsing succeeeded - [[nodiscard]] std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); + [[nodiscard]] std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); void addWorker(std::shared_ptr s, const ComboAddress& dest); [[nodiscard]] std::string getMetrics() const; - void markAsFree(XskPacketPtr&& packet); + void markAsFree(XskPacket&& packet); [[nodiscard]] WorkerContainer& getWorkers() { return workers; @@ -167,8 +167,8 @@ public: // picks up entries that have been processed (sent) from cq and push them into uniqueEmptyFrameOffset void recycle(size_t size) noexcept; // look at delayed packets, and send the ones that are ready - void pickUpReadyPacket(std::vector& packets); - void pushDelayed(XskPacketPtr&& packet) + void pickUpReadyPacket(std::vector& packets); + void pushDelayed(XskPacket&& packet) { waitForDelay.push(std::move(packet)); } @@ -238,11 +238,11 @@ public: [[nodiscard]] uint32_t getFrameLen() const noexcept; [[nodiscard]] PacketBuffer clonePacketBuffer() const; void cloneIntoPacketBuffer(PacketBuffer& buffer) const; - [[nodiscard]] std::unique_ptr cloneHeadertoPacketBuffer() const; + [[nodiscard]] PacketBuffer cloneHeadertoPacketBuffer() const; void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept; bool setPayload(const PacketBuffer& buf); void rewrite() noexcept; - void setHeader(const PacketBuffer& buf); + void setHeader(PacketBuffer& buf); XskPacket(uint8_t* frame, size_t dataSize, size_t frameSize); void addDelay(int relativeMilliseconds) noexcept; void updatePacket() noexcept; @@ -258,7 +258,7 @@ public: return frame - base; } }; -bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; +bool operator<(const XskPacket& s1, const XskPacket& s2) noexcept; /* g++ defines __SANITIZE_THREAD__ clang++ supports the nice __has_feature(thread_sanitizer), @@ -275,9 +275,9 @@ bool operator<(const XskPacketPtr& s1, const XskPacketPtr& s2) noexcept; class XskWorker { #if defined(__SANITIZE_THREAD__) - using XskPacketRing = LockGuarded>>; + using XskPacketRing = LockGuarded>>; #else - using XskPacketRing = boost::lockfree::spsc_queue>; + using XskPacketRing = boost::lockfree::spsc_queue>; #endif public: @@ -300,9 +300,9 @@ public: static int createEventfd(); static void notify(int fd); static std::shared_ptr create(); - void pushToProcessingQueue(XskPacketPtr&& packet); - void pushToSendQueue(XskPacketPtr&& packet); - void markAsFree(XskPacketPtr&& packet); + void pushToProcessingQueue(XskPacket&& packet); + void pushToSendQueue(XskPacket&& packet); + void markAsFree(XskPacket&& packet); // notify worker that at least one packet is available for processing void notifyWorker() noexcept; // notify the router that packets are ready to be sent @@ -315,7 +315,7 @@ public: void fillUniqueEmptyOffset(); // look for an empty umem entry in uniqueEmptyFrameOffset // then sharedEmptyFrameOffset if needed - XskPacketPtr getEmptyFrame(); + std::optional getEmptyFrame(); }; std::vector getPollFdsForWorker(XskWorker& info); #else From f140f2b83d21fcbe7fb37b724a2a17ca2346c21b Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 11 Jan 2024 16:24:38 +0100 Subject: [PATCH 31/65] dnsdist: Fix XSK between dnsdist and its backends --- contrib/xdp-filter.ebpf.src | 24 ++- contrib/xdp.h | 16 +- contrib/xdp.py | 5 +- pdns/dnsdist-lua-bindings.cc | 5 +- pdns/dnsdist-lua.cc | 23 ++- pdns/dnsdist.cc | 122 +++----------- pdns/dnsdist.hh | 29 ++-- pdns/dnsdistdist/dnsdist-backend.cc | 110 +++++++++++- pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc | 9 +- pdns/xsk.cc | 159 +++++++++++++++--- pdns/xsk.hh | 52 +++--- 11 files changed, 380 insertions(+), 174 deletions(-) diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index 786d9d75a355..ec0d07145d26 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -35,6 +35,8 @@ BPF_TABLE_PINNED7("lpm_trie", struct CIDR6, struct map_value, cidr6filter, 1024, __attribute__((section("maps/xskmap:" _pinned))) struct _name##_table_t _name = {.max_entries = (_max_entries)} BPF_XSKMAP_PIN(xsk_map, 16, "/sys/fs/bpf/dnsdist/xskmap"); +BPF_TABLE_PINNED("hash", struct IPv4AndPort, bool, xskDestinationsV4, 1024, "/sys/fs/bpf/dnsdist/xsk-destinations-v4"); +BPF_TABLE_PINNED("hash", struct IPv6AndPort, bool, xskDestinationsV6, 1024, "/sys/fs/bpf/dnsdist/xsk-destinations-v6"); #endif /* UseXsk */ #define COMPARE_PORT(x, p) ((x) == bpf_htons(p)) @@ -159,9 +161,19 @@ static inline enum xdp_action parseIPV4(struct xdp_md* ctx, struct cursor* c) if (!(udp = parse_udphdr(c))) { return XDP_PASS; } +#ifdef UseXsk + struct IPv4AndPort v4Dest; + memset(&v4Dest, 0, sizeof(v4Dest)); + v4Dest.port = udp->dest; + v4Dest.addr = ipv4->daddr; + if (!xskDestinationsV4.lookup(&v4Dest)) { + return XDP_PASS; + } +#else /* UseXsk */ if (!IN_DNS_PORT_SET(udp->dest)) { return XDP_PASS; } +#endif /* UseXsk */ if (!(dns = parse_dnshdr(c))) { return XDP_DROP; } @@ -253,10 +265,20 @@ static inline enum xdp_action parseIPV6(struct xdp_md* ctx, struct cursor* c) if (!(udp = parse_udphdr(c))) { return XDP_PASS; } +#ifdef UseXsk + struct IPv6AndPort v6Dest; + memset(&v6Dest, 0, sizeof(v6Dest)); + v6Dest.port = udp->dest; + memcpy(&v6Dest.addr, &ipv6->daddr, sizeof(v6Dest.addr)); + if (!xskDestinationsV6.lookup(&v6Dest)) { + return XDP_PASS; + } +#else /* UseXsk */ if (!IN_DNS_PORT_SET(udp->dest)) { return XDP_PASS; } - if (!(dns = parse_dnshdr(c))) { +#endif /* UseXsk */ + if (!(dns = parse_dnshdr(c))) { return XDP_DROP; } break; diff --git a/contrib/xdp.h b/contrib/xdp.h index 87fef3a776cd..0d63fcfd963d 100644 --- a/contrib/xdp.h +++ b/contrib/xdp.h @@ -108,6 +108,18 @@ struct CIDR6 struct in6_addr addr; }; +struct IPv4AndPort +{ + uint32_t addr; + uint16_t port; +}; + +struct IPv6AndPort +{ + struct in6_addr addr; + uint16_t port; +}; + /* * Store the matching counter and the associated action for a blocked element */ @@ -128,7 +140,7 @@ static inline void cursor_init(struct cursor *c, struct xdp_md *ctx) c->pos = (void *)(long)ctx->data; } -/* +/* * Header parser functions * Copyright 2020, NLnet Labs, All rights reserved. */ @@ -180,4 +192,4 @@ static inline struct ethhdr *parse_eth(struct cursor *c, uint16_t *eth_proto) return eth; } -#endif +#endif diff --git a/contrib/xdp.py b/contrib/xdp.py index 1b9187007ff2..67ad96b917f7 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -46,6 +46,9 @@ cidr6filter = xdp.get_table("cidr6filter") qnamefilter = xdp.get_table("qnamefilter") +if useXsk: + xskDestinations = xdp.get_table("xskDestinationsV4") + for ip in blocked_ipv4: print(f"Blocking {ip}") key = v4filter.Key(int(netaddr.IPAddress(ip[0]).value)) @@ -106,7 +109,7 @@ print("Filter is ready") try: - xdp.trace_print() + xdp.trace_print() except KeyboardInterrupt: pass diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 5a6d8f4e95d8..45ee564cf64a 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -754,11 +754,8 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) else { throw std::runtime_error("xskMapPath field is required!"); } - if (opts.count("pool") == 1) { - poolName = boost::get(opts.at("pool")); - } extern std::vector> g_xsk; - auto socket = std::make_shared(frameNums, ifName, queue_id, path, poolName); + auto socket = std::make_shared(frameNums, ifName, queue_id, path); g_xsk.push_back(socket); return socket; }); diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index c4ee51962812..793f226df697 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -636,13 +636,22 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #ifdef HAVE_XSK std::shared_ptr xskSocket; if (getOptionalValue>(vars, "xskSocket", xskSocket) > 0) { + if (g_configurationDone) { + throw std::runtime_error("Adding a server with xsk at runtime is not supported"); + } ret->registerXsk(xskSocket); std::string mac; - if (getOptionalValue(vars, "MACAddr", mac) != 1) { - throw runtime_error("field MACAddr is required!"); + if (getOptionalValue(vars, "MACAddr", mac) > 0) { + auto* addr = &ret->d_config.destMACAddr[0]; + sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", addr, addr + 1, addr + 2, addr + 3, addr + 4, addr + 5); + } + else { + mac = getMACAddress(ret->d_config.remote); + if (mac.size() != ret->d_config.destMACAddr.size()) { + throw runtime_error("Field 'MACAddr' is not set on 'newServer' directive for '" + ret->d_config.remote.toStringWithPort() + "' and cannot be retriever from the system either!"); + } + memcpy(ret->d_config.destMACAddr.data(), mac.data(), ret->d_config.destMACAddr.size()); } - auto* addr = &ret->d_config.destMACAddr[0]; - sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", addr, addr + 1, addr + 2, addr + 3, addr + 4, addr + 5); } #endif /* HAVE_XSK */ if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) { @@ -783,7 +792,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (socket) { udpCS->xskInfo = XskWorker::create(); udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; - socket->addWorker(udpCS->xskInfo, loc); + socket->addWorker(udpCS->xskInfo); + socket->addWorkerRoute(udpCS->xskInfo, loc); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); @@ -835,7 +845,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (socket) { udpCS->xskInfo = XskWorker::create(); udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; - socket->addWorker(udpCS->xskInfo, loc); + socket->addWorker(udpCS->xskInfo); + socket->addWorkerRoute(udpCS->xskInfo, loc); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index a4dcd3f1bd18..cd71ac6b5c3d 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -35,7 +35,6 @@ #ifdef HAVE_XSK #include -#include #endif /* HAVE_XSK */ #ifdef HAVE_LIBEDIT @@ -815,25 +814,6 @@ static bool processResponderPacket(std::shared_ptr& dss, Packet #ifdef HAVE_XSK namespace dnsdist::xsk { -static void doHealthCheck(std::shared_ptr& dss, std::unordered_map>& map, bool initial = false) -{ - auto& xskInfo = dss->xskInfo; - std::shared_ptr data; - auto packet = getHealthCheckPacket(dss, nullptr, data); - data->d_initial = initial; - setHealthCheckTime(dss, data); - auto xskPacket = xskInfo->getEmptyFrame(); - if (!xskPacket) { - return; - } - xskPacket->setAddr(dss->d_config.sourceAddr, dss->d_config.sourceMACAddr, dss->d_config.remote, dss->d_config.destMACAddr); - xskPacket->setPayload(packet); - xskPacket->rewrite(); - xskInfo->pushToSendQueue(std::move(*xskPacket)); - const auto queryId = data->d_queryID; - map[queryId] = std::move(data); -} - void responderThread(std::shared_ptr dss) { if (dss->xskInfo == nullptr) { @@ -846,16 +826,6 @@ void responderThread(std::shared_ptr dss) auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); auto xskInfo = dss->xskInfo; auto pollfds = getPollFdsForWorker(*xskInfo); - std::unordered_map> healthCheckMap; - dnsdist::xsk::doHealthCheck(dss, healthCheckMap, true); - itimerspec tm; - tm.it_value.tv_sec = dss->d_config.checkTimeout / 1000; - tm.it_value.tv_nsec = (dss->d_config.checkTimeout % 1000) * 1000000; - tm.it_interval = tm.it_value; - auto res = timerfd_settime(pollfds[1].fd, 0, &tm, nullptr); - if (res) { - throw std::runtime_error("timerfd_settime failed:" + stringerror(errno)); - } const auto xskFd = xskInfo->workerWaker.getHandle(); while (!dss->isStopped()) { poll(pollfds.data(), pollfds.size(), -1); @@ -881,14 +851,6 @@ void responderThread(std::shared_ptr dss) } } if (!ids) { - // this has to go before we can refactor the duplicated response handling code - auto iter = healthCheckMap.find(queryId); - if (iter != healthCheckMap.end()) { - auto data = std::move(iter->second); - healthCheckMap.erase(iter); - packet.cloneIntoPacketBuffer(data->d_buffer); - data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); - } xskInfo->markAsFree(std::move(packet)); return; } @@ -902,7 +864,6 @@ void responderThread(std::shared_ptr dss) vinfolog("XSK packet pushed to queue because processResponderPacket failed"); return; } - vinfolog("XSK packet - processResponderPacket OK"); if (response.size() > packet.getCapacity()) { /* fallback to sending the packet via normal socket */ sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); @@ -910,9 +871,7 @@ void responderThread(std::shared_ptr dss) xskInfo->markAsFree(std::move(packet)); return; } - //vinfolog("XSK packet - set header"); packet.setHeader(ids->xskPacketHeader); - //vinfolog("XSK packet - set payload"); if (!packet.setPayload(response)) { vinfolog("Unable to set payload !"); } @@ -920,42 +879,11 @@ void responderThread(std::shared_ptr dss) vinfolog("XSK packet - adding delay"); packet.addDelay(ids->delayMsec); } - //vinfolog("XSK packet - update packet"); packet.updatePacket(); - //vinfolog("XSK packet pushed to send queue"); xskInfo->pushToSendQueue(std::move(packet)); }); xskInfo->cleanSocketNotification(); } - if (pollfds[1].revents & POLLIN) { - timeval now; - gettimeofday(&now, nullptr); - for (auto i = healthCheckMap.begin(); i != healthCheckMap.end();) { - auto& ttd = i->second->d_ttd; - if (ttd < now) { - dss->submitHealthCheckResult(i->second->d_initial, false); - i = healthCheckMap.erase(i); - } - else { - ++i; - } - } - needNotify = true; - dss->updateStatisticsInfo(); - dss->handleUDPTimeouts(); - if (dss->d_nextCheck <= 1) { - dss->d_nextCheck = dss->d_config.checkInterval; - if (dss->d_config.availability == DownstreamState::Availability::Auto) { - doHealthCheck(dss, healthCheckMap); - } - } - else { - --dss->d_nextCheck; - } - - uint64_t tmp; - res = read(pollfds[1].fd, &tmp, sizeof(tmp)); - } if (needNotify) { xskInfo->notifyXskSocket(); } @@ -987,7 +915,7 @@ static bool isXskQueryAcceptable(const XskPacket& packet, ClientState& cs, Local return true; } -void XskRouter(std::shared_ptr xsk) +static void XskRouter(std::shared_ptr xsk) { setThreadName("dnsdist/XskRouter"); uint32_t failed; @@ -996,8 +924,6 @@ void XskRouter(std::shared_ptr xsk) const auto& fds = xsk->getDescriptors(); // list of workers that need to be notified std::set needNotify; - const auto& xskWakerIdx = xsk->getWorkers().get<0>(); - const auto& destIdx = xsk->getWorkers().get<1>(); while (true) { try { auto ready = xsk->wait(-1); @@ -1007,13 +933,13 @@ void XskRouter(std::shared_ptr xsk) dnsdist::metrics::g_stats.nonCompliantQueries += failed; for (auto &packet : packets) { const auto dest = packet.getToAddr(); - auto res = destIdx.find(dest); - if (res == destIdx.end()) { + auto worker = xsk->getWorkerByDestination(dest); + if (!worker) { xsk->markAsFree(std::move(packet)); continue; } - res->worker->pushToProcessingQueue(std::move(packet)); - needNotify.insert(res->workerWaker); + worker->pushToProcessingQueue(std::move(packet)); + needNotify.insert(worker->workerWaker.getHandle()); } for (auto i : needNotify) { uint64_t x = 1; @@ -1031,7 +957,7 @@ void XskRouter(std::shared_ptr xsk) for (size_t fdIndex = 1; fdIndex < fds.size() && ready > 0; fdIndex++) { if (fds.at(fdIndex).revents & POLLIN) { ready--; - auto& info = xskWakerIdx.find(fds.at(fdIndex).fd)->worker; + auto& info = xsk->getWorkerByDescriptor(fds.at(fdIndex).fd); #if defined(__SANITIZE_THREAD__) info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { #else @@ -1131,19 +1057,14 @@ void responderThread(std::shared_ptr dss) if (processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfo) { #ifdef HAVE_XSK - //vinfolog("processResponderPacket OK"); auto& xskInfo = ids->cs->xskInfo; auto xskPacket = xskInfo->getEmptyFrame(); if (!xskPacket) { continue; } - //vinfolog("XSK setHeader"); xskPacket->setHeader(ids->xskPacketHeader); - //vinfolog("XSK payload"); xskPacket->setPayload(response); - //vinfolog("XSK update packet"); xskPacket->updatePacket(); - //vinfolog("XSK pushed to send queue"); xskInfo->pushToSendQueue(std::move(*xskPacket)); xskInfo->notifyXskSocket(); #endif /* HAVE_XSK */ @@ -1701,11 +1622,6 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders ++dq.ids.cs->responses; return ProcessQueryResult::SendAnswer; } -#ifdef HAVE_XSK - if (dq.ids.cs->xskInfo) { - dq.ids.poolName = dq.ids.cs->xskInfo->poolName; - } -#endif /* HAVE_XSK */ std::shared_ptr serverPool = getPool(*holders.pools, dq.ids.poolName); std::shared_ptr poolPolicy = serverPool->policy; dq.ids.packetCache = serverPool->packetCache; @@ -2222,6 +2138,8 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p if (dq.ids.delayMsec > 0) { packet.addDelay(dq.ids.delayMsec); } + const auto dh = dq.getHeader(); + handleResponseSent(ids.qname, ids.qtype, 0., remote, ComboAddress(), query.size(), *dh, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false); return true; } @@ -2247,6 +2165,7 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p return false; } +#ifdef HAVE_XSK if (!ss->xskInfo) { assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); return false; @@ -2255,11 +2174,16 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p int fd = ss->xskInfo->workerWaker; ids.backendFD = fd; assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, false); - packet.setAddr(ss->d_config.sourceAddr,ss->d_config.sourceMACAddr, ss->d_config.remote,ss->d_config.destMACAddr); + auto sourceAddr = ss->pickSourceAddressForSending(); + packet.setAddr(sourceAddr, ss->d_config.sourceMACAddr, ss->d_config.remote, ss->d_config.destMACAddr); packet.setPayload(query); packet.rewrite(); return true; } +#else /* HAVE_XSK */ + assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); + return false; +#endif /* HAVE_XSK */ } catch (const std::exception& e) { vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what()); @@ -2638,11 +2562,6 @@ static void healthChecksThread() std::unique_ptr mplexer{nullptr}; for (auto& dss : *states) { -#ifdef HAVE_XSK - if (dss->xskInfo) { - continue; - } -#endif /* HAVE_XSK */ dss->updateStatisticsInfo(); dss->handleUDPTimeouts(); @@ -3387,6 +3306,8 @@ static void startFrontends() for (auto& clientState : g_frontends) { #ifdef HAVE_XSK if (clientState->xskInfo) { + XskSocket::addDestinationAddress(clientState->local); + std::thread xskCT(dnsdist::xsk::xskClientThread, clientState.get()); if (!clientState->cpus.empty()) { mapThreadToCPUList(xskCT.native_handle(), clientState->cpus); @@ -3493,6 +3414,10 @@ int main(int argc, char** argv) dnsdist::initRandom(); g_hashperturb = dnsdist::getRandomValue(0xffffffff); +#ifdef HAVE_XSK + XskSocket::clearDestinationAddresses(); +#endif /* HAVE_XSK */ + ComboAddress clientAddress = ComboAddress(); g_cmdLine.config=SYSCONFDIR "/dnsdist.conf"; @@ -3655,11 +3580,6 @@ int main(int argc, char** argv) auto states = g_dstates.getCopy(); // it is a copy, but the internal shared_ptrs are the real deal auto mplexer = std::unique_ptr(FDMultiplexer::getMultiplexerSilent(states.size())); for (auto& dss : states) { -#ifdef HAVE_XSK - if (dss->xskInfo) { - continue; - } -#endif /* HAVE_XSK */ if (dss->d_config.availability == DownstreamState::Availability::Auto || dss->d_config.availability == DownstreamState::Availability::Lazy) { if (dss->d_config.availability == DownstreamState::Availability::Auto) { diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 0081c492a625..4cec0d03043c 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -819,7 +819,10 @@ public: std::vector sockets; StopWatch sw; QPSLimiter qps; +#ifdef HAVE_XSK std::shared_ptr xskInfo{nullptr}; + std::shared_ptr d_xskSocket{nullptr}; +#endif std::atomic idOffset{0}; size_t socketsOffset{0}; double latencyUsec{0.0}; @@ -834,10 +837,17 @@ private: void handleUDPTimeout(IDState& ids); void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional currentTime = std::nullopt); void connectUDPSockets(); +#ifdef HAVE_XSK + void addXSKDestination(int fd); + void removeXSKDestination(int fd); +#endif /* HAVE_XSK */ std::thread tid; std::mutex connectLock; std::condition_variable d_connectedWait; +#ifdef HAVE_XSK + SharedLockGuarded> d_socketSourceAddresses; +#endif std::atomic_flag threadStarted; uint8_t consecutiveSuccessfulChecks{0}; bool d_stopped{false}; @@ -979,16 +989,8 @@ public: std::optional getState(uint16_t id); #ifdef HAVE_XSK - void registerXsk(std::shared_ptr& xsk) - { - xskInfo = XskWorker::create(); - if (d_config.sourceAddr.sin4.sin_family == 0) { - throw runtime_error("invalid source addr"); - } - xsk->addWorker(xskInfo, d_config.sourceAddr); - d_config.sourceMACAddr = xsk->source; - xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; - } + void registerXsk(std::shared_ptr& xsk); + [[nodiscard]] ComboAddress pickSourceAddressForSending(); #endif /* HAVE_XSK */ dnsdist::Protocol getProtocol() const @@ -1194,3 +1196,10 @@ ssize_t udpClientSendRequestToBackend(const std::shared_ptr& ss bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend); void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend); + +#ifdef HAVE_XSK +namespace dnsdist::xsk +{ +void responderThread(std::shared_ptr dss); +} +#endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index bd7592545a49..02cfea5c7d30 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -19,7 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - +#include "config.h" #include "dnsdist.hh" #include "dnsdist-backoff.hh" #include "dnsdist-metrics.hh" @@ -28,6 +28,7 @@ #include "dnsdist-rings.hh" #include "dnsdist-tcp.hh" #include "dolog.hh" +#include "xsk.hh" bool DownstreamState::passCrossProtocolQuery(std::unique_ptr&& cpq) { @@ -39,6 +40,36 @@ bool DownstreamState::passCrossProtocolQuery(std::unique_ptr return g_tcpclientthreads && g_tcpclientthreads->passCrossProtocolQueryToThread(std::move(cpq)); } +#ifdef HAVE_XSK +void DownstreamState::addXSKDestination(int fd) +{ + auto socklen = d_config.remote.getSocklen(); + ComboAddress local; + if (getsockname(fd, reinterpret_cast(&local), &socklen)) { + return; + } + + { + auto addresses = d_socketSourceAddresses.write_lock(); + addresses->push_back(local); + } + XskSocket::addDestinationAddress(local); + d_xskSocket->addWorkerRoute(xskInfo, local); +} + +void DownstreamState::removeXSKDestination(int fd) +{ + auto socklen = d_config.remote.getSocklen(); + ComboAddress local; + if (getsockname(fd, reinterpret_cast(&local), &socklen)) { + return; + } + + XskSocket::removeDestinationAddress(local); + d_xskSocket->removeWorkerRoute(local); +} +#endif /* HAVE_XSK */ + bool DownstreamState::reconnect(bool initialAttempt) { std::unique_lock tl(connectLock, std::try_to_lock); @@ -52,11 +83,23 @@ bool DownstreamState::reconnect(bool initialAttempt) } connected = false; +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + auto addresses = d_socketSourceAddresses.write_lock(); + addresses->clear(); + } +#endif /* HAVE_XSK */ + for (auto& fd : sockets) { if (fd != -1) { if (sockets.size() > 1) { (*mplexer.lock())->removeReadFD(fd); } +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + removeXSKDestination(fd); + } +#endif /* HAVE_XSK */ /* shutdown() is needed to wake up recv() in the responderThread */ shutdown(fd, SHUT_RDWR); close(fd); @@ -87,6 +130,11 @@ bool DownstreamState::reconnect(bool initialAttempt) if (sockets.size() > 1) { (*mplexer.lock())->addReadFD(fd, [](int, boost::any) {}); } +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + addXSKDestination(fd); + } +#endif /* HAVE_XSK */ connected = true; } catch (const std::runtime_error& error) { @@ -100,8 +148,19 @@ bool DownstreamState::reconnect(bool initialAttempt) /* if at least one (re-)connection failed, close all sockets */ if (!connected) { +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + auto addresses = d_socketSourceAddresses.write_lock(); + addresses->clear(); + } +#endif /* HAVE_XSK */ for (auto& fd : sockets) { if (fd != -1) { +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + removeXSKDestination(fd); + } +#endif /* HAVE_XSK */ if (sockets.size() > 1) { try { (*mplexer.lock())->removeReadFD(fd); @@ -268,7 +327,16 @@ DownstreamState::DownstreamState(DownstreamState::Config&& config, std::shared_p void DownstreamState::start() { if (connected && !threadStarted.test_and_set()) { +#ifdef HAVE_XSK + if (xskInfo != nullptr) { + tid = std::thread(dnsdist::xsk::responderThread, shared_from_this()); + } + else { + tid = std::thread(responderThread, shared_from_this()); + } +#else tid = std::thread(responderThread, shared_from_this()); +#endif /* HAVE_XSK */ if (!d_config.d_cpus.empty()) { mapThreadToCPUList(tid.native_handle(), d_config.d_cpus); @@ -797,6 +865,46 @@ void DownstreamState::submitHealthCheckResult(bool initial, bool newResult) } } +#ifdef HAVE_XSK +[[nodiscard]] ComboAddress DownstreamState::pickSourceAddressForSending() +{ + if (!connected) { + waitUntilConnected(); + } + + auto addresses = d_socketSourceAddresses.read_lock(); + auto numberOfAddresses = addresses->size(); + if (numberOfAddresses == 0) { + throw std::runtime_error("No source address available for sending XSK data to backend " + getNameWithAddr()); + } + size_t idx = dnsdist::getRandomValue(numberOfAddresses); + return (*addresses)[idx % numberOfAddresses]; +} + +void DownstreamState::registerXsk(std::shared_ptr& xsk) +{ + d_xskSocket = xsk; + + if (d_config.sourceAddr.sin4.sin_family == 0 || (IsAnyAddress(d_config.sourceAddr))) { + const auto& ifName = xsk->getInterfaceName(); + auto addresses = getListOfAddressesOfNetworkInterface(ifName); + if (addresses.empty()) { + throw std::runtime_error("Unable to get source address from interface " + ifName); + } + + if (addresses.size() > 1) { + warnlog("More than one address configured on interface %s, picking the first one (%s) for XSK. Set the 'source' parameter on 'newServer' if you want to use a different address.", ifName, addresses.at(0).toString()); + } + d_config.sourceAddr = addresses.at(0); + } + xskInfo = XskWorker::create(); + xsk->addWorker(xskInfo); + reconnect(false); + d_config.sourceMACAddr = xsk->getSourceMACAddress(); + xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; +} +#endif /* HAVE_XSK */ + size_t ServerPool::countServers(bool upOnly) { std::shared_ptr servers = nullptr; diff --git a/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc b/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc index 813eeac44247..e05c0b56b4af 100644 --- a/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc +++ b/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc @@ -74,6 +74,13 @@ void responderThread(std::shared_ptr dss) { } +namespace dnsdist::xsk +{ +void responderThread(std::shared_ptr dss) +{ +} +} + string g_outputBuffer; std::atomic g_configurationDone{false}; @@ -181,7 +188,7 @@ BOOST_AUTO_TEST_CASE(test_firstAvailableWithOrderAndQPS) { servers.push_back({ 2, std::make_shared(ComboAddress("192.0.2.2:53")) }); /* Second server has a higher order, so most queries should be routed to the first (remember that we need to keep them ordered!). - However the first server has a QPS limit at 10 qps, so any query above that should be routed + However the first server has a QPS limit at 10 qps, so any query above that should be routed to the second server. */ servers.at(0).second->d_config.order = 1; servers.at(1).second->d_config.order = 2; diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 371587da5254..15d9f14ec5f4 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -104,8 +104,8 @@ int XskSocket::firstTimeout() return res; } -XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_) : - frameNum(frameNum_), ifName(ifName_), poolName(poolName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) +XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath) : + frameNum(frameNum_), ifName(ifName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) { if (!isPowOfTwo(frameNum_) || !isPowOfTwo(frameSize) || !isPowOfTwo(fqCapacity) || !isPowOfTwo(cqCapacity) || !isPowOfTwo(rxCapacity) || !isPowOfTwo(txCapacity)) { @@ -175,6 +175,113 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu } } +// see xdp.h in contrib/ + +struct IPv4AndPort +{ + uint32_t addr; + uint16_t port; +}; +struct IPv6AndPort +{ + struct in6_addr addr; + uint16_t port; +}; + +static void clearDestinationMap(bool v6) +{ + const std::string mapPath = !v6 ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; + + const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); + if (destMapFd.getHandle() < 0) { + throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); + } + + if (!v6) { + IPv4AndPort prevKey{}; + IPv4AndPort key{}; + while (bpf_map_get_next_key(destMapFd.getHandle(), &prevKey, &key) == 0) { + bpf_map_delete_elem(destMapFd.getHandle(), &key); + prevKey = key; + } + } + else { + IPv6AndPort prevKey{}; + IPv6AndPort key{}; + while (bpf_map_get_next_key(destMapFd.getHandle(), &prevKey, &key) == 0) { + bpf_map_delete_elem(destMapFd.getHandle(), &key); + prevKey = key; + } + } +} + +void XskSocket::clearDestinationAddresses() +{ + clearDestinationMap(false); + clearDestinationMap(true); +} + +void XskSocket::addDestinationAddress(const ComboAddress& destination) +{ + // see xdp.h in contrib/ + + const std::string mapPath = destination.isIPv4() ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; + //if (!s_destinationAddressesMap) { + // throw std::runtime_error("The path of the XSK (AF_XDP) destination addresses map has not been set! Please consider using setXSKDestinationAddressesMapPath()."); + //} + + const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); + if (destMapFd.getHandle() < 0) { + throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); + } + + bool value = true; + if (destination.isIPv4()) { + IPv4AndPort key{}; + key.addr = destination.sin4.sin_addr.s_addr; + key.port = destination.sin4.sin_port; + auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); + if (ret) { + throw std::runtime_error("Error inserting into xsk_map '" + mapPath + "': " + std::to_string(ret)); + } + } + else { + IPv6AndPort key{}; + key.addr = destination.sin6.sin6_addr; + key.port = destination.sin6.sin6_port; + auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); + if (ret) { + throw std::runtime_error("Error inserting into XSK destination addresses map '" + mapPath + "': " + std::to_string(ret)); + } + } +} + +void XskSocket::removeDestinationAddress(const ComboAddress& destination) +{ + const std::string mapPath = destination.isIPv4() ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; + //if (!s_destinationAddressesMap) { + // throw std::runtime_error("The path of the XSK (AF_XDP) destination addresses map has not been set! Please consider using setXSKDestinationAddressesMapPath()."); + //} + + const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); + if (destMapFd.getHandle() < 0) { + throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); + } + + if (destination.isIPv4()) { + IPv4AndPort key{}; + key.addr = destination.sin4.sin_addr.s_addr; + key.port = destination.sin4.sin_port; + bpf_map_delete_elem(destMapFd.getHandle(), &key); + } + else { + IPv6AndPort key{}; + key.addr = destination.sin6.sin6_addr; + key.port = destination.sin6.sin6_port; + bpf_map_delete_elem(destMapFd.getHandle(), &key); + } +} + void XskSocket::fillFq(uint32_t fillSize) noexcept { { @@ -412,14 +519,17 @@ XskSocket::XskUmem::~XskUmem() [[nodiscard]] ethhdr XskPacket::getEthernetHeader() const noexcept { ethhdr ethHeader{}; - assert(frameLength >= sizeof(ethHeader)); - memcpy(ðHeader, frame, sizeof(ethHeader)); + if (frameLength >= sizeof(ethHeader)) { + memcpy(ðHeader, frame, sizeof(ethHeader)); + } return ethHeader; } void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept { - assert(frameLength >= sizeof(ethHeader)); + if (frameLength < sizeof(ethHeader)) { + frameLength = sizeof(ethHeader); + } memcpy(frame, ðHeader, sizeof(ethHeader)); } @@ -631,8 +741,8 @@ bool XskPacket::isIPV6() const noexcept return v6; } -XskPacket::XskPacket(uint8_t* frame_, size_t dataSize, size_t frameSize) : - frame(frame_), frameLength(dataSize), frameSize(frameSize - XDP_PACKET_HEADROOM) +XskPacket::XskPacket(uint8_t* frame_, size_t dataSize, size_t frameSize_) : + frame(frame_), frameLength(dataSize), frameSize(frameSize_ - XDP_PACKET_HEADROOM) { } @@ -757,7 +867,7 @@ void XskPacket::rewrite() noexcept ipHeader.protocol = IPPROTO_UDP; udpHeader.source = from.sin4.sin_port; udpHeader.dest = to.sin4.sin_port; - udpHeader.len = htons(getDataSize()); + udpHeader.len = htons(getDataSize() + sizeof(udpHeader)); udpHeader.check = 0; /* needed to get the correct checksum */ setIPv4Header(ipHeader); @@ -963,32 +1073,27 @@ std::shared_ptr XskWorker::create() return std::make_shared(); } -void XskSocket::addWorker(std::shared_ptr s, const ComboAddress& dest) +void XskSocket::addWorker(std::shared_ptr worker) { - extern std::atomic g_configurationDone; - if (g_configurationDone) { - throw runtime_error("Adding a server with xsk at runtime is not supported"); - } - s->poolName = poolName; - const auto socketWaker = s->xskSocketWaker.getHandle(); - const auto workerWaker = s->workerWaker.getHandle(); - const auto& socketWakerIdx = workers.get<0>(); - if (socketWakerIdx.contains(socketWaker)) { - throw runtime_error("Server already exist"); - } - s->umemBufBase = umem.bufBase; - workers.insert(XskRouteInfo{ - .worker = std::move(s), - .dest = dest, - .xskSocketWaker = socketWaker, - .workerWaker = workerWaker, - }); + const auto socketWaker = worker->xskSocketWaker.getHandle(); + worker->umemBufBase = umem.bufBase; + d_workers.insert({socketWaker, std::move(worker)}); fds.push_back(pollfd{ .fd = socketWaker, .events = POLLIN, .revents = 0}); }; +void XskSocket::addWorkerRoute(const std::shared_ptr& worker, const ComboAddress& dest) +{ + d_workerRoutes.lock()->insert({dest, worker}); +} + +void XskSocket::removeWorkerRoute(const ComboAddress& dest) +{ + d_workerRoutes.lock()->erase(dest); +} + uint64_t XskWorker::frameOffset(const XskPacket& packet) const noexcept { return packet.getFrameOffsetFrom(umemBufBase); diff --git a/pdns/xsk.hh b/pdns/xsk.hh index dc9f285751b2..702855a4739f 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -71,13 +71,6 @@ using XskPacketPtr = std::unique_ptr; class XskSocket { - struct XskRouteInfo - { - std::shared_ptr worker; - ComboAddress dest; - int xskSocketWaker; - int workerWaker; - }; struct XskUmem { xsk_umem* umem{nullptr}; @@ -87,12 +80,11 @@ class XskSocket ~XskUmem(); XskUmem() = default; }; - using WorkerContainer = boost::multi_index_container< - XskRouteInfo, - boost::multi_index::indexed_by< - boost::multi_index::hashed_unique>, - boost::multi_index::hashed_unique, ComboAddress::addressPortOnlyHash>>>; - WorkerContainer workers; + using WorkerContainer = std::unordered_map>; + WorkerContainer d_workers; + using WorkerRoutesMap = std::unordered_map, ComboAddress::addressPortOnlyHash>; + // it might be better to move to a StateHolder for performance + LockGuarded d_workerRoutes; // number of frames to keep in sharedEmptyFrameOffset static constexpr size_t holdThreshold = 256; // number of frames to insert into the fill queue @@ -102,8 +94,8 @@ class XskSocket const size_t frameNum; // responses that have been delayed std::priority_queue waitForDelay; + MACAddr source; const std::string ifName; - const std::string poolName; // AF_XDP socket then worker waker sockets vector fds; // list of frames, aka (indexes of) umem entries that can be reused to fill fq, @@ -135,14 +127,16 @@ class XskSocket void getMACFromIfName(); public: + static void clearDestinationAddresses(); + static void addDestinationAddress(const ComboAddress& destination); + static void removeDestinationAddress(const ComboAddress& destination); static constexpr size_t getFrameSize() { return frameSize; } // list of free umem entries that can be reused std::shared_ptr>> sharedEmptyFrameOffset; - XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath, const std::string& poolName_); - MACAddr source; + XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath); [[nodiscard]] int xskFd() const noexcept; // wait until one event has occurred [[nodiscard]] int wait(int timeout); @@ -150,17 +144,36 @@ public: void send(std::vector& packets); // look at incoming packets in rx, return them if parsing succeeeded [[nodiscard]] std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); - void addWorker(std::shared_ptr s, const ComboAddress& dest); + void addWorker(std::shared_ptr s); + void addWorkerRoute(const std::shared_ptr& worker, const ComboAddress& dest); + void removeWorkerRoute(const ComboAddress& dest); [[nodiscard]] std::string getMetrics() const; void markAsFree(XskPacket&& packet); - [[nodiscard]] WorkerContainer& getWorkers() + [[nodiscard]] const std::shared_ptr& getWorkerByDescriptor(int desc) const + { + return d_workers.at(desc); + } + [[nodiscard]] std::shared_ptr getWorkerByDestination(const ComboAddress& destination) { - return workers; + auto routes = d_workerRoutes.lock(); + auto workerIt = routes->find(destination); + if (workerIt == routes->end()) { + return nullptr; + } + return workerIt->second; } [[nodiscard]] const std::vector& getDescriptors() const { return fds; } + [[nodiscard]] MACAddr getSourceMACAddress() const + { + return source; + } + [[nodiscard]] const std::string& getInterfaceName() const + { + return ifName; + } // pick ups available frames from uniqueEmptyFrameOffset // insert entries from uniqueEmptyFrameOffset into fq void fillFq(uint32_t fillSize = fillThreshold) noexcept; @@ -291,7 +304,6 @@ public: std::shared_ptr>> sharedEmptyFrameOffset; // list of frames that we own, used to generate new packets (health-check) vector uniqueEmptyFrameOffset; - std::string poolName; const size_t frameSize{XskSocket::getFrameSize()}; FDWrapper workerWaker; FDWrapper xskSocketWaker; From 2a5e9760ee8474ddfdfd5f2708a576972d2d3421 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 15:14:29 +0100 Subject: [PATCH 32/65] dnsdist: Clean up and reorganize XSK code --- pdns/dnsdist-lua-bindings.cc | 4 +- pdns/dnsdist.cc | 239 ++-------------- pdns/dnsdist.hh | 19 +- pdns/dnsdistdist/Makefile.am | 2 + pdns/dnsdistdist/dnsdist-backend.cc | 7 +- pdns/dnsdistdist/dnsdist-xsk.cc | 266 ++++++++++++++++++ pdns/dnsdistdist/dnsdist-xsk.hh | 46 +++ pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc | 7 - pdns/test-dnsdist_cc.cc | 13 +- pdns/xsk.cc | 224 ++++++++------- pdns/xsk.hh | 46 +-- 11 files changed, 499 insertions(+), 374 deletions(-) create mode 100644 pdns/dnsdistdist/dnsdist-xsk.cc create mode 100644 pdns/dnsdistdist/dnsdist-xsk.hh diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 45ee564cf64a..d0e602e22dfc 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -26,6 +26,7 @@ #include "dnsdist-lua.hh" #include "dnsdist-resolver.hh" #include "dnsdist-svc.hh" +#include "dnsdist-xsk.hh" #include "dolog.hh" #include "xsk.hh" @@ -754,9 +755,8 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) else { throw std::runtime_error("xskMapPath field is required!"); } - extern std::vector> g_xsk; auto socket = std::make_shared(frameNums, ifName, queue_id, path); - g_xsk.push_back(socket); + dnsdist::xsk::g_xsk.push_back(socket); return socket; }); luaCtx.registerFunction::*)()const>("getMetrics", [](const std::shared_ptr& xsk) { diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index cd71ac6b5c3d..81ad234bfe27 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -33,10 +33,6 @@ #include #include -#ifdef HAVE_XSK -#include -#endif /* HAVE_XSK */ - #ifdef HAVE_LIBEDIT #if defined (__OpenBSD__) || defined(__NetBSD__) // If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h @@ -73,6 +69,7 @@ #include "dnsdist-tcp.hh" #include "dnsdist-web.hh" #include "dnsdist-xpf.hh" +#include "dnsdist-xsk.hh" #include "base64.hh" #include "capabilities.hh" @@ -88,6 +85,7 @@ #include "misc.hh" #include "sstuff.hh" #include "threadname.hh" +#include "xsk.hh" /* Known sins: @@ -116,7 +114,6 @@ std::vector> g_dohlocals; std::vector> g_doqlocals; std::vector> g_doh3locals; std::vector> g_dnsCryptLocals; -std::vector> g_xsk; shared_ptr g_defaultBPFFilter{nullptr}; std::vector > g_dynBPFFilters; @@ -775,7 +772,7 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } } -static bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) +bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) { const dnsheader_aligned dh(response.data()); @@ -811,185 +808,6 @@ static bool processResponderPacket(std::shared_ptr& dss, Packet return true; } -#ifdef HAVE_XSK -namespace dnsdist::xsk -{ -void responderThread(std::shared_ptr dss) -{ - if (dss->xskInfo == nullptr) { - throw std::runtime_error("Starting XSK responder thread for a backend without XSK!"); - } - - try { - setThreadName("dnsdist/XskResp"); - auto localRespRuleActions = g_respruleactions.getLocal(); - auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); - auto xskInfo = dss->xskInfo; - auto pollfds = getPollFdsForWorker(*xskInfo); - const auto xskFd = xskInfo->workerWaker.getHandle(); - while (!dss->isStopped()) { - poll(pollfds.data(), pollfds.size(), -1); - bool needNotify = false; - if (pollfds[0].revents & POLLIN) { - needNotify = true; -#if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { -#else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { -#endif - if (packet.getDataLen() < sizeof(dnsheader)) { - xskInfo->markAsFree(std::move(packet)); - return; - } - const dnsheader_aligned dnsHeader(packet.getPayloadData()); - const auto queryId = dnsHeader->id; - auto ids = dss->getState(queryId); - if (ids) { - if (xskFd != ids->backendFD || !ids->isXSK()) { - dss->restoreState(queryId, std::move(*ids)); - ids = std::nullopt; - } - } - if (!ids) { - xskInfo->markAsFree(std::move(packet)); - return; - } - auto response = packet.clonePacketBuffer(); - if (response.size() > packet.getCapacity()) { - /* fallback to sending the packet via normal socket */ - ids->xskPacketHeader.clear(); - } - if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { - xskInfo->markAsFree(std::move(packet)); - vinfolog("XSK packet pushed to queue because processResponderPacket failed"); - return; - } - if (response.size() > packet.getCapacity()) { - /* fallback to sending the packet via normal socket */ - sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); - vinfolog("XSK packet falling back because packet is too large"); - xskInfo->markAsFree(std::move(packet)); - return; - } - packet.setHeader(ids->xskPacketHeader); - if (!packet.setPayload(response)) { - vinfolog("Unable to set payload !"); - } - if (ids->delayMsec > 0) { - vinfolog("XSK packet - adding delay"); - packet.addDelay(ids->delayMsec); - } - packet.updatePacket(); - xskInfo->pushToSendQueue(std::move(packet)); - }); - xskInfo->cleanSocketNotification(); - } - if (needNotify) { - xskInfo->notifyXskSocket(); - } - } - } - catch (const std::exception& e) { - errlog("XSK responder thread died because of exception: %s", e.what()); - } - catch (const PDNSException& e) { - errlog("XSK responder thread died because of PowerDNS exception: %s", e.reason); - } - catch (...) { - errlog("XSK responder thread died because of an exception: %s", "unknown"); - } -} - -static bool isXskQueryAcceptable(const XskPacket& packet, ClientState& cs, LocalHolders& holders, bool& expectProxyProtocol) noexcept -{ - const auto& from = packet.getFromAddr(); - expectProxyProtocol = expectProxyProtocolFrom(from); - if (!holders.acl->match(from) && !expectProxyProtocol) { - vinfolog("Query from %s dropped because of ACL", from.toStringWithPort()); - ++dnsdist::metrics::g_stats.aclDrops; - return false; - } - cs.queries++; - ++dnsdist::metrics::g_stats.queries; - - return true; -} - -static void XskRouter(std::shared_ptr xsk) -{ - setThreadName("dnsdist/XskRouter"); - uint32_t failed; - // packets to be submitted for sending - vector fillInTx; - const auto& fds = xsk->getDescriptors(); - // list of workers that need to be notified - std::set needNotify; - while (true) { - try { - auto ready = xsk->wait(-1); - // descriptor 0 gets incoming AF_XDP packets - if (fds.at(0).revents & POLLIN) { - auto packets = xsk->recv(64, &failed); - dnsdist::metrics::g_stats.nonCompliantQueries += failed; - for (auto &packet : packets) { - const auto dest = packet.getToAddr(); - auto worker = xsk->getWorkerByDestination(dest); - if (!worker) { - xsk->markAsFree(std::move(packet)); - continue; - } - worker->pushToProcessingQueue(std::move(packet)); - needNotify.insert(worker->workerWaker.getHandle()); - } - for (auto i : needNotify) { - uint64_t x = 1; - auto written = write(i, &x, sizeof(x)); - if (written != sizeof(x)) { - // oh, well, the worker is clearly overloaded - // but there is nothing we can do about it, - // and hopefully the queue will be processed eventually - } - } - needNotify.clear(); - ready--; - } - const auto backup = ready; - for (size_t fdIndex = 1; fdIndex < fds.size() && ready > 0; fdIndex++) { - if (fds.at(fdIndex).revents & POLLIN) { - ready--; - auto& info = xsk->getWorkerByDescriptor(fds.at(fdIndex).fd); -#if defined(__SANITIZE_THREAD__) - info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { -#else - info->outgoingPacketsQueue.consume_all([&](XskPacket& packet) { -#endif - if (!(packet.getFlags() & XskPacket::UPDATE)) { - xsk->markAsFree(std::move(packet)); - return; - } - if (packet.getFlags() & XskPacket::DELAY) { - xsk->pushDelayed(std::move(packet)); - return; - } - fillInTx.push_back(std::move(packet)); - }); - info->cleanWorkerNotification(); - } - } - xsk->pickUpReadyPacket(fillInTx); - xsk->recycle(4096); - xsk->fillFq(); - xsk->send(fillInTx); - ready = backup; - } - catch (...) { - vinfolog("Exception in XSK router loop"); - } - } -} -} -#endif /* HAVE_XSK */ - // listens on a dedicated socket, lobs answers from downstream servers to original requestors void responderThread(std::shared_ptr dss) { @@ -1065,7 +883,7 @@ void responderThread(std::shared_ptr dss) xskPacket->setHeader(ids->xskPacketHeader); xskPacket->setPayload(response); xskPacket->updatePacket(); - xskInfo->pushToSendQueue(std::move(*xskPacket)); + xskInfo->pushToSendQueue(*xskPacket); xskInfo->notifyXskSocket(); #endif /* HAVE_XSK */ } @@ -2061,7 +1879,7 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct #ifdef HAVE_XSK namespace dnsdist::xsk { -static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) +bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) { uint16_t queryId = 0; const auto& remote = packet.getFromAddr(); @@ -2077,7 +1895,7 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p try { bool expectProxyProtocol = false; - if (!isXskQueryAcceptable(packet, cs, holders, expectProxyProtocol)) { + if (!XskIsQueryAcceptable(packet, cs, holders, expectProxyProtocol)) { return false; } @@ -2191,36 +2009,6 @@ static bool ProcessXskQuery(ClientState& cs, LocalHolders& holders, XskPacket& p return false; } -static void xskClientThread(ClientState* cs) -{ - setThreadName("dnsdist/xskClient"); - auto xskInfo = cs->xskInfo; - LocalHolders holders; - - for (;;) { -#if defined(__SANITIZE_THREAD__) - while (!xskInfo->incomingPacketsQueue.lock()->read_available()) { -#else - while (!xskInfo->incomingPacketsQueue.read_available()) { -#endif - xskInfo->waitForXskSocket(); - } -#if defined(__SANITIZE_THREAD__) - xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { -#else - xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { -#endif - if (ProcessXskQuery(*cs, holders, packet)) { - packet.updatePacket(); - xskInfo->pushToSendQueue(std::move(packet)); - } - else { - xskInfo->markAsFree(std::move(packet)); - } - }); - xskInfo->notifyXskSocket(); - } -} } #endif /* HAVE_XSK */ @@ -3295,7 +3083,7 @@ namespace dnsdist static void startFrontends() { #ifdef HAVE_XSK - for (auto& xskContext : g_xsk) { + for (auto& xskContext : dnsdist::xsk::g_xsk) { std::thread xskThread(dnsdist::xsk::XskRouter, std::move(xskContext)); xskThread.detach(); } @@ -3306,9 +3094,9 @@ static void startFrontends() for (auto& clientState : g_frontends) { #ifdef HAVE_XSK if (clientState->xskInfo) { - XskSocket::addDestinationAddress(clientState->local); + dnsdist::xsk::addDestinationAddress(clientState->local); - std::thread xskCT(dnsdist::xsk::xskClientThread, clientState.get()); + std::thread xskCT(dnsdist::xsk::XskClientThread, clientState.get()); if (!clientState->cpus.empty()) { mapThreadToCPUList(xskCT.native_handle(), clientState->cpus); } @@ -3415,7 +3203,14 @@ int main(int argc, char** argv) g_hashperturb = dnsdist::getRandomValue(0xffffffff); #ifdef HAVE_XSK - XskSocket::clearDestinationAddresses(); +#warning FIXME: we need to provide a way to clear the map from Lua, as well as a way to change the map path + try { + dnsdist::xsk::clearDestinationAddresses(); + } + catch (const std::exception& exp) { + /* silently handle failures: at this point we don't even know if XSK is enabled, + and we might not have the correct map (not the default one). */ + } #endif /* HAVE_XSK */ ComboAddress clientAddress = ComboAddress(); diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 4cec0d03043c..254e64af61e4 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -56,7 +56,6 @@ #include "uuid-utils.hh" #include "proxy-protocol.hh" #include "stat_t.hh" -#include "xsk.hh" uint64_t uptimeOfProcess(const std::string& str); @@ -472,6 +471,10 @@ struct QueryCount { extern QueryCount g_qcount; +class XskPacket; +class XskSocket; +class XskWorker; + struct ClientState { ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set& cpus_, bool enableProxyProtocol): cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol) @@ -704,8 +707,10 @@ struct DownstreamState: public std::enable_shared_from_this std::string d_dohPath; std::string name; std::string nameWithAddr; - MACAddr sourceMACAddr; - MACAddr destMACAddr; +#ifdef HAVE_XSK + std::array sourceMACAddr; + std::array destMACAddr; +#endif /* HAVE_XSK */ size_t d_numberOfSockets{1}; size_t d_maxInFlightQueriesPerConn{1}; size_t d_tcpConcurrentConnectionsLimit{0}; @@ -1189,6 +1194,7 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders bool processResponse(PacketBuffer& response, const std::vector& respRuleActions, const std::vector& insertedRespRuleActions, DNSResponse& dr, bool muted); bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop); bool processResponseAfterRules(PacketBuffer& response, const std::vector& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted); +bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids); bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool actuallySend = true); @@ -1196,10 +1202,3 @@ ssize_t udpClientSendRequestToBackend(const std::shared_ptr& ss bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend); void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend); - -#ifdef HAVE_XSK -namespace dnsdist::xsk -{ -void responderThread(std::shared_ptr dss); -} -#endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index c95629daac97..1a1e3080c1c3 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -207,6 +207,7 @@ dnsdist_SOURCES = \ dnsdist-tcp.cc dnsdist-tcp.hh \ dnsdist-web.cc dnsdist-web.hh \ dnsdist-xpf.cc dnsdist-xpf.hh \ + dnsdist-xsk.cc dnsdist-xsk.hh \ dnsdist.cc dnsdist.hh \ dnslabeltext.cc \ dnsname.cc dnsname.hh \ @@ -305,6 +306,7 @@ testrunner_SOURCES = \ dnsdist-tcp-downstream.cc \ dnsdist-tcp.cc dnsdist-tcp.hh \ dnsdist-xpf.cc dnsdist-xpf.hh \ + dnsdist-xsk.cc dnsdist-xsk.hh \ dnsdist.hh \ dnslabeltext.cc \ dnsname.cc dnsname.hh \ diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 02cfea5c7d30..49b47b8cdcca 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -27,6 +27,7 @@ #include "dnsdist-random.hh" #include "dnsdist-rings.hh" #include "dnsdist-tcp.hh" +#include "dnsdist-xsk.hh" #include "dolog.hh" #include "xsk.hh" @@ -53,7 +54,7 @@ void DownstreamState::addXSKDestination(int fd) auto addresses = d_socketSourceAddresses.write_lock(); addresses->push_back(local); } - XskSocket::addDestinationAddress(local); + dnsdist::xsk::addDestinationAddress(local); d_xskSocket->addWorkerRoute(xskInfo, local); } @@ -65,7 +66,7 @@ void DownstreamState::removeXSKDestination(int fd) return; } - XskSocket::removeDestinationAddress(local); + dnsdist::xsk::removeDestinationAddress(local); d_xskSocket->removeWorkerRoute(local); } #endif /* HAVE_XSK */ @@ -329,7 +330,7 @@ void DownstreamState::start() if (connected && !threadStarted.test_and_set()) { #ifdef HAVE_XSK if (xskInfo != nullptr) { - tid = std::thread(dnsdist::xsk::responderThread, shared_from_this()); + tid = std::thread(dnsdist::xsk::XskResponderThread, shared_from_this()); } else { tid = std::thread(responderThread, shared_from_this()); diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc new file mode 100644 index 000000000000..4d1bec33cdc0 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -0,0 +1,266 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "dnsdist.hh" +#include "dnsdist-xsk.hh" + +#ifdef HAVE_XSK +#include + +#include "dnsdist-metrics.hh" +#include "dnsdist-proxy-protocol.hh" +#include "threadname.hh" +#include "xsk.hh" + +namespace dnsdist::xsk +{ +std::vector> g_xsk; + +void XskResponderThread(std::shared_ptr dss) +{ + if (dss->xskInfo == nullptr) { + throw std::runtime_error("Starting XSK responder thread for a backend without XSK!"); + } + + try { + setThreadName("dnsdist/XskResp"); + auto localRespRuleActions = g_respruleactions.getLocal(); + auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); + auto xskInfo = dss->xskInfo; + auto pollfds = getPollFdsForWorker(*xskInfo); + const auto xskFd = xskInfo->workerWaker.getHandle(); + while (!dss->isStopped()) { + poll(pollfds.data(), pollfds.size(), -1); + bool needNotify = false; + if ((pollfds[0].revents & POLLIN) != 0) { + needNotify = true; +#if defined(__SANITIZE_THREAD__) + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { +#else + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { +#endif + if (packet.getDataLen() < sizeof(dnsheader)) { + xskInfo->markAsFree(packet); + return; + } + const dnsheader_aligned dnsHeader(packet.getPayloadData()); + const auto queryId = dnsHeader->id; + auto ids = dss->getState(queryId); + if (ids) { + if (xskFd != ids->backendFD || !ids->isXSK()) { + dss->restoreState(queryId, std::move(*ids)); + ids = std::nullopt; + } + } + if (!ids) { + xskInfo->markAsFree(packet); + return; + } + auto response = packet.clonePacketBuffer(); + if (response.size() > packet.getCapacity()) { + /* fallback to sending the packet via normal socket */ + ids->xskPacketHeader.clear(); + } + if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { + xskInfo->markAsFree(packet); + vinfolog("XSK packet pushed to queue because processResponderPacket failed"); + return; + } + if (response.size() > packet.getCapacity()) { + /* fallback to sending the packet via normal socket */ + sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); + vinfolog("XSK packet falling back because packet is too large"); + xskInfo->markAsFree(packet); + return; + } + packet.setHeader(ids->xskPacketHeader); + if (!packet.setPayload(response)) { + vinfolog("Unable to set XSK payload !"); + } + if (ids->delayMsec > 0) { + packet.addDelay(ids->delayMsec); + } + packet.updatePacket(); + xskInfo->pushToSendQueue(packet); + }); + xskInfo->cleanSocketNotification(); + } + if (needNotify) { + xskInfo->notifyXskSocket(); + } + } + } + catch (const std::exception& e) { + errlog("XSK responder thread died because of exception: %s", e.what()); + } + catch (const PDNSException& e) { + errlog("XSK responder thread died because of PowerDNS exception: %s", e.reason); + } + catch (...) { + errlog("XSK responder thread died because of an exception: %s", "unknown"); + } +} + +bool XskIsQueryAcceptable(const XskPacket& packet, ClientState& clientState, LocalHolders& holders, bool& expectProxyProtocol) +{ + const auto& from = packet.getFromAddr(); + expectProxyProtocol = expectProxyProtocolFrom(from); + if (!holders.acl->match(from) && !expectProxyProtocol) { + vinfolog("Query from %s dropped because of ACL", from.toStringWithPort()); + ++dnsdist::metrics::g_stats.aclDrops; + return false; + } + clientState.queries++; + ++dnsdist::metrics::g_stats.queries; + + return true; +} + +void XskRouter(std::shared_ptr xsk) +{ + setThreadName("dnsdist/XskRouter"); + uint32_t failed = 0; + // packets to be submitted for sending + vector fillInTx; + const auto& fds = xsk->getDescriptors(); + // list of workers that need to be notified + std::set needNotify; + while (true) { + try { + auto ready = xsk->wait(-1); + // descriptor 0 gets incoming AF_XDP packets + if ((fds.at(0).revents & POLLIN) != 0) { + auto packets = xsk->recv(64, &failed); + dnsdist::metrics::g_stats.nonCompliantQueries += failed; + for (auto &packet : packets) { + const auto dest = packet.getToAddr(); + auto worker = xsk->getWorkerByDestination(dest); + if (!worker) { + xsk->markAsFree(packet); + continue; + } + worker->pushToProcessingQueue(packet); + needNotify.insert(worker->workerWaker.getHandle()); + } + for (auto socket : needNotify) { + uint64_t value = 1; + auto written = write(socket, &value, sizeof(value)); + if (written != sizeof(value)) { + // oh, well, the worker is clearly overloaded + // but there is nothing we can do about it, + // and hopefully the queue will be processed eventually + } + } + needNotify.clear(); + ready--; + } + const auto backup = ready; + for (size_t fdIndex = 1; fdIndex < fds.size() && ready > 0; fdIndex++) { + if ((fds.at(fdIndex).revents & POLLIN) != 0) { + ready--; + const auto& info = xsk->getWorkerByDescriptor(fds.at(fdIndex).fd); +#if defined(__SANITIZE_THREAD__) + info->outgoingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { +#else + info->outgoingPacketsQueue.consume_all([&](XskPacket& packet) { +#endif + if ((packet.getFlags() & XskPacket::UPDATE) == 0) { + xsk->markAsFree(packet); + return; + } + if ((packet.getFlags() & XskPacket::DELAY) != 0) { + xsk->pushDelayed(packet); + return; + } + fillInTx.push_back(packet); + }); + info->cleanWorkerNotification(); + } + } + xsk->pickUpReadyPacket(fillInTx); + xsk->recycle(4096); + xsk->fillFq(); + xsk->send(fillInTx); + ready = backup; + } + catch (...) { + vinfolog("Exception in XSK router loop"); + } + } +} + +void XskClientThread(ClientState* clientState) +{ + setThreadName("dnsdist/xskClient"); + auto xskInfo = clientState->xskInfo; + LocalHolders holders; + + for (;;) { +#if defined(__SANITIZE_THREAD__) + while (xskInfo->incomingPacketsQueue.lock()->read_available() == 0U) { +#else + while (xskInfo->incomingPacketsQueue.read_available() == 0U) { +#endif + xskInfo->waitForXskSocket(); + } +#if defined(__SANITIZE_THREAD__) + xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { +#else + xskInfo->incomingPacketsQueue.consume_all([&](XskPacket& packet) { +#endif + if (XskProcessQuery(*clientState, holders, packet)) { + packet.updatePacket(); + xskInfo->pushToSendQueue(packet); + } + else { + xskInfo->markAsFree(packet); + } + }); + xskInfo->notifyXskSocket(); + } +} + +static std::string getDestinationMap(bool isV6) { + return !isV6 ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; +} + +void addDestinationAddress(const ComboAddress& addr) +{ + auto map = getDestinationMap(addr.isIPv6()); + XskSocket::addDestinationAddress(map, addr); +} + +void removeDestinationAddress(const ComboAddress& addr) +{ + auto map = getDestinationMap(addr.isIPv6()); + XskSocket::removeDestinationAddress(map, addr); +} + +void clearDestinationAddresses() +{ + auto map = getDestinationMap(false); + XskSocket::clearDestinationMap(map, false); + map = getDestinationMap(true); + XskSocket::clearDestinationMap(map, true); +} + +} +#endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/dnsdist-xsk.hh b/pdns/dnsdistdist/dnsdist-xsk.hh new file mode 100644 index 000000000000..6a862d75b4f7 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-xsk.hh @@ -0,0 +1,46 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include "config.h" + +#ifdef HAVE_XSK +class XskPacket; +class XskSocket; +class XskWorker; + +#include + +namespace dnsdist::xsk +{ +void XskResponderThread(std::shared_ptr dss); +bool XskIsQueryAcceptable(const XskPacket& packet, ClientState& clientState, LocalHolders& holders, bool& expectProxyProtocol); +bool XskProcessQuery(ClientState& clientState, LocalHolders& holders, XskPacket& packet); +void XskRouter(std::shared_ptr xsk); +void XskClientThread(ClientState* clientState); +void addDestinationAddress(const ComboAddress& addr); +void removeDestinationAddress(const ComboAddress& addr); +void clearDestinationAddresses(); + +extern std::vector> g_xsk; +} +#endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc b/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc index e05c0b56b4af..bcb73b26529c 100644 --- a/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc +++ b/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc @@ -74,13 +74,6 @@ void responderThread(std::shared_ptr dss) { } -namespace dnsdist::xsk -{ -void responderThread(std::shared_ptr dss) -{ -} -} - string g_outputBuffer; std::atomic g_configurationDone{false}; diff --git a/pdns/test-dnsdist_cc.cc b/pdns/test-dnsdist_cc.cc index c29fdabba5e9..d3e0d29fc0b9 100644 --- a/pdns/test-dnsdist_cc.cc +++ b/pdns/test-dnsdist_cc.cc @@ -33,6 +33,7 @@ #include "dnsdist-internal-queries.hh" #include "dnsdist-tcp.hh" #include "dnsdist-xpf.hh" +#include "dnsdist-xsk.hh" #include "dolog.hh" #include "dnsname.hh" @@ -73,8 +74,18 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(DownstreamState const&) { return false; } +namespace dnsdist::xsk +{ +bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) +{ + return false; +} +} -std::vector> g_xsk; +bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) +{ + return false; +} BOOST_AUTO_TEST_SUITE(test_dnsdist_cc) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 15d9f14ec5f4..b656757a2d22 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -94,7 +94,7 @@ int XskSocket::firstTimeout() if (waitForDelay.empty()) { return -1; } - timespec now; + timespec now{}; gettime(&now); const auto& firstTime = waitForDelay.top().getSendTime(); const auto res = timeDifference(now, firstTime); @@ -104,8 +104,8 @@ int XskSocket::firstTimeout() return res; } -XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queue_id, const std::string& xskMapPath) : - frameNum(frameNum_), ifName(ifName_), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) +XskSocket::XskSocket(size_t frameNum_, std::string ifName_, uint32_t queue_id, const std::string& xskMapPath) : + frameNum(frameNum_), ifName(std::move(ifName_)), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared>>()) { if (!isPowOfTwo(frameNum_) || !isPowOfTwo(frameSize) || !isPowOfTwo(fqCapacity) || !isPowOfTwo(cqCapacity) || !isPowOfTwo(rxCapacity) || !isPowOfTwo(txCapacity)) { @@ -118,7 +118,7 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu memset(&tx, 0, sizeof(tx)); memset(&rx, 0, sizeof(rx)); - xsk_umem_config umemCfg; + xsk_umem_config umemCfg{}; umemCfg.fill_size = fqCapacity; umemCfg.comp_size = cqCapacity; umemCfg.frame_size = frameSize; @@ -127,7 +127,7 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu umem.umemInit(frameNum_ * frameSize, &cq, &fq, &umemCfg); { - xsk_socket_config socketCfg; + xsk_socket_config socketCfg{}; socketCfg.rx_size = rxCapacity; socketCfg.tx_size = txCapacity; socketCfg.bind_flags = XDP_USE_NEED_WAKEUP; @@ -170,13 +170,12 @@ XskSocket::XskSocket(size_t frameNum_, const std::string& ifName_, uint32_t queu } auto ret = bpf_map_update_elem(xskMapFd.getHandle(), &queue_id, &xskfd, 0); - if (ret) { + if (ret != 0) { throw std::runtime_error("Error inserting into xsk_map '" + xskMapPath + "': " + std::to_string(ret)); } } // see xdp.h in contrib/ - struct IPv4AndPort { uint32_t addr; @@ -188,16 +187,19 @@ struct IPv6AndPort uint16_t port; }; -static void clearDestinationMap(bool v6) +static FDWrapper getDestinationMap(const std::string& mapPath) { - const std::string mapPath = !v6 ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; - - const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); + auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); if (destMapFd.getHandle() < 0) { throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); } + return destMapFd; +} - if (!v6) { +void XskSocket::clearDestinationMap(const std::string& mapPath, bool isV6) +{ + auto destMapFd = getDestinationMap(mapPath); + if (!isV6) { IPv4AndPort prevKey{}; IPv4AndPort key{}; while (bpf_map_get_next_key(destMapFd.getHandle(), &prevKey, &key) == 0) { @@ -215,33 +217,16 @@ static void clearDestinationMap(bool v6) } } -void XskSocket::clearDestinationAddresses() +void XskSocket::addDestinationAddress(const std::string& mapPath, const ComboAddress& destination) { - clearDestinationMap(false); - clearDestinationMap(true); -} - -void XskSocket::addDestinationAddress(const ComboAddress& destination) -{ - // see xdp.h in contrib/ - - const std::string mapPath = destination.isIPv4() ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; - //if (!s_destinationAddressesMap) { - // throw std::runtime_error("The path of the XSK (AF_XDP) destination addresses map has not been set! Please consider using setXSKDestinationAddressesMapPath()."); - //} - - const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); - if (destMapFd.getHandle() < 0) { - throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); - } - + auto destMapFd = getDestinationMap(mapPath); bool value = true; if (destination.isIPv4()) { IPv4AndPort key{}; key.addr = destination.sin4.sin_addr.s_addr; key.port = destination.sin4.sin_port; auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); - if (ret) { + if (ret != 0) { throw std::runtime_error("Error inserting into xsk_map '" + mapPath + "': " + std::to_string(ret)); } } @@ -250,24 +235,15 @@ void XskSocket::addDestinationAddress(const ComboAddress& destination) key.addr = destination.sin6.sin6_addr; key.port = destination.sin6.sin6_port; auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); - if (ret) { + if (ret != 0) { throw std::runtime_error("Error inserting into XSK destination addresses map '" + mapPath + "': " + std::to_string(ret)); } } } -void XskSocket::removeDestinationAddress(const ComboAddress& destination) +void XskSocket::removeDestinationAddress(const std::string& mapPath, const ComboAddress& destination) { - const std::string mapPath = destination.isIPv4() ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; - //if (!s_destinationAddressesMap) { - // throw std::runtime_error("The path of the XSK (AF_XDP) destination addresses map has not been set! Please consider using setXSKDestinationAddressesMapPath()."); - //} - - const auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); - if (destMapFd.getHandle() < 0) { - throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); - } - + auto destMapFd = getDestinationMap(mapPath); if (destination.isIPv4()) { IPv4AndPort key{}; key.addr = destination.sin4.sin_addr.s_addr; @@ -334,7 +310,7 @@ int XskSocket::wait(int timeout) void XskSocket::send(std::vector& packets) { - while (packets.size() > 0) { + while (!packets.empty()) { auto packetSize = packets.size(); if (packetSize > std::numeric_limits::max()) { packetSize = std::numeric_limits::max(); @@ -375,6 +351,7 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou return res; } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const auto baseAddr = reinterpret_cast(umem.bufBase); uint32_t failed = 0; uint32_t processed = 0; @@ -382,6 +359,7 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou for (; processed < recvSize; processed++) { try { const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) XskPacket packet = XskPacket(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); #ifdef DEBUG_UMEM checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); @@ -389,10 +367,10 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou if (!packet.parse(false)) { ++failed; - markAsFree(std::move(packet)); + markAsFree(packet); } else { - res.push_back(std::move(packet)); + res.push_back(packet); } } catch (const std::exception& exp) { @@ -409,7 +387,7 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou // which will only be made available again when pushed into the fill // queue xsk_ring_cons__release(&rx, processed); - if (failedCount) { + if (failedCount != nullptr) { *failedCount = failed; } @@ -418,11 +396,12 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou void XskSocket::pickUpReadyPacket(std::vector& packets) { - timespec now; + timespec now{}; gettime(&now); while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top().getSendTime()) <= 0) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) auto& top = const_cast(waitForDelay.top()); - packets.push_back(std::move(top)); + packets.push_back(top); waitForDelay.pop(); } } @@ -461,10 +440,10 @@ void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* completionQueue std::string XskSocket::getMetrics() const { - struct xdp_statistics stats; + xdp_statistics stats{}; socklen_t optlen = sizeof(stats); int err = getsockopt(xskFd(), SOL_XDP, XDP_STATISTICS, &stats, &optlen); - if (err) { + if (err != 0) { return ""; } if (optlen != sizeof(struct xdp_statistics)) { @@ -481,7 +460,7 @@ std::string XskSocket::getMetrics() const return ret.str(); } -void XskSocket::markAsFree(XskPacket&& packet) +void XskSocket::markAsFree(const XskPacket& packet) { auto offset = frameOffset(packet); #ifdef DEBUG_UMEM @@ -493,10 +472,10 @@ void XskSocket::markAsFree(XskPacket&& packet) XskSocket::XskUmem::~XskUmem() { - if (umem) { + if (umem != nullptr) { xsk_umem__delete(umem); } - if (bufBase) { + if (bufBase != nullptr) { munmap(bufBase, size); } } @@ -536,39 +515,49 @@ void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept [[nodiscard]] iphdr XskPacket::getIPv4Header() const noexcept { iphdr ipv4Header{}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv4Header))); assert(!v6); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(&ipv4Header, frame + sizeof(ethhdr), sizeof(ipv4Header)); return ipv4Header; } void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(iphdr))); assert(!v6); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(frame + sizeof(ethhdr), &ipv4Header, sizeof(ipv4Header)); } [[nodiscard]] ipv6hdr XskPacket::getIPv6Header() const noexcept { ipv6hdr ipv6Header{}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); assert(v6); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(&ipv6Header, frame + sizeof(ethhdr), sizeof(ipv6Header)); return ipv6Header; } void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); assert(v6); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(frame + sizeof(ethhdr), &ipv6Header, sizeof(ipv6Header)); } [[nodiscard]] udphdr XskPacket::getUDPHeader() const noexcept { udphdr udpHeader{}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(&udpHeader, frame + getL4HeaderOffset(), sizeof(udpHeader)); return udpHeader; } @@ -600,7 +589,9 @@ bool XskPacket::parse(bool fromSetHeader) // check ip.check == ipv4Checksum() is not needed! // We check it in BPF program // we don't, actually. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) from = makeComboAddressFromRaw(4, reinterpret_cast(&ipHeader.saddr), sizeof(ipHeader.saddr)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) to = makeComboAddressFromRaw(4, reinterpret_cast(&ipHeader.daddr), sizeof(ipHeader.daddr)); l4Protocol = ipHeader.protocol; if (!fromSetHeader && (frameLength - sizeof(ethhdr)) != ntohs(ipHeader.tot_len)) { @@ -614,7 +605,9 @@ bool XskPacket::parse(bool fromSetHeader) } v6 = true; auto ipHeader = getIPv6Header(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) from = makeComboAddressFromRaw(6, reinterpret_cast(&ipHeader.saddr), sizeof(ipHeader.saddr)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) to = makeComboAddressFromRaw(6, reinterpret_cast(&ipHeader.daddr), sizeof(ipHeader.daddr)); l4Protocol = ipHeader.nexthdr; if (!fromSetHeader && (frameLength - (sizeof(ethhdr) + sizeof(ipv6hdr))) != ntohs(ipHeader.payload_len)) { @@ -668,12 +661,12 @@ void XskPacket::changeDirectAndUpdateChecksum() noexcept { auto ethHeader = getEthernetHeader(); { - uint8_t tmp[ETH_ALEN]; - static_assert(sizeof(tmp) == sizeof(ethHeader.h_dest), "Size Error"); - static_assert(sizeof(tmp) == sizeof(ethHeader.h_source), "Size Error"); - memcpy(tmp, ethHeader.h_dest, sizeof(tmp)); - memcpy(ethHeader.h_dest, ethHeader.h_source, sizeof(tmp)); - memcpy(ethHeader.h_source, tmp, sizeof(tmp)); + std::array tmp; + static_assert(tmp.size() == sizeof(ethHeader.h_dest), "Size Error"); + static_assert(tmp.size() == sizeof(ethHeader.h_source), "Size Error"); + memcpy(tmp.data(), ethHeader.h_dest, tmp.size()); + memcpy(ethHeader.h_dest, ethHeader.h_source, tmp.size()); + memcpy(ethHeader.h_source, tmp.data(), tmp.size()); } if (ethHeader.h_proto == htons(ETH_P_IPV6)) { // IPV6 @@ -750,6 +743,7 @@ PacketBuffer XskPacket::clonePacketBuffer() const { const auto size = getDataSize(); PacketBuffer tmp(size); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(tmp.data(), frame + getDataOffset(), size); return tmp; } @@ -758,6 +752,7 @@ void XskPacket::cloneIntoPacketBuffer(PacketBuffer& buffer) const { const auto size = getDataSize(); buffer.resize(size); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(buffer.data(), frame + getDataOffset(), size); } @@ -769,7 +764,9 @@ bool XskPacket::setPayload(const PacketBuffer& buf) return false; } flags |= UPDATE; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(frame + getDataOffset(), buf.data(), bufSize); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) frameLength = getDataOffset() + bufSize; return true; } @@ -777,14 +774,14 @@ bool XskPacket::setPayload(const PacketBuffer& buf) void XskPacket::addDelay(const int relativeMilliseconds) noexcept { gettime(&sendTime); - sendTime.tv_nsec += static_cast(relativeMilliseconds) * 1000000L; + sendTime.tv_nsec += static_cast(relativeMilliseconds) * 1000000L; sendTime.tv_sec += sendTime.tv_nsec / 1000000000L; sendTime.tv_nsec %= 1000000000L; } -bool operator<(const XskPacket& s1, const XskPacket& s2) noexcept +bool operator<(const XskPacket& lhs, const XskPacket& rhs) noexcept { - return s1.getSendTime() < s2.getSendTime(); + return lhs.getSendTime() < rhs.getSendTime(); } const ComboAddress& XskPacket::getFromAddr() const noexcept @@ -797,11 +794,11 @@ const ComboAddress& XskPacket::getToAddr() const noexcept return to; } -void XskWorker::notify(int fd) +void XskWorker::notify(int desc) { uint64_t value = 1; ssize_t res = 0; - while ((res = write(fd, &value, sizeof(value))) == EINTR) { + while ((res = write(desc, &value, sizeof(value))) == EINTR) { } if (res != sizeof(value)) { throw runtime_error("Unable Wake Up XskSocket Failed"); @@ -813,38 +810,39 @@ XskWorker::XskWorker() : { } -void XskWorker::pushToProcessingQueue(XskPacket&& packet) +void XskWorker::pushToProcessingQueue(XskPacket& packet) { #if defined(__SANITIZE_THREAD__) - if (!incomingPacketsQueue.lock()->push(std::move(packet))) { + if (!incomingPacketsQueue.lock()->push(packet)) { #else - if (!incomingPacketsQueue.push(std::move(packet))) { + if (!incomingPacketsQueue.push(packet)) { #endif - markAsFree(std::move(packet)); + markAsFree(packet); } } -void XskWorker::pushToSendQueue(XskPacket&& packet) +void XskWorker::pushToSendQueue(XskPacket& packet) { #if defined(__SANITIZE_THREAD__) - if (!outgoingPacketsQueue.lock()->push(std::move(packet))) { + if (!outgoingPacketsQueue.lock()->push(packet)) { #else - if (!outgoingPacketsQueue.push(std::move(packet))) { + if (!outgoingPacketsQueue.push(packet)) { #endif - markAsFree(std::move(packet)); + markAsFree(packet); } } const void* XskPacket::getPayloadData() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return frame + getDataOffset(); } void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept { auto ethHeader = getEthernetHeader(); - memcpy(ethHeader.h_dest, &toMAC[0], sizeof(MACAddr)); - memcpy(ethHeader.h_source, &fromMAC[0], sizeof(MACAddr)); + memcpy(ethHeader.h_dest, toMAC.data(), toMAC.size()); + memcpy(ethHeader.h_source, fromMAC.data(), fromMAC.size()); setEthernetHeader(ethHeader); to = to_; from = from_; @@ -901,17 +899,18 @@ void XskPacket::rewrite() noexcept setEthernetHeader(ethHeader); } -[[nodiscard]] __be16 XskPacket::ipv4Checksum(const struct iphdr* ip) noexcept +[[nodiscard]] __be16 XskPacket::ipv4Checksum(const struct iphdr* ipHeader) noexcept { - auto partial = ip_checksum_partial(ip, sizeof(iphdr), 0); + auto partial = ip_checksum_partial(ipHeader, sizeof(iphdr), 0); return ip_checksum_fold(partial); } -[[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum(const struct iphdr* ip) const noexcept +[[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum(const struct iphdr* ipHeader) const noexcept { // ip options is not supported !!! const auto l4Length = static_cast(getDataSize() + sizeof(udphdr)); - auto sum = tcp_udp_v4_header_checksum_partial(ip->saddr, ip->daddr, ip->protocol, l4Length); + auto sum = tcp_udp_v4_header_checksum_partial(ipHeader->saddr, ipHeader->daddr, ipHeader->protocol, l4Length); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); return ip_checksum_fold(sum); } @@ -920,6 +919,7 @@ void XskPacket::rewrite() noexcept { const auto l4Length = static_cast(getDataSize() + sizeof(udphdr)); uint64_t sum = tcp_udp_v6_header_checksum_partial(&ipv6->saddr, &ipv6->daddr, ipv6->nexthdr, l4Length); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); return ip_checksum_fold(sum); } @@ -930,21 +930,24 @@ void XskPacket::rewrite() noexcept /* Main loop: 32 bits at a time */ for (position = 0; position < len; position += sizeof(uint32_t)) { uint32_t value{}; - memcpy(&value, reinterpret_cast(ptr) + position, sizeof(value)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + memcpy(&value, static_cast(ptr) + position, sizeof(value)); sum += value; } /* Handle un-32bit-aligned trailing bytes */ if ((len - position) >= 2) { uint16_t value{}; - memcpy(&value, reinterpret_cast(ptr) + position, sizeof(value)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + memcpy(&value, static_cast(ptr) + position, sizeof(value)); sum += value; position += sizeof(value); } if ((len - position) > 0) { - const auto* p8 = static_cast(ptr) + position; - sum += ntohs(*p8 << 8); /* RFC says pad last byte */ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const auto* ptr8 = static_cast(ptr) + position; + sum += ntohs(*ptr8 << 8); /* RFC says pad last byte */ } return sum; @@ -952,10 +955,10 @@ void XskPacket::rewrite() noexcept [[nodiscard]] __be16 XskPacket::ip_checksum_fold(uint64_t sum) noexcept { - while (sum & ~0xffffffffULL) { + while ((sum & ~0xffffffffULL) != 0U) { sum = (sum >> 32) + (sum & 0xffffffffULL); } - while (sum & 0xffff0000ULL) { + while ((sum & 0xffff0000ULL) != 0U) { sum = (sum >> 16) + (sum & 0xffffULL); } @@ -963,9 +966,12 @@ void XskPacket::rewrite() noexcept } #ifndef __packed -#define __packed __attribute__((packed)) +#define packed_attribute __attribute__((packed)) +#else +#define packed_attribute __packed #endif +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) [[nodiscard]] uint64_t XskPacket::tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept { struct header @@ -982,7 +988,8 @@ void XskPacket::rewrite() noexcept /* We use a union here to avoid aliasing issues with gcc -O2 */ union { - header __packed fields; + header packed_attribute fields; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) uint32_t words[3]; }; }; @@ -1005,6 +1012,7 @@ void XskPacket::rewrite() noexcept struct in6_addr src_ip; struct in6_addr dst_ip; __be32 length; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) __uint8_t mbz[3]; __uint8_t next_header; }; @@ -1014,7 +1022,8 @@ void XskPacket::rewrite() noexcept /* We use a union here to avoid aliasing issues with gcc -O2 */ union { - header __packed fields; + header packed_attribute fields; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) uint32_t words[10]; }; }; @@ -1025,6 +1034,7 @@ void XskPacket::rewrite() noexcept pseudo_header.fields.src_ip = *src_ip; pseudo_header.fields.dst_ip = *dst_ip; pseudo_header.fields.length = htonl(len); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memset(pseudo_header.fields.mbz, 0, sizeof(pseudo_header.fields.mbz)); pseudo_header.fields.next_header = protocol; return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); @@ -1051,19 +1061,19 @@ PacketBuffer XskPacket::cloneHeadertoPacketBuffer() const int XskWorker::createEventfd() { - auto fd = ::eventfd(0, EFD_CLOEXEC); - if (fd < 0) { + auto desc = ::eventfd(0, EFD_CLOEXEC); + if (desc < 0) { throw runtime_error("Unable create eventfd"); } - return fd; + return desc; } -void XskWorker::waitForXskSocket() noexcept +void XskWorker::waitForXskSocket() const noexcept { uint64_t x = read(workerWaker, &x, sizeof(x)); } -void XskWorker::notifyXskSocket() noexcept +void XskWorker::notifyXskSocket() const { notify(xskSocketWaker); } @@ -1107,8 +1117,8 @@ void XskWorker::notifyWorker() noexcept void XskSocket::getMACFromIfName() { ifreq ifr{}; - auto fd = FDWrapper(::socket(AF_INET, SOCK_DGRAM, 0)); - if (fd < 0) { + auto desc = FDWrapper(::socket(AF_INET, SOCK_DGRAM, 0)); + if (desc < 0) { throw std::runtime_error("Error creating a socket to get the MAC address of interface " + ifName); } @@ -1117,27 +1127,27 @@ void XskSocket::getMACFromIfName() } strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); - if (ioctl(fd.getHandle(), SIOCGIFHWADDR, &ifr) < 0 || ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + if (ioctl(desc.getHandle(), SIOCGIFHWADDR, &ifr) < 0 || ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { throw std::runtime_error("Error getting MAC address for interface " + ifName); } static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= std::tuple_size{}, "The size of an ARPHRD_ETHER MAC address is smaller than expected"); memcpy(source.data(), ifr.ifr_hwaddr.sa_data, source.size()); } -[[nodiscard]] int XskSocket::timeDifference(const timespec& t1, const timespec& t2) noexcept +[[nodiscard]] int XskSocket::timeDifference(const timespec& lhs, const timespec& rhs) noexcept { - const auto res = t1.tv_sec * 1000 + t1.tv_nsec / 1000000L - (t2.tv_sec * 1000 + t2.tv_nsec / 1000000L); + const auto res = lhs.tv_sec * 1000 + lhs.tv_nsec / 1000000L - (rhs.tv_sec * 1000 + rhs.tv_nsec / 1000000L); return static_cast(res); } -void XskWorker::cleanWorkerNotification() noexcept +void XskWorker::cleanWorkerNotification() const noexcept { - uint64_t x = read(xskSocketWaker, &x, sizeof(x)); + uint64_t value = read(xskSocketWaker, &value, sizeof(value)); } -void XskWorker::cleanSocketNotification() noexcept +void XskWorker::cleanSocketNotification() const noexcept { - uint64_t x = read(workerWaker, &x, sizeof(x)); + uint64_t value = read(workerWaker, &value, sizeof(value)); } std::vector getPollFdsForWorker(XskWorker& info) @@ -1175,18 +1185,20 @@ std::optional XskWorker::getEmptyFrame() if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return XskPacket(offset + umemBufBase, 0, frameSize); } fillUniqueEmptyOffset(); if (!uniqueEmptyFrameOffset.empty()) { auto offset = uniqueEmptyFrameOffset.back(); uniqueEmptyFrameOffset.pop_back(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return XskPacket(offset + umemBufBase, 0, frameSize); } return std::nullopt; } -void XskWorker::markAsFree(XskPacket&& packet) +void XskWorker::markAsFree(const XskPacket& packet) { auto offset = frameOffset(packet); #ifdef DEBUG_UMEM @@ -1202,10 +1214,10 @@ uint32_t XskPacket::getFlags() const noexcept void XskPacket::updatePacket() noexcept { - if (!(flags & UPDATE)) { + if ((flags & UPDATE) == 0U) { return; } - if (!(flags & REWRITE)) { + if ((flags & REWRITE) == 0U) { changeDirectAndUpdateChecksum(); } } diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 702855a4739f..a2cc6f036b71 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -54,7 +54,7 @@ class XskPacket; class XskWorker; class XskSocket; -using MACAddr = std::array; +using MACAddr = std::array; #ifdef HAVE_XSK using XskPacketPtr = std::unique_ptr; @@ -76,7 +76,7 @@ class XskSocket xsk_umem* umem{nullptr}; uint8_t* bufBase{nullptr}; size_t size{0}; - void umemInit(size_t memSize, xsk_ring_cons* cq, xsk_ring_prod* fq, xsk_umem_config* config); + void umemInit(size_t memSize, xsk_ring_cons* completionQueue, xsk_ring_prod* fillQueue, xsk_umem_config* config); ~XskUmem(); XskUmem() = default; }; @@ -94,7 +94,7 @@ class XskSocket const size_t frameNum; // responses that have been delayed std::priority_queue waitForDelay; - MACAddr source; + MACAddr source{}; const std::string ifName; // AF_XDP socket then worker waker sockets vector fds; @@ -104,13 +104,13 @@ class XskSocket // simply recycled from cq after being processed by the kernel vector uniqueEmptyFrameOffset; // completion ring: queue where sent packets are stored by the kernel - xsk_ring_cons cq; + xsk_ring_cons cq{}; // rx ring: queue where the incoming packets are stored, read by XskRouter - xsk_ring_cons rx; + xsk_ring_cons rx{}; // fill ring: queue where umem entries available to be filled (put into rx) are stored - xsk_ring_prod fq; + xsk_ring_prod fq{}; // tx ring: queue where outgoing packets are stored - xsk_ring_prod tx; + xsk_ring_prod tx{}; std::unique_ptr socket; XskUmem umem; @@ -127,16 +127,16 @@ class XskSocket void getMACFromIfName(); public: - static void clearDestinationAddresses(); - static void addDestinationAddress(const ComboAddress& destination); - static void removeDestinationAddress(const ComboAddress& destination); + static void clearDestinationMap(const std::string& mapPath, bool isV6); + static void addDestinationAddress(const std::string& mapPath, const ComboAddress& destination); + static void removeDestinationAddress(const std::string& mapPath, const ComboAddress& destination); static constexpr size_t getFrameSize() { return frameSize; } // list of free umem entries that can be reused std::shared_ptr>> sharedEmptyFrameOffset; - XskSocket(size_t frameNum, const std::string& ifName, uint32_t queue_id, const std::string& xskMapPath); + XskSocket(size_t frameNum, std::string ifName, uint32_t queue_id, const std::string& xskMapPath); [[nodiscard]] int xskFd() const noexcept; // wait until one event has occurred [[nodiscard]] int wait(int timeout); @@ -144,11 +144,11 @@ public: void send(std::vector& packets); // look at incoming packets in rx, return them if parsing succeeeded [[nodiscard]] std::vector recv(uint32_t recvSizeMax, uint32_t* failedCount); - void addWorker(std::shared_ptr s); + void addWorker(std::shared_ptr worker); void addWorkerRoute(const std::shared_ptr& worker, const ComboAddress& dest); void removeWorkerRoute(const ComboAddress& dest); [[nodiscard]] std::string getMetrics() const; - void markAsFree(XskPacket&& packet); + void markAsFree(const XskPacket& packet); [[nodiscard]] const std::shared_ptr& getWorkerByDescriptor(int desc) const { return d_workers.at(desc); @@ -181,9 +181,9 @@ public: void recycle(size_t size) noexcept; // look at delayed packets, and send the ones that are ready void pickUpReadyPacket(std::vector& packets); - void pushDelayed(XskPacket&& packet) + void pushDelayed(XskPacket& packet) { - waitForDelay.push(std::move(packet)); + waitForDelay.push(packet); } }; @@ -203,7 +203,7 @@ public: private: ComboAddress from; ComboAddress to; - timespec sendTime; + timespec sendTime{}; uint8_t* frame{nullptr}; size_t frameLength{0}; size_t frameSize{0}; @@ -312,16 +312,16 @@ public: static int createEventfd(); static void notify(int fd); static std::shared_ptr create(); - void pushToProcessingQueue(XskPacket&& packet); - void pushToSendQueue(XskPacket&& packet); - void markAsFree(XskPacket&& packet); + void pushToProcessingQueue(XskPacket& packet); + void pushToSendQueue(XskPacket& packet); + void markAsFree(const XskPacket& packet); // notify worker that at least one packet is available for processing void notifyWorker() noexcept; // notify the router that packets are ready to be sent - void notifyXskSocket() noexcept; - void waitForXskSocket() noexcept; - void cleanWorkerNotification() noexcept; - void cleanSocketNotification() noexcept; + void notifyXskSocket() const; + void waitForXskSocket() const noexcept; + void cleanWorkerNotification() const noexcept; + void cleanSocketNotification() const noexcept; [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; // reap empty umem entry from sharedEmptyFrameOffset into uniqueEmptyFrameOffset void fillUniqueEmptyOffset(); From cce416f263ab6519a0621c2d0af8adf90f20a351 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 15:44:31 +0100 Subject: [PATCH 33/65] dnsdist: Format and delint the XSK code --- pdns/dnsdist.cc | 1 - pdns/dnsdist.hh | 2 +- pdns/dnsdistdist/dnsdist-xsk.cc | 9 ++++---- pdns/xsk.cc | 41 ++++++++++++++++++++++++--------- pdns/xsk.hh | 18 +++++++-------- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 81ad234bfe27..7bc864dbb5bc 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -3203,7 +3203,6 @@ int main(int argc, char** argv) g_hashperturb = dnsdist::getRandomValue(0xffffffff); #ifdef HAVE_XSK -#warning FIXME: we need to provide a way to clear the map from Lua, as well as a way to change the map path try { dnsdist::xsk::clearDestinationAddresses(); } diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 254e64af61e4..1a92f25dab10 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -987,7 +987,7 @@ public: void handleUDPTimeouts(); void reportTimeoutOrError(); void reportResponse(uint8_t rcode); - void submitHealthCheckResult(bool initial, bool newState); + void submitHealthCheckResult(bool initial, bool newResult); time_t getNextLazyHealthCheck(); uint16_t saveState(InternalQueryState&&); void restoreState(uint16_t id, InternalQueryState&&); diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc index 4d1bec33cdc0..fbc0313ae76c 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.cc +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -150,7 +150,7 @@ void XskRouter(std::shared_ptr xsk) if ((fds.at(0).revents & POLLIN) != 0) { auto packets = xsk->recv(64, &failed); dnsdist::metrics::g_stats.nonCompliantQueries += failed; - for (auto &packet : packets) { + for (auto& packet : packets) { const auto dest = packet.getToAddr(); auto worker = xsk->getWorkerByDestination(dest); if (!worker) { @@ -172,7 +172,6 @@ void XskRouter(std::shared_ptr xsk) needNotify.clear(); ready--; } - const auto backup = ready; for (size_t fdIndex = 1; fdIndex < fds.size() && ready > 0; fdIndex++) { if ((fds.at(fdIndex).revents & POLLIN) != 0) { ready--; @@ -199,7 +198,6 @@ void XskRouter(std::shared_ptr xsk) xsk->recycle(4096); xsk->fillFq(); xsk->send(fillInTx); - ready = backup; } catch (...) { vinfolog("Exception in XSK router loop"); @@ -238,8 +236,9 @@ void XskClientThread(ClientState* clientState) } } -static std::string getDestinationMap(bool isV6) { - return !isV6 ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; +static std::string getDestinationMap(bool isV6) +{ + return !isV6 ? "/sys/fs/bpf/dnsdist/xsk-destinations-v4" : "/sys/fs/bpf/dnsdist/xsk-destinations-v6"; } void addDestinationAddress(const ComboAddress& addr) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index b656757a2d22..cfa6912419b7 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -59,10 +59,17 @@ extern "C" #include "xsk.hh" #ifdef DEBUG_UMEM -namespace { +namespace +{ struct UmemEntryStatus { - enum class Status: uint8_t { Free, FillQueue, Received, TXQueue }; + enum class Status : uint8_t + { + Free, + FillQueue, + Received, + TXQueue + }; Status status{Status::Free}; }; @@ -144,7 +151,6 @@ XskSocket::XskSocket(size_t frameNum_, std::string ifName_, uint32_t queue_id, c uniqueEmptyFrameOffset.reserve(frameNum); { for (uint64_t i = 0; i < frameNum; i++) { - //uniqueEmptyFrameOffset.push_back(i * frameSize); uniqueEmptyFrameOffset.push_back(i * frameSize + XDP_PACKET_HEADROOM); #ifdef DEBUG_UMEM { @@ -261,11 +267,14 @@ void XskSocket::removeDestinationAddress(const std::string& mapPath, const Combo void XskSocket::fillFq(uint32_t fillSize) noexcept { { -#warning why are we collecting frames from unique into shared here, even though we need unique ones? + // if we have less than holdThreshold frames in the shared queue (which might be an issue + // when the XskWorker needs empty frames), move frames from the unique container into the + // shared one. This might not be optimal right now. auto frames = sharedEmptyFrameOffset->lock(); if (frames->size() < holdThreshold) { const auto moveSize = std::min(holdThreshold - frames->size(), uniqueEmptyFrameOffset.size()); if (moveSize > 0) { + // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) frames->insert(frames->end(), std::make_move_iterator(uniqueEmptyFrameOffset.end() - moveSize), std::make_move_iterator(uniqueEmptyFrameOffset.end())); uniqueEmptyFrameOffset.resize(uniqueEmptyFrameOffset.size() - moveSize); } @@ -304,7 +313,8 @@ int XskSocket::wait(int timeout) return packet.getFrameOffsetFrom(umem.bufBase); } -[[nodiscard]] int XskSocket::xskFd() const noexcept { +[[nodiscard]] int XskSocket::xskFd() const noexcept +{ return xsk_socket__fd(socket.get()); } @@ -359,7 +369,7 @@ std::vector XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCou for (; processed < recvSize; processed++) { try { const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) XskPacket packet = XskPacket(reinterpret_cast(desc->addr + baseAddr), desc->len, frameSize); #ifdef DEBUG_UMEM checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); @@ -565,6 +575,7 @@ void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept void XskPacket::setUDPHeader(const udphdr& udpHeader) noexcept { assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(frame + getL4HeaderOffset(), &udpHeader, sizeof(udpHeader)); } @@ -661,11 +672,14 @@ void XskPacket::changeDirectAndUpdateChecksum() noexcept { auto ethHeader = getEthernetHeader(); { - std::array tmp; + std::array tmp{}; static_assert(tmp.size() == sizeof(ethHeader.h_dest), "Size Error"); static_assert(tmp.size() == sizeof(ethHeader.h_source), "Size Error"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(tmp.data(), ethHeader.h_dest, tmp.size()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(ethHeader.h_dest, ethHeader.h_source, tmp.size()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(ethHeader.h_source, tmp.data(), tmp.size()); } if (ethHeader.h_proto == htons(ETH_P_IPV6)) { @@ -841,7 +855,9 @@ const void* XskPacket::getPayloadData() const void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept { auto ethHeader = getEthernetHeader(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(ethHeader.h_dest, toMAC.data(), toMAC.size()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(ethHeader.h_source, fromMAC.data(), fromMAC.size()); setEthernetHeader(ethHeader); to = to_; @@ -993,7 +1009,7 @@ void XskPacket::rewrite() noexcept uint32_t words[3]; }; }; - struct ipv4_pseudo_header_t pseudo_header; + struct ipv4_pseudo_header_t pseudo_header{}; static_assert(sizeof(pseudo_header) == 12, "IPv4 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ @@ -1027,7 +1043,7 @@ void XskPacket::rewrite() noexcept uint32_t words[10]; }; }; - struct ipv6_pseudo_header_t pseudo_header; + struct ipv6_pseudo_header_t pseudo_header{}; static_assert(sizeof(pseudo_header) == 40, "IPv6 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ @@ -1070,7 +1086,7 @@ int XskWorker::createEventfd() void XskWorker::waitForXskSocket() const noexcept { - uint64_t x = read(workerWaker, &x, sizeof(x)); + uint64_t value = read(workerWaker, &value, sizeof(value)); } void XskWorker::notifyXskSocket() const @@ -1109,7 +1125,7 @@ uint64_t XskWorker::frameOffset(const XskPacket& packet) const noexcept return packet.getFrameOffsetFrom(umemBufBase); } -void XskWorker::notifyWorker() noexcept +void XskWorker::notifyWorker() const { notify(workerWaker); } @@ -1126,11 +1142,13 @@ void XskSocket::getMACFromIfName() throw std::runtime_error("Unable to get MAC address for interface " + ifName + ": name too long"); } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); if (ioctl(desc.getHandle(), SIOCGIFHWADDR, &ifr) < 0 || ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { throw std::runtime_error("Error getting MAC address for interface " + ifName); } static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= std::tuple_size{}, "The size of an ARPHRD_ETHER MAC address is smaller than expected"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) memcpy(source.data(), ifr.ifr_hwaddr.sa_data, source.size()); } @@ -1175,6 +1193,7 @@ void XskWorker::fillUniqueEmptyOffset() auto frames = sharedEmptyFrameOffset->lock(); const auto moveSize = std::min(static_cast(32), frames->size()); if (moveSize > 0) { + // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) uniqueEmptyFrameOffset.insert(uniqueEmptyFrameOffset.end(), std::make_move_iterator(frames->end() - moveSize), std::make_move_iterator(frames->end())); frames->resize(frames->size() - moveSize); } diff --git a/pdns/xsk.hh b/pdns/xsk.hh index a2cc6f036b71..4ea36e38b36a 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -29,12 +29,12 @@ #include #include #include +#include #include #include #include #include #include -//#include #include #include #include @@ -120,7 +120,7 @@ class XskSocket static constexpr uint32_t txCapacity = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2; constexpr static bool isPowOfTwo(uint32_t value) noexcept; - [[nodiscard]] static int timeDifference(const timespec& t1, const timespec& t2) noexcept; + [[nodiscard]] static int timeDifference(const timespec& lhs, const timespec& rhs) noexcept; [[nodiscard]] uint64_t frameOffset(const XskPacket& packet) const noexcept; [[nodiscard]] int firstTimeout(); @@ -224,7 +224,7 @@ private: [[nodiscard]] __be16 tcp_udp_v4_checksum(const struct iphdr*) const noexcept; // You must set l4Header.check = 0 before calling this method [[nodiscard]] __be16 tcp_udp_v6_checksum(const struct ipv6hdr*) const noexcept; - /* offset of the L4 (udphdr) header (after ethhdr and iphdr/ipv6hdr) */ + /* offset of the L4 (udphdr) header (after ethhdr and iphdr/ipv6hdr) */ [[nodiscard]] size_t getL4HeaderOffset() const noexcept; /* offset of the data after the UDP header */ [[nodiscard]] size_t getDataOffset() const noexcept; @@ -271,7 +271,7 @@ public: return frame - base; } }; -bool operator<(const XskPacket& s1, const XskPacket& s2) noexcept; +bool operator<(const XskPacket& lhs, const XskPacket& rhs) noexcept; /* g++ defines __SANITIZE_THREAD__ clang++ supports the nice __has_feature(thread_sanitizer), @@ -288,9 +288,9 @@ bool operator<(const XskPacket& s1, const XskPacket& s2) noexcept; class XskWorker { #if defined(__SANITIZE_THREAD__) - using XskPacketRing = LockGuarded>>; + using XskPacketRing = LockGuarded>>; #else - using XskPacketRing = boost::lockfree::spsc_queue>; + using XskPacketRing = boost::lockfree::spsc_queue>; #endif public: @@ -299,7 +299,7 @@ public: // queue of packets processed by this worker (to be sent, or discarded) XskPacketRing outgoingPacketsQueue; - uint8_t* umemBufBase; + uint8_t* umemBufBase{nullptr}; // list of frames that are shared with the XskRouter std::shared_ptr>> sharedEmptyFrameOffset; // list of frames that we own, used to generate new packets (health-check) @@ -310,13 +310,13 @@ public: XskWorker(); static int createEventfd(); - static void notify(int fd); + static void notify(int desc); static std::shared_ptr create(); void pushToProcessingQueue(XskPacket& packet); void pushToSendQueue(XskPacket& packet); void markAsFree(const XskPacket& packet); // notify worker that at least one packet is available for processing - void notifyWorker() noexcept; + void notifyWorker() const; // notify the router that packets are ready to be sent void notifyXskSocket() const; void waitForXskSocket() const noexcept; From 682b927deaa11dc78de89763ecc5460b8e50bc6c Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 16:08:51 +0100 Subject: [PATCH 34/65] dnsdist: Revert the now unneeded changes made to dnsdist-healthchecks --- pdns/dnsdistdist/dnsdist-healthchecks.cc | 116 ++++++++++++++--------- pdns/dnsdistdist/dnsdist-healthchecks.hh | 38 -------- 2 files changed, 69 insertions(+), 85 deletions(-) diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.cc b/pdns/dnsdistdist/dnsdist-healthchecks.cc index d60c9dc58410..36805573e784 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.cc +++ b/pdns/dnsdistdist/dnsdist-healthchecks.cc @@ -31,7 +31,40 @@ bool g_verboseHealthChecks{false}; -bool handleResponse(std::shared_ptr& data) +struct HealthCheckData +{ + enum class TCPState : uint8_t + { + WritingQuery, + ReadingResponseSize, + ReadingResponse + }; + + HealthCheckData(FDMultiplexer& mplexer, std::shared_ptr downstream, DNSName&& checkName, uint16_t checkType, uint16_t checkClass, uint16_t queryID) : + d_ds(std::move(downstream)), d_mplexer(mplexer), d_udpSocket(-1), d_checkName(std::move(checkName)), d_checkType(checkType), d_checkClass(checkClass), d_queryID(queryID) + { + } + + const std::shared_ptr d_ds; + FDMultiplexer& d_mplexer; + std::unique_ptr d_tcpHandler{nullptr}; + std::unique_ptr d_ioState{nullptr}; + PacketBuffer d_buffer; + Socket d_udpSocket; + DNSName d_checkName; + struct timeval d_ttd + { + 0, 0 + }; + size_t d_bufferPos{0}; + uint16_t d_checkType; + uint16_t d_checkClass; + uint16_t d_queryID; + TCPState d_tcpState{TCPState::WritingQuery}; + bool d_initial{false}; +}; + +static bool handleResponse(std::shared_ptr& data) { const auto& downstream = data->d_ds; try { @@ -174,7 +207,7 @@ static void healthCheckUDPCallback(int descriptor, FDMultiplexer::funcparam_t& p } ++data->d_ds->d_healthCheckMetrics.d_networkErrors; data->d_ds->submitHealthCheckResult(data->d_initial, false); - data->d_mplexer->removeReadFD(descriptor); + data->d_mplexer.removeReadFD(descriptor); return; } } while (got < 0); @@ -191,7 +224,7 @@ static void healthCheckUDPCallback(int descriptor, FDMultiplexer::funcparam_t& p return; } - data->d_mplexer->removeReadFD(descriptor); + data->d_mplexer.removeReadFD(descriptor); data->d_ds->submitHealthCheckResult(data->d_initial, handleResponse(data)); } @@ -267,51 +300,36 @@ static void healthCheckTCPCallback(int descriptor, FDMultiplexer::funcparam_t& p } } -PacketBuffer getHealthCheckPacket(const std::shared_ptr& downstream, FDMultiplexer* mplexer, std::shared_ptr& data) -{ - uint16_t queryID = dnsdist::getRandomDNSID(); - DNSName checkName = downstream->d_config.checkName; - uint16_t checkType = downstream->d_config.checkType.getCode(); - uint16_t checkClass = downstream->d_config.checkClass; - dnsheader checkHeader{}; - memset(&checkHeader, 0, sizeof(checkHeader)); - - checkHeader.qdcount = htons(1); - checkHeader.id = queryID; - - checkHeader.rd = true; - if (downstream->d_config.setCD) { - checkHeader.cd = true; - } - - if (downstream->d_config.checkFunction) { - auto lock = g_lua.lock(); - auto ret = downstream->d_config.checkFunction(checkName, checkType, checkClass, &checkHeader); - checkName = std::get<0>(ret); - checkType = std::get<1>(ret); - checkClass = std::get<2>(ret); - } - PacketBuffer packet; - GenericDNSPacketWriter dpw(packet, checkName, checkType, checkClass); - dnsheader* requestHeader = dpw.getHeader(); - *requestHeader = checkHeader; - data = std::make_shared(mplexer, downstream, std::move(checkName), checkType, checkClass, queryID); - return packet; -} - -void setHealthCheckTime(const std::shared_ptr& downstream, const std::shared_ptr& data) -{ - gettimeofday(&data->d_ttd, nullptr); - data->d_ttd.tv_sec += static_castd_ttd.tv_sec)>(downstream->d_config.checkTimeout / 1000); /* ms to seconds */ - data->d_ttd.tv_usec += static_castd_ttd.tv_usec)>((downstream->d_config.checkTimeout % 1000) * 1000); /* remaining ms to us */ - normalizeTV(data->d_ttd); -} - bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared_ptr& downstream, bool initialCheck) { try { - std::shared_ptr data; - PacketBuffer packet = getHealthCheckPacket(downstream, mplexer.get(), data); + uint16_t queryID = dnsdist::getRandomDNSID(); + DNSName checkName = downstream->d_config.checkName; + uint16_t checkType = downstream->d_config.checkType.getCode(); + uint16_t checkClass = downstream->d_config.checkClass; + dnsheader checkHeader{}; + memset(&checkHeader, 0, sizeof(checkHeader)); + + checkHeader.qdcount = htons(1); + checkHeader.id = queryID; + + checkHeader.rd = true; + if (downstream->d_config.setCD) { + checkHeader.cd = true; + } + + if (downstream->d_config.checkFunction) { + auto lock = g_lua.lock(); + auto ret = downstream->d_config.checkFunction(checkName, checkType, checkClass, &checkHeader); + checkName = std::get<0>(ret); + checkType = std::get<1>(ret); + checkClass = std::get<2>(ret); + } + + PacketBuffer packet; + GenericDNSPacketWriter dpw(packet, checkName, checkType, checkClass); + dnsheader* requestHeader = dpw.getHeader(); + *requestHeader = checkHeader; /* we need to compute that _before_ adding the proxy protocol payload */ uint16_t packetSize = packet.size(); @@ -350,9 +368,13 @@ bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared sock.bind(downstream->d_config.sourceAddr, false); } + auto data = std::make_shared(*mplexer, downstream, std::move(checkName), checkType, checkClass, queryID); data->d_initial = initialCheck; - setHealthCheckTime(downstream, data); + gettimeofday(&data->d_ttd, nullptr); + data->d_ttd.tv_sec += static_castd_ttd.tv_sec)>(downstream->d_config.checkTimeout / 1000); /* ms to seconds */ + data->d_ttd.tv_usec += static_castd_ttd.tv_usec)>((downstream->d_config.checkTimeout % 1000) * 1000); /* remaining ms to us */ + normalizeTV(data->d_ttd); if (!downstream->doHealthcheckOverTCP()) { sock.connect(downstream->d_config.remote); @@ -361,7 +383,7 @@ bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared if (sent < 0) { int ret = errno; if (g_verboseHealthChecks) { - infolog("Error while sending a health check query (ID %d) to backend %s: %d", data->d_queryID, downstream->getNameWithAddr(), ret); + infolog("Error while sending a health check query (ID %d) to backend %s: %d", queryID, downstream->getNameWithAddr(), ret); } return false; } diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.hh b/pdns/dnsdistdist/dnsdist-healthchecks.hh index 2c5fc6015c80..e9da6c66de8b 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.hh +++ b/pdns/dnsdistdist/dnsdist-healthchecks.hh @@ -24,46 +24,8 @@ #include "dnsdist.hh" #include "mplexer.hh" #include "sstuff.hh" -#include "tcpiohandler-mplexer.hh" extern bool g_verboseHealthChecks; bool queueHealthCheck(std::unique_ptr& mplexer, const std::shared_ptr& downstream, bool initial = false); void handleQueuedHealthChecks(FDMultiplexer& mplexer, bool initial = false); - -struct HealthCheckData -{ - enum class TCPState : uint8_t - { - WritingQuery, - ReadingResponseSize, - ReadingResponse - }; - - HealthCheckData(FDMultiplexer* mplexer, std::shared_ptr downstream, DNSName&& checkName, uint16_t checkType, uint16_t checkClass, uint16_t queryID) : - d_ds(std::move(downstream)), d_mplexer(mplexer), d_udpSocket(-1), d_checkName(std::move(checkName)), d_checkType(checkType), d_checkClass(checkClass), d_queryID(queryID) - { - } - - const std::shared_ptr d_ds; - FDMultiplexer* d_mplexer{nullptr}; - std::unique_ptr d_tcpHandler{nullptr}; - std::unique_ptr d_ioState{nullptr}; - PacketBuffer d_buffer; - Socket d_udpSocket; - DNSName d_checkName; - struct timeval d_ttd - { - 0, 0 - }; - size_t d_bufferPos{0}; - uint16_t d_checkType; - uint16_t d_checkClass; - uint16_t d_queryID; - TCPState d_tcpState{TCPState::WritingQuery}; - bool d_initial{false}; -}; - -PacketBuffer getHealthCheckPacket(const std::shared_ptr& ds, FDMultiplexer* mplexer, std::shared_ptr& data); -void setHealthCheckTime(const std::shared_ptr& ds, const std::shared_ptr& data); -bool handleResponse(std::shared_ptr& data); From 39bbdd1b1e0761fe2d965f4b08b25a153b4bcfcc Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 16:09:22 +0100 Subject: [PATCH 35/65] xdp.py: The ports set is now unused in XSK mode --- contrib/xdp.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/xdp.py b/contrib/xdp.py index 67ad96b917f7..d6ef369708e9 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -13,7 +13,7 @@ DROP_ACTION = 1 TC_ACTION = 2 -# The interface on wich the filter will be attached +# The interface on wich the filter will be attached DEV = "eth1" # The list of blocked IPv4, IPv6 and QNames @@ -28,12 +28,13 @@ # Main useXsk = True -Ports = [53] cflag = [] if useXsk: cflag.append("-DUseXsk") -IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x),"+str(i)+")" for i in Ports) -cflag.append(r"-DIN_DNS_PORT_SET(x)=(" + IN_DNS_PORT_SET + r")") +else: + Ports = [53] + IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x),"+str(i)+")" for i in Ports) + cflag.append(r"-DIN_DNS_PORT_SET(x)=(" + IN_DNS_PORT_SET + r")") xdp = BPF(src_file="xdp-filter.ebpf.src", cflags=cflag) From 73a6da77257abc2fa21be05e9cbcda965b394b6f Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 16:09:52 +0100 Subject: [PATCH 36/65] dnsdist: Report the `AF_XDP` feature --- pdns/dnsdist.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 7bc864dbb5bc..358e970e52e4 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -2778,6 +2778,9 @@ static void reportFeatures() cout<<"dnsdist "< Date: Mon, 15 Jan 2024 16:50:45 +0100 Subject: [PATCH 37/65] dnsdist: Document the XSK feature --- pdns/dnsdist-lua.cc | 2 +- pdns/dnsdistdist/docs/advanced/index.rst | 1 + pdns/dnsdistdist/docs/advanced/tuning.rst | 31 +++++ pdns/dnsdistdist/docs/advanced/xsk.rst | 112 ++++++++++++++++++ .../docs/imgs/af_xdp_refused_cpu.png | Bin 0 -> 14861 bytes .../docs/imgs/af_xdp_refused_qps.png | Bin 0 -> 30140 bytes pdns/dnsdistdist/docs/reference/config.rst | 11 +- pdns/dnsdistdist/docs/reference/index.rst | 1 + pdns/dnsdistdist/docs/reference/tuning.rst | 1 + pdns/dnsdistdist/docs/reference/xsk.rst | 29 +++++ 10 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 pdns/dnsdistdist/docs/advanced/xsk.rst create mode 100644 pdns/dnsdistdist/docs/imgs/af_xdp_refused_cpu.png create mode 100644 pdns/dnsdistdist/docs/imgs/af_xdp_refused_qps.png create mode 100644 pdns/dnsdistdist/docs/reference/xsk.rst diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 793f226df697..724af95c67c8 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -648,7 +648,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) else { mac = getMACAddress(ret->d_config.remote); if (mac.size() != ret->d_config.destMACAddr.size()) { - throw runtime_error("Field 'MACAddr' is not set on 'newServer' directive for '" + ret->d_config.remote.toStringWithPort() + "' and cannot be retriever from the system either!"); + throw runtime_error("Field 'MACAddr' is not set on 'newServer' directive for '" + ret->d_config.remote.toStringWithPort() + "' and cannot be retrieved from the system either!"); } memcpy(ret->d_config.destMACAddr.data(), mac.data(), ret->d_config.destMACAddr.size()); } diff --git a/pdns/dnsdistdist/docs/advanced/index.rst b/pdns/dnsdistdist/docs/advanced/index.rst index e390db09eb7b..de4e05394992 100644 --- a/pdns/dnsdistdist/docs/advanced/index.rst +++ b/pdns/dnsdistdist/docs/advanced/index.rst @@ -22,3 +22,4 @@ These chapters contain information on the advanced features of dnsdist tls-sessions-management internal-design asynchronous-processing + xsk diff --git a/pdns/dnsdistdist/docs/advanced/tuning.rst b/pdns/dnsdistdist/docs/advanced/tuning.rst index c8d78a0d67a6..e831485a7980 100644 --- a/pdns/dnsdistdist/docs/advanced/tuning.rst +++ b/pdns/dnsdistdist/docs/advanced/tuning.rst @@ -70,6 +70,11 @@ For DNS over HTTPS, every :func:`addDOHLocal` directive adds a new thread dealin When dealing with a large traffic load, it might happen that the internal pipe used to pass queries between the threads handling the incoming connections and the one getting a response from the backend become full too quickly, degrading performance and causing timeouts. This can be prevented by increasing the size of the internal pipe buffer, via the `internalPipeBufferSize` option of :func:`addDOHLocal`. Setting a value of `1048576` is known to yield good results on Linux. +UDP buffer sizes +---------------- + +The operating system usually maintains buffers of incoming and outgoing datagrams for UDP sockets, to deal with short spikes where packets are received or emitted faster than the network layer can process them. On medium to large setups, it is usually useful to increase these buffers to deal with large spikes. This can be done via the :func:`setUDPSocketBufferSizes`. + Outgoing DoH ------------ @@ -193,3 +198,29 @@ Memory usage per connection for connected protocols: +---------------------------------+-----------------------------+ | DoH (w/ releaseBuffers) | 15 kB | +---------------------------------+-----------------------------+ + +Firewall connection tracking +---------------------------- + +When dealing with a lot of queries per second, dnsdist puts a severe stress on any stateful (connection tracking) firewall, so much so that the firewall may fail. + +Specifically, many Linux distributions run with a connection tracking firewall configured. For high load operation (thousands of queries/second), it is advised to either turn off ``iptables`` and ``nftables`` completely, or use the ``NOTRACK`` feature to make sure client DNS traffic bypasses the connection tracking. + +Network interface receive queues +-------------------------------- + +Most high-speed (>= 10 Gbps) network interfaces support multiple queues to offer better performance, using hashing to dispatch incoming packets into a specific queue. + +Unfortunately the default hashing algorithm is very often considering the source and destination addresses only, which might be an issue when dnsdist is placed behind a frontend, for example. + +On Linux it is possible to inspect the current network flow hashing policy via ``ethtool``:: + + $ sudo ethtool -n enp1s0 rx-flow-hash udp4 + UDP over IPV4 flows use these fields for computing Hash flow key: + IP SA + IP DA + +In this example only the source (``IP SA``) and destination (``IP DA``) addresses are indeed used, meaning that all packets coming from the same source address to the same destination address will end up in the same receive queue, which is not optimal. To take the source and destination ports into account as well:: + + $ sudo ethtool -N enp1s0 rx-flow-hash udp4 sdfn + $ diff --git a/pdns/dnsdistdist/docs/advanced/xsk.rst b/pdns/dnsdistdist/docs/advanced/xsk.rst new file mode 100644 index 000000000000..c61f88e9b229 --- /dev/null +++ b/pdns/dnsdistdist/docs/advanced/xsk.rst @@ -0,0 +1,112 @@ +``AF_XDP`` / ``XSK`` +==================== + +Since 1.9.0, :program:`dnsdist` can use `AF_XDP `_ for high performance UDP packet processing recent Linux kernels (4.18+). It requires :program:`dnsdist` to have the ``CAP_NET_ADMIN`` and ``CAP_SYS_ADMIN`` capabilities at startup, and to have been compiled with the ``--with-xsk`` configure option. + +.. note:: + To retain the required capabilities it is necessary to call :func:`addCapabilitiesToRetain` during startup, as :program:`dnsdist` drops capabilities after startup. + +.. note:: + ``AppArmor`` users might need to update their policy to allow :program:`dnsdist` to keep the capabilities. Adding ``capability sys_admin,`` (for ``CAP_SYS_ADMIN``) and ``capability net_admin,`` (for ``CAP_NET_ADMIN``) lines to the policy file is usually enough. + +The way ``AF_XDP`` works is that :program:`dnsdist` allocates a number of frames in a memory area called a ``UMEM``, which is accessible both by the program, in userspace, and by the kernel. Using in-memory ring buffers, the receive (``RX``), transmit (``TX``), completion (``cq``) and fill (``fq``) rings, the kernel can very efficiently pass raw incoming packets to :program:`dnsdist`, which can in return pass raw outgoing packets to the kernel. +In addition to these, an ``eBPF`` ``XDP`` program needs to be loaded to decide which packets to distribute via the ``AF_XDP`` socket (and to which, as there are usually more than one). This program uses a ``BPF`` map of type ``XSKMAP`` (located at ``/sys/fs/bpf/dnsdist/xskmap`` by default) that is populated by :program:``dnsdist` at startup to locate the ``AF_XDP`` socket to use. :program:`dnsdist` also sets up two additional ``BPF`` maps (located at ``/sys/fs/bpf/dnsdist/xsk-destinations-v4`` and ``/sys/fs/bpf/dnsdist/xsk-destinations-v6``) to let the ``XDP`` program know which IP destinations are to be routed to the ``AF_XDP`` sockets and which are to be passed to the regular network stack (health-checks queries and responses, for example). A ready-to-use `XDP program `_ can be found in the ``contrib`` directory of the PowerDNS Git repository. The name of the network interface to use might have to be updated, though. Once it has been updated, the ``XDP`` program can be started:: + + $ python xdp.py + +Then :program:`dnsdist` needs to be configured to use ``AF_XDP``, first by creating a :class:`XskSocket` object that are tied to a specific queue of a specific network interface: + +.. code-block:: lua + + xsk = newXsk({ifName="enp1s0", NIC_queue_id=0, frameNums=65536, xskMapPath="/sys/fs/bpf/dnsdist/xskmap"}) + +This ties the new object to the first receive queue on ``enp1s0``, allocating 65536 frames and populating the map located at ``/sys/fs/bpf/dnsdist/xskmap``. + +Then we can tell :program:`dnsdist` to listen for ``AF_XDP`` packets to ``108.61.103.88:53``, in addition to packets coming via the regular network stack: + +.. code-block:: lua + + addLocal("192.0.2.1:53", {xskSocket=xsk}) + +In practice most high-speed (>= 10 Gbps) network interfaces support multiple queues to offer better performance, so we need to allocate one :class:`XskSocket` per queue. We can retrieve the number of queues for a given interface via:: + + $ sudo ethtool -l enp1s0 + Channel parameters for enp1s0: + Pre-set maximums: + RX: n/a + TX: n/a + Other: 1 + Combined: 8 + Current hardware settings: + RX: n/a + TX: n/a + Other: 1 + Combined: 8 + +The ``Combined`` lines tell us that the interface supports 8 queues, so we can do something like this: + +.. code-block:: lua + + for i=1,8 do + xsk = newXsk({ifName="enp1s0", NIC_queue_id=i-1, frameNums=65536, xskMapPath="/sys/fs/bpf/dnsdist/xskmap"}) + addLocal("192.0.2.1:53", {xskSocket=xsk, reusePort=true}) + end + +We can also instructs :program:`dnsdist` to use ``AF_XDP`` to send and receive UDP packets to a backend: + +.. code-block:: lua + + newServer("192.0.2.2:53", {xskSocket=xsk}) + +We are not passing the MAC address of the backend (or the gateway to reach it) directly, so :program:`dnsdist` will try to fetch it from the system MAC address cache. This may not work, in which case we might need to pass explicitly: + +.. code-block:: lua + + newServer("192.0.2.2:53", {xskSocket=xsk, MACAddr='00:11:22:33:44:55'}) + + +Performance +----------- + +Using `kxdpgun `_, we can compare the performance of :program:`dnsdist` using the regular network stack and ``AF_XDP``. + +This test was realized using two Intel E3-1270 with 4 cores (8 threads) running at 3.8 Ghz, using 10 Gbps network cards. On both the injector running ``kxdpgun`` and the box running :program:`dnsdist` there was no firewall, the governor was set to ``performance``, the UDP buffers were raised to ``16777216`` and the receive queue hash policy set to use the IP addresses and ports (see :doc:`tuning`). + +:program:`dnsdist` was configured to immediately respond to incoming queries with ``REFUSED``: + +.. code-block:: lua + + addAction(AllRule(), RCodeAction(DNSRCode.REFUSED)) + +On the injector box we executed:: + + $ sudo kxdpgun -Q 2500000 -p 53 -i random_1M 192.0.2.1 -t 60 + using interface enp1s0, XDP threads 8, UDP, native mode + [...] + +We first ran without ``AF_XDP``: + +.. code-block:: lua + + for i=1,8 do + addLocal("192.0.2.1:53", {reusePort=true}) + end + +then with: + +.. code-block:: lua + + for i=1,8 do + xsk = newXsk({ifName="enp1s0", NIC_queue_id=i-1, frameNums=65536, xskMapPath="/sys/fs/bpf/dnsdist/xskmap"}) + addLocal("192.0.2.1:53", {xskSocket=xsk, reusePort=true}) + end + +.. figure:: ../imgs/af_xdp_refused_qps.png + :align: center + :alt: AF_XDP QPS + +.. figure:: ../imgs/af_xdp_refused_cpu.png + :align: center + :alt: AF_XDP CPU + +The first run handled roughly 1 million QPS, the second run 2.5 millions, with the CPU usage being much lower in the ``AF_XDP`` case. diff --git a/pdns/dnsdistdist/docs/imgs/af_xdp_refused_cpu.png b/pdns/dnsdistdist/docs/imgs/af_xdp_refused_cpu.png new file mode 100644 index 0000000000000000000000000000000000000000..f9d2debdf2a32b29dcf69de2c5ca6df48e15e35f GIT binary patch literal 14861 zcmd6Oc{r8r_wOzhQX&c=$vk98rcFYINIOG@%%#X2HbRCB6&W&S+L1AYWXjwiLgtz9 z3fbmNnK|oG%KQEOe&;&Zxy~PFU+;Cj+WUE)d);gJtj}8adVG~J@`n$eIEX@_4l7)e zQAME$$Wf?0{6u@<8-Jm5EWb_A4kIP&# zyhce~CDpix@W61wd}iW5s`4~6KVH#f$H#g4)BW(T9kjEdk;M9<(TC)em5ZVY9-lr% zOuUcr!Kor_77oKUhH)$|zn~ZsxsZ&`&u}rB8OYTtiyj|$9?$gt<~q=mf0<3;=pK*; zg_831{Ykq0*8;&)f$i_S4~SAA-$>s1fB8ifnbYkXH(tzaSQhpjU!mV#lIR)xuu=$f zjWs717gMM}dt2MB+qY#U_vvlfOA*pzPvcj=q{g`w-}rbSrvCU8{hlxd1%)VK>watZ zH60eNiTgB4!yMe)GI7N4w6!m>q7=jw*-1%Bl$4b0*VP`+DhUb-HZ982SHD1>rqaAf z!>mM?xc=yg1hPLePhSc)ITtFAhjb!$C2P#^0#emkfkRF4UZlk@@hD}2RV73TL`^2& zXcwZg-Z^$x=KLT0Orfl)Z|*44N8Y+fQ*MuXxyO|ig?hYiUYPDn4)b{@O}9M9eGB^+ zqLvT1rf~hx6S8?VBytYFuT5QR%EgaY$C5r6wH5{$ z>WgpVyC%55@2Q$bKWZ&=TQU`Wk5=!doF-*|wBc2AA}epu*DE`KpL)Y>eDSRgvG2u( z%Kg&AO81{#p_>pPJ&eZhRk@L0@h0){`aNP^*r?5QAFoPB>nr5apavJmq?hkn-}@gz zdvPd#kRkHrrzXgcm#nz>IM#ROjjz$Rqv%~4zI^Xfcz`#aXiW5?)U0f#Ei2CvRxxHa zHur^Qd9@U!G5qqNiSsDVadWL=YmMTn_zT-QG;VsjWahb$rM9+qblnIi4^MxdshYZa zxPJCPiKDfeT4?7)fUmEqm6d*l+eVI2wdbUQqGD}=>=U$@SmX2aYjAgMT$XOpTxMZE zM1xvU+{THo!i4lC=)j9!m~(mzDYxea%@*xgver->#joA@9vFPDj}f-)$$O^nY}%7& zy43G7GuE0K=Q1r?rqAoXicbvcw`)9~YtpD+;xN^cd|6FR&C9~9B}qX(kj`n)hstd} zW*}0DRr<3xF~#`A#Qv+1M5{_oIos|i)7oB;w{+b^QE8&=^@nuzhRV(N!^gbd|5#|f zy0t!fL74hbcz8H(#rm7pvC_p39fo`V>`@5O*3mI=8VN5hE*|{&=)(LLfsv6BFTx{k zlR4GHGbu?)U!I?LdGEP@v;oKD{Z=W0XuQ;6S+vS*Bm{<`NJw04ZjE<`6(K}bq zo;~aLlmL{>v9;Z!mhVMM06>=yX@kN2^ZGD>L%D!){vw z3dSVe7!3+!{SzlnWEFjno*k-=QA#-?vHZ_ucdl?BR?XBjEmmW?zijP$jI|K;p%CUz zm6i85S4R6P%!Oj$bCJ_uZfLkBG&Cf{^IX)e&PaZv2wsD7lg5)zK7~AcR%ks?K2rCL zSEs;Y@PiNM$H`728_U3%UP+M-8@dzNCXVX%o?&52&g^`1{zI`dUNrWl`n-%XsjVv! z_{G(%EFS4grlw;P*%fX}eU2B_CUbp!d@?k%F;}k8^WUsYxe_xh$gBJIX=LOjB2!Dt z@WP?Q@@@*_}hPx~2DK(^8+CSzA979&x-1hNe2B&CIKtbMsTQpm|jQ zt){+yxzqf2^$d;M#>O4(?ad<88%G#zSG^hivqfjskBsFu%-my5=*}1Yd4$#c(#dC?6nF1E?Hq~_&*I{`+%nx)!r-=i zBez?LsHUzi=Irg-EWHvUM#p}q(Y7=-V#dtO%>4X(2>GrYqoSgs?(S~yrz(kZnOZrg zpMDy=TMD5FE_agurZ*;4;6y+u>lN)nD?JMfi+6pbrEF(eLroiEMQMetn7!LNJL_Cu zJs=?b`MJz{uPZCQtNoWn*_7kbFrU`oU|E;4LKiNG*iCjFxl1I)1ycvip4io`cgdO z(Q{s1FbxJpK5wpD8gbgOXpMVcw~1c-tdjTB#<;ki(fR|P8(uWWe)#K`t1SELBWmAC zQ1NP*cyHf30=X;$gcQGW*B^_7Vd|OxVg7NCRr>KqS$99ysw1o@6`5U_%(i{SK1igI zkQDzyHO|&^A*$zGu|9dnF0gmy=tq z3L=Ld5?p+A+}K3^Ube^a_{M#|0G1RzvTsZvt#o{ps{BaGai!9`gBn4ja?tyvLw$aD5DfMRyUn(?aH%Uib462fv>@$zM2 zMkgmHK=(QK^=kJn3n7+)`V@X1v;I=2q3R&_vE*kBI58?(S~1tvh0*6O(>c{ALoY0z zkD7i~u|D$VcHQ>|T}scA!(7wU9R2?N05KaAQ({g|Fxkz*UVv;R*4D2@b|dm_Hojz{6a%*@s*#+H zxD-R z>moJ=kGMyQJK4fg)`YfpcC@s#kVr=zmz)jHofzpO@oqN@7bqAWz7=|M3ne%-sAu$h(oHS z7Lwhfl+ZXUz@oLK8S6WD-hPiZhoo$MHVOpu0xq&zaJTh}aCk&S?IWwD={^LF-W#6s z7Qr=GS8PO(sb0VCw7M`B7#R5T=TE@zygV+`!fp#ID=X5XP`N~#x#1cp0a#dAq%?ZV zT$cW~dD2xv=F4AS_ijm}BM4mkzMPnqWd#gG%yF8I&+y&A*5;CqRnbUBb~z^p2aTA6 zg{7q>*jo?z^!=BV`P22hprC!_WAYdw%aAQ+$i|hGGSZjitB=gvJC*fM&lugzT95}0 z2n5D+vUhcHqD()#dZ_5$>^HTm=iB@Hd8~CIEq63eEY9VZc64;eJ~>`PqWMZI$GAQk zVkG?8GaxTc?dj@KQBfd9deHHU2;ta>wE6|Q%INT`K6d#+vS%%Ct{eVzJW4@v=6K!8 z{OCvp8rQJ8>MZKp4T{NjTg-@#j&@!9Srd1B`@_o0DzUXb@7QPG33-)=*Lqi0O-)K- z?|#tI($dz}R$5y6?c2=?uH`C^wYws8JkPkexH3vhb@go*+ckAEJEkW-U#tMaD(1X^ zfB5i#^d%!BBRRPPCZeHao!@sk4WTP*l@0Zwtaow6L^jk}d2HOr>(bRT4zXqKEw4v$4AHj9|HB-u}NtUOF!k_$=dD{;KA{^O%@ zZWI|%DB;_+VR|JFD;j)JN_OAB08>~~1<*wDt_W&(-UVM@zG<}ibU`xi=N=>%&{3nq zyqz8VX0m}lB9O7ZGT++Lf<^1Ox^9s0mfjs}VUd1ZH-fhLCIf*cK7)kDa|l2h0pm*{ zSP1y3-a;{41Hgwx_nQ$PDt#?@2;eLcYzD~?&A*_P_~@qUM1 z%jP!(D9Ky*=BAiZIrF1=4I8o_HI_|j2MCegR|4W~E~~5i6%x|VDq3qmStWBS{Cm(% z*elk`e9>}ifk+@0#aw{ux{C>I?&3; zah#QbyqW;Tgyugb7T>n-l}e?9iwyT6jBw|9@EsI5D5ifCYRnG_iba#uAFh0Rl)e4~ zPG z_gGQXyU7J1&X(^vF-nf?w8^*sPJ_QWHQXu%t&e|Q=9@UQ>}02*BVROL^0Ucg?*`GX zwQwTnz&PQ;68xHHHm{5lp=2BRX_K2p2Ydp_sjyA_86@2%sspv-7%kTn-o_5O|M7yr zIlP%pPp|ID@Moic)-YGzMko;KhJ(0TOzTA=j&uuue^QT^Oo&wu*ZyjrU_`$Jnxwz( zA!htUr9PsB7kJ?2jo3$!eLHJ{M!)*25ahbDM){TN(_lsVWWxj8SKZ@Emm7^-d-tNq ztC}9o+$-xxs+z~|4V%WW-cPfT50BS-G738u;`tIj&L4VSw%c`VW%HtF@{jixaSByE zEjRM*wN$@JXN}!0e!>;w$3^>uN60~pyLj%nxl`tOXcjDskx0rAiyqA zp#dCrF6*}{gfhj!!NK^}twVGoO%_5fW&Qmm`CTqP0GTp#VF&fxvCw16MQfU?F}J2j z$Y@^c7U394@2`WEh!{0=N}x=|-!#gp2)=5IHF%yW+pw>TZ8A13KYtODj0Aw`mnZ@F zOgXHX#JP6#^hD!uWsWobV$*MpAl=~LUsP4q)#l_LN42t~UKmyGr^kL7r=AfYm)7r7 zSr`vYC?6Zfw>1V3OYS3@NxyDlVgl&{$i0Ju5M$-g>^Wg!O?CD9Xu*ljip|9^4z<9O z=X;u)n_XVijkFkV9eY2gMTn=i+1`UtH85elhDUU`kFs`t9H?`r{bLnEk|0|_X4&km z*oY~uoN{>f>=`T)qr{rq9gD;DH8q`5P@VCaTTZ?6ewVQs*OWAL2xaxIC1gm(5ibya z_#8EyVf*pn;kwVCpFX=3($?J_6&A)EG}kePWerVBPp_SkE?j$b_S=Ub;GHq`azxFm zi@=e{UoY#Mo0*;d{!I)`ryx@;g`FmSRNGmwTKB#)r>BRB0aupna}yi9(!D4CXtJo9 zy!@m4>Q-MO`OAGyHeQ{65zbsQVhg-sMek+j#0X0R0}!e!SFU)IWf}y?8SUdP?|k*@ z)#IY&KvMam&5KKT=oYn`97C5e4x@vE8v3Q#m?lr+IxdKzt|f#rE7UljIdi7B&>9%d zB_$)q9`&U_6{XjZD_>k1q);jYp*r(n%iXml?xW6`4 zXill+xLlt4@K;UxIP2w`*|EY3)ie^Pfv>R!N8((1^EuB*P|C@(oNGB#T*y$OSL40E z`R2Du-{$`P@r;C|bjokEsh8>lXauUBP1A|r`%yfRby*4dz~6>OMv0vh=ONL(zVQ(V zbJL59F5F=mk~E<;%oVft&+{J6rlP8MC%!+R_HifB zE&7G!pAg9tdYlOR8MWYCmVG9vbWiSj|4pLpFE6;wfLX=!OFkl{1q zqb(bcD*p>7XVjhguTxI|m{Z?Kq4Ktye<#9z8zNBgB-xur znWgtk2NTWhTB3?*hoU4eNCTvIpP)oL=r&1Jzv8(S=R55sul0l!@$!xKO=~3;)ffY`5alJjY`rUda-o#I{rJZ-|7-{4+)= zrtYFjDj!+)$3wo~V})Prg94{78lvI@cL|Ik(~Aj}CcG3otAu6P zOMpTI*(EsVE6NjWh{mC*9M6DM#TJJT(u?unf!Kjx&LPE-^l~C$37;|9DT-@kB0s?8~B@d6w&;7#+Oek$?b5C-wKdvT5OGa zlh|dF&~=93S9^L#^QZB7ksM-sWpWy2a&!)O9EbIDu6s#WhmoN1k?lF%CJARhi9-5r z%0~Pk7yN(&{J=DG&1lWhh@#AO`E-mcE2mm4g+FBCgGeU+?bgr^Th`im6&}R9QMYhI z;o;qiI^#Is<487?>DqE$W7O3%x>=MrDu93J>*7%Prg(+n zlhUu~Z&eAttp)4%E%bC6N88?jK(b5talQj|uMhuelghLnw%y_@?pC!eHEgyn6z6x^ zOb{*iR?wFiTTIq@9w{!L9vIZ_xRoX1%4$wx%lhsdSbwrJzDc$ZoGE&on>6|A{-}~$ z{SoT!k>uv{XU6({eS_LBsJL4M7N%DhviBzktwf=SkTkv~$c|NUge+pO7n3`xF60xL z**;HIf4#)I{_`+(OD2T?cg^cf+?E(DZTD=GU*LV$2A2kHe#rv=b7BukLvf1V9UOA( zNWSJYcb%9MpYCPMU}`p{n;E^0(bNxJCxs<*r%-L)a*9y;X`yfrILmnesRiwTv>EBK z2Xn&c8o9ed3|=R@Kxa2Usv*?I#;vexOMPIT1Zk#+6wT@pu zlNUxe^D%F~A68{bKvdVYG#;`j5~^{&rq^+G7q7YO1!<9pzSnjv+UV(ag{{-3gxKnFm_WL;3E`;d2 zrbt{h+rKUI%~J5f{osWvohx(Hxt)oMRLhC6>GN~6ehP=G1on_2gs{O}*=@TkON2H! z8QPj)w&Z!g+qOuzK4N16n5PG7x1)Ep@84t}{p_~-N9-67MycqxFkd>%R?%MwNF`Aa z`Xbp9K2=BuZU5SE0MeZ>M(u*5vboWYyHO;qvuIK+pF{0=UisxE@EqHH!zI z%4uCYpNK%|UWCs(9j?trj@>8}h4Je&>c#PK>7^WvL8QuB()U}{iC+xt=-n@Ru!qFaL~4G&s~aPzFnKZRo->!@*@cf zJaTA|qYJ?C7=WXzT~@V9GOk9^l#UvOFz|1s2S&F0V7`fUrkcGWH(oc&1oyk9^hEr; zG>t;l{`(Svk~U)$W0gR&o04_g8q_UMz&+}~BL2VJqv>keg%sg&q*;noA|s$#InZos zFlIv+l@I_)(hKmrGH1Slm&{@^Hy}SfJyV>Dmr{3^{bpzIj?;&pU1ijzbV?Io~1F0?Z;my*fC#T@(F2Q*KVu+&n=(9 zuG9~xHKh&6X#I&BFR;?9UvcyDSlfjIJ}ER~C=|_SWENnV?ayVoaM|Q9V{Tuz%vOA7 zDT?z)spvaS&}wXF{Q7p6TMBy(Etyc?*!F!+#+DGJjO67%`Xr+dCnbY;e@6tj{Mn9! zeQ+2*!VVB0_(K>b$HOw^%&n{tSVB;I=OKYExY+lRF&tl-=v5=^Ze4gGEG!I+W#8$^ z*qSK^XipUPUEsI_$9-^y30<1cJ}M7(bX#49X)v@scp*8SB;T`JJG0+b76JjahmYML zKO>{gs?eYqo0gj^z|YSQ4JHl_g%IYFKK<+0;VdO8IGF$LKa#oKlZ(zSylp%WpYmxz z=tS>?j$B;Y%QyV)mz3 z%bRO6aT2b@Wo3Oc(o;8yhnQ z0J?%uStx|ELhGhXf5x_m8Mc~MS}HCk)_d!0RQ-22d4WD!1xZP9@wvlHB7dGmj*TLg zL*S~idOYnd5dz)R$*!!SHEbIzVP-mwVwIZe$MQGW3q0~%x7~6Bw5f3s@k}`Lc|SBp&P4~iYXgU z&-h}TTQ{-_ZDdYPPUA0;!I6<#dU|Eh7xf{biU<#1)Q=1fX0%R)&Y+v!`toc|C>tCH z1XX)!mVP^yEnddsJVR@(He~J|^_{U&@^MFYQ1I8XzU$z zb#-u!%a`k-1We!{!kCZRnKvxEw&u_(d2ToN)=~2fZ36>_Jx^xxQ&L!O@{}ij43@!e zpo=4^&of>)+S=}gQpw{zmwKX?Sg=-tNdbYIbBh$BQ%o~qkx%*^rw-`yqy zt;za*b(i1Q{T zWK1gX`G9lgUWlq4J5NB3@`Hr?=gN4Ji*QwJ?tp5mk6+fykcJJ9vuH!zP<>AOolcfHp!cC- zAVE++ z-7Cr~@4u`~8hAftJ5ttaqG{|oyOmVUoz|6jc6RTnv;Ssj99q>DnJFo1bU5yM-mbGu z9VtJ!qKK<#atc_ExpYkH%&(AC^v+cd(>(d^?_$3AmBhfoATzK^Mh&IR=5Mm_)1d1W zu%sR9sL}PkPU6~f{bF#=M1rw{T)2a@@ytoV!Mfo%^{(^hI~bzq1jg2z6?nO)(wMx- z&RR`WX;OWi)VX6R)v!+s(cOEK=NESnTgXzCTF*0U6mNaG#^!R(?7HU}J7OEH15{f- zpU=Gz9@b^Qa>w)pb{ZSc6>f_)Hn%tS%*i_3dUj9B?yPQ;{UoSet+l!5sWg3nmuZ1U z5bQ&QrI=wr>yV!P_$SmI;;Nkit|5J>nN(UibOJOYj%PM&LnfC0U47qt4fI$Lp~r11 zMeb96m~Jyso1@_-3!LJSd8%}AN^ZAKqPUTcg1(ffNnr2b9SxJk`EzB^VWXRciE!OOHr7bD*}`ULDar#hsp|% zkf!3&P{On^AQe+`LbnN9Et1^_bfJhVUmD4$%o5lui8R1F%#`ueNYk_geLF*?B=}5v zv&#Jf{%uD5$4Usv$U!yl?FMfH#;Hv9=^djrX_KKvK`gRLy6c%*Id@AypNF8;EQ zNwDwgR&?vpLB<)B_k(}&8@(X5hV&M2l%4|b z3;NG0HSgPI^!Y%z55P!Zbrxne`WmaS>QVYnCB4pde6kdV$9ENX^aI7gHRrBoN+X>A z_HIXl0u15K__>&hpkY!}Jl(&|@yT43#<=nl))J#cL>h_gZO1zZ^{BzM;8)5;ye&`I zQE5)TPLv@s=d;5r=&Oru(?bQwS>uG+QU$N!&s@G^nvidU!Xr4lQ_2l#jovM?>k*9Q z&@m6AQ}Q#t;+;q!c^EXV1TK^_1}@}>h6IuV8;m$`FzBl9>b(@GoqczNO#$EO`sF*H z%(Z!*sWs)Cphu}7j{H|UJRxWIH{;TiMEgHyA5wcjAha7!e)&)PKteWivD9ee%RJrY z6&?1g`>pcro8F)#_wU})0Ge~iFAGnZ9c6c`VpV4$oK6XlEIfj%_#H!gK@;8a10K%1 z)k`<=s(|$OEv5ykpWC(Y35RW&boOp1n;i=$Cg=9BZ@D-2++(dpyOW$DjSdw=Ns_?A ze|J}WsrFvbuTf8ab%30dZOHM-o%?(%-^BhnP}fG0?fUgkT+C()2+P}@yc>SRnwSQ+ znm5S?&z@bTgdDspHp=Zqed1c$e2U6Hj%Xws#Wymm-6v0sM(OOvwmV}l8#yU8EXHR< zjBbdlH3V-2eLAbbvuW zk?DXxt$;sySmT~l^_1@>Z+g(SJ*K1r2bpOjZpGi(<8g4ekb(B!h4QcRj-`>9i~ydf zlj2BE>RDbc>6V@UW_0R0^beZ%qK^Ih!TCJ3TipWfCJEUj>*UXyNw$mFGzb`>?SKK7 zFixnm-aOfe-@hGh*C5GSj1OY;q&HYSN9iZa;MdduP&~ zJpPp%a@4?-ufUWq@p;q*bPg6)>Yao%G_ar-EcnOJFY&Jm9LA}_KLngf%}@^y4472{ z(bAMV`3OAFps2|)Qgn~1yX;gArgZUPsZ)tRw53RXbeh} zmvu~kQv+n<6^WfIm#=y`mPY&29ZDt*aqekr*32#ON9^b+i& z4)&pzZEja=r;TT$8?1_GYDrS8u5z_Q@jZq$nLzj4CGlREG8mN3dQ&}QK((parj$vu zZuq^MeaX%9Gxy>4PgFLRq)HN+pY6$uTIx((A%;!TKKxv_10doI6g#HL{?}(TfZ+@pqCiRn?7d~=KV8nd^ z6GH*u^APDDB?I@k0o)_4Exfa5-mK9yfnq(cBUXiShy&V@Yh`2s>Vh>&b?9Yuj&@?zue({^Lnb<*h zmaSed*G_(c=e}fT-9}yDnDWzz=r-&9b7?rbPSZzw?8BCy7k({^MB@3_nxBMqcLlWV zASi59xsjc;kd@_T- z%S^84?%Ob{=k#4{f_R0IAbw48^+%>!>bceK>kZH3M2Z!%c{kd1c&RK0u#M@$=SCW zj!cNb%M;@cC2&|M0wZMJ-utfDYh*0K^cXuAm&bIexw6~u4bLixKEv8sS;?2gW zRgO>DmiBnws8IA8mXr#^nC}b%V`0s_}$=7($kkdLJO%FJe3V8D@=; zc`vJmwu{0y;e4A(?(+;x-IW+s2R2`cbjD9Qtij0bac-@rPoLg}p)VLs6JKb$*fVA! z)C=?5FxPV=H8s_Ju7=I!=j2M?jGMgd@RH_-G3;?l%CC+Ct}u1JIRMjtF!Iv}L-0Lg zM2wF}>5IowV#;B*k2B&Q^c04}&Atbx>bY*$@h$8qUQ@ONc1<2XU=XwR?FlcmKU{BP zp=5@U!N`aRIU*SAHa|g2E7+c#lmz2%1h(DSdcn?ff`X>$ByW^SaIIr)AHH?AfBkAv z*b4(q3PeRP;B;q$f#K5V$BD&hI4p*3M32LncDF@D2`>oPvAUG>=FJ?8Jx8ojz{cUY z_RyK&0R!K;xjASGz4s!dkhUsY?64bcfTMiVR3>kjpBH0ed*pXI2~(oxXMl4^4Lh|J zn=g{Q+-~ZuxaZW~g_+nIvwZ!xcLyVMtY**fM=8OCPS}9vwQIVri|x_l2M$r6ic=Cp zqv7;FIwmF~ji*UE0ErIZs%)5`#}N}4MV9`U9Vph6huVS zWj9nq&xP>FNu?z}GiyB!jfmc<^1Fkcy;D3qJmo)o%mv%YrJ>WbUUlbOK_N^%VK{jz zDvpBhq@|_J9n8tefiwQ(WLYts1BmRXIGfQr4Z1fp!@{3WejK4hjV}zD+l4T@f@R@N zi`k8(CLA(KecpNDJt2d?ht<~R251-1Bl?_aNJ{ZPjB1@MtgM(wK1N<%ULKxl7^c{S zfy5%i_j?0$F{$(5qVB`N@@EAEuHw-7mc2rpS(0c51JDLUSK4DwJPh7j!Llb*XZ^WhD_I96%~mJ3N9|s4#9BU(5!Ov%0~Fzl0Lb_=EA`DhWN?JHjMmj zIB!t7ki4(i)aKWIzG+eoGO(lC#;RRY;E3D!_U#q=MO6)r0DlxPQ^; z^SZ`H_xXBZ7KIiJ)h&j+CKfif0o%GWbCPlVMpa!F1sxq40lEZWHj%rA#>T9Sw}w9l z3&muXmFbmnJ;i*75fvEEznP;x6?9pcCu)jhxZ|mm6~AJH zeDlFxNu1kPll1KD3hY=5JcF3AzP^5Ix%xs128XujdObhdIMY|M1bx=43JT5I&!VHp zZHo>ZJ|RQ2GQ#U#1V~X(Ae7tvDws$4^NBHNoqeCZcjy2Cqf7{Mb|+Uw=ZA2(<8lbn z8CA&c%y8V_QbRZeV|Fkneg4?xyT~M_x~3ir8_&guhipXXNFRCCKJArk4mcqQ!+qVGE2p2s&yIi?H+j?+(ZJ6RO>12WQ#gZ*&KF=l7C@mzbs1a?jdNe?9Y{}4XIAKew}+&p z9;BcdV3s>{Ys@qqLgt%hY99y7@CB1%*iSK0tSYLBvfp<7&6_s`1qJY$LUrM-&GnT$ z9;JovJ?7b&85zBKrtkvNl}neFH)hpUQ^&+^@PWUvfNp^eDWdQGA$I z2nL+>T&D72l07m1HUvGq<BaHuU^o0!wVr literal 0 HcmV?d00001 diff --git a/pdns/dnsdistdist/docs/imgs/af_xdp_refused_qps.png b/pdns/dnsdistdist/docs/imgs/af_xdp_refused_qps.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf566940b42c089c094183813ff2bcb6938e256 GIT binary patch literal 30140 zcmdRXcRber_qV1r2&E_^8A-^w%N|9=l~5$4%gWv}B2)-v@4d^) zO7^l(?vp?aOa?Cru^2uC>CG-1|=#`AP4M4tx>&#I(nF z^z*J~R|UPPDj8fEIhWG=l6%m}iG;_wuo;UVjom3q2Wuf9 zsG-b%+qwQH0=>JLM981sF+SnlhWsICXB`vrogm;s+fL+r5835lRqu!)GHjQkvVlOG`@~(iqo&hVQG)EML_OsH>~(ead9zsvZv>b z$oEaZ-*#Qd>}I~LYqI1BKk)(7&_LZK@q=&tdBd}bQVB+l$X}I^V}vwHEe&f#nFH8Q z+}o2ccVQ|ugqKrGP_$VQd)$3DHRjIn&6^$`RUtD^hzac8;W4n#0S5lSk&!aP$iSng z*`oUTLU(`5mDZy6f42P0*^Z<`s+2Fsj~F%dYz#KGWVATzwyUc%RSYZ3u&Y?g+Hj#< zu=!5HdJbn%lV)Uqc=5=?(W9MmCT531YPffeZFINMO*B@mmKr+ zl^fqYo!U4DoDIXLf3SZ&x+09rO!T1IFXOYN`Tezi(mJip(9L`c4nq_GZ;mo z$0NBVtM>0X);o}q{V8QpYdLtntF&}*sPRu*rtm&Z;~eygt)(Xf5p1#D(+di1QU zCZC_PsU&MoS&K33wy6^(#HVZ3!2haXLT*g;l~@f{;m_NQlgkVCPD~Wu?!9P{UB<{m zRr%zEShDsQZijbZ`%Y?%Y+jffF)C$9@Nvn_g~ibnTE|JzsZty{GZQoOKG*A9LFwt~ zr&(DWjb}djv!0}V z*W0Eh<&4(1mi-@1nv)OE3Omk>G}`A2pE&WpyW6Gc;ej`A-i*l`@&;>7y{P;;G-T-Z zzNf#x-Z?-eC4%{a1v#dM6S+Qmqs7DR_wMjK<;=I9TWlv5tCu=z@4#(Jxcf7g&e!Dv}rdCGW z($aEaVS(7jk^~H=yKz5eZ=>aLDol-3BKh<&78-^*Kku>N;oMK>C*Ee*OmvC46OXl| z^$(Q$!D3I4mXwrG?%&NRx7_E+@zIAiML7Y!Bp)B;G^%4rQqFUj8M}al|I*T;rrcfJ3L}nh_vQD ze*73B?9|rFK7Ir4bsF5#($V!>8aB<)*AZPx>(XJI_4Vu5NA*Q+Zf>t4BGNOEFFuPd zO-;?j`r-Y%i@sI{4^{+O^}M_1&#GdarB0uV9er-A6VPa!rFsU1XXoc{xy6NJwlFf{ z46YOv6&1e~YQ(`W?S#Z3a z+)moq)I_y^x25A4`iFhKc1rUwh9mz+-D67b@6*#*6h22LN!cW;ZE?~5RqfHkhZ)Tq zjI*4boh4pPCFhOb88WlFcqHH7-+z|~iSO&yEQ4yAxMZS?uxujKP@pv!GG;AW*Y*iI zRo%4dx%X^aBAaVtU2T`s(kyZXm*=O%p9KW)=~o0!PTr<}gu|6rR4_yg*T*n3GLk+m zzZD-FdnHauydlqKf{2{yVC@bP!7J+O##}*ntgK4CsgF@nNsB8+%L>?xYf7?GWr~D* zcD1y$EH5v6zbF|}5X;o-fFDmr6jEvxtSGg=Q%%#O8ZTXc`#4$ugYKapC{!MNt5>WaRH z@)iRF$6-r-6mQa(l;%wz>~^V7(vqR1d=`Q>Y0kC#Fy4FXjSLC*&0>$oj~_2g4?5(X z&g8HT9p@Ku55VI(Oi!` z^bx*f^6}eJi|pHqZO-k$U)%^MiK7tHQ(Vsvhq-wh9l_Ly4j$Ix^C)x{O3P{KHpb>; zizZc3^2*ZEjQ1*UGZOvn2!`H9jPAyjl#nMisnWZz$z`gOdwS+~O7yTN#>hFUaM!RX zt;aD!t?a36b-bXqxJ<)U$wOBTZvE@hT(lUM@~p8lifU?VCndeVz0M^3@b`NlVbR%{=|zNEE@t={iy{{n7meM( z)0j}_w$f6A`e?a=f`ZQT?NKrTA+~)ub|>*ywfiZ#21XhaI=N?MRpo>2H|;LjXt8@K zPD`AohQ+<6rbd#+j^X6VSFy2|58lts&283pNXp8>F8&zS{rK2!ZDp>cXxvf6vai^t z%WiNiyL)vybnT2V)_kDsX=`h%k&#g_p9Rf1;|4-n$C}!w###22FV1OXn|G$^O4{4! zhKACTUXqkNwtqJ*ZRl*?1xYx)8IL|9RMbXjV`##4Uc7naDIw<52^4A(yvOoXDeagB z#2>rw102p%``k<2@86G%iqgAvD>6Dd1cfy&d30F3p{w>PqcEosKlo*E@O;NElFJ4$js5n`bgHzk z?c<<*nWHO988++=tK{;y=?7$DjwN9I%A)Yj&T@{3IH#474F~Ubj2cX z-t4#5lV+)c?-%K)Hw7P-3(V-VN0@^%#NE`#A_G2NYzSLbQMn{1S9xy-p|<^Slm{5% z2z(yD$S?qjYuB#9GD!CCSBg^tH%97H{rU4TZhcu>TYl1#;^JHS`qm70YzKY}*ZcYU zdX(Mbx~8NQs}#TP;vC)cI`R){t^+aMwq4Yi8oNu)2ktyP@T!U?`Oe_;>~80!v{IU- z?zNR}0L#yxQ{-zGnzp7lH8s6>@j_mB;a2-=Ggtt)QO`$@=;>e81_lKMg@mBRl@qXl z9H`F~^**(;vrAA;dLdvHO}9r+PtTp0x~%|w_gFjixbL{{ZI5`TwUEKu(V3~KsV?(f zfEwf0G4m-Qd{eUjx0ggjk-I*kimwxr8or|Oe7Z?h3$ApwAC9CQWKy7 ziT35axQ7n^`7s2yw{Ha)qJ($J!CTBH>xU`EZln;~P*YbiA|mn!2;}K2bLH6B*j(vi zl}>v}oLrr+Z*4T58s>GSYdIov*vFD|)0+HLnc2zdO73)h6Y(Sdv9n{$%-GEA9F^A* zc1?bVS<|amuR1y9)9sm=nSr02VrAtHuI#%}Sy>4u@!-J&_^}qsMV=)jO!3+JGKf(G zFMG_}lpr0M15XMND$Dd=K1L%fg4!loEFz()3CWzvoe< znSWXj<9b2dKw3K2iI*VP>yvPFlMv(rPGfCZMeZa3VZiIOba#h(#3v+l+xB}IgxG!U zEAfQm6GzZQS=nVv-L=(~l4|8^*Kig9Iilr5lNI8Muk9aqWaHo<*|*Q6P%F9_RtV>0 z|M9`lvt!iB3=v|OWQl+$VRfSqULL=8(e+vF%m|~$+Fk6Z>G*O&Kw#idRq3|=fq?;j zk#|Yh;Nmx3gd{XC&zUI0M^)-)IF)RBT2@wOQMmHlBU(17Znb~M?)?Y5!N*80pEqBl zqM-PSyz^|DRrOI){KZ#A{^A&qs>Y$>*~_)@uWR2}qTy9F+%WnXYA^GDfPofHaof=J z3=Y~rxW4c1-p(wD^$+0!R1)zAsQCAMr0ZNqLj_|4ci+9cVCnSv?p=bV?K@{hQRg8I z5f(0JuLS?`?$n0Q@MhTeU~OMoeObN~Q|AshjAQ*U9bO|}&Iw}f#33-Of~lFAEQ@cx zt`o+)Q4cn9%XNbm`4ljL75#0Rl)0Au9+NyeJ-hz0SxS^(DDYn#-Ddc= z@lv==JM}EQX#380GC=lBS|7Wd(u{C2d52;DBPF8f>3x@JU-XxwQ$-Lmk!FNf_~%yq7oz}+ zEOS>P;oX?O!bQnYUq3E&_qItMI89k4Zotz*8>pI^fEbd&5jCKA_SUYWlz#@2|Kd*l z%Ud0K<%v5%k8M>hCBJ4kv-yewkhUf+RC0t{jUA=khL4zAk(?7LJw zQ-#^~GsvN7P5~vMi4adeui8I$_kVoT{-f3;7@2Ch5A8QgTvik@0f< zyEK0p)6WOI)v3GyjMY!FfPnx0*4y(R?a|PG0-B_?oPV4QF~@i9wuh)C`6Mj6d>zT{JHp=3-0Z0_GCInknR7*1 zS)Vt!gzY!ToYmF5zsYwn)Z_SaaC~`o7vLGU`OJ(>P0z5i3j+rT*lMD~d9vsID@h;q zGZa&$(b3W5%;@9%x5E+!4HA&^^T`Vv#XB_|>9NYBoEOYVaS@;7XyTP9q3b)!(fF?3?U`+C7Bf6T|bkO2TDw6U-mkg@wDuT3X zNNc}*78hq^RJw9u*Df9_CN|Z{`b7za{uNIaZ-Nk^QRG^A=jF>$c`*U;)|=d0-OSAO@ZSQ?FQ2pJaoHr>MC3W+J(79OmCxtx z+nak)oO*3vT`em?=SKqonB$JxuWBI|1cu3FuG6M)`3uL=k6|(KJ|Do!>>M0(Q&VZ` zNVNpA2pgJ{G|_;ispKHv!(cL%6Cigmo2EK+XaIqFD~mJJzu_&j3PwPnt;pReMy`H! zSiV3p#@NKzk#K(v@v*NCbDha=XEO?Y9@VW@ugwcDCC=8%JMG(-RbE~Y9vuyZ4)DMdju$dhvDH zeho5MoSSeN^Z{1l3@hu&2)Im=5)xf@75Jf<#rb(#cY^}U{)fy<_rl%0&mF-C$z)rz z>D{_wkHRK|VZ&t5(Q{p|cJ0gMRE$5Ldmc8BjGRR{#G5wYYK#IYsnirnkS7BZlYPZ5 zL3uRVdwNF)b!0*^@-;qG3Q%~o8K?+nXuelPlPy(6OoY2O;3+@l`j-1?)ZsGIYx{9 z$8yiRwclf>ccnN(M;~M#%pk&i9&l6NI9(^+NoI^_|NcYG+57kGN#(du6D5iwAQ%!Y z;~Y>z>R4B=9HjJ82zjj-NfT+cfA{d+$TH!}&D|_iUJ{P|qoXF>Em70czlpv@?fV6ZG!mqXTN?C#*p#vVBaMq?r2Fs20cEyyDBANbnaCGVMJ+Z%H3v*)JIB zw}%OH^9WTPQxD75ldj5dBt$h^wzluQYk)-d2qY*^ya^j6Md1`EUw`GrX&Dj`(bR40 zA>Fz%Z?vplN7dP*Ug)5oNr$1)9E*OB_{ zpD6nFm0w@^Y5Zt$CQ)_dj&}#Pd>~+rztR@G4MsswEovVMcl{!B6;P2vtz{aOkTp7a9%}F)f`l{k!s-IsET8#SfK6RJ(@Zx!tRZi zW51X=`AZA%hpxvT!H{)I4Emz$2)om5Ik!Eb-10vB%~Fd!8eb!JTyXxMy~+_x-GREa zqxhRYjpR>^=NMwJPk$tX;uIh!KSwge5GB@G6E5!SCAhr@fhvD?kG~mL9wc6^a)S|N zna>(2_wFRTXPN@?o&lOQQz_H8Ec_xd5#*tiXOjEIwowz%KxU2eU%&MOuHG)xDOSOe z4*IDwKh0PJGNT&14)7KVBMeTi$E<3%(TRX9m?9u=&~h_4+O*;OP?p3`+N-Cq*O;jt zNB>53+BG<jz=Np=%yi-K(w+hw8ttobi`hlOd1i_^GQJk}L{0l-1eP2sZ zyWhRQJaDgDF~877=30L0S8(RcTbwfB-@iE7s#hpW++?_zP-Mo*E?Jx99Cr-ay)LGA z+qe8XJ0<35IZ{%Zy4K!rq_#KtXJ$f7_Yb9QagWVzn2rjw4*<>GHeX#)=6v*9OA?#9 zZ8ccfB-J=1Ov$hh**4)aJQ>uT!mRKOlSvzn`p;dN*@Nc~j?ZimL*bxY{+Fk*zYj68 zm}r=$MrtRV;nuJ=com;L9_dDWcYI4t3onUPh>_FsKtCExNX2q)ThDklecmDbAuv8Z z;F-3%XrGbd!&Hpwj_FpN_{&du1?q9sMtzT@$$Dj(twY}Aj|&js|Nfl5qd}NAmySgE z-NaosKyjM+vY-4W&K$RAoib0m21ko8-I5R{)g43)362A(SHs+y<1Hun%DatXYhbcM zS<<5&-@0SWo;aBM?T3o+y2RxV42RGbEjP|~sOk!ajID?0Ld@2)QN;Ao;T`ODi`AYm z+a7{cr^44=N3W~52EL(sYD}DY$1j5yTer)iUDVmAxvz(@YRxp3>i)+}s5vp5QGxNh z?y_!q^q>IV_Q)~7VYO10x^x7|i0xcKvaPP$q7cecn7{X++Qvv59e?oRtb#j&dx_T{ zz^R0|Ha>7|apX&v9um)Vz;9V7vK@Q>f3gz()KG%lD2M=ZwzE*t`NZ+SB zd=^c(P6%%L9ey4rY+D1qi-|A#YQ`PTOU1;@k#g(4p(P%aMa|Haa^N&O+ZnWFEhB^o(%-}DDI>2( zVP1KUPckf4+LppjtiEr-_!lUo~b&{nI?j+$UIRRxQ@@}PT z*T&dqn@295iB^BxcR0nigjHM5N$9(RLiXC1#pUU0sG6#~6TZ1EdF9F#Y3WZzBqHCI z&ss!pXw3Gy&LH?1z9jw^glYHwb9LLrF|8F6k>$zvgtTYW(j#5gR>d*XUn~1deRSU1 zPCYoHJp{GAj@;?N>d~1+kixus_YO+O;vJ_#*+oRw8sZcozXh>|kKp&qh0ZIt8UX6QC3mr8qms1ob(r=hG>LgxPhox%G=J_&fNqE%`9Tox% z32ZNl28YB`dseE45eUpoq})Mw9L%L3__h|f7HuE1^T6+-S>=YVTbSK%wOuE zRC!#)*|9d_61-VTGpIdOmGORFcZMqTtwM#Jo^$B1zbOm%_8_JaJbLWdF=}eFvq!65 z3Y@&r*6{K0`WItCq2t0GZS9au0mWmTNgCP0OW!gWWuE_lLPA(rkDt7-TF&ioM_?3k z5#azL8h}j|Uine)vfNKwxH4wJNGh=GcQ&{ZDS(K$IIF6*sOUJ&v>Jp~;?JuHQv-gO=jHhv%l`31bC%7TPf~Z|o zRZsbs^@fT=8gFZBtEzgGRPH7J6dXO=r0>I*U#A>$M=ca693L52lD7O_m~mW(g&2Fs zC5oWN?Co>j&bM|MIXSi;?h|#@8D+I?+p*hY!ezeK9cqIb%J|UG(0Qk}hK3)VHr-y7 z+!D%KlE?Jwf-3brOVV{qz2$;=K{E9!K3-b6voe^^&DE9oIR9jmhJ{&YZdOFZMd1Lv ztE+2j>RBUq<@V9t2dI#$A=Gy3>h6G0%V8p4MVXp3PU-ZNm&B_#PD`&_O`jKknUs%s z{hF7bUsdp3bF)f9V^eeU=NLbdKQA#nbo0{>IG4d*ol^wK{diH7=t1O6yO+Q6&8C=g zj7UpM7c7nE#e~>@+L6mqxcoiXqJWaeum-G4ud&?b0dr6cbNz)=i1eUYM0ewhDJs}uFUF!5DzNSjwq?B zjsy4sxKNloj#2y#<#U_frYjnRZ{{^&qIKy0v5;}728KF*F9&_Y_8mKf!W_V{=r~T5 zfJiSNUeT4VEXHqQ?|DJ1>(_hk6Ok87i6A`B=*WmHlb<6>M5GW7^!~$#WLGg5jFwg> z*oEF5+%tdwRuI^Y=YDqbwCbE0ZJx1Tp@g!?Bv-{Y3u zWe*y!1f|m=A|g^4;xz^!iezLwSB`p?^o@?PhN6ABZLTqwsyrgcuivikPViQC^GEjH z9kq?gj*E{UY0EOP$e*Dgg}V-}>J$shFwh}WspH^lBzChXh2|uv@{MmtwL^-D)@8_d`3m;oMpUnk5VlOyvzTiq%IM}0-*)e%AuGJf*Sdk{3rZB^oHJVHFX}#YEITEWZ(0l_LU?Fjo_(-DsQ-N z)R&m+fBG`~>cp8bis(Ci>vY*{bssKpd{EPOLrGonda)vnOOuzC477hV;-w{kM?D^*;?jTu(q_YDrZxlWBq7`ulg$dMm;mb#8HZXB|ZE<$& zJk;R9p+j^7S}?zGu`wkQl~E)@l%$LZ4!|h+`1nYucnm@ElfLfRV*MuIu+Rl5he*~Y zzFiqyNI68HDb9GA=QuGE?%(m*<{+Z_f76}+FNF91zIgrrornB)Wb{%8cTUNH(oE8* z9w=vU#sg_gkUN=xKpo1ox!&_Xu@lrt#vT&ji1zN4mXflAP=Ke6P9J2~7Eft@l?^I? z+Y(KKPtj-MDaldO5P+|bWLXJ#nC8_Y%dpT7CS{-giQEIg*28E zb)M*`fg08=$L~7V>^TpYAxo&HD1A0Gi3}dMby@7=F#D0Ptl2KsWj>y-{ zT-cByWT2>r%f{NL2yT=A?lW*hi+?k_DPWfCykc)@nbrv6OqC>Ml4HDQ1O+>alD5~m zBAWaSDHzTpUv9j^lbH{FhlxI2JqcufA*LixcEB6XB4T{P4Z%cUV^Ep6_)OGH$TMpq zW&uA>LW0?KJ_}R`KlM-LScL-#xO};eL{ZR#`}z500O-vJ+zATe>JTBgafl>iXrA<> z#J9GrgoNkgPVomeRGOG+hK_Y!0z|7n)uz6qkyOg$4#E7o|1O7*Wux-a2f?>WiuR1P znCiV5mR?f|*p}PBwk0har*ekkbF+@iN{<@}S#2Pvu5ya@%7CBzYdZ^rzG58UFju-j z+33W?M9}T>8U%~Dtb(V0ETL;|o}8lxT161Foj!ef@4kH+xfr917e7GoRoBp{)Srf~ zkQ7jvPUO!;M@2bJSMmX@pPQNK%(2uJ72Wnnq6va*332h*#t~>+f$$n!`3=qGcX-n| z6?e54e@~p;tc}OhQ2tXKpDA!194Dv~wbE~u18_b@LBV8hYG!tTgoNYNDOFgWq2bW? zFP{-e(9qBT!dK8Il0L1}&jWA()w)!}+6ZVU5T5I>>LUb54grQ)?!}>l2cP)*rr+-U z0G=I$%Sv@a{yUMY2p9jSMxHYvV~#iN?QT&OyFt7SARPx-2Z-7c0FPu;KTd+Wez%)w zg<9b+t>+r=Z5~iYPZX|o-Qpsx4bX9BF+a3391cp-`*>PtczpaL4{z%86qJ;$fSTyj zRnNqr@$?Z;0t8JE;qKjJS4~a7fw22uWcE@H)QA4DzEz;T0{)Ff{LdJ2?;%= z4wGhOW(HP%1zqR5jGk@}BHL%9@I=Sd4`Vjeq`Td2Kq3eI90E|x1bCgLo-`0}k>$Gi zug-@dasH6@TUd|=H2+l3r$JhW>XcVe{i&{BY-v%od`M0@402QLl=@*rEK13(|E)Dc zGGgfPZu{V0`fTP8&K8Qn@6*|-Q=$I=3XwjvA|W22Lx&O??a`yL3UP?4>ucHDw{JOh zKYAp1m97g(UkmgsFHA$FPzTZS`jB5WHy?+51cY%o!%xpxpfe@UX^EF~2JQl63*tZC z?H~a#@qrDEw%{w+}!NtGj%T=&<@|d=yqG^-sY7GFj zSVNH#_6e2&TE8uF(~P>2qy%r1{#kw6qhqk8~pn{NJt(JaCtcW3>~0UKU0FNZvn6I@Jub%FG-|Qy#NX#)&n0W4WyoQN; zBUO55J!qoiC2C|uS9_y$(L`r54Bc_u*ziy81BJqd@)SfKOTv=y@9 z;xBJ02dnhSuPQ4yl|4O~-$EnxuH#Q6O6_iMxYm@U5fBn$e)g!x5M*Tgq2F3)j|`T-Gdr zWCBg|?3k34REBTHOJlJ!lf6aY&61i&$;rr;S628;Tb=|5CnzUCKNlq_ zX}EHNjEoFaisAB2fF?Qd?OQ=kPD>E-BSZrm#b;OpA@T7X$UA1)CS z%-65MNc+D8=%D50*D5WX>r-?@I1Tm^5lI1m1Nu=pIU(#w!=uBj6WWJ7B(;$jC#74+}xkD}F1Z_2GWXJ7AaKG3~+P_d!8ALduV6*Up`hk&$OK zb0#4l1sfCsHweA~cM65dh4h|_OTiJSf{9c{;2e5;d!eTcnxn9`@4&(kzj3SF7dRl^ zGiNHg%5rjYp!;U_z~sBA=M;c83fxD2mYv&pf`wrvpiKq007)sEG4h_0IB~C*`+ZaOytf&s9-XHI!FQR2~KL=8|{-hYroz>jb3N zR8)M9a^9?qlu}Q!1~?MHu66pb3b`_#H}r6-lqmF~w#<0xED z@qx*^@X1clw87+u^C(#UF5_@gS65d)RJgNXo3GBbH#eM~cyF=SDZXr+2Xx$#0Eaa{ zc>~gJHjUS^foD(f^Ya6eIDcM4Ff1oWHOA`TJmlA)k;ECow!me8@$HhD-}5PEwLy@i%Uw1OjmG2CqP@K zkzx!Q+t-NnL1@R+Lu;4}w|+%ShJpM*dWxW5@>_muNrPSe{Er5Sk(s%XO`%fPrDIe0K^J$lm9P#!zR z8d85_$Fp6V0`DCI3&l&KCGP_7(J1C!9!pg}%``}&lD1|GosRtvEp$_Y7go^GarxH5X_?*t zyhR>vcENUtjZ!|i`{Qs^p|u68ihkgu0}{TpG5do#C3I{-i=bdMdHJVL zR04M*6H$mqgOu}UPYLVgwt{MWN>nq&&-0F&PPsedGo^MN`#XMqVC5+C=_*jKk{; z^Iuv3_((O2dOWm0xU4P=0twB+!s0StMC)tW6)Ij&#q#XMAh*6VP1=_ zWhi<;n1E;kj)_ex7xQSA-@YF4Gb z1Hd?FXE{nrIt7GO!Rq4ZtSYb!C)L+~p_jxZ5SaQHT1?B`S(WP*ft=9M=nMoRu57g{ z8A9Gsu3H}OuTM@*8Ik9-p7|onZ{rXQ>6@2C|AT!;kUEEwl9G-N$DWH$PAl*e$N|5Z znVFljv$C?DIg^-@GEuj%zz&M~jm{l!wN8A#+~Hk^LzEmIf^ZHp8092o_lFOmy0Khx zL`N0^?45zj>grUacmXSY@Im5-sHwGpl=mJ2cQ-gV*lzmzgxEvPJnJz?LAs!q*>%@G z4?~Db+qP|kvjKS}+>Snol--WgatV#lnpih7GBPG$?p7A zKlC|o#Q(s#LlhJbA3VU(@&s2JRJ}YA0ckgEE6=%eodr&L(a}r{48?YaXyKvMZNw=T zVvd~U<(-9Y7WDRjo!PsG!OS4uIXXE(2VJJRnXTJUl!*2?>K`MX*V~Qpqq#G%8-4OMsXOMh6R1du^(ws;Y{` zX0=?iT$(kTvPIH9xA&<42?eaaLUfl%X|`y~97YF}-vZ<*e(3n|K@!`#i{Rp`3lpWCrRz;8i1L&(Yd<&?hg%k_rM6e~ODzHKeDzd%k*sZORC#*Se zEo)K9NlE99?l+To4_PP~HT6mbceQF#lM!51JnGtr>e_>%-TZI2-{g3llLnB>tO{TO ztM%KfdGF?+q4I`XCskyj)MdF8{Pb&!ZnQY`ltG0Hz&~RIz@3-pZP4Fj z7XjHrp~z`<*^VAawnZo?O;1msuagTE7Ghz+dB`#;Ub^%^ItnsYAhV5U3bRf?$_Xy2 zuhNi{lM_O{_`t-3j-sM&mOAv7iIE6JHX3Uc?jdGF-)>O+-ZMf$hplh z&>!2^Xh@qg>SeQwh=R`0(C`!+n-JI}6dfhJsPwA0{i8=M*)06Z0=Ji!S6{5b610Zx zb#K?Rr{*zyQmW~JepA~kO^*_9jv6wrIWVV#jm)$1JkJ(@d+B>)ZCTVQH}LrCTs3Wj z0Ao>`;{e{Ks}=g{A^ss12;eiqV`DuKA_21MRP)YUsGFTXaRTXRKTN`Y>QoLCuJ-TV zK}xy*A?T|)AKbaDt*s688RUhp%{xaXCR~6+g24v>|4E>#RHarY@rn~7s*sQqr10Ph zq2fsw0VQeZ^!@hj8zguZt?7(BJQ#>t?Akl?zKrKhSbwo~KXBW2m$9+2@WOx}JUM^r z<1s+|6q38ga$y31hm*zF4x6+u>=Eg64OkzfKnI=|us-B-ZiFPWb92lQ;HVH~ZjeF9 z8{4UyYHF0E(1ynkfxV-p(8XmcqtBFs9%5p!uv0FOH!7St(^-(qL2y{H4$@{leR?J6nFi^M9(~{JDfTEySlQj7F=^yRR?!Eq5ZgfZ zCnRv;pB&xk6JtK`ECvUv9r} zW%GzGmiPXv==BLMe<4zR>~f`N7X-0awg!l1Q#*;38e<%v56RbbX9Rg9utaLmPX=vD&+L?_%eK zb2enrtSZR|VdMx{WlBCPR8frp7x4xs!B`1@6>W&&Oc793K1)x3F-bY#`Ey8&x=eW`f6XEQqWg90mtm8^Yc+oc(~yQhNr4KQ)FHVIz`aw#14!Vd1?R-6ux-u2jX&U{rM~ihTkLWmZ zIxpL1Ep|WjZ*z$h0)56wiN}FFM!w1kolyD!*yeF+17r}}%$!pFo4OgBi1dW=*{vOs z2$T70Ce&eO#2<7bK#g~F96xl3;j8@n_wT`ln|YfW83 zzK+5C009IDrj-c3e!zom1uOnf+s1F-=)nS$+NJ(Xs4} zIGbe;-Us}8c>?};0)%|60VPU`!_Dj1gV!9L0x zM3iv~Ce-7H6DiEb{I#=$R<7nqyZ@b=bB1t}KK-}3TSY+gxWPI_f5r*#mGi?&#%7WL zHfIz}P$M4OL9zE2JDvu7F(b?j07hbVqPq|P8JL{Do*vRmKjVW?e&DLd`NZO&wwt&|NORCd_Mb^D&JZ9exUd>S9&mY(gJruw+GwDsw*rG- zmgoC@n9(l-U%ouE$F=+xpyv3*#N$96{xjRg8LlobfJt*xdG-0&aNQXwyOy8~gw;lZ zow*|L&vg9XhI|3h37j2>$gYEn_T*A-mOJF;AnnL zXQKaBZ8pLSxoY7;rNs|>A<|(?F2pym5@ANhsMy#cXz*u_05A+>mFbsH{!m_pkz6pH zW40QRz(AP=igd*Px^fL~?bR8hUK$rIe=AIonqj~{G;SmG-Fz(AJ69V)@YHBL4 zm(iwrXIzohzf7r#{kMXr_#rB)T$s6qIO}ugW`Nm+QZ#i|42<9UL3S0!XgNC<6lrlG zm6P;XBZ6&H$odCC7P91Lt{o{K|P?l22|fivIMOGl%M50F4dIJxKXP4}zV~ z)k@OhXkC8PN`jCOcx#ENqi*l%zNY_9RnL*CB{c!}^T-2u13^f&=&9fk!0;`&f;@&k za}=7^GyI;F<6Whur6u%N8jSORs&<@WjK@$hjvN3TBnf>6FuucM2#9#Vj=$BRDu1 zWLU+^Ed2aAka<)Zh+Vu0L<2&>z&wwsz@PEQVmre1TtWtdw$rjzzjT(A=oHaIwax$HK0tjqQWCKDyqbkmvGlE0aN)U z^4uXvae=SpSpTxf_E8*aPSc}^-O?1fQ>@=EkRufb9_8>Z&(-0Mj^Veo2BxSgdRi(Nd*L% z|E{hM3=Vp-P?5$p8Yd}h=;&DRP{Ay=VlVZirt0cyXhOv{YY`V&{G4#s#}Ge@jQ#1q zG9Zi$tPz3{7e8eU>x?6VDjTnfjHH8E6UFHhq(JfTJo5k{>}rFeJn2;^#e%2}=J*A$X+YHd$6T~;WmxbQNadf7?n0D; zTo6}@lsIwqKdEl}xpAQzA9m8)_uM0tmXVQTQrxKZmhj34aeaY0)vR+g@0M9<4CH@i zrQt6xbwxjNeRPj=hfwS8K|_<2K#!Lqno*S>VYrp#apF8s9)pt`Oi$+ms{tAvk~z2x z=B?>ppiUJ6Gwfg-U=Ex|MTKmZ`U+CRhfwG|RSncT@Mm8>2LJ*$fB+v8Lk^eaFteaQ zB={K!l;C0FQc^^$zXj&Y#|lZjN?Q?_{@)qHR`=H!HWf+sxvQ;9eQ!Oq*Vfw4FZINJ z${-QpR3qDS0=Mln-D%&8@sW$)Pe??1j6Fk(#$OJih~DE}lg4irgf==~m05SAP5qMI z-dLsB1`_Qg-%oiGQt3;pX=d6FO?jDH2Vdq_XEe`9 zGJPw3Guy@B-t81>P(6hX;$rO6(x;7=98|!+j|vxyQBl!S%C{VmV9TvC2_A2E>c-k= z#_07o1);Fx`1;&+X#&hTJV1|lEDzt$H616ww zTDl^H3K-eXet;U4hFUNQCVT$rlQg%4obnTz;0p7Qnm0-Yw~dBsh8v9p1NiEhp4|Px z*t?yi@s-%GA3v7+Ufs^~r5G0nqp8~4=(`+*8TIBoNV{$n9e2JKc4RnB>iYD3;`N0y zD>DmnvqVKJU+ZjZqFAuPNbhQEQ@VCIt$ggV$Yq1saC;1UBc&!43Ud>SZ{}^JXhtn; z&y8|v4w7Q=k5^K0J9V41PS>xslKl6YbF1o^3agV7I?i9XbLTrKEokkB!o4-j3s+`g znB-6v>_q_-prqq~UUV-^tAg9L`h~+qZDN#@U^uP(+gBpI40d-q**-I-`y?oJOU7Q1$-7V_iokZz1d|Sxvlpv znD>Di)YE;Of4pK%o?lz@F7=_+f!Ly>lkyA(d>Q~%1dv&;dhgEdz!ob?k3lI8#<2lq zrW|s(%T`UT6@DNVUIUeEUbD7Lm{1C24~(*7Vq`pg=+J%LdI)7mZUGSot^k4&Am$@U zacpergwySPe_bQgbhO+JaD6aElnV;0-Wo7pGx)`-qN#~VLia@Vr%x{+34(Y9%_Jxo zv=!9=y#0rOXJ#fQ`*v=JjU^3jR13i4J3BfYhuMTfCxBprdjtwsVc<>y%R`w6X;=pk z=@G*nv@6kj8kU{fHxSo zXFc8#_YB~Mrfu3Yg-@e1NWsaWd#UT)y&Z+q_zA=0jt74q*Fb3Yu%one*r>w}X}mmc znKQ9IM!s;SNdxwZXl>e!L^ua%H^4fH&lW%O6-)sJ)XBNsn;rw57dBA!k&c7n1!OvK zKCIOW($xI7dsuZAq~~vdEIP`suKs=e4nD{FIZU8z# zGwtRj2ZtpXiMclEE`o7h2FL^xhx4w#cZIYQ_zHE$=L#brB2ayE=L=Ht04$3Pj)sI^ zI_?79ZJ1918C7U?%rh#WBO%uMD?puozJ$Zs5>jRNi{T*o1Lh*C)Z&pP!vhlUk4I9P z&+C19=3zthwwMfsS$8ekTD2!Hb$!tWf889JXZTx87xmM(n*{&f z4Mh>D=6#?%IOqKWsg6|z^GyI9+n6Pq@pOwn$2D@AZ72Hsm;-t5?TjpC$_Qru<%jLD z1;(N5)a#DF<(BpI{i-OGn~az{It8t3IwEKmKh-qU#((z`{HuH5l{a2j_2=(1q}p9{ zuj;9T)g|bfkzc+5@g@%%!ZxB9vvO%B5d)nNZ&0UTxK-S*-)q}R_O~hgXG^__l?}eI z{cpRDmZRJDm!H=vHd4f1&GJjx_8%?i|H{Jib1&##2=F%gd($BFYiK6O9I>Hh9IP+F?1+b_>s z7IxDXd;!T51S<|!)=}HXmo{Of4Vx;a3Wf{=5-})%YpSZ+4v#@@4z6R*&h5P}uQM}2 zpJ@P8kx*yBCK%&|Dy&YqirB+J6H{Xo6R1503) zy;`(uai2BB zt`!y$@r=)Z-81mu_gO9iyZm2z+@Sur++1yBE2*oe14In7GRRICiO+p=`Hs|O$)8Ubk7VZ9x)e6H{9^UVviG3bhr#*I3b#D-G6wWqny(aF zdAXJN{3ZIzlvZ-<$~FzJi_xYR&5pjY8NB5+&3WYGe48$TN^r9`4~(P-?-)}{ad?W> zQB;=txsU^F_Yp!n#da;clrPunYmNQcH zA~>av?}vAE^?QVyyv(6q@G0=P&{05(?d874 z?R@5Lom(FR8=NfHcdh4gUMk8TDCj%I5WVrL+by3Inv83Dpx|WVaT342CKAnzPV5@s)wUHqnPcA^+ z)rB(Q(U0udL2SFaqk5;ehOcQ-S`+xbc0rE1Ug%Y_!%5rFewRKHv?BOcH^1OcbHKzf z4FT_+ye1C`dwN)Kjo4Uj& z!YbY?xK9W;4-sTQ1b=@P0eQ}Q1~XNccB^n*6>1&vCk z7KSme9fm>6VOW~WGc|*6Y3`KHjurioCxh?h#La4D$j2BxN~ek#&m(8Tuano0 zb1+H0?Ed?M9ott3ZRKJeByw0NkG1Ejw zct9O93wwv~ReMH^aqOK1eS8ymX*(PiQ}B=1L7+2R)VQTb7o<`|te}JmP{I=uN3m#M zQfkLY)D7LidJrgdUf=vo3sZJm^rW-SsudQF0caPz3x9a(f>_jVIKDguNd55-!D~=nzD}8wq2G3EW zZ&K@*C6x%jECUBT3;Ue}i+Q`uF0DX4rFvYgKa3?&U5}iiC=7lIytHI^0Zf0+7@v5b z!rOdDvM+}UKWm&Txn&67W@5C$63UaqWtGlSpoZIG zt4I({79V3F2CTj&!*$m*ev%bcYty5Ki$suE`nwcjY*|+q$qnZJ(4=bp3H!dRHk*}bakf+ z2?ya#@!L-dJ?o{K-Yqn#TqsfDyK>ADjoWNTmOTCKJwsu7(>2CtUmZ=>7~vYV(yXO_ zAU)FO&$!B)G8Rrj*E9s{UQl!`@Pu#wgp=+)RMRjB*EkY=_%I@m2F5UbnzeQ7 z*6Pb6JnL)@9`NAWgIx?*+S!r{-}!jLQSy>kuQt%x99tv5W3-bfEi^-dY^4wFVSzzG zlURRn>t9)R&`<5;QBqBoEIVaCeSsom5dm3nv$CGdx~1ZkTPs0ZrtL7($nm0v6T8;* zuXhW{hiR~dOd!X)_4g`y9Q7nv@%B5cIa&0gvuZv@_yA6)!G{!<_?2qWa2VH^q7OBt z%~_FZ%5uG1)9Jgz-26)7Q~x!#&Hl5?YTws;D=%F-Wn_RkEPFHIc)GVdNVi`P{x$7w zY!x=Igx5IX&mo61#jS$WvoDl01oW2dR+n6gO}cZ>oyRk6+`GVwR(tqx3)&T-%gVEk z`xiGvT@XzoG^2YYKrDv8C{C~?owj_G0!9nCTu~hDFShGsE$J$@SN#7mxFs-Y7{%?; z(Iy}0A~;;}mjHZ$&h_iEWg{r=&;a~Cn+Y~@p9c@52SgrSpEE5!Y#L-B&h-pDvg5~( zBY}zL`uuW8h+Tqe@ws{?($4Y*pLd~=NWSYixwS`e-0l-xG6i!3mp}m1sJGCC6S)7B z+51D<5hDa*)|J%`fCUr+WX&g0adZb~@da`VR5)4rP2F3OWYTF<0vZei7;NZ>e=`mhG5Q|pS>ETYw}`Z{&OQ( z8~eYi3o3;mM5)%;MF>@*ViK*7w}uJ;@S?Us25L;i#KlQVOXneg2>dh8lM-3hPiRMg z(*J(=unu)=km5prbTvv}s@o#Js~?0;gteRz+eVjxJj}2UWT+Z#)*Yu%NfY`DRi)_C zT$(Ln#wKK#L`Eq#p zAo$z0?2_VQ%jCW5v)qdxKSt2^b|QE2>iQ@E1IKo8E7Vu50`(K?vr%TElxN$=NbTr^ zQ+tRMrPUclYivRg(gnK)ii2Km)A7 z@D_3hdXdWO;Ov~aEza?2tX@@Jrcq#gs&lAhb6MSaqPgeL+@9%~wP@4;Hnuw2Ft`a2 z?}y5zhKUU$aGz-gHrC9BND^Uq0dD^6L&;_})#o$>pS-*kry_EGAL(V_X>JE-NlKZ5 z-tK@f1mfnj%tI*3ljzBjI2#m%ejV)oCkN@z(P0csF@!PjCnp<~hoO>^l3!l8 zn-=)^ZwH4gt*3b_!D_-;EO{?or1D@=K$|-%>o)7LJhLwaPzIp`UKs3gUYp#o@AzGd zw8aD)qMjgz^9HC^%s$@zX_*m!{Go<0 zdJ^yx*zFJrh=U=ny8BLnHtR(fh-75|bV+{%uyp=|n`upS(C|&($nYR3O7Z4ODG>ueWkFQC__fwMVx2<&%la}Qq1C~K38}kPjeJM8}PZBW@w8m&s^VR(mA&@>nn&aI#3ce8&#<4e0 z&l_TSYd?wplx%$BCKbVRWN}($A4@c4M@9V`j1v;@2ktM=H#0XUdB8xHk+grT9YBew z?>#dM8bBa~x$r$A{59C$zb9YG&4ONbic*18=|&@Vdm`|qN=HxJ%14baN8GO ziimIwQTcQek9^(ST*zrPcSW#2At{#~9mQ<8E9 zMG}o-yah{%1%lzw!5 z;1<7$OJq9Zc3fjEYh#4#u?&Kn#WMuw)P@{Gyf+J^ zfTQ+E32>VD_KEs*FvUqLs+Q}=Rb?gvRtHV@woDX>;GRaG(VlQ)*nLi;8Z8G={20G4({gciM2&Z=f4HZCq$VVwsMl#Y%L zuu8Z92^mgnUnS`sJlF>GaF6#;FDWztvJZ;>q?mp6*~Di^`Hw`*Z;N;lE3#{Wa*W{c z8nW5f9X>)@O^6sx9qk#qWW6Kl6q-CCg)|wxCt0xYe?8dkJyTrpFl5bIKADMuzOf!> I!`_qs1;v<84FCWD literal 0 HcmV?d00001 diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 2e15ebd8fc03..7311fc81f555 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -84,7 +84,7 @@ Listen Sockets Added ``maxInFlight`` and ``maxConcurrentTCPConnections`` parameters. .. versionchanged:: 1.9.0 - Added ``enableProxyProtocol`` parameter, which was always ``true`` before 1.9.0. + Added the ``enableProxyProtocol`` parameter, which was always ``true`` before 1.9.0, and the``xskSocket`` one. Add to the list of listen addresses. Note that for IPv6 link-local addresses, it might be necessary to specify the interface to use: ``fe80::1%eth0``. On recent Linux versions specifying the interface via the ``interface`` parameter should work as well. @@ -103,6 +103,7 @@ Listen Sockets * ``maxInFlight=0``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing. * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited. * ``enableProxyProtocol=true``: str - Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address in :func:`setProxyProtocolACL`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the :func:`setProxyProtocolACL` ACL. + * ``xskSocket``: :class:`XskSocket` - A socket to enable ``XSK`` / ``AF_XDP`` support for this frontend. See :doc:`../advanced/xsk` for more information. .. code-block:: lua @@ -127,7 +128,7 @@ Listen Sockets ``additionalAddresses``, ``ignoreTLSConfigurationErrors`` and ``keepIncomingHeaders`` options added. .. versionchanged:: 1.9.0 - ``enableProxyProtocol``, ``library``, ``readAhead`` and ``proxyProtocolOutsideTLS`` options added. + ``enableProxyProtocol``, ``library``, ``proxyProtocolOutsideTLS`` and ``readAhead`` options added. Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate. If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead. @@ -175,7 +176,7 @@ Listen Sockets * ``library``: str - Which underlying HTTP2 library should be used, either h2o or nghttp2. Until 1.9.0 only h2o was available, but the use of this library is now deprecated as it is no longer maintained. nghttp2 is the new default since 1.9.0. * ``readAhead``: bool - When the TLS provider is set to OpenSSL, whether we tell the library to read as many input bytes as possible, which leads to better performance by reducing the number of syscalls. Default is true. * ``proxyProtocolOutsideTLS``: bool - When the use of incoming proxy protocol is enabled, whether the payload is prepended after the start of the TLS session (so inside, meaning it is protected by the TLS layer providing encryption and authentication) or not (outside, meaning it is in clear-text). Default is false which means inside. Note that most third-party software like HAproxy expect the proxy protocol payload to be outside, in clear-text. - * ``enableProxyProtocol=true``: str - Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address in :func:`setProxyProtocolACL`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the :func:`setProxyProtocolACL` ACL. + * ``enableProxyProtocol=true``: bool - Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address in :func:`setProxyProtocolACL`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the :func:`setProxyProtocolACL` ACL. .. function:: addDOH3Local(address, certFile(s), keyFile(s) [, options]) @@ -630,7 +631,7 @@ Servers Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool``, ``maxConcurrentTCPConnections``, ``subjectAddr``, ``lazyHealthCheckSampleSize``, ``lazyHealthCheckMinSampleCount``, ``lazyHealthCheckThreshold``, ``lazyHealthCheckFailedInterval``, ``lazyHealthCheckMode``, ``lazyHealthCheckUseExponentialBackOff``, ``lazyHealthCheckMaxBackOff``, ``lazyHealthCheckWhenUpgraded``, ``healthCheckMode`` and ``ktls`` to server_table. .. versionchanged:: 1.9.0 - Added ``proxyProtocolAdvertiseTLS`` to server_table. + Added ``MACAddr``, ``proxyProtocolAdvertiseTLS`` and ``xskSocket`` to server_table. :param str server_string: A simple IP:PORT string. :param table server_table: A table with at least an ``address`` key @@ -719,6 +720,8 @@ Servers ``lazyHealthCheckWhenUpgraded`` ``bool`` "Whether the auto-upgraded version of this backend (see ``autoUpgrade``) should use the lazy health-checking mode. Default is false, which means it will use the regular health-checking mode." ``ktls`` ``bool`` "Whether to enable the experimental kernel TLS support on Linux, if both the kernel and the OpenSSL library support it. Default is false. Currently both DoT and DoH backend support this option." ``proxyProtocolAdvertiseTLS`` ``bool`` "Whether to set the SSL Proxy Protocol TLV in the proxy protocol payload sent to the backend if the query was received over an encrypted channel (DNSCrypt, DoQ, DoH or DoT). Requires ``useProxyProtocol=true``. Default is false." + ``xskSocket`` :class:`XskSocket` "A socket to enable ``XSK`` / ``AF_XDP`` support for this backend. See :doc:`../advanced/xsk` for more information." + ``MACAddr`` ``str`` "When the ``xskSocket`` option is set, this parameter can be used to specify the destination MAC address to use to reach the backend. If this options is not specified, dnsdist will try to get it from the IP of the backend by looking into the system's MAC address table, but it will fail if the corresponding MAC address is not present." .. function:: getServer(index) -> Server diff --git a/pdns/dnsdistdist/docs/reference/index.rst b/pdns/dnsdistdist/docs/reference/index.rst index 2d53990a77ae..640dc3308ac3 100755 --- a/pdns/dnsdistdist/docs/reference/index.rst +++ b/pdns/dnsdistdist/docs/reference/index.rst @@ -27,3 +27,4 @@ These chapters contain extensive information on all functions and object availab web svc custommetrics + xsk diff --git a/pdns/dnsdistdist/docs/reference/tuning.rst b/pdns/dnsdistdist/docs/reference/tuning.rst index 756dc8cb898c..c6147313f121 100644 --- a/pdns/dnsdistdist/docs/reference/tuning.rst +++ b/pdns/dnsdistdist/docs/reference/tuning.rst @@ -188,6 +188,7 @@ Tuning related functions Set the size of the receive (``SO_RCVBUF``) and send (``SO_SNDBUF``) buffers for incoming UDP sockets. On Linux the default values correspond to ``net.core.rmem_default`` and ``net.core.wmem_default`` , and the maximum values are restricted by ``net.core.rmem_max`` and ``net.core.wmem_max``. + Since 1.9.0, on Linux, dnsdist will automatically try to raise the buffer sizes to the maximum value allowed by the system (``net.core.rmem_max`` and ``net.core.wmem_max``) if :func:`setUDPSocketBufferSizes` is not set. :param int recv: ``SO_RCVBUF`` value. Default is 0, meaning the system value will be kept. :param int send: ``SO_SNDBUF`` value. Default is 0, meaning the system value will be kept. diff --git a/pdns/dnsdistdist/docs/reference/xsk.rst b/pdns/dnsdistdist/docs/reference/xsk.rst new file mode 100644 index 000000000000..d095024758dc --- /dev/null +++ b/pdns/dnsdistdist/docs/reference/xsk.rst @@ -0,0 +1,29 @@ +XSK / AF_XDP functions and objects +================================== + +These are all the functions, objects and methods related to :doc:`../advanced/xsk`. + +.. function:: newXSK(options) + + .. versionadded:: 1.9.0 + + This function creates a new :class:`XskSocket` object, tied to a network interface and queue, to accept ``XSK`` / ``AF_XDP`` packet from the Linux kernel. The returned object can be passed as a parameter to :func:`addLocal` to use XSK for ``UDP`` packets between clients and dnsdist. It can also be passed to ``newServer`` to use XSK for ``UDP`` packets between dnsdist a backend. + + :param table options: A table with key: value pairs with listen options. + + Options: + + * ``ifName``: str - The name of the network interface this object will be tied to. + * ``NIC_queue_id``: int - The queue of the network interface this object will be tied to. + * ``frameNums``: int - The number of ``UMEM`` frames to allocate for this socket. More frames mean that a higher number of packets can be processed at the same time. 65535 is a good choice for maximum performance. + * ``xskMapPath``: str - The path of the BPF map used to communicate with the kernel space XDP program, usually ``/sys/fs/bpf/dnsdist/xskmap``. + +.. class:: XskSocket + + .. versionadded:: 1.9.0 + + Represents a ``XSK`` / ``AF_XDP`` socket tied to a specific network interface and queue. This object can be created via :func:``newXSK`` and passed to :func:`addLocal` to use XSK for ``UDP`` packets between clients and dnsdist. It can also be passed to ``newServer`` to use XSK for ``UDP`` packets between dnsdist a backend. + + .. method:: XskSocket:getMetrics() -> str + + Returns a string containing ``XSK`` / ``AF_XDP`` metrics for this object, as reported by the Linux kernel. From 1b4bfa786896d6a75abfefb1b89ed6fcb348db6f Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 17:07:45 +0100 Subject: [PATCH 38/65] dnsdist: Add xsk, xskmap and umem to the spellchecker allow-list --- .github/actions/spell-check/expect.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 0465e6d06b0d..dda6d6b72df3 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -388,6 +388,7 @@ Ekkelenkamp elgoog endbr Enden +enp ent envoutput epel @@ -449,6 +450,7 @@ gaba gacogne gatech Gavarret +Gbps gdpr Geijn genindex @@ -477,6 +479,7 @@ gettime gettsigkey Geuze GFm +Ghz Gibheer Gieben Gillstrom @@ -648,6 +651,7 @@ ktls KTNAME Kuehrer kvs +kxdpgun Ladot Lafon Lakkas @@ -861,6 +865,7 @@ NETWORKMASK Neue Neuf newcontent +nftables nic Nilsen nimber @@ -1183,6 +1188,7 @@ Schueler schwer scopebits scopemask +sdfn sdfoijdfio sdig secpoll @@ -1409,6 +1415,7 @@ Ueber Ueli UIDs Uisms +UMEM unauth unbreak unescaping @@ -1513,6 +1520,8 @@ Xiang xorbooter xpf XRecord +xsk +xskmap XXXXXX yahttp yamlconversion From bd1e67ab639ceb0aaceac2c5e9af2d1d2cfff02d Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 17:01:26 +0100 Subject: [PATCH 39/65] dnsdist: Fix XSK/AF_XDP detection --- pdns/dnsdistdist/configure.ac | 4 ++++ pdns/dnsdistdist/m4/pdns_with_xsk.m4 | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pdns/dnsdistdist/configure.ac b/pdns/dnsdistdist/configure.ac index d9f6c719ddc3..81e39c8bc983 100644 --- a/pdns/dnsdistdist/configure.ac +++ b/pdns/dnsdistdist/configure.ac @@ -219,6 +219,10 @@ AS_IF([test "x$systemd" != "xn"], [AC_MSG_NOTICE([systemd: yes])], [AC_MSG_NOTICE([systemd: no])] ) +AS_IF([test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"], + [AC_MSG_NOTICE([AF_XDP/XSK: yes])], + [AC_MSG_NOTICE([AF_XDP/XSK: no])] +) AS_IF([test "x$HAVE_IPCIPHER" = "x1"], [AC_MSG_NOTICE([ipcipher: yes])], [AC_MSG_NOTICE([ipcipher: no])] diff --git a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 index 655d410f808f..32cee2707765 100644 --- a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 +++ b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 @@ -17,8 +17,12 @@ AC_DEFUN([PDNS_WITH_XSK],[ ], [:]) ]) ]) - AC_DEFINE([HAVE_XSK], [test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"], [Define to 1 if you have AF_XDP (XSK) support enabled]) + AM_CONDITIONAL([HAVE_XSK], [test x"$BPF_LIBS" != "x" -a x"$XDP_LIBS" != "x"]) + AM_COND_IF([HAVE_XSK], [ + AC_DEFINE([HAVE_XSK], [1], [Define to 1 if you have AF_XDP (XSK) support enabled]) + ]) + AS_IF([test "x$with_xsk" = "xyes"], [ AS_IF([test x"$BPF_LIBS" = "x" -o x"$XDP_LIBS" = "x" ], [ AC_MSG_ERROR([AF_XDP (XSK) support requested but required libbpf and/or libxdp were not found]) From 1c9b2109e8a12ea3a925f17bbbf88fb9449ed16a Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 17:01:59 +0100 Subject: [PATCH 40/65] dnsdist: We need the regular, non-XSK threads as well! --- pdns/dnsdist.cc | 1 - pdns/dnsdist.hh | 1 - pdns/dnsdistdist/dnsdist-backend.cc | 13 ++++++------- pdns/xsk.cc | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 358e970e52e4..75f33d2eb4c7 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -3104,7 +3104,6 @@ static void startFrontends() mapThreadToCPUList(xskCT.native_handle(), clientState->cpus); } xskCT.detach(); - continue; } #endif /* HAVE_XSK */ diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 1a92f25dab10..32fdf9561f43 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -847,7 +847,6 @@ private: void removeXSKDestination(int fd); #endif /* HAVE_XSK */ - std::thread tid; std::mutex connectLock; std::condition_variable d_connectedWait; #ifdef HAVE_XSK diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 49b47b8cdcca..0138c6e3987a 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -330,19 +330,18 @@ void DownstreamState::start() if (connected && !threadStarted.test_and_set()) { #ifdef HAVE_XSK if (xskInfo != nullptr) { - tid = std::thread(dnsdist::xsk::XskResponderThread, shared_from_this()); - } - else { - tid = std::thread(responderThread, shared_from_this()); + auto xskResponderThread = std::thread(dnsdist::xsk::XskResponderThread, shared_from_this()); + if (!d_config.d_cpus.empty()) { + mapThreadToCPUList(xskResponderThread.native_handle(), d_config.d_cpus); + } + xskResponderThread.detach(); } -#else - tid = std::thread(responderThread, shared_from_this()); #endif /* HAVE_XSK */ + auto tid = std::thread(responderThread, shared_from_this()); if (!d_config.d_cpus.empty()) { mapThreadToCPUList(tid.native_handle(), d_config.d_cpus); } - tid.detach(); } } diff --git a/pdns/xsk.cc b/pdns/xsk.cc index cfa6912419b7..66da6824002f 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -1009,7 +1009,7 @@ void XskPacket::rewrite() noexcept uint32_t words[3]; }; }; - struct ipv4_pseudo_header_t pseudo_header{}; + ipv4_pseudo_header_t pseudo_header{}; static_assert(sizeof(pseudo_header) == 12, "IPv4 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ @@ -1043,7 +1043,7 @@ void XskPacket::rewrite() noexcept uint32_t words[10]; }; }; - struct ipv6_pseudo_header_t pseudo_header{}; + ipv6_pseudo_header_t pseudo_header{}; static_assert(sizeof(pseudo_header) == 40, "IPv6 pseudo-header size is incorrect"); /* Fill in the pseudo-header. */ From 38244a9b699484dd8ab162fb18381bae25edf56b Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 Jan 2024 17:02:38 +0100 Subject: [PATCH 41/65] dnsdist: Fix a clang-tidy warning --- pdns/dnsdist-lua-actions.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index 94ced07641c6..e6430077c90a 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -1070,7 +1070,7 @@ class SetMacAddrAction : public DNSAction DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override { - dnsdist::MacAddress mac; + dnsdist::MacAddress mac{}; int res = dnsdist::MacAddressesCache::get(dnsquestion->ids.origRemote, mac.data(), mac.size()); if (res != 0) { return Action::None; From 70b5d529ab3438bcc936d3f8a80089057c505466 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 16 Jan 2024 11:34:21 +0100 Subject: [PATCH 42/65] dnsdist: Add default values for the map and number of frames in `newXsk` --- pdns/dnsdist-lua-bindings.cc | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index d0e602e22dfc..12bf66526833 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -727,10 +727,15 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) return std::shared_ptr(nullptr); } uint32_t queue_id; - uint32_t frameNums; + uint32_t frameNums{65536}; std::string ifName; - std::string path; - std::string poolName; + std::string path("/sys/fs/bpf/dnsdist/xskmap"); + if (opts.count("ifName") == 1) { + ifName = boost::get(opts.at("ifName")); + } + else { + throw std::runtime_error("ifName field is required!"); + } if (opts.count("NIC_queue_id") == 1) { queue_id = boost::get(opts.at("NIC_queue_id")); } @@ -740,21 +745,9 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) if (opts.count("frameNums") == 1) { frameNums = boost::get(opts.at("frameNums")); } - else { - throw std::runtime_error("frameNums field is required!"); - } - if (opts.count("ifName") == 1) { - ifName = boost::get(opts.at("ifName")); - } - else { - throw std::runtime_error("ifName field is required!"); - } if (opts.count("xskMapPath") == 1) { path = boost::get(opts.at("xskMapPath")); } - else { - throw std::runtime_error("xskMapPath field is required!"); - } auto socket = std::make_shared(frameNums, ifName, queue_id, path); dnsdist::xsk::g_xsk.push_back(socket); return socket; From c184b26aed67aebb7b0f45ddeb3b77db17115fbc Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 16 Jan 2024 11:42:45 +0100 Subject: [PATCH 43/65] dnsdist: Install libbpf and libxdp in our CI image --- .github/workflows/build-and-test-all.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- tasks.py | 54 ++++++++++++++---------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-and-test-all.yml b/.github/workflows/build-and-test-all.yml index 2b97fd36f302..7528f78352ef 100644 --- a/.github/workflows/build-and-test-all.yml +++ b/.github/workflows/build-and-test-all.yml @@ -626,7 +626,7 @@ jobs: name: dnsdist-full-${{ matrix.sanitizers }}-${{ env.normalized-branch-name }} path: /opt/dnsdist - run: inv install-clang-runtime - - run: inv install-dnsdist-test-deps + - run: inv install-dnsdist-test-deps 'with-xdp' - run: inv test-dnsdist - run: inv generate-coverage-info /opt/dnsdist/bin/dnsdist $GITHUB_WORKSPACE if: ${{ env.COVERAGE == 'yes' && matrix.sanitizers != 'tsan' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 38a0d87b5bbc..ea0493e72900 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -147,7 +147,7 @@ jobs: - name: Install dependencies for dnsdist if: matrix.product == 'dnsdist' run: | - inv install-dnsdist-build-deps + inv install-dnsdist-build-deps --skipXDP=True - name: Autoreconf dnsdist if: matrix.product == 'dnsdist' working-directory: ./pdns/dnsdistdist/ diff --git a/tasks.py b/tasks.py index 599f7690565d..d8f54a91b05f 100644 --- a/tasks.py +++ b/tasks.py @@ -84,6 +84,10 @@ 'libre2-dev', 'libsnmp-dev', ] +dnsdist_xdp_build_deps = [ + 'libbpf-dev', + 'libxdp-dev', +] auth_test_deps = [ # FIXME: we should be generating some of these from shlibdeps in build 'authbind', 'bc', @@ -294,26 +298,30 @@ def install_rec_test_deps(c): # FIXME: rename this, we do way more than apt-get c.sudo('chmod 755 /var/agentx') @task -def install_dnsdist_test_deps(c): # FIXME: rename this, we do way more than apt-get - c.sudo('apt-get install -y \ - libluajit-5.1-2 \ - libboost-all-dev \ - libcap2 \ - libcdb1 \ - libcurl4-openssl-dev \ - libfstrm0 \ - libgnutls30 \ - libh2o-evloop0.13 \ - liblmdb0 \ - libnghttp2-14 \ - "libre2-[1-9]+" \ - libssl-dev \ - libsystemd0 \ - libsodium23 \ - lua-socket \ - patch \ - protobuf-compiler \ - python3-venv snmpd prometheus') +def install_dnsdist_test_deps(c, xdp=True): # FIXME: rename this, we do way more than apt-get + deps = 'libluajit-5.1-2 \ + libboost-all-dev \ + libcap2 \ + libcdb1 \ + libcurl4-openssl-dev \ + libfstrm0 \ + libgnutls30 \ + libh2o-evloop0.13 \ + liblmdb0 \ + libnghttp2-14 \ + "libre2-[1-9]+" \ + libssl-dev \ + libsystemd0 \ + libsodium23 \ + lua-socket \ + patch \ + protobuf-compiler \ + python3-venv snmpd prometheus' + if xdp: + deps = deps + 'libbpf1 \ + libxdp1' + + c.sudo(f'apt-get install -y {deps}') c.run('sed "s/agentxperms 0700 0755 dnsdist/agentxperms 0777 0755/g" regression-tests.dnsdist/snmpd.conf | sudo tee /etc/snmp/snmpd.conf') c.sudo('/etc/init.d/snmpd restart') time.sleep(5) @@ -323,9 +331,9 @@ def install_dnsdist_test_deps(c): # FIXME: rename this, we do way more than apt- def install_rec_build_deps(c): c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + rec_build_deps)) -@task -def install_dnsdist_build_deps(c): - c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + dnsdist_build_deps)) +@task(optional=['skipXDP']) +def install_dnsdist_build_deps(c, skipXDP=False): + c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + dnsdist_build_deps + dnsdist_xdp_build_deps if not skipXDP else [])) @task def ci_autoconf(c): From 991210c38e7370774a44b843e5ca13c8640dc6a5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 19 Jan 2024 09:10:44 +0100 Subject: [PATCH 44/65] dnsdist: Log whether UDP queries are forwarded via XSK --- pdns/dnsdist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 75f33d2eb4c7..f58442b034da 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -1707,7 +1707,7 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint1 dq.ids.origID = queryID; dq.ids.forwardedOverUDP = true; - vinfolog("Got query for %s|%s from %s%s, relayed to %s", dq.ids.qname.toLogString(), QType(dq.ids.qtype).toString(), dq.ids.origRemote.toStringWithPort(), (doh ? " (https)" : ""), ds->getNameWithAddr()); + vinfolog("Got query for %s|%s from %s%s, relayed to %s%s", dq.ids.qname.toLogString(), QType(dq.ids.qtype).toString(), dq.ids.origRemote.toStringWithPort(), (doh ? " (https)" : ""), ds->getNameWithAddr(), actuallySend ? "" : " (xsk)"); /* make a copy since we cannot touch dq.ids after the move */ auto proxyProtocolPayloadSize = dq.ids.d_proxyProtocolPayloadSize; From f928337f89bdd81a6d9d45434bea31ee14ad9235 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 19 Jan 2024 09:14:38 +0100 Subject: [PATCH 45/65] dnsdist: Fix XSK over IPv6 --- pdns/xsk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 66da6824002f..3c4f8d3b7b22 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -902,7 +902,7 @@ void XskPacket::rewrite() noexcept ipHeader.nexthdr = IPPROTO_UDP; udpHeader.source = from.sin6.sin6_port; udpHeader.dest = to.sin6.sin6_port; - udpHeader.len = htons(getDataSize()); + udpHeader.len = htons(getDataSize() + sizeof(udpHeader)); udpHeader.check = 0; /* needed to get the correct checksum */ setIPv6Header(ipHeader); From 73dc64aba93eda49a1a81b2e97b236afc9b2e00b Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 19 Jan 2024 09:53:48 +0100 Subject: [PATCH 46/65] dnsdist: Log whether we are using XSK, and which mode (native or emulated) --- pdns/dnsdist-lua.cc | 13 ++++++++++--- pdns/dnsdist.cc | 5 +++++ pdns/xsk.cc | 22 ++++++++++++++++++++++ pdns/xsk.hh | 1 + 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 724af95c67c8..8d931a4c1fe3 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -630,9 +630,6 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) // create but don't connect the socket in client or check-config modes auto ret = std::make_shared(std::move(config), std::move(tlsCtx), !(client || configCheck)); - if (!(client || configCheck)) { - infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); - } #ifdef HAVE_XSK std::shared_ptr xskSocket; if (getOptionalValue>(vars, "xskSocket", xskSocket) > 0) { @@ -652,6 +649,14 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } memcpy(ret->d_config.destMACAddr.data(), mac.data(), ret->d_config.destMACAddr.size()); } + infolog("Added downstream server %s via XSK in %s mode", ret->d_config.remote.toStringWithPort(), xskSocket->getXDPMode()); + } + else if (!(client || configCheck)) { + infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); + } +#else /* HAVE_XSK */ + if (!(client || configCheck)) { + infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); } #endif /* HAVE_XSK */ if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) { @@ -794,6 +799,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; socket->addWorker(udpCS->xskInfo); socket->addWorkerRoute(udpCS->xskInfo, loc); + vinfolog("Enabling XSK in %s mode for incoming UDP packets to %s", socket->getXDPMode(), loc.toStringWithPort()); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); @@ -847,6 +853,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; socket->addWorker(udpCS->xskInfo); socket->addWorkerRoute(udpCS->xskInfo, loc); + vinfolog("Enabling XSK in %s mode for incoming UDP packets to %s", socket->getXDPMode(), loc.toStringWithPort()); } #endif /* HAVE_XSK */ g_frontends.push_back(std::move(udpCS)); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index f58442b034da..6f4bca6c6804 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -2624,6 +2624,11 @@ static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, else if (clientState.dnscryptCtx != nullptr) { infolog("Listening on %s for DNSCrypt", addr.toStringWithPort()); } +#ifdef HAVE_XSK + else if (clientState.xskInfo != nullptr) { + infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort()); + } +#endif else { infolog("Listening on %s", addr.toStringWithPort()); } diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 3c4f8d3b7b22..0eaf08110614 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -470,6 +470,28 @@ std::string XskSocket::getMetrics() const return ret.str(); } +[[nodiscard]] std::string XskSocket::getXDPMode() const +{ + unsigned int itfIdx = if_nametoindex(ifName.c_str()); + if (itfIdx == 0) { + return {}; + } + struct bpf_xdp_query_opts info = { .sz = sizeof(info) }; + int ret = bpf_xdp_query(itfIdx, 0, &info); + if (ret != 0) { + return {}; + } + switch (info.attach_mode) { + case XDP_ATTACHED_DRV: + case XDP_ATTACHED_HW: + return "native"; + case XDP_ATTACHED_SKB: + return "emulated"; + default: + return "unknown"; + } +} + void XskSocket::markAsFree(const XskPacket& packet) { auto offset = frameOffset(packet); diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 4ea36e38b36a..3b16717989f9 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -148,6 +148,7 @@ public: void addWorkerRoute(const std::shared_ptr& worker, const ComboAddress& dest); void removeWorkerRoute(const ComboAddress& dest); [[nodiscard]] std::string getMetrics() const; + [[nodiscard]] std::string getXDPMode() const; void markAsFree(const XskPacket& packet); [[nodiscard]] const std::shared_ptr& getWorkerByDescriptor(int desc) const { From 40f605d2f05651668c1327eaf03ad9a9f9f49264 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 19 Jan 2024 15:40:28 +0100 Subject: [PATCH 47/65] dnsdist: Cosmetic fixes for XSK --- pdns/dnsdistdist/dnsdist-xsk.cc | 1 + pdns/xsk.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc index fbc0313ae76c..81480a99a179 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.cc +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -25,6 +25,7 @@ #ifdef HAVE_XSK #include +#include "dolog.hh" #include "dnsdist-metrics.hh" #include "dnsdist-proxy-protocol.hh" #include "threadname.hh" diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 0eaf08110614..7c6d67e1bd3b 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -476,7 +476,8 @@ std::string XskSocket::getMetrics() const if (itfIdx == 0) { return {}; } - struct bpf_xdp_query_opts info = { .sz = sizeof(info) }; + bpf_xdp_query_opts info{}; + info.sz = sizeof(info); int ret = bpf_xdp_query(itfIdx, 0, &info); if (ret != 0) { return {}; From db96b412c8563fd2a5d50670b868bd33a7a54bb6 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 19 Jan 2024 17:22:57 +0100 Subject: [PATCH 48/65] dnsdist: Refactor XSK support between dnsdist and the backends --- pdns/dnsdist-lua.cc | 20 +++++++----- pdns/dnsdist.cc | 26 +++++++-------- pdns/dnsdist.hh | 7 ++-- pdns/dnsdistdist/dnsdist-backend.cc | 50 ++++++++++++++++++++--------- pdns/dnsdistdist/dnsdist-xsk.cc | 16 ++++----- pdns/dnsdistdist/dnsdist-xsk.hh | 2 +- 6 files changed, 68 insertions(+), 53 deletions(-) diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 8d931a4c1fe3..e22a83cd68d6 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -310,7 +310,7 @@ static bool checkConfigurationTime(const std::string& name) // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) { - typedef LuaAssociativeTable, std::shared_ptr, DownstreamState::checkfunc_t>> newserver_t; + using newserver_t = LuaAssociativeTable, LuaArray>, DownstreamState::checkfunc_t>>; luaCtx.writeFunction("inClientStartup", [client]() { return client && !g_configurationDone; }); @@ -631,12 +631,16 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) // create but don't connect the socket in client or check-config modes auto ret = std::make_shared(std::move(config), std::move(tlsCtx), !(client || configCheck)); #ifdef HAVE_XSK - std::shared_ptr xskSocket; - if (getOptionalValue>(vars, "xskSocket", xskSocket) > 0) { + LuaArray> luaXskSockets; + if (getOptionalValue>>(vars, "xskSockets", luaXskSockets) > 0 && !luaXskSockets.empty()) { if (g_configurationDone) { throw std::runtime_error("Adding a server with xsk at runtime is not supported"); } - ret->registerXsk(xskSocket); + std::vector> xskSockets; + for (auto& socket : luaXskSockets) { + xskSockets.push_back(socket.second); + } + ret->registerXsk(xskSockets); std::string mac; if (getOptionalValue(vars, "MACAddr", mac) > 0) { auto* addr = &ret->d_config.destMACAddr[0]; @@ -649,15 +653,15 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } memcpy(ret->d_config.destMACAddr.data(), mac.data(), ret->d_config.destMACAddr.size()); } - infolog("Added downstream server %s via XSK in %s mode", ret->d_config.remote.toStringWithPort(), xskSocket->getXDPMode()); + infolog("Added downstream server %s via XSK in %s mode", ret->d_config.remote.toStringWithPort(), xskSockets.at(0)->getXDPMode()); } else if (!(client || configCheck)) { infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); } #else /* HAVE_XSK */ - if (!(client || configCheck)) { - infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); - } + if (!(client || configCheck)) { + infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); + } #endif /* HAVE_XSK */ if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) { dnsdist::ServiceDiscovery::addUpgradeableServer(ret, upgradeInterval, upgradePool, upgradeDoHKey, keepAfterUpgrade); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 6f4bca6c6804..32521e7fbe29 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -370,7 +370,7 @@ bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, } catch (const std::exception& e) { if (remote && response.size() > 0 && static_cast(response.size()) > sizeof(dnsheader)) { - vinfolog("Backend %s sent us a response with id %d that did not parse: %s", remote->d_config.remote.toStringWithPort(), ntohs(dh->id), e.what()); + infolog("Backend %s sent us a response with id %d that did not parse: %s", remote->d_config.remote.toStringWithPort(), ntohs(dh->id), e.what()); } ++dnsdist::metrics::g_stats.nonCompliantResponses; if (remote) { @@ -881,7 +881,8 @@ void responderThread(std::shared_ptr dss) continue; } xskPacket->setHeader(ids->xskPacketHeader); - xskPacket->setPayload(response); + if (!xskPacket->setPayload(response)) { + } xskPacket->updatePacket(); xskInfo->pushToSendQueue(*xskPacket); xskInfo->notifyXskSocket(); @@ -1983,14 +1984,13 @@ bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) return false; } -#ifdef HAVE_XSK - if (!ss->xskInfo) { + if (ss->d_xskInfos.empty()) { assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); return false; } else { - int fd = ss->xskInfo->workerWaker; - ids.backendFD = fd; + const auto& xskInfo = ss->pickWorkerForSending(); + ids.backendFD = xskInfo->workerWaker; assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, false); auto sourceAddr = ss->pickSourceAddressForSending(); packet.setAddr(sourceAddr, ss->d_config.sourceMACAddr, ss->d_config.remote, ss->d_config.destMACAddr); @@ -1998,10 +1998,6 @@ bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) packet.rewrite(); return true; } -#else /* HAVE_XSK */ - assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, true); - return false; -#endif /* HAVE_XSK */ } catch (const std::exception& e) { vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what()); @@ -2624,11 +2620,6 @@ static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, else if (clientState.dnscryptCtx != nullptr) { infolog("Listening on %s for DNSCrypt", addr.toStringWithPort()); } -#ifdef HAVE_XSK - else if (clientState.xskInfo != nullptr) { - infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort()); - } -#endif else { infolog("Listening on %s", addr.toStringWithPort()); } @@ -2638,6 +2629,11 @@ static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, } else if (clientState.doh3Frontend != nullptr) { infolog("Listening on %s for DoH3", addr.toStringWithPort()); } +#ifdef HAVE_XSK + else if (clientState.xskInfo != nullptr) { + infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort()); + } +#endif } } diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 32fdf9561f43..52019d56e285 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -825,8 +825,8 @@ public: StopWatch sw; QPSLimiter qps; #ifdef HAVE_XSK - std::shared_ptr xskInfo{nullptr}; - std::shared_ptr d_xskSocket{nullptr}; + std::vector> d_xskInfos; + std::vector> d_xskSockets; #endif std::atomic idOffset{0}; size_t socketsOffset{0}; @@ -993,8 +993,9 @@ public: std::optional getState(uint16_t id); #ifdef HAVE_XSK - void registerXsk(std::shared_ptr& xsk); + void registerXsk(std::vector>& xsks); [[nodiscard]] ComboAddress pickSourceAddressForSending(); + [[nodiscard]] const std::shared_ptr& pickWorkerForSending(); #endif /* HAVE_XSK */ dnsdist::Protocol getProtocol() const diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 0138c6e3987a..966b3ff9c20a 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -55,7 +55,9 @@ void DownstreamState::addXSKDestination(int fd) addresses->push_back(local); } dnsdist::xsk::addDestinationAddress(local); - d_xskSocket->addWorkerRoute(xskInfo, local); + for (size_t idx = 0; idx < d_xskSockets.size(); idx++) { + d_xskSockets.at(idx)->addWorkerRoute(d_xskInfos.at(idx), local); + } } void DownstreamState::removeXSKDestination(int fd) @@ -67,7 +69,9 @@ void DownstreamState::removeXSKDestination(int fd) } dnsdist::xsk::removeDestinationAddress(local); - d_xskSocket->removeWorkerRoute(local); + for (auto& xskSocket : d_xskSockets) { + xskSocket->removeWorkerRoute(local); + } } #endif /* HAVE_XSK */ @@ -85,7 +89,7 @@ bool DownstreamState::reconnect(bool initialAttempt) connected = false; #ifdef HAVE_XSK - if (xskInfo != nullptr) { + if (!d_xskInfos.empty()) { auto addresses = d_socketSourceAddresses.write_lock(); addresses->clear(); } @@ -97,7 +101,7 @@ bool DownstreamState::reconnect(bool initialAttempt) (*mplexer.lock())->removeReadFD(fd); } #ifdef HAVE_XSK - if (xskInfo != nullptr) { + if (d_xskInfos.empty()) { removeXSKDestination(fd); } #endif /* HAVE_XSK */ @@ -132,7 +136,7 @@ bool DownstreamState::reconnect(bool initialAttempt) (*mplexer.lock())->addReadFD(fd, [](int, boost::any) {}); } #ifdef HAVE_XSK - if (xskInfo != nullptr) { + if (!d_xskInfos.empty()) { addXSKDestination(fd); } #endif /* HAVE_XSK */ @@ -150,7 +154,7 @@ bool DownstreamState::reconnect(bool initialAttempt) /* if at least one (re-)connection failed, close all sockets */ if (!connected) { #ifdef HAVE_XSK - if (xskInfo != nullptr) { + if (!d_xskInfos.empty()) { auto addresses = d_socketSourceAddresses.write_lock(); addresses->clear(); } @@ -158,7 +162,7 @@ bool DownstreamState::reconnect(bool initialAttempt) for (auto& fd : sockets) { if (fd != -1) { #ifdef HAVE_XSK - if (xskInfo != nullptr) { + if (!d_xskInfos.empty()) { removeXSKDestination(fd); } #endif /* HAVE_XSK */ @@ -329,8 +333,8 @@ void DownstreamState::start() { if (connected && !threadStarted.test_and_set()) { #ifdef HAVE_XSK - if (xskInfo != nullptr) { - auto xskResponderThread = std::thread(dnsdist::xsk::XskResponderThread, shared_from_this()); + for (auto& xskInfo : d_xskInfos) { + auto xskResponderThread = std::thread(dnsdist::xsk::XskResponderThread, shared_from_this(), xskInfo); if (!d_config.d_cpus.empty()) { mapThreadToCPUList(xskResponderThread.native_handle(), d_config.d_cpus); } @@ -881,12 +885,22 @@ void DownstreamState::submitHealthCheckResult(bool initial, bool newResult) return (*addresses)[idx % numberOfAddresses]; } -void DownstreamState::registerXsk(std::shared_ptr& xsk) +[[nodiscard]] const std::shared_ptr& DownstreamState::pickWorkerForSending() +{ + auto numberOfWorkers = d_xskInfos.size(); + if (numberOfWorkers == 0) { + throw std::runtime_error("No XSK worker available for sending XSK data to backend " + getNameWithAddr()); + } + size_t idx = dnsdist::getRandomValue(numberOfWorkers); + return d_xskInfos[idx % numberOfWorkers]; +} + +void DownstreamState::registerXsk(std::vector>& xsks) { - d_xskSocket = xsk; + d_xskSockets = xsks; if (d_config.sourceAddr.sin4.sin_family == 0 || (IsAnyAddress(d_config.sourceAddr))) { - const auto& ifName = xsk->getInterfaceName(); + const auto& ifName = xsks.at(0)->getInterfaceName(); auto addresses = getListOfAddressesOfNetworkInterface(ifName); if (addresses.empty()) { throw std::runtime_error("Unable to get source address from interface " + ifName); @@ -897,11 +911,15 @@ void DownstreamState::registerXsk(std::shared_ptr& xsk) } d_config.sourceAddr = addresses.at(0); } - xskInfo = XskWorker::create(); - xsk->addWorker(xskInfo); + d_config.sourceMACAddr = d_xskSockets.at(0)->getSourceMACAddress(); + + for (auto& xsk : d_xskSockets) { + auto xskInfo = XskWorker::create(); + d_xskInfos.push_back(xskInfo); + xsk->addWorker(xskInfo); + xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; + } reconnect(false); - d_config.sourceMACAddr = xsk->getSourceMACAddress(); - xskInfo->sharedEmptyFrameOffset = xsk->sharedEmptyFrameOffset; } #endif /* HAVE_XSK */ diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc index 81480a99a179..2996683c95de 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.cc +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -35,17 +35,12 @@ namespace dnsdist::xsk { std::vector> g_xsk; -void XskResponderThread(std::shared_ptr dss) +void XskResponderThread(std::shared_ptr dss, std::shared_ptr xskInfo) { - if (dss->xskInfo == nullptr) { - throw std::runtime_error("Starting XSK responder thread for a backend without XSK!"); - } - try { setThreadName("dnsdist/XskResp"); auto localRespRuleActions = g_respruleactions.getLocal(); auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal(); - auto xskInfo = dss->xskInfo; auto pollfds = getPollFdsForWorker(*xskInfo); const auto xskFd = xskInfo->workerWaker.getHandle(); while (!dss->isStopped()) { @@ -66,7 +61,8 @@ void XskResponderThread(std::shared_ptr dss) const auto queryId = dnsHeader->id; auto ids = dss->getState(queryId); if (ids) { - if (xskFd != ids->backendFD || !ids->isXSK()) { + if (!ids->isXSK()) { + // if (xskFd != ids->backendFD || !ids->isXSK()) { dss->restoreState(queryId, std::move(*ids)); ids = std::nullopt; } @@ -82,19 +78,19 @@ void XskResponderThread(std::shared_ptr dss) } if (!processResponderPacket(dss, response, *localRespRuleActions, *localCacheInsertedRespRuleActions, std::move(*ids))) { xskInfo->markAsFree(packet); - vinfolog("XSK packet pushed to queue because processResponderPacket failed"); + infolog("XSK packet pushed to queue because processResponderPacket failed"); return; } if (response.size() > packet.getCapacity()) { /* fallback to sending the packet via normal socket */ sendUDPResponse(ids->cs->udpFD, response, ids->delayMsec, ids->hopLocal, ids->hopRemote); - vinfolog("XSK packet falling back because packet is too large"); + infolog("XSK packet falling back because packet is too large"); xskInfo->markAsFree(packet); return; } packet.setHeader(ids->xskPacketHeader); if (!packet.setPayload(response)) { - vinfolog("Unable to set XSK payload !"); + infolog("Unable to set XSK payload !"); } if (ids->delayMsec > 0) { packet.addDelay(ids->delayMsec); diff --git a/pdns/dnsdistdist/dnsdist-xsk.hh b/pdns/dnsdistdist/dnsdist-xsk.hh index 6a862d75b4f7..f677b78604dc 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.hh +++ b/pdns/dnsdistdist/dnsdist-xsk.hh @@ -32,7 +32,7 @@ class XskWorker; namespace dnsdist::xsk { -void XskResponderThread(std::shared_ptr dss); +void XskResponderThread(std::shared_ptr dss, std::shared_ptr xskInfo); bool XskIsQueryAcceptable(const XskPacket& packet, ClientState& clientState, LocalHolders& holders, bool& expectProxyProtocol); bool XskProcessQuery(ClientState& clientState, LocalHolders& holders, XskPacket& packet); void XskRouter(std::shared_ptr xsk); From 4b99619110687818dac3de00805722d37c4ea4d5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 22 Jan 2024 12:28:09 +0100 Subject: [PATCH 49/65] dnsdist: Properly delay response packets in incoming-only XSK mode --- pdns/dnsdist.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 32521e7fbe29..cf3f6129e947 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -883,6 +883,9 @@ void responderThread(std::shared_ptr dss) xskPacket->setHeader(ids->xskPacketHeader); if (!xskPacket->setPayload(response)) { } + if (ids->delayMsec > 0) { + xskPacket->addDelay(ids->delayMsec); + } xskPacket->updatePacket(); xskInfo->pushToSendQueue(*xskPacket); xskInfo->notifyXskSocket(); From 465bf9e23403a94a79f19de752dc1bc035032d00 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 22 Jan 2024 12:28:25 +0100 Subject: [PATCH 50/65] dnsdist: Clear the XSK responder notification queue right away Otherwise we might discard a valid notification between our last look at the queue and the clearing. --- pdns/dnsdistdist/dnsdist-xsk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc index 2996683c95de..fc77706aa020 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.cc +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -48,6 +48,7 @@ void XskResponderThread(std::shared_ptr dss, std::shared_ptrcleanSocketNotification(); #if defined(__SANITIZE_THREAD__) xskInfo->incomingPacketsQueue.lock()->consume_all([&](XskPacket& packet) { #else @@ -98,7 +99,6 @@ void XskResponderThread(std::shared_ptr dss, std::shared_ptrpushToSendQueue(packet); }); - xskInfo->cleanSocketNotification(); } if (needNotify) { xskInfo->notifyXskSocket(); From 86b47ec8b89cdb54b3bf1999b2e863cf2ce6f3bb Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 22 Jan 2024 16:49:53 +0100 Subject: [PATCH 51/65] dnsdist: Enable XSK (AF_XDP) on supported OSes --- .../debian/dnsdist/debian-bookworm/compat | 1 + .../debian/dnsdist/debian-bookworm/control | 39 +++ .../debian/dnsdist/debian-bookworm/copyright | 284 ++++++++++++++++++ .../dnsdist/debian-bookworm/dnsdist.dirs | 1 + .../dnsdist/debian-bookworm/dnsdist.examples | 1 + .../dnsdist/debian-bookworm/dnsdist.postinst | 43 +++ .../debian/dnsdist/debian-bookworm/docs | 1 + .../debian/dnsdist/debian-bookworm/gbp.conf | 4 + .../debian-bookworm/missing-sources/d3.js | 1 + .../debian-bookworm/missing-sources/jquery.js | 1 + .../debian-bookworm/missing-sources/moment.js | 1 + .../missing-sources/rickshaw.js | 1 + .../debian/dnsdist/debian-bookworm/rules | 103 +++++++ .../dnsdist/debian-bookworm/source/format | 1 + .../debian-bookworm/upstream/signing-key.asc | 189 ++++++++++++ .../debian/dnsdist/debian-bookworm/watch | 3 + .../Dockerfile.target.debian-bookworm | 2 +- .../Dockerfile.target.debian-trixie | 2 +- .../Dockerfile.target.ubuntu-mantic | 2 +- builder-support/specs/dnsdist.spec | 4 + 20 files changed, 681 insertions(+), 3 deletions(-) create mode 100644 builder-support/debian/dnsdist/debian-bookworm/compat create mode 100644 builder-support/debian/dnsdist/debian-bookworm/control create mode 100644 builder-support/debian/dnsdist/debian-bookworm/copyright create mode 100644 builder-support/debian/dnsdist/debian-bookworm/dnsdist.dirs create mode 100644 builder-support/debian/dnsdist/debian-bookworm/dnsdist.examples create mode 100644 builder-support/debian/dnsdist/debian-bookworm/dnsdist.postinst create mode 100644 builder-support/debian/dnsdist/debian-bookworm/docs create mode 100644 builder-support/debian/dnsdist/debian-bookworm/gbp.conf create mode 120000 builder-support/debian/dnsdist/debian-bookworm/missing-sources/d3.js create mode 120000 builder-support/debian/dnsdist/debian-bookworm/missing-sources/jquery.js create mode 120000 builder-support/debian/dnsdist/debian-bookworm/missing-sources/moment.js create mode 120000 builder-support/debian/dnsdist/debian-bookworm/missing-sources/rickshaw.js create mode 100755 builder-support/debian/dnsdist/debian-bookworm/rules create mode 100644 builder-support/debian/dnsdist/debian-bookworm/source/format create mode 100644 builder-support/debian/dnsdist/debian-bookworm/upstream/signing-key.asc create mode 100644 builder-support/debian/dnsdist/debian-bookworm/watch diff --git a/builder-support/debian/dnsdist/debian-bookworm/compat b/builder-support/debian/dnsdist/debian-bookworm/compat new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/compat @@ -0,0 +1 @@ +10 diff --git a/builder-support/debian/dnsdist/debian-bookworm/control b/builder-support/debian/dnsdist/debian-bookworm/control new file mode 100644 index 000000000000..023b966cfbec --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/control @@ -0,0 +1,39 @@ +Source: dnsdist +Section: net +Priority: optional +Maintainer: PowerDNS.COM BV +Uploaders: PowerDNS.COM BV +Build-Depends: debhelper (>= 10), + libboost-all-dev, + libbpf-dev [linux-any], + libcap-dev, + libcdb-dev, + libedit-dev, + libfstrm-dev, + libgnutls28-dev, + liblmdb-dev, + libluajit-5.1-dev [!arm64 !s390x], + liblua5.3-dev [arm64 s390x], + libnghttp2-dev, + libre2-dev, + libsnmp-dev, + libsodium-dev, + libssl-dev, + libsystemd-dev [linux-any], + libwslay-dev, + libxdp-dev [linux-any], + pkg-config, + ragel, + systemd [linux-any] +Standards-Version: 4.1.5 +Homepage: https://dnsdist.org + +Package: dnsdist +Architecture: any +Depends: ${misc:Depends}, + ${shlibs:Depends} +Description: DNS loadbalancer + Highly DoS- and abuse-aware load balancing tool for DNS traffic, + with Lua scripting and configuration capability. + Can be configured to use various sets of rules to classify, route + and reject traffic. diff --git a/builder-support/debian/dnsdist/debian-bookworm/copyright b/builder-support/debian/dnsdist/debian-bookworm/copyright new file mode 100644 index 000000000000..5fbb60206907 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/copyright @@ -0,0 +1,284 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dnsdist +Source: https://dnsdist.org + +Files: * +Copyright: 2002-2022 PowerDNS.COM BV and contributors +License: GPL-2 with OpenSSL Exception + +Files: debian/* +Copyright: 2002-2016 PowerDNS.COM BV and contributors + 2016 Chris Hofstaedtler +License: GPL-2 with OpenSSL Exception +Comment: Debian packaging is under same license as upstream code + +Files: ext/json11/* +Copyright: 2013 Dropbox, Inc. +License: Expat + +Files: ext/libbpf/* +Copyright: 2015, 2016 Alexei Starovoitov +License: GPL-2 +Comment: taken from Linux kernel source + +Files: ext/luawrapper/* +Copyright: 2013, Pierre KRIEGER +License: BSD-3 + +Files: ext/yahttp/* +Copyright: 2014 Aki Tuomi +License: Expat + +Files: compile ltmain.sh +Copyright: 1996-2011 Free Software Foundation, Inc. +License: GPL-2+ + +Files: m4/ax_cxx_compile_stdcxx_11.m4 +Copyright: 2008 Benjamin Kosnik + 2012 Zack Weinberg + 2013 Roy Stogner + 2014, 2015 Google Inc.; contributed by Alexey Sokolov +License: free-generic + +Files: m4/boost.m4 +Copyright: 2007-2011, 2014 Benoit Sigoure +License: GPL-3 or Autoconf + +Files: m4/libtool.m4 m4/lt*.m4 +Copyright: 1996-2011 Free Software Foundation, Inc. +License: free-fsf + +Files: m4/systemd.m4 +Copyright: 2014 Luis R. Rodriguez + 2016 Pieter Lexis +License: GPL-2+ + +Files: m4/warnings.m4 +Copyright: 2008-2015 Free Software Foundation, Inc. +License: free-fsf + +Files: m4/pdns_d_fortify_source.m4 m4/pdns_param_ssp_buffer_size.m4 m4/pdns_pie.m4 m4/pdns_relro.m4 m4/pdns_stack_protector.m4 +Copyright: 2013 Red Hat, Inc. +License: LGPL-2.1+ + +Files: src_js/d3.js +Copyright: 2010-2016 Mike Bostock +License: Expat + +Files: src_js/jquery.js +Copyright: JS Foundation and other contributors +License: Expat + +Files: src_js/moment.js +Copyright: JS Foundation and other contributors +License: Expat + +Files: src_js/rickshaw.js +Copyright: 2011-2014 by Shutterstock Images, LLC +License: Expat + +Files: */libdnsdist-quiche.so +Copyright: 2018-2019, Cloudflare, Inc. +License: BSD-2-clause + +License: Unlicense + This is free and unencumbered software released into the public domain. + . + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + . + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + . + For more information, please refer to + +License: GPL-2 with OpenSSL Exception + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + . + In addition, for the avoidance of any doubt, permission is granted to + link this program with OpenSSL and to (re)distribute the binaries + produced as the result of such linking. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +License: BSD-2-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: BSD-3 + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: LGPL-2.1+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see + . + . + On Debian systems, the full text of the GNU Lesser General Public + License version 2.1 can be found in the file + `/usr/share/common-licenses/LGPL-2.1'. + +License: GPL-2 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. + +License: GPL-2+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. + +License: GPL-3 or Autoconf + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + Additional permission under section 7 of the GNU General Public + License, version 3 ("GPLv3"): + . + If you convey this file as part of a work that contains a + configuration script generated by Autoconf, you may do so under + terms of your choice. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the full text of the GNU General Public + License version 3 can be found in the file + `/usr/share/common-licenses/GPL-3'. + +License: free-fsf + This file is free software; the Free Software Foundation gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + +License: free-generic + Copying and distribution of this file, with or without modification, are + permitted in any medium without royalty provided the copyright notice + and this notice are preserved. This file is offered as-is, without any + warranty. diff --git a/builder-support/debian/dnsdist/debian-bookworm/dnsdist.dirs b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.dirs new file mode 100644 index 000000000000..a97ae06ea1b7 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.dirs @@ -0,0 +1 @@ +/etc/dnsdist diff --git a/builder-support/debian/dnsdist/debian-bookworm/dnsdist.examples b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.examples new file mode 100644 index 000000000000..636562bed4d9 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.examples @@ -0,0 +1 @@ +dnsdist.conf diff --git a/builder-support/debian/dnsdist/debian-bookworm/dnsdist.postinst b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.postinst new file mode 100644 index 000000000000..8f7a7ce1811d --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/dnsdist.postinst @@ -0,0 +1,43 @@ +#! /bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + + adduser --force-badname --system --home /nonexistent --group \ + --no-create-home --quiet _dnsdist || true + + if [ "`stat -c '%U:%G' /etc/dnsdist/dnsdist.conf`" = "root:root" ]; then + chown root:_dnsdist /etc/dnsdist/dnsdist.conf + # Make sure that dnsdist can read it; the default used to be 0600 + chmod g+r /etc/dnsdist/dnsdist.conf + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/builder-support/debian/dnsdist/debian-bookworm/docs b/builder-support/debian/dnsdist/debian-bookworm/docs new file mode 100644 index 000000000000..b43bf86b50fd --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/docs @@ -0,0 +1 @@ +README.md diff --git a/builder-support/debian/dnsdist/debian-bookworm/gbp.conf b/builder-support/debian/dnsdist/debian-bookworm/gbp.conf new file mode 100644 index 000000000000..9eee0d42b88e --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/gbp.conf @@ -0,0 +1,4 @@ +[DEFAULT] +pristine-tar = True +multimaint-merge = True +patch-numbers = False diff --git a/builder-support/debian/dnsdist/debian-bookworm/missing-sources/d3.js b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/d3.js new file mode 120000 index 000000000000..19eca87ffc38 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/d3.js @@ -0,0 +1 @@ +../../src_js/d3.js \ No newline at end of file diff --git a/builder-support/debian/dnsdist/debian-bookworm/missing-sources/jquery.js b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/jquery.js new file mode 120000 index 000000000000..a2586f462d14 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/jquery.js @@ -0,0 +1 @@ +../../src_js/jquery.js \ No newline at end of file diff --git a/builder-support/debian/dnsdist/debian-bookworm/missing-sources/moment.js b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/moment.js new file mode 120000 index 000000000000..0cd9cc493593 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/moment.js @@ -0,0 +1 @@ +../../src_js/moment.js \ No newline at end of file diff --git a/builder-support/debian/dnsdist/debian-bookworm/missing-sources/rickshaw.js b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/rickshaw.js new file mode 120000 index 000000000000..c13670340d41 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/missing-sources/rickshaw.js @@ -0,0 +1 @@ +../../src_js/rickshaw.js \ No newline at end of file diff --git a/builder-support/debian/dnsdist/debian-bookworm/rules b/builder-support/debian/dnsdist/debian-bookworm/rules new file mode 100755 index 000000000000..a633dc0f3e19 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/rules @@ -0,0 +1,103 @@ +#!/usr/bin/make -f +include /usr/share/dpkg/architecture.mk +include /usr/share/dpkg/pkg-info.mk + +# Enable hardening features for daemons +export DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow,+pie +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +# for atomic support on powerpc (automatic on mipsel) +LDFLAGS += -latomic + +# Only enable systemd integration on Linux operating systems +ifeq ($(DEB_HOST_ARCH_OS),linux) +CONFIGURE_ARGS += --enable-systemd --with-systemd=/lib/systemd/system +DH_ARGS += --with systemd +else +CONFIGURE_ARGS += --disable-systemd +endif + +# Only enable BPF/XDP on Linux operating systems +ifeq ($(DEB_HOST_ARCH_OS),linux) +CONFIGURE_ARGS += --with-xsk +else +CONFIGURE_ARGS += --without-xsk +endif + +# Only disable luajit on arm64 +ifneq ($(DEB_HOST_ARCH),arm64) +CONFIGURE_ARGS += --with-lua=luajit +else +CONFIGURE_ARGS += --with-lua=lua5.3 +endif + +%: + dh $@ \ + --with autoreconf \ + $(DH_ARGS) + +override_dh_auto_clean: + rm -f dnslabeltext.cc + dh_auto_clean + +override_dh_auto_configure: + ./configure \ + --host=$(DEB_HOST_GNU_TYPE) \ + --build=$(DEB_BUILD_GNU_TYPE) \ + --prefix=/usr \ + --sysconfdir=/etc/dnsdist \ + --mandir=\$${prefix}/share/man \ + --infodir=\$${prefix}/share/info \ + --libdir='$${prefix}/lib/$(DEB_HOST_MULTIARCH)' \ + --libexecdir='$${prefix}/lib' \ + --enable-lto=thin \ + --enable-dns-over-https \ + --enable-dns-over-quic \ + --enable-dns-over-http3 \ + --enable-dns-over-tls \ + --enable-dnscrypt \ + --enable-dnstap \ + --with-ebpf \ + --with-gnutls \ + --with-h2o \ + --with-net-snmp \ + --with-libcap \ + --with-libsodium \ + --with-quiche \ + --with-re2 \ + --with-service-user='_dnsdist' \ + --with-service-group='_dnsdist' \ + $(CONFIGURE_ARGS) \ + PKG_CONFIG_PATH=/opt/lib/pkgconfig + +override_dh_auto_build-arch: + dh_auto_build -- V=1 + +override_dh_install: + dh_auto_install + install -Dm644 /usr/lib/libdnsdist-quiche.so debian/dnsdist/usr/lib/libdnsdist-quiche.so +ifeq ($(DEB_HOST_ARCH_BITS),32) + echo RestrictAddressFamilies is broken on 32bit, removing it from service file + perl -ni -e 'print unless /RestrictAddressFamilies/' debian/dnsdist/lib/systemd/system/*.service +else + echo Keeping RestrictAddressFamilies in debian/dnsdist/lib/systemd/system/*.service +endif + +override_dh_installexamples: + cp dnsdist.conf-dist dnsdist.conf + dh_installexamples + rm -f dnsdist.conf + +override_dh_installinit: + # do nothing here. avoids referencing a non-existant init script. + +override_dh_fixperms: + dh_fixperms + # these files often contain passwords. 640 as it is chowned to root:_dnsdist + touch debian/dnsdist/etc/dnsdist/dnsdist.conf + chmod 0640 debian/dnsdist/etc/dnsdist/dnsdist.conf + +override_dh_builddeb: + dh_builddeb -- -Zgzip diff --git a/builder-support/debian/dnsdist/debian-bookworm/source/format b/builder-support/debian/dnsdist/debian-bookworm/source/format new file mode 100644 index 000000000000..163aaf8d82b6 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/builder-support/debian/dnsdist/debian-bookworm/upstream/signing-key.asc b/builder-support/debian/dnsdist/debian-bookworm/upstream/signing-key.asc new file mode 100644 index 000000000000..bc7e1ec64765 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/upstream/signing-key.asc @@ -0,0 +1,189 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBE5fJpEBEADl7Epp8pxg5ENBY4KM7U/lrxRg33BPDJcZTxqnCLbNCdEOSO1T +Ej3jWl1HEh236NlWLvHsXgrsKiB1jX037q62QKrp10trQMsM6QiEUjwmrGJxgxv2 +D/+U2PJPh6/ElFhx1PqGEC1Ih3pTpP1YINzfX6cQ9/e3nc64BcBTQqYA2/YIv4pH +MYXZrPm398JZbPpT0ot9ggdLulUYSRJQ9dfNJbGpstMMfOkA2IFvfmKc5BT5Y/ZA +ayF7xPBEGbBMLaZuT8q+x5S39ZyzxzCMSIJD7nYAh7qI0xiosfu8YyjXPN3x1OYX +kdBKzYEk8ji9xgyNZ/9Hlsq3JhJzuGKuXKuC3GKf8BcNw0JH+/VWmq+3kd30a8dy +GgCW+YJok+zyo51WWVLeJ//5tZ2/GvRhbIivA6Gp2cxQlwl9Ouj7SkBNRWvjBJUr +N0NF1FxKo1Yq9OECq2KgLn3l2vURW+LUtdXDbZcn9GcYbGHIE0xdCGQSH/H+Dkgl +T63rQIgBN2MTQ4lhun/5ABLq7s82BAtakhQ5S+3gD+LykCcvCxgHApV28yJJT3ZZ ++Qt6uNtHf2y6T4eJpiE+bWJpG3ujCwzQxu3x5L76jOgiRaj6HcwzT79LpjZMzhnK +1sKhDAuJP2VNIYhAXn8UF+z54dmBRK58t8zQVop+BpJAE7QM/DFDp3uLhwARAQAB +tC1QZXRlciB2YW4gRGlqayA8cGV0ZXIudmFuLmRpamtAbmV0aGVybGFicy5ubD6I +RgQQEQIABgUCUWu5/QAKCRAcXumQ0ucVdWzlAKC1r7xlQ54vi/tOqTDFid0eV+UG +qwCeKBoxjQUwcICQpz4wi5u6fTDD1AeJAjgEEwECACIFAk5fJpECGwMGCwkIBwMC +BhUIAgkKCwQWAgMBAh4BAheAAAoJENz1E/p+7RnzoQQQAJjEVUbLcBd4blXL6EW3 +VMqIMFbxBt4CiHRjsSo02+rUMWLOqZBERfynv0oufhrW3AqTO0OMoqPLWjWFNeOH +OdKieBJdcXHDJPO8qRUpbcYh5CXr54X09d5WZU8sGipnd8wxO68J8g+5vux3xscE +aZTwWZTwyelWA77OxJm6WlPPxJ+lTyIuhVC3KoBUWRwfNrxE/ij/0tkVFoIXvczb +AQqB6+nApHZvtoR4Wys4bzmCWuo9PUj0r3+eyjsWEB0A4Ya1bwaJOchubi/Gq99w +fp71zJC8FcSMWmoGPRnpg6oLpkxC8YreV/16DUgiMnxUPyJAEpb+AH0MMudmp6tn +UaWBs/hWnpyWPXqjt6wzs7X31X2oj93ANKjnSpglOgUEBKk4GTyOuBo3S+kyXD9W +W977kyKVtUQf3U5EHUR08UA/DuEJPGDnMa9lujXM17h//iyixa0RhJXX+ZRKRwEA +Zqj6H8wNayF045JdwMJ6TIePuymV2ltyG5E0M5l5SOc4fELNHJyHvjhi1Fb23lqB +xNhvdm8+RtwtFz+QtFwihP/cEBMue5lcj5Bkvwx3NERJxoPi/Qe82mLZLaMCdlP+ ++jzvSrsVrRWkyw+i08T0+Dp9/V5YoEUkhSfNp1w26FtrFVqC4XpVxtjda32Ipw3a +ygpOqEkCxNsy3+C1buzr/QK9iQIcBBABAgAGBQJSExvtAAoJEHcKmPCS30+Zo9wQ +AI+4GsOZCtV1jd8M88KIDl5b0Kh0ogK/pg6orYu0kyDF9W16p5qEn0sTZP2QP4+D +yZWDfPTe1fxHlSac3KXMTqGtLKDq0xP0WIoqjhSnMRmvhmODNxnODueSL7Jmg8cv +XKvj7FEYaI+mqgChyikX9JSJdTWiMuQMOC6adGr2EL77e6e3jAaI31OtCTbam+EA +JFwxpYSlBMop+SemBbeokHHRivEyg2huO6o7m4SprxkvZZWmiT7DmdIGhaPt5CHo +DbUdUbj8ni2EdSZnYJcCUpPHJqF1FUkwsc8NDH6tiAo7cHsjkrDAx5Waetnt9mRk +MkG0tUgnULcChcx6NJiG3lhIWNDnj9MLzhA7kDaktvtEwCyuQA8iyWWfOgIABofY +Jk3zbPG63N1XhWyZ/ic3IbmnWWrEaK2xYDABvTN6s8nOgex0D+kArhsPE+RMWPzF +9F+2YgrbP7R68ek2+/4SdQNifUloMDJJA38nmxkM0SsLNgbIWLaltw3GwT/0LQ7s +TtcLLMhl7bkgyYLmmII8MxXPQhvr1oXX17t6fwJLiQjokO0CVw20CT6QeFo4P+pg +oYkSPn7tFtfkB3sgZhea3Lr545NDpK5Vj/0WxMOYhqEUmgCRjyzmczklyoXPMFD7 +rX5LxEENXUnxkGGkKFB6OIMq7zrheBscB0/wcZcO05pFiQIcBBABAgAGBQJVB+GX +AAoJEF5QcVvy/+GnMCIQAITJ/73QIgrsUFh6fWGfKMOgY8f2JUfwe5g/vSO2BQPS +cSgTjoKdpy4DCILI3WzSZ2xzxOlS0SMj8hoDIwQxSydYuZhIfAmlUmaT0Q5p6Zae +f7/+pFRVkas9CA4NE4V3ZCEhQjVvEI8bXabdld452PE2Fahi6m58JEFwFnU84sII +sQJCiFFFFj7OxNGGMK63vZFxgE9dhW1kpMGBfxdKLFyglEpll2qGbCp13shFLeZS +Cg5WJ/pC6R2t0K5tW2XAHz7TRj94dnFTVD8DMlydrBrxYh7DMVaeFLDgepxtT5n8 +yW/RLThHAvg7Qyvie/l5bt8Ukk12ISPv7sY9bYdM0wHWj0913RKbK5Ic22LM3RK3 +My/yXeKMI0u8PTUuCppIiCRhqNjFr23XsaixOYRDSsvo6ca70oCUzyVMJs1nmknF +oimw9yRhT4bUN5yS30E9jqhgNb06cXUcCM/rPYvVqe2/OoUMYBHjRHqn64uHzvtn +nrJKAGUk0EqTdqyRCfWzo3+mClCzeyes2P0zzGYzwZ/fo+fIVcT552wWCbJa7KW7 +XcW7CTzWgAucupK7tm9jOezvd2Zt68lROAVRL1F+P2HUQvzLcXVaSqOIHkiySMAK +fVEBfwA6y2oBjUkBv0oKuSV8xk+cq3B3sxDQra4Vw2MjyyiCrw+piIntgqnSrzTv +tCxQZXRlciB2YW4gRGlqayA8cGV0ZXIudmFuLmRpamtAcG93ZXJkbnMuY29tPokC +HAQQAQIABgUCVQfhlwAKCRBeUHFb8v/hp2pbD/9YaX+vGZ8ZJTtXbwmbMQ6ZXC+a +nWLygPxk6d3DTFfdbSOwYHH0RSaqymhJ84lVypoUP7tZxRBL5pGvBE5i7iZVTAj9 +y+mV95EKeM/bR/k4EYQi6nAgaSRKFnN/BkimfZRFBiV0ox/3TxBjIZFUG7P+0TgY +/8C8jIpz2Gt1MuC6J6wI+DkUD7mFTjzQSz/HTIQctngcTq4lFvZiP8pcBDt/kg8u +/ATlJih9LPfixyZo7YIunbj77jLomkKkShsNFQEDPOIWsQotjIYnFb3YK1vgIQEp +CZZE9ElAEw/6yfXIVyqPzg8ZiP1UOEDQJw7/AVhfb1uiR/US2yPe8xxe+sVaYDgi +M6VCG1lbun/l79bQEo2tjacOih/Uba5UPOzudseW7XMghwIf+1Y4U1r7HEEbRRd3 +pGqmjBFjGcdnw5XzpZ7KyzwGjie57uf0xzPwpjtIIr+HjiIfRmBuFSx92JvpVieu +ciGT646F2d4vsxKMNsY89vSoO8dqXEFWOjTabZjk5ZWjz3tCbpqDAgAcj8VVn4XX +HPM2Rood1mIENYp2IsICS2Js9RRRfxpY5Gk5E7zFXkHvJLfitwu3VKwB2iWP1m8L +TxQHZFjLN2TPF09yt8qfrCUyuY92HJtoRqJ2X4N2DO0gTrtiCylefdJvkSf8NM4s +z5OwEW38V/8Drni3S4kCOAQTAQIAIgUCVMofzwIbAwYLCQgHAwIGFQgCCQoLBBYC +AwECHgECF4AACgkQ3PUT+n7tGfOjhg/+NtAiH9AVGTmbpdNHNyWrmPxgO5XKtfLZ ++4gz6D1QpFwFO/YPL9iN8RhzsZJCestI6tP9vuBPvku7Nd1IFfuZlUmg26gftUTQ +UmRMd/lMm264WOYPXu12g8+PvkUwXfyoAcd32nOpSMkpiFymRN8GtTzIm4qOgWA2 ++mdFWMl0xTSuKv60MiNIszEKD80UxDS2bSj1cv2VBxDFwmlrzPEa/ozxAI9t9CxY +8Lv6CsEfr5yHSWOkV/mqSo+4OegdNjiRRoeMo0/bUBOtkZSykj2ONSVBn1oIOYQt +ForUhtRyZFLJIiO11szngRDqRYOslmMMLuZ2k+/b/K4E9jvJvz7yt+Y3BPOsjRtV +9tP7oSKJpTN43PTnTbKMM2RpVTN3bPtfhZ1+iQubk0y2H2XaKOnKX6wEdjaWOoBR +6SRzVzSz8dgOqvkfBhA/bJchwWHnyckk7bZJXMszafnkJZzW/eVeuopUgkyWeMHp +6lngpwpSqCPXn10zm3bBYNSOFdCCX2Qs3hB8fLi8OQGv9puikZmvdIwL1jWP1GEk +kP94xFTtv4wqgBykySjTMBs9zEDOOGq2x2ndUJZuUpoY7AHBtzmMrfwuuHwyFHfT +VpalE36S6f7D8UA631vO/BGcDj+5xnAhhTBiiFbworNjAfZs4/bfKam4v5rYb+ri +z+OJYVa0m9K5Ag0ETl8mkQEQAM4WIsHIK/1+/39QZbh376iVXfc4NVdE3ID/Lozz +9JDanjkpScpikwugDwguVx+8JdO2tTyo6JTzpiZ+CoaxmjudJpUTT7fD5ONcAd1s +tpHKUQFwJczU6LSXpTQCpmhV5s13pwumxjymKRlotxLdr9+zxFl0e4VTFb5oj4Ik +2wu6sehcIt73AxM38C8smFRrRegPQL2Xnq9BE+WUF2yyY3TOVAK5TP2MbwQTkrTO +iTYJZdNHNlvjIpZaxHKOLqytNXSmXn1k20nitmyssIzv0aEC1UdktWIL/gD1Z+Sj +rJQB7/y56Dx7o6gr6J2MZZeo7a211TLdblejD6bMjGaH4CTnjzmkMtDC/2b+FUc3 +x3/GlQF4hWB4iaT4aCjiKOVNQgaQyAeRTsv1BUoqf8LDytW1/MdalLYElKS77t69 +HEQ9HSyt7QHU3sjAG6qgso8yWn8ebYCefm1lyZSP3BbvZ/UpoKuB+aGlXjteaXQh +IRLRA1TgijiGA3Yw1dTcz2Cb42w4UNZw4r55yN60QDRBH4l1yrRPltdyAaX3qEg4 +4U/Z7LU2YTDX+4JL1O4ZE+snDVsTPMpuZLvRFkxCLG1FTXZacZRXfzlFzw6YWhpn +HUYORO3fGhb+PKMKYEloTyLywjkVLHFbvaPts96dCxWyDrcMOqhgiLOLJo7qC+/S +q8k9ABEBAAGJAh8EGAECAAkFAk5fJpECGwwACgkQ3PUT+n7tGfNv1A//dYWV+vL1 +jiL+X4vRSCrDM8bBmt/cZfN5O0i3HYPMdSD9lVr9O+WYKJogxEXX1ofgEO74rwZx +Gw0crrMN8VM9SgMZ3jioGI15NF3INnA1r53GNGhJ4JVnz0KV2NKtshk7CtSxrjoR +8qplwbMMICVgTIERVP1enuOb3FEtbhI4rcy+2UTw3hwURBhIfUotVFO6SKu3ZLsc +ItbiNxpTqTpL6AIp9UOrZjcqfCuFs8P+57uusAHcp6GYhhIhNIdXf64RQs7gtdLV +W71z0diSxu3KFWlrXOx0rrm7RTAQn1VOLl4W5oBPvcF2ZVQvd84I74TMtpP0MRDF +gLuK0HHFVyDff0vx76rubQgom6z8ajiIa6MfEmd7z9xhQT5PU0FApYY6H/kW7ao+ +f2h2IIjz/+QjHuYn0CqqcjkkLC76RAgQjHYO9NIpL9Gi9O+I2AFz8YjOK3hOpxMr +F/LjPJtxBXGFEwP4ud+hzDMjwaa7PklcmDPUBuSDIgbNvsVNA6gn7AkbQn6NH+DI +mdrpzgpSr1FHMbjIWqpXWbAZtmOurxn9f5ZXPKAgMvlV4TS4NZqnWT5HZCKs2b5P +ed2L+zAdLP5NmyzJrSIyVTJ7JMLLfCLaWu/qsHRGt1w86gewg7uMPdA1IEvjjXaI +WNhYKUq6ik+DNrq0Y3fUuRg35QHaPTcab+eZAQ0EVjikBwEIAIhTkdGQEbdVwF8l +qp63Eigp0tHFbdeZ4LCu4sW3oM3erxtO2w25Awkdrw5jRopYmheM5BJsGgpIZUAU +pOakJR8fi+ESu3wNarKCVF+KjYvdxN7jwZmOI5t1ctnGewg0DHZZtymgJEpON1Zf +QwfYmD/J/k9Lqdv6CVyVGwNCZUZCO33a/bec12wKnwj2uM/X5tDLmIcHUiJC4Uno +MFAmGBZDOSxPZrNnzdoAO9zj/4WDtUVhLNkeSn3w1/LNSSJTNiLQjk7Lgq/Khd5L +8Jf1a1AYzW+NkBdeIP44MnQ68HYSwJRPq3iL2lZaH/4uc21FYhWfw8l5BsIA7bAm +UzFfbwEAEQEAAbQoUmVtaSBHYWNvZ25lIDxyZW1pLmdhY29nbmVAcG93ZXJkbnMu +Y29tPokBPQQTAQoAJwUCVrBxMgIbAwUJEswDAAULCQgHBAUVCgkICwUWAgMBAAIe +AQIXgAAKCRCiCO1PivWERnTOB/4jLvex0M+TE5iL/FUki8EHyj6648sOCHnUHHnS ++slME2b71iAvLJxClDJjLD43Jj7FL0hu2LOnw+5PQZrhLyB1WEa1tC0tLvIkPuzC +VJPI4FH7+AegmBrGYN6554Hy0C/YRF8mOGngL58hrumJTgjB7vC+CvDp0714WQG/ +SgcKqk4jkIz/Iep2vj3dCifdh+kJkaK/nnzIT1euiOzp8xLByiVbCOdlbvYoVetq +vJcqIhOHCglv045lZcAp9kP9pm/kEzHM34PhkH6SrR/uodshOH4p3Ux0wGgwUbou +DvHUtjlK+GB8cYXdRny0tvdGBYUO7CsFNzPoRC8CvD+VY8DltC1HYWNvZ25lLCBS +ZW1pIDxyZW1pLmdhY29nbmVAb3Blbi14Y2hhbmdlLmNvbT6JATEEEwECABsFAlY4 +pAcCGwMECwkIBwYVCAIJCgsFCRLMAwAACgkQogjtT4r1hEbMMAf/WS0+yuheoWrx +CZ4qYQo+AjlaenFTPQwrEDNioj6gjST/eAaQW1/+trFPzwNrBSenDE6bwPcPdL51 +mXg+30fNzHLWrBPDsMqBlPTIvpBbQ/bVqjV3JnU8I8dHfdKmInJRrCJM21gDTprQ +dqfBfSHJHgM5TG2+fUxpdLIAhBRknXt4+TuE272DJf6gHxnDs1oqQ6kAxC0ANJyE +ufFXJGeERN2OsFtSygOcUiHeXwWyM77RGf73gkS9+bCoftiuM4gbKSibk4BbUVBZ +JCs28fDnAsmIstZldUGZgIuy0vUfH153DTJflN+CIGEvRUwk+nrDIwYkV0pr9eZ0 +lz/OFhwzJ7kBDQRWOKQHAQgAjr1xEZh1yglszi94+HLNFcgRPgRNktg2vxOGf64d +AreJvL5iDrS2lrFMknh5BNuj7nJZ2r40OOS91oH1qkVk+v9Cyo/3xwCpCOPQCkhz +HpuQWXoMGMw/3/0tG6zTxnYdC999faCH0lLA8oDwHCHlZSHgsH9+qSNyjaJXvS+H +VoGYzyuanU6OTM7EM5c7RCPhNjT9JzHLISnwaxgDpwi7Ez6yudcrg6DqS/uUwkyN +tWyesx1DF9y2VJUNwa4NKIJkSH+niEoxK9NBfBAmAKc4o5+KPs6BvpvpiYY9gTKa +aLypPHNcveQTDFv/26XHyzrCZmwuGlcYBjboH/BWzKbhuQARAQABiQExBBgBAgAb +BQJWOKQHAhsMBAsJCAcGFQgCCQoLBQkSzAMAAAoJEKII7U+K9YRGXJQH/3PtQG0A +krXOpkOMXFLTKdCEViNNHN94VIaceVn60zbmXzxhYeKz7K345/EqATi3P3/yDHch +t7j3uYPhvaMjy3smN6vEwX7Ue40PbFDWmm8mHpLdlOfPXF0SRUD8KTSD6+W2VJfE +cDI6DDfUmCx9yYZ1U5u+O8Aj+1l2gdQbgAioPnQgqzf43qgnRcsfNmsVsXg7EbHs +pRpJOR1XyXl/9KrDP7p6kjwWTQ1NoRjCw0qaX93odLeKIpd2riShlB7GteUTps0I +fuiL94CA58PV2YvZapN1KmwDohHU8rndN7zte7jbCyv1Vv9tP6Ns0TvycBAqlOZY +dgabrT+Pccb4jCeZAg0EVPRvsgEQAMeXMm92zU2ooQOE4AbQhYY3gn+MG87l088W +rAMlpTfbH7jBPDQ47EJyAVh3NY+XXucXCMLzJ5e9sAIJk3PrYqDmjWVYDox3Hx5r +MKIY65N1Rud1kMGWsgQCzU5RmarFNLJ0OdpE0K0tMTajS3gxqJ1zOOKdSfZGS+u2 ++UKyLUelB07mZROv9uanu/ia8I/m8RG5jb6pVzUpuoWW0J5XQoA6mvWREbJDgP0s +WWgWSvt+0XRtrcHR9sie7a4ynjowL6M+iTm4ShPrqX5TuxmwJSQcfTZjqz+5cnpp +yTzj+mG2/jHBERGWkL3sx37s3uohhWt2EZVuyIcUQMigGssCp9216K+ihyC4tEj8 +RSfbon35t7OGYJlRS7V/raIm4GdYLCOkQs0yUIila6AcC5xpRnHXHIXvUNHrk1nA +yz5PEER/6BiW+vMObonx+GR8oUfo+2uMg4LSYKx+o5jgWBFiSdNl3gQK5+RswpdF +fzyq9gZf4WvVOCdBH0YKEQ8iJYX17drkn4OWMG7u0QnQs6GdZTcClQJMTWVBIaCt +RMNCxuKX6XI9WOgwj6vHM/ijeugPLOsUSv+uWcK/fH7SSqmtJdMmpwNKFUmr4ZTa +WZ5QLLv6kXbIoKqEKFwXkJAPgm36mVEE+ruy3FoNl0um9S0W8tGXgs7pMmM0AZ/n +Hb6R++6lABEBAAG0KFBpZXRlciBMZXhpcyA8cGlldGVyLmxleGlzQHBvd2VyZG5z +LmNvbT6JAhwEEAECAAYFAlT0dSoACgkQ3PUT+n7tGfPs5RAAlQXWPO+ZJNjCLFcu +AKEyNfNl5ssCVykUmQzfem8Y/Z8NojoMkb1mBZlt1OIItYOcpB+bGiH2fnY4WUjd +s1y29mPThrx8vpm4QY0/lxw+h89IrQrdXcHXS9KvedZfWCnseEMZo/xwGVCBiyDX +LSFv7RwugnJr6VeLg7oYrGyGNgIeiax66OFJ7cWxKX7zG7Z3hfZ2YFM/djCyts2A +Lgb8WKEd1+xaINpmXLIBZb6oKA7JrPHjGHXCtiOFccyONmW4ukcGOsPrn0Wm1rAX +IbHl/sxC/KIONdPSjktuY9uOW8DtinW55eAMxlX91CHpX9XK3WENAqkkSL9hu8ov +JPXrz58XdqHMKdgwGQdYXcUY8lU7YX1dXmp2th/8kMkSQXcqYv/LmbRFRwq7pd8A +6U3LsIT9JIHC1LcTxyIaKZBQegHvtVm4oDOBBDJ9ImBH53FJhJ9hModrG5Hcfmwr +nquITLicuBgMM2SgZmH/ykeDpO3atQrCSDzlozFR6SKsssnfmIrbyUJzrsJ6tuz1 +/3kNosdPuFaHIxLlNawAhUkJL4CrTRLcWuknJo5OOgCtYWRGcCBQj9iS10EZDcvg +uLsotQbTvdeE5T+o97gaF0p7ww6XaFcII9HLUrEjMLQFSFmHAjyxdm1Esh8I8yEA +oR3FXtdNwgs0CBuR2bY399oT26qJAj4EEwECACgFAlT0b7ICGwMFCQlmAYAGCwkI +BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEF5QcVvy/+GnS1YQAMbqFxpsYGMsVRpw +rFZ1/NXyfekLk7XCWD++YgwZ10d8jXQAv0BHl5ubkpCEuZdb7SqEkLgKbjRfw4KD +qri7iDGjwoYckmqh1xl20qyTYkfeQnKizuBWmNsVL0JT0xUzKhcHBh1bWx4FPF+i +ojOlZQLKwViPpGOacstlBcPfRPQhaP7QLKrOVvVQd0ebfbbsjSBP+olik6OWRiyX +iIfsAmGq0OFDtk+f7jI2UIMC+/ADpulzMmJdr8l28wgoudtVM+w4LB6hbFOYSvVy ++kcMqWI+yQ64Dy6OnFVI5cZH3jxQXviqEs+QNzM4jUQ37Kp3TPBEDvHNQkdYllk7 +E24ecF/bi/3kDISBfVobSdMGMERxPJhnNWCG3sEP+WPT5beDKywcWX4kOHpmhL3w +RxJbZzEusA7IXyeeb2AWfsJBdMtM5Ur2u4yW9Dq9uNFDvXvo4mQXqM0aPmpKN5Ir +SzTBT/9bKvu6iOI+JBiEY/A5wCySrqIsjqeJ5Wac6dPCcNsxLV1qdEVMV2eg86A/ +lbymPiQg5xJzF6RSFwmVpbye7HFqcKTLwbZJHzf46CpgFyK0Wuavp90Nxrr0oTaH +xfnAHlTDdXMybm3Z7wlEXEg2hSh5rrTKYfjgtoIJRkCjaRh40jRkIM0YLx5uR5PB +k6hAzjik/7sYvgG5XuPra3L6Y8XGuQINBFT0b7IBEACjecdg3e1IF3zBMadFbFah +7ZSPFK4Y8Q+OMbeiu0TzXP65rRXDQi595jdIcQY6/7gB1IguqC0HWUo/Ns7GFnNn +WbrAaoVWpLjHXgMJ9hqdyIEgluoJECH53d0Y73oi+PBoYUU5z2tHi7AiiJc9qMG4 +m9q2P7xUrnqCqmGO4pU9nFJTFUAodf/ioNk9EdmciLmFUm7XkHNtUcKVQGWER7vi +dedWLW1fhHAzhI1hYkN85ZfIULfrVNZBn1U/L4nry7P7HO0IQxoK7POs6apxU4Jy +ATEyvsnjYU+UOCDPXRIKHAZ4joEnFhyHPyURgdMLxQb0s1hnbTEC+szvqb9kC0rC +an1GRb6/VeW9eRi1CoBpHtQEwY6k+YgWpvcfR0w9+6BH5aqypGWnNDCWcOTINUro +uALb68oxgnEAowhWIa0ujUYy+PMYF0AFArjLVxu1IBKaMD/Wsk0ws389xAnbVW81 +bhHN2Ye2NznDe3YfK5FkUyWXO6GA1tFQw+joxt6+TPcTxRJLS/MG/gXcluQE3Kv+ +jteqi/dbt5A+potX6qGN+F1GJwD/mQKyULklzlcZCIYZN9OnKVbSxfn2xQ89bjvk +NvRjuO33x0IozIr/R/uz4T0H9Ve4UoNj2vT4pH/Ba/ergQSfrrAJMDyIB+SRIgY7 +LCQFB3rOIvg/HiqAY3VL1wARAQABiQIlBBgBAgAPBQJU9G+yAhsMBQkJZgGAAAoJ +EF5QcVvy/+Gng+UP/0CjLMF30xjRim/+/qzx+2OZ1S6R6B2mp971lQxB8gCA7dn8 +0UhSZZMHfMeo2N34itI4HEhQb+2jTOgQvNjv36zMppZjHQUg4+xabvZU33FrB2hh +D/ZdNTm7lCD87vKxz07flApkscw20VenY8E81z15GuWLK/UqE9wK5sbVoFB36mwN +Fqgh8W3oBBJTJoxWBFnqZu7arsaXhEWpVW2+36I2SWaWFmIPwrUbuwIXSuv+h+ks +EOdVXr87AgxX4qsPs44N6z57yhCIz6g3ow7R06IolsDSE8wyOWL313X6UE1R5Qyq +AX1yQ0BtmcPRh05SC/vRe7WeP2q+TyHMrM1/YLN/W9X4Y7loPL38fpmWMEaNT/Rg +ZPFjqDbqxYpyN6Kymdsfr3YPYNrzcYlc0WRplvpZh3D67PhI9XuRsb2c7GAU1jVz +e9Za1ZrPpcCCJgp562I9E1D+a4x7w9fsiGDkPOm5Iy3HTg9FH8VUWM3uwret74Zl +QyQkE1XYgRjqHrjqJOajJg8Qw4meItY0QB/5kxmAW1h96OoKBUZq5GaQ8AhtPnH+ +4peGQHG9fvSL58pukqeLGHkSwgdMPIFYZTHiIDt2tVkbi7vE3uvKPm1bZpvM2T6m +9ZUkVWV39P1W9lkqWvXSVfit1GRUpFd2onM7Rs0jxbZ9VfiRi2OblZ9Wkvts +=/oKT +-----END PGP PUBLIC KEY BLOCK----- diff --git a/builder-support/debian/dnsdist/debian-bookworm/watch b/builder-support/debian/dnsdist/debian-bookworm/watch new file mode 100644 index 000000000000..8c81a53389f5 --- /dev/null +++ b/builder-support/debian/dnsdist/debian-bookworm/watch @@ -0,0 +1,3 @@ +# Site Directory Pattern Version Script +version=3 +opts="pgpsigurlmangle=s/$/.asc/,versionmangle=s/-/~/" https://downloads.powerdns.com/releases/ dnsdist-([0-9]+.*)\.tar\.bz2 debian uupdate diff --git a/builder-support/dockerfiles/Dockerfile.target.debian-bookworm b/builder-support/dockerfiles/Dockerfile.target.debian-bookworm index 7f1b953daf19..073bb7645f72 100644 --- a/builder-support/dockerfiles/Dockerfile.target.debian-bookworm +++ b/builder-support/dockerfiles/Dockerfile.target.debian-bookworm @@ -26,7 +26,7 @@ ADD builder-support/debian/recursor/debian-buster/ pdns-recursor-${BUILDER_VERSI @ENDIF @IF [ -n "$M_dnsdist$M_all" ] -ADD builder-support/debian/dnsdist/debian-buster/ dnsdist-${BUILDER_VERSION}/debian/ +ADD builder-support/debian/dnsdist/debian-bookworm/ dnsdist-${BUILDER_VERSION}/debian/ @ENDIF @INCLUDE Dockerfile.debbuild diff --git a/builder-support/dockerfiles/Dockerfile.target.debian-trixie b/builder-support/dockerfiles/Dockerfile.target.debian-trixie index 0c23eb98b00f..8642f323b08d 100644 --- a/builder-support/dockerfiles/Dockerfile.target.debian-trixie +++ b/builder-support/dockerfiles/Dockerfile.target.debian-trixie @@ -26,7 +26,7 @@ ADD builder-support/debian/recursor/debian-buster/ pdns-recursor-${BUILDER_VERSI @ENDIF @IF [ -n "$M_dnsdist$M_all" ] -ADD builder-support/debian/dnsdist/debian-buster/ dnsdist-${BUILDER_VERSION}/debian/ +ADD builder-support/debian/dnsdist/debian-bookworm/ dnsdist-${BUILDER_VERSION}/debian/ @ENDIF @INCLUDE Dockerfile.debbuild diff --git a/builder-support/dockerfiles/Dockerfile.target.ubuntu-mantic b/builder-support/dockerfiles/Dockerfile.target.ubuntu-mantic index 2dbc488e6955..a55089dfe987 100644 --- a/builder-support/dockerfiles/Dockerfile.target.ubuntu-mantic +++ b/builder-support/dockerfiles/Dockerfile.target.ubuntu-mantic @@ -26,7 +26,7 @@ ADD builder-support/debian/recursor/debian-buster/ pdns-recursor-${BUILDER_VERSI @ENDIF @IF [ -n "$M_dnsdist$M_all" ] -ADD builder-support/debian/dnsdist/debian-buster/ dnsdist-${BUILDER_VERSION}/debian/ +ADD builder-support/debian/dnsdist/debian-bookworm/ dnsdist-${BUILDER_VERSION}/debian/ @ENDIF @INCLUDE Dockerfile.debbuild diff --git a/builder-support/specs/dnsdist.spec b/builder-support/specs/dnsdist.spec index 4cbb78b8999a..407a03d3cd7a 100644 --- a/builder-support/specs/dnsdist.spec +++ b/builder-support/specs/dnsdist.spec @@ -55,6 +55,10 @@ Requires(pre): shadow-utils BuildRequires: fstrm-devel %systemd_requires %endif +%if 0%{?rhel} >= 8 +BuildRequires: libbpf-devel +BuildRequires: libxdp-devel +%endif %description dnsdist is a high-performance DNS loadbalancer that is scriptable in Lua. From f941004dcb9a4ec1d445b2c1e760ebae30b80226 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 22 Jan 2024 17:04:44 +0100 Subject: [PATCH 52/65] dnsdist: Hopefully fix building with/without XDP in CI --- .github/workflows/build-and-test-all.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/misc-dailies.yml | 2 +- tasks.py | 11 ++++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-test-all.yml b/.github/workflows/build-and-test-all.yml index 7528f78352ef..2b97fd36f302 100644 --- a/.github/workflows/build-and-test-all.yml +++ b/.github/workflows/build-and-test-all.yml @@ -626,7 +626,7 @@ jobs: name: dnsdist-full-${{ matrix.sanitizers }}-${{ env.normalized-branch-name }} path: /opt/dnsdist - run: inv install-clang-runtime - - run: inv install-dnsdist-test-deps 'with-xdp' + - run: inv install-dnsdist-test-deps - run: inv test-dnsdist - run: inv generate-coverage-info /opt/dnsdist/bin/dnsdist $GITHUB_WORKSPACE if: ${{ env.COVERAGE == 'yes' && matrix.sanitizers != 'tsan' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ea0493e72900..3a76bef49841 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -147,7 +147,7 @@ jobs: - name: Install dependencies for dnsdist if: matrix.product == 'dnsdist' run: | - inv install-dnsdist-build-deps --skipXDP=True + inv install-dnsdist-build-deps --skipXDP - name: Autoreconf dnsdist if: matrix.product == 'dnsdist' working-directory: ./pdns/dnsdistdist/ diff --git a/.github/workflows/misc-dailies.yml b/.github/workflows/misc-dailies.yml index 90b84a94c504..ea31204d8ee2 100644 --- a/.github/workflows/misc-dailies.yml +++ b/.github/workflows/misc-dailies.yml @@ -81,7 +81,7 @@ jobs: submodules: recursive - run: build-scripts/gh-actions-setup-inv-no-dist-upgrade - run: inv install-clang - - run: inv install-dnsdist-build-deps + - run: inv install-dnsdist-build-deps --skipXDP - run: inv install-coverity-tools dnsdist - run: inv coverity-clang-configure - run: inv ci-autoconf diff --git a/tasks.py b/tasks.py index d8f54a91b05f..bb7c08af9f1a 100644 --- a/tasks.py +++ b/tasks.py @@ -297,8 +297,8 @@ def install_rec_test_deps(c): # FIXME: rename this, we do way more than apt-get time.sleep(5) c.sudo('chmod 755 /var/agentx') -@task -def install_dnsdist_test_deps(c, xdp=True): # FIXME: rename this, we do way more than apt-get +@task(optional=['skipXDP']) +def install_dnsdist_test_deps(c, skipXDP=False): # FIXME: rename this, we do way more than apt-get deps = 'libluajit-5.1-2 \ libboost-all-dev \ libcap2 \ @@ -317,8 +317,9 @@ def install_dnsdist_test_deps(c, xdp=True): # FIXME: rename this, we do way more patch \ protobuf-compiler \ python3-venv snmpd prometheus' - if xdp: - deps = deps + 'libbpf1 \ + if not skipXDP: + deps = deps + '\ + libbpf1 \ libxdp1' c.sudo(f'apt-get install -y {deps}') @@ -333,7 +334,7 @@ def install_rec_build_deps(c): @task(optional=['skipXDP']) def install_dnsdist_build_deps(c, skipXDP=False): - c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + dnsdist_build_deps + dnsdist_xdp_build_deps if not skipXDP else [])) + c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + dnsdist_build_deps + (dnsdist_xdp_build_deps if not skipXDP else []))) @task def ci_autoconf(c): From cf66dd2224055ba1d994214ce0e933ec696f00e1 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 10:09:30 +0100 Subject: [PATCH 53/65] dnsdist: Switch to Debian 12 for our Docker image, enable XSK --- Dockerfile-dnsdist | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile-dnsdist b/Dockerfile-dnsdist index c9cf09a94eef..2b96ab3ef919 100644 --- a/Dockerfile-dnsdist +++ b/Dockerfile-dnsdist @@ -1,5 +1,5 @@ # our chosen base image -FROM debian:11-slim AS builder +FROM debian:12-slim AS builder ENV NO_LUA_JIT="s390x arm64" @@ -14,7 +14,7 @@ RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y --no-instal COPY builder-support /source/builder-support # TODO: control file is not in tarballs at all right now -RUN mk-build-deps -i -t 'apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends' /source/builder-support/debian/dnsdist/debian-buster/control && \ +RUN mk-build-deps -i -t 'apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends' /source/builder-support/debian/dnsdist/debian-bookworm/control && \ apt-get clean COPY pdns /source/pdns @@ -84,7 +84,7 @@ RUN cd /tmp && mkdir /build/tmp/ && mkdir debian && \ # Runtime -FROM debian:11-slim +FROM debian:12-slim # Reusable layer for base update - Should be cached from builder RUN apt-get update && apt-get -y dist-upgrade && apt-get clean From cd807ad2f610453cabac0fb9528435e10202547a Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 11:03:50 +0100 Subject: [PATCH 54/65] dnsdist: Relax file descriptor check for XSK-enabled backends We cannot guarantee that the response will be coming via XSK, so we need to accept that a response that does not come from the expected socket descriptor. --- pdns/dnsdist.cc | 4 +--- pdns/dnsdist.hh | 1 - pdns/dnsdistdist/dnsdist-backend.cc | 10 ---------- pdns/dnsdistdist/dnsdist-xsk.cc | 2 -- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index cf3f6129e947..67382a04bbfa 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -868,7 +868,7 @@ void responderThread(std::shared_ptr dss) continue; } - if (fd != ids->backendFD) { + if (!ids->isXSK() && fd != ids->backendFD) { dss->restoreState(queryId, std::move(*ids)); continue; } @@ -1992,8 +1992,6 @@ bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) return false; } else { - const auto& xskInfo = ss->pickWorkerForSending(); - ids.backendFD = xskInfo->workerWaker; assignOutgoingUDPQueryToBackend(ss, dh->id, dq, query, false); auto sourceAddr = ss->pickSourceAddressForSending(); packet.setAddr(sourceAddr, ss->d_config.sourceMACAddr, ss->d_config.remote, ss->d_config.destMACAddr); diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 52019d56e285..4dba2db925f8 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -995,7 +995,6 @@ public: #ifdef HAVE_XSK void registerXsk(std::vector>& xsks); [[nodiscard]] ComboAddress pickSourceAddressForSending(); - [[nodiscard]] const std::shared_ptr& pickWorkerForSending(); #endif /* HAVE_XSK */ dnsdist::Protocol getProtocol() const diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 966b3ff9c20a..9c5da43e5635 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -885,16 +885,6 @@ void DownstreamState::submitHealthCheckResult(bool initial, bool newResult) return (*addresses)[idx % numberOfAddresses]; } -[[nodiscard]] const std::shared_ptr& DownstreamState::pickWorkerForSending() -{ - auto numberOfWorkers = d_xskInfos.size(); - if (numberOfWorkers == 0) { - throw std::runtime_error("No XSK worker available for sending XSK data to backend " + getNameWithAddr()); - } - size_t idx = dnsdist::getRandomValue(numberOfWorkers); - return d_xskInfos[idx % numberOfWorkers]; -} - void DownstreamState::registerXsk(std::vector>& xsks) { d_xskSockets = xsks; diff --git a/pdns/dnsdistdist/dnsdist-xsk.cc b/pdns/dnsdistdist/dnsdist-xsk.cc index fc77706aa020..058e381908da 100644 --- a/pdns/dnsdistdist/dnsdist-xsk.cc +++ b/pdns/dnsdistdist/dnsdist-xsk.cc @@ -42,7 +42,6 @@ void XskResponderThread(std::shared_ptr dss, std::shared_ptrworkerWaker.getHandle(); while (!dss->isStopped()) { poll(pollfds.data(), pollfds.size(), -1); bool needNotify = false; @@ -63,7 +62,6 @@ void XskResponderThread(std::shared_ptr dss, std::shared_ptrgetState(queryId); if (ids) { if (!ids->isXSK()) { - // if (xskFd != ids->backendFD || !ids->isXSK()) { dss->restoreState(queryId, std::move(*ids)); ids = std::nullopt; } From 4a94ee50cf3fae71e823f91f9b375dc30d964850 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 12:01:02 +0100 Subject: [PATCH 55/65] dnsdist: Fix warnings from clang-tidy --- pdns/bpf-filter.cc | 76 +++++------ pdns/dnsdist-lua.cc | 177 +++++++++++++------------ pdns/dnsdist-tcp.cc | 2 +- pdns/dnsdist.cc | 79 +++++------ pdns/dnsdist.hh | 4 +- pdns/dnsdistdist/doh.cc | 2 +- pdns/dnsdistdist/doh3.cc | 2 +- pdns/dnsdistdist/doq.cc | 2 +- pdns/dnsdistdist/test-dnsdisttcp_cc.cc | 2 +- pdns/test-dnsdist_cc.cc | 4 +- 10 files changed, 173 insertions(+), 177 deletions(-) diff --git a/pdns/bpf-filter.cc b/pdns/bpf-filter.cc index 19343955c81f..225432cc1c4a 100644 --- a/pdns/bpf-filter.cc +++ b/pdns/bpf-filter.cc @@ -33,32 +33,18 @@ #include "misc.hh" -int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, - int max_entries, int map_flags); -int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); -int bpf_lookup_elem(int fd, void *key, void *value); -int bpf_delete_elem(int fd, void *key); -int bpf_get_next_key(int fd, void *key, void *next_key); - -int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int insn_len, - const char *license, int kern_version); - -int bpf_obj_pin(int fd, const char *pathname); -int bpf_obj_get(const char *pathname); - -static __u64 ptr_to_u64(void *ptr) +static __u64 ptr_to_u64(const void *ptr) { return (__u64) (unsigned long) ptr; } /* these can be static as they are not declared in libbpf.h: */ -static int bpf_pin_map(int fd, const std::string& path) +static int bpf_pin_map(int descriptor, const std::string& path) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.bpf_fd = fd; - attr.pathname = ptr_to_u64(const_cast(path.c_str())); + attr.bpf_fd = descriptor; + attr.pathname = ptr_to_u64(path.c_str()); return syscall(SYS_bpf, BPF_OBJ_PIN, &attr, sizeof(attr)); } @@ -66,11 +52,11 @@ static int bpf_load_pinned_map(const std::string& path) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.pathname = ptr_to_u64(const_cast(path.c_str())); + attr.pathname = ptr_to_u64(path.c_str()); return syscall(SYS_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); } -static void bpf_check_map_sizes(int fd, uint32_t expectedKeySize, uint32_t expectedValueSize) +static void bpf_check_map_sizes(int descriptor, uint32_t expectedKeySize, uint32_t expectedValueSize) { struct bpf_map_info info; uint32_t info_len = sizeof(info); @@ -78,7 +64,7 @@ static void bpf_check_map_sizes(int fd, uint32_t expectedKeySize, uint32_t expec union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.info.bpf_fd = fd; + attr.info.bpf_fd = descriptor; attr.info.info_len = info_len; attr.info.info = ptr_to_u64(&info); @@ -97,8 +83,8 @@ static void bpf_check_map_sizes(int fd, uint32_t expectedKeySize, uint32_t expec } } -int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, - int max_entries, int map_flags) +static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, + int max_entries, int map_flags) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); @@ -110,49 +96,49 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, return syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); } -int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags) +static int bpf_update_elem(int descriptor, void *key, void *value, unsigned long long flags) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.map_fd = fd; + attr.map_fd = descriptor; attr.key = ptr_to_u64(key); attr.value = ptr_to_u64(value); attr.flags = flags; return syscall(SYS_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } -int bpf_lookup_elem(int fd, void *key, void *value) +static int bpf_lookup_elem(int descriptor, void *key, void *value) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.map_fd = fd; + attr.map_fd = descriptor; attr.key = ptr_to_u64(key); attr.value = ptr_to_u64(value); return syscall(SYS_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } -int bpf_delete_elem(int fd, void *key) +static int bpf_delete_elem(int descriptor, void *key) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.map_fd = fd; + attr.map_fd = descriptor; attr.key = ptr_to_u64(key); return syscall(SYS_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); } -int bpf_get_next_key(int fd, void *key, void *next_key) +static int bpf_get_next_key(int descriptor, void *key, void *next_key) { union bpf_attr attr; memset(&attr, 0, sizeof(attr)); - attr.map_fd = fd; + attr.map_fd = descriptor; attr.key = ptr_to_u64(key); attr.next_key = ptr_to_u64(next_key); return syscall(SYS_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); } -int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int prog_len, - const char *license, int kern_version) +static int bpf_prog_load(enum bpf_prog_type prog_type, + const struct bpf_insn *insns, int prog_len, + const char *license, int kern_version) { char log_buf[65535]; union bpf_attr attr; @@ -351,15 +337,15 @@ BPFFilter::Map::Map(const BPFFilter::MapConfiguration& config, BPFFilter::MapFor static FDWrapper loadProgram(const struct bpf_insn* filter, size_t filterSize) { - auto fd = FDWrapper(bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, - filter, - filterSize, - "GPL", - 0)); - if (fd.getHandle() == -1) { + auto descriptor = FDWrapper(bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, + filter, + filterSize, + "GPL", + 0)); + if (descriptor.getHandle() == -1) { throw std::runtime_error("error loading BPF filter: " + stringerror()); } - return fd; + return descriptor; } @@ -442,8 +428,8 @@ BPFFilter::BPFFilter(std::unordered_map& configs, void BPFFilter::addSocket(int sock) { - int fd = d_mainfilter.getHandle(); - int res = setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &fd, sizeof(fd)); + int descriptor = d_mainfilter.getHandle(); + int res = setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &descriptor, sizeof(descriptor)); if (res != 0) { throw std::runtime_error("Error attaching BPF filter to this socket: " + stringerror()); @@ -452,8 +438,8 @@ void BPFFilter::addSocket(int sock) void BPFFilter::removeSocket(int sock) { - int fd = d_mainfilter.getHandle(); - int res = setsockopt(sock, SOL_SOCKET, SO_DETACH_BPF, &fd, sizeof(fd)); + int descriptor = d_mainfilter.getHandle(); + int res = setsockopt(sock, SOL_SOCKET, SO_DETACH_BPF, &descriptor, sizeof(descriptor)); if (res != 0) { throw std::runtime_error("Error detaching BPF filter from this socket: " + stringerror()); diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index e22a83cd68d6..50112a3730b0 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -307,10 +307,101 @@ static bool checkConfigurationTime(const std::string& name) return false; } +using newserver_t = LuaAssociativeTable, LuaArray>, DownstreamState::checkfunc_t>>; + +static void handleNewServerHealthCheckParameters(boost::optional& vars, DownstreamState::Config& config) +{ + std::string valueStr; + + if (getOptionalValue(vars, "checkInterval", valueStr) > 0) { + config.checkInterval = static_cast(std::stoul(valueStr)); + } + + if (getOptionalValue(vars, "healthCheckMode", valueStr) > 0) { + const auto& mode = valueStr; + if (pdns_iequals(mode, "auto")) { + config.availability = DownstreamState::Availability::Auto; + } + else if (pdns_iequals(mode, "lazy")) { + config.availability = DownstreamState::Availability::Lazy; + } + else if (pdns_iequals(mode, "up")) { + config.availability = DownstreamState::Availability::Up; + } + else if (pdns_iequals(mode, "down")) { + config.availability = DownstreamState::Availability::Down; + } + else { + warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode); + } + } + + if (getOptionalValue(vars, "checkName", valueStr) > 0) { + config.checkName = DNSName(valueStr); + } + + getOptionalValue(vars, "checkType", config.checkType); + getOptionalIntegerValue("newServer", vars, "checkClass", config.checkClass); + getOptionalValue(vars, "checkFunction", config.checkFunction); + getOptionalIntegerValue("newServer", vars, "checkTimeout", config.checkTimeout); + getOptionalValue(vars, "checkTCP", config.d_tcpCheck); + getOptionalValue(vars, "setCD", config.setCD); + getOptionalValue(vars, "mustResolve", config.mustResolve); + + if (getOptionalValue(vars, "lazyHealthCheckSampleSize", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckSampleSize", value); + config.d_lazyHealthCheckSampleSize = value; + } + + if (getOptionalValue(vars, "lazyHealthCheckMinSampleCount", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckMinSampleCount", value); + config.d_lazyHealthCheckMinSampleCount = value; + } + + if (getOptionalValue(vars, "lazyHealthCheckThreshold", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckThreshold", value, std::numeric_limits::max()); + config.d_lazyHealthCheckThreshold = value; + } + + if (getOptionalValue(vars, "lazyHealthCheckFailedInterval", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckFailedInterval", value); + config.d_lazyHealthCheckFailedInterval = value; + } + + getOptionalValue(vars, "lazyHealthCheckUseExponentialBackOff", config.d_lazyHealthCheckUseExponentialBackOff); + + if (getOptionalValue(vars, "lazyHealthCheckMaxBackOff", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckMaxBackOff", value); + config.d_lazyHealthCheckMaxBackOff = value; + } + + if (getOptionalValue(vars, "lazyHealthCheckMode", valueStr) > 0) { + const auto& mode = valueStr; + if (pdns_iequals(mode, "TimeoutOnly")) { + config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly; + } + else if (pdns_iequals(mode, "TimeoutOrServFail")) { + config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail; + } + else { + warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode); + } + } + + getOptionalValue(vars, "lazyHealthCheckWhenUpgraded", config.d_upgradeToLazyHealthChecks); + + getOptionalIntegerValue("newServer", vars, "maxCheckFailures", config.maxCheckFailures); + getOptionalIntegerValue("newServer", vars, "rise", config.minRiseSuccesses); +} + // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) { - using newserver_t = LuaAssociativeTable, LuaArray>, DownstreamState::checkfunc_t>>; luaCtx.writeFunction("inClientStartup", [client]() { return client && !g_configurationDone; }); @@ -406,9 +497,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalIntegerValue("newServer", vars, "tcpSendTimeout", config.tcpSendTimeout); getOptionalIntegerValue("newServer", vars, "tcpRecvTimeout", config.tcpRecvTimeout); - if (getOptionalValue(vars, "checkInterval", valueStr) > 0) { - config.checkInterval = static_cast(std::stoul(valueStr)); - } + handleNewServerHealthCheckParameters(vars, config); bool fastOpen{false}; if (getOptionalValue(vars, "tcpFastOpen", fastOpen) > 0) { @@ -430,84 +519,6 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) config.id = boost::uuids::string_generator()(valueStr); } - if (getOptionalValue(vars, "healthCheckMode", valueStr) > 0) { - const auto& mode = valueStr; - if (pdns_iequals(mode, "auto")) { - config.availability = DownstreamState::Availability::Auto; - } - else if (pdns_iequals(mode, "lazy")) { - config.availability = DownstreamState::Availability::Lazy; - } - else if (pdns_iequals(mode, "up")) { - config.availability = DownstreamState::Availability::Up; - } - else if (pdns_iequals(mode, "down")) { - config.availability = DownstreamState::Availability::Down; - } - else { - warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode); - } - } - - if (getOptionalValue(vars, "checkName", valueStr) > 0) { - config.checkName = DNSName(valueStr); - } - - getOptionalValue(vars, "checkType", config.checkType); - getOptionalIntegerValue("newServer", vars, "checkClass", config.checkClass); - getOptionalValue(vars, "checkFunction", config.checkFunction); - getOptionalIntegerValue("newServer", vars, "checkTimeout", config.checkTimeout); - getOptionalValue(vars, "checkTCP", config.d_tcpCheck); - getOptionalValue(vars, "setCD", config.setCD); - getOptionalValue(vars, "mustResolve", config.mustResolve); - - if (getOptionalValue(vars, "lazyHealthCheckSampleSize", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckSampleSize", value); - config.d_lazyHealthCheckSampleSize = value; - } - - if (getOptionalValue(vars, "lazyHealthCheckMinSampleCount", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckMinSampleCount", value); - config.d_lazyHealthCheckMinSampleCount = value; - } - - if (getOptionalValue(vars, "lazyHealthCheckThreshold", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckThreshold", value, std::numeric_limits::max()); - config.d_lazyHealthCheckThreshold = value; - } - - if (getOptionalValue(vars, "lazyHealthCheckFailedInterval", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckFailedInterval", value); - config.d_lazyHealthCheckFailedInterval = value; - } - - getOptionalValue(vars, "lazyHealthCheckUseExponentialBackOff", config.d_lazyHealthCheckUseExponentialBackOff); - - if (getOptionalValue(vars, "lazyHealthCheckMaxBackOff", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckMaxBackOff", value); - config.d_lazyHealthCheckMaxBackOff = value; - } - - if (getOptionalValue(vars, "lazyHealthCheckMode", valueStr) > 0) { - const auto& mode = valueStr; - if (pdns_iequals(mode, "TimeoutOnly")) { - config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly; - } - else if (pdns_iequals(mode, "TimeoutOrServFail")) { - config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail; - } - else { - warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode); - } - } - - getOptionalValue(vars, "lazyHealthCheckWhenUpgraded", config.d_upgradeToLazyHealthChecks); - getOptionalValue(vars, "useClientSubnet", config.useECS); getOptionalValue(vars, "useProxyProtocol", config.useProxyProtocol); getOptionalValue(vars, "proxyProtocolAdvertiseTLS", config.d_proxyProtocolAdvertiseTLS); @@ -515,8 +526,6 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "ipBindAddrNoPort", config.ipBindAddrNoPort); getOptionalIntegerValue("newServer", vars, "addXPF", config.xpfRRCode); - getOptionalIntegerValue("newServer", vars, "maxCheckFailures", config.maxCheckFailures); - getOptionalIntegerValue("newServer", vars, "rise", config.minRiseSuccesses); getOptionalValue(vars, "reconnectOnUp", config.reconnectOnUp); diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index 3baf478ea22b..9d8edaf572ff 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -674,7 +674,7 @@ IncomingTCPConnectionState::QueryProcessingResult IncomingTCPConnectionState::ha { /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */ const dnsheader_aligned dnsHeader(query.data()); - if (!checkQueryHeaders(dnsHeader.get(), *d_ci.cs)) { + if (!checkQueryHeaders(*dnsHeader.get(), *d_ci.cs)) { return QueryProcessingResult::InvalidHeaders; } diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 67382a04bbfa..204db07f1413 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -366,6 +366,7 @@ bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, uint16_t rqtype, rqclass; DNSName rqname; try { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) rqname = DNSName(reinterpret_cast(response.data()), response.size(), sizeof(dnsheader), false, &rqtype, &rqclass); } catch (const std::exception& e) { @@ -746,7 +747,7 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re } bool muted = true; - if (ids.cs && !ids.cs->muted && !ids.isXSK()) { + if (ids.cs != nullptr && !ids.cs->muted && !ids.isXSK()) { sendUDPResponse(ids.cs->udpFD, response, dr.ids.delayMsec, ids.hopLocal, ids.hopRemote); muted = false; } @@ -775,15 +776,15 @@ static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& re bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids) { - const dnsheader_aligned dh(response.data()); - auto queryId = dh->id; + const dnsheader_aligned dnsHeader(response.data()); + auto queryId = dnsHeader->id; if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss)) { dss->restoreState(queryId, std::move(ids)); return false; } - auto du = std::move(ids.du); + auto dohUnit = std::move(ids.du); dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [&ids](dnsheader& header) { header.id = ids.origID; return true; @@ -793,13 +794,13 @@ bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& double udiff = ids.queryRealTime.udiff(); // do that _before_ the processing, otherwise it's not fair to the backend dss->latencyUsec = (127.0 * dss->latencyUsec / 128.0) + udiff / 128.0; - dss->reportResponse(dh->rcode); + dss->reportResponse(dnsHeader->rcode); /* don't call processResponse for DOH */ - if (du) { + if (dohUnit) { #ifdef HAVE_DNS_OVER_HTTPS - // DoH query, we cannot touch du after that - DOHUnitInterface::handleUDPResponse(std::move(du), std::move(response), std::move(ids), dss); + // DoH query, we cannot touch dohUnit after that + DOHUnitInterface::handleUDPResponse(std::move(dohUnit), std::move(response), std::move(ids), dss); #endif return false; } @@ -860,8 +861,8 @@ void responderThread(std::shared_ptr dss) } response.resize(static_cast(got)); - const dnsheader_aligned dh(response.data()); - queryId = dh->id; + const dnsheader_aligned dnsHeader(response.data()); + queryId = dnsHeader->id; auto ids = dss->getState(queryId); if (!ids) { @@ -1337,22 +1338,22 @@ bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ return false; } -bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs) +bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState) { - if (dh->qr) { // don't respond to responses + if (dnsHeader.qr) { // don't respond to responses ++dnsdist::metrics::g_stats.nonCompliantQueries; - ++cs.nonCompliantQueries; + ++clientState.nonCompliantQueries; return false; } - if (dh->qdcount == 0) { + if (dnsHeader.qdcount == 0) { ++dnsdist::metrics::g_stats.emptyQueries; if (g_dropEmptyQueries) { return false; } } - if (dh->rd) { + if (dnsHeader.rd) { ++dnsdist::metrics::g_stats.rdQueries; } @@ -1684,38 +1685,38 @@ ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::sha return ProcessQueryResult::Drop; } -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool actuallySend) +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend) { - bool doh = dq.ids.du != nullptr; + bool doh = dnsQuestion.ids.du != nullptr; bool failed = false; - if (ds->d_config.useProxyProtocol) { + if (downstream->d_config.useProxyProtocol) { try { - addProxyProtocol(dq, &dq.ids.d_proxyProtocolPayloadSize); + addProxyProtocol(dnsQuestion, &dnsQuestion.ids.d_proxyProtocolPayloadSize); } catch (const std::exception& e) { - vinfolog("Adding proxy protocol payload to %s query from %s failed: %s", (dq.ids.du ? "DoH" : ""), dq.ids.origDest.toStringWithPort(), e.what()); + vinfolog("Adding proxy protocol payload to %s query from %s failed: %s", (dnsQuestion.ids.du ? "DoH" : ""), dnsQuestion.ids.origDest.toStringWithPort(), e.what()); return false; } } - if (doh && !dq.ids.d_packet) { - dq.ids.d_packet = std::make_unique(query); + if (doh && !dnsQuestion.ids.d_packet) { + dnsQuestion.ids.d_packet = std::make_unique(query); } try { - int fd = ds->pickSocketForSending(); + int fd = downstream->pickSocketForSending(); if (actuallySend) { - dq.ids.backendFD = fd; + dnsQuestion.ids.backendFD = fd; } - dq.ids.origID = queryID; - dq.ids.forwardedOverUDP = true; + dnsQuestion.ids.origID = queryID; + dnsQuestion.ids.forwardedOverUDP = true; - vinfolog("Got query for %s|%s from %s%s, relayed to %s%s", dq.ids.qname.toLogString(), QType(dq.ids.qtype).toString(), dq.ids.origRemote.toStringWithPort(), (doh ? " (https)" : ""), ds->getNameWithAddr(), actuallySend ? "" : " (xsk)"); + vinfolog("Got query for %s|%s from %s%s, relayed to %s%s", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort(), (doh ? " (https)" : ""), downstream->getNameWithAddr(), actuallySend ? "" : " (xsk)"); - /* make a copy since we cannot touch dq.ids after the move */ - auto proxyProtocolPayloadSize = dq.ids.d_proxyProtocolPayloadSize; - auto idOffset = ds->saveState(std::move(dq.ids)); + /* make a copy since we cannot touch dnsQuestion.ids after the move */ + auto proxyProtocolPayloadSize = dnsQuestion.ids.d_proxyProtocolPayloadSize; + auto idOffset = downstream->saveState(std::move(dnsQuestion.ids)); /* set the correct ID */ memcpy(&query.at(proxyProtocolPayloadSize), &idOffset, sizeof(idOffset)); @@ -1725,7 +1726,7 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint1 /* you can't touch ids or du after this line, unless the call returned a non-negative value, because it might already have been freed */ - ssize_t ret = udpClientSendRequestToBackend(ds, fd, query); + ssize_t ret = udpClientSendRequestToBackend(downstream, fd, query); if (ret < 0) { failed = true; @@ -1734,12 +1735,12 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint1 if (failed) { /* clear up the state. In the very unlikely event it was reused in the meantime, so be it. */ - auto cleared = ds->getState(idOffset); + auto cleared = downstream->getState(idOffset); if (cleared) { - dq.ids.du = std::move(cleared->du); + dnsQuestion.ids.du = std::move(cleared->du); } ++dnsdist::metrics::g_stats.downstreamSendErrors; - ++ds->sendErrors; + ++downstream->sendErrors; return false; } } @@ -1795,14 +1796,14 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct { /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */ - const dnsheader_aligned dh(query.data()); - queryId = ntohs(dh->id); + const dnsheader_aligned dnsHeader(query.data()); + queryId = ntohs(dnsHeader->id); - if (!checkQueryHeaders(dh.get(), cs)) { + if (!checkQueryHeaders(*dnsHeader.get(), cs)) { return; } - if (dh->qdcount == 0) { + if (dnsHeader->qdcount == 0) { dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) { header.rcode = RCode::NotImp; header.qr = true; @@ -1922,7 +1923,7 @@ bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) dnsheader_aligned dnsHeader(query.data()); queryId = ntohs(dnsHeader->id); - if (!checkQueryHeaders(dnsHeader.get(), cs)) { + if (!checkQueryHeaders(*dnsHeader.get(), cs)) { return false; } diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 4dba2db925f8..19eb3c715a46 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -1171,7 +1171,7 @@ void resetLuaSideEffect(); // reset to indeterminate state bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr& remote); -bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs); +bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState); extern std::vector> g_dnsCryptLocals; int handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response); @@ -1195,7 +1195,7 @@ bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::s bool processResponseAfterRules(PacketBuffer& response, const std::vector& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted); bool processResponderPacket(std::shared_ptr& dss, PacketBuffer& response, const std::vector& localRespRuleActions, const std::vector& cacheInsertedRespRuleActions, InternalQueryState&& ids); -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool actuallySend = true); +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend = true); ssize_t udpClientSendRequestToBackend(const std::shared_ptr& ss, const int sd, const PacketBuffer& request, bool healthCheck = false); bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index 0ac9e33874fc..f40923d385b1 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -719,7 +719,7 @@ static void processDOHQuery(DOHUnitUniquePtr&& unit, bool inMainThread = false) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { unit->status_code = 400; handleImmediateResponse(std::move(unit), "DoH invalid headers"); return; diff --git a/pdns/dnsdistdist/doh3.cc b/pdns/dnsdistdist/doh3.cc index 2201e23b3d02..66081eb08a6d 100644 --- a/pdns/dnsdistdist/doh3.cc +++ b/pdns/dnsdistdist/doh3.cc @@ -513,7 +513,7 @@ static void processDOH3Query(DOH3UnitUniquePtr&& doh3Unit) /* don't keep that pointer around, it will be invalidated if the buffer is ever resized */ dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { dnsdist::PacketMangling::editDNSHeaderFromPacket(unit->query, [](dnsheader& header) { header.rcode = RCode::ServFail; header.qr = true; diff --git a/pdns/dnsdistdist/doq.cc b/pdns/dnsdistdist/doq.cc index af951f395797..3f52d33dc962 100644 --- a/pdns/dnsdistdist/doq.cc +++ b/pdns/dnsdistdist/doq.cc @@ -425,7 +425,7 @@ static void processDOQQuery(DOQUnitUniquePtr&& doqUnit) /* don't keep that pointer around, it will be invalidated if the buffer is ever resized */ dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { dnsdist::PacketMangling::editDNSHeaderFromPacket(unit->query, [](dnsheader& header) { header.rcode = RCode::ServFail; header.qr = true; diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index fd37dda3196d..22a27c692c1a 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -51,7 +51,7 @@ bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ return false; } -bool checkQueryHeaders(const struct dnsheader* dh, ClientState&) +bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState) { return true; } diff --git a/pdns/test-dnsdist_cc.cc b/pdns/test-dnsdist_cc.cc index d3e0d29fc0b9..90a513fc8809 100644 --- a/pdns/test-dnsdist_cc.cc +++ b/pdns/test-dnsdist_cc.cc @@ -58,7 +58,7 @@ bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMs return false; } -bool assignOutgoingUDPQueryToBackend(std::shared_ptr& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, bool) +bool assignOutgoingUDPQueryToBackend(std::shared_ptr& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend) { return true; } @@ -76,7 +76,7 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(DownstreamState const&) } namespace dnsdist::xsk { -bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) +bool XskProcessQuery(ClientState& clientState, LocalHolders& holders, XskPacket& packet) { return false; } From e053dc9dc470c819fce7120e0cf68f949a7b4104 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 12:53:36 +0100 Subject: [PATCH 56/65] dnsdist: Implement proper parameters handling in the XDP helper --- contrib/xdp.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/contrib/xdp.py b/contrib/xdp.py index d6ef369708e9..089e68d9b1b1 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 - -from bcc import BPF +import argparse import ctypes as ct import netaddr import socket +from bcc import BPF + # Constants QTYPES = {'LOC': 29, '*': 255, 'IXFR': 251, 'UINFO': 100, 'NSEC3': 50, 'AAAA': 28, 'CNAME': 5, 'MINFO': 14, 'EID': 31, 'GPOS': 27, 'X25': 19, 'HINFO': 13, 'CAA': 257, 'NULL': 10, 'DNSKEY': 48, 'DS': 43, 'ISDN': 20, 'SOA': 6, 'RP': 17, 'UID': 101, 'TALINK': 58, 'TKEY': 249, 'PX': 26, 'NSAP-PTR': 23, 'TXT': 16, 'IPSECKEY': 45, 'DNAME': 39, 'MAILA': 254, 'AFSDB': 18, 'SSHFP': 44, 'NS': 2, 'PTR': 12, 'SPF': 99, 'TA': 32768, 'A': 1, 'NXT': 30, 'AXFR': 252, 'RKEY': 57, 'KEY': 25, 'NIMLOC': 32, 'A6': 38, 'TLSA': 52, 'MG': 8, 'HIP': 55, 'NSEC': 47, 'GID': 102, 'SRV': 33, 'DLV': 32769, 'NSEC3PARAM': 51, 'UNSPEC': 103, 'TSIG': 250, 'ATMA': 34, 'RRSIG': 46, 'OPT': 41, 'MD': 3, 'NAPTR': 35, 'MF': 4, 'MB': 7, 'DHCID': 49, 'MX': 15, 'MAILB': 253, 'CERT': 37, 'NINFO': 56, 'APL': 42, 'MR': 9, 'SIG': 24, 'WKS': 11, 'KX': 36, 'NSAP': 22, 'RT': 21, 'SINK': 40} INV_QTYPES = {v: k for k, v in QTYPES.items()} @@ -13,9 +14,6 @@ DROP_ACTION = 1 TC_ACTION = 2 -# The interface on wich the filter will be attached -DEV = "eth1" - # The list of blocked IPv4, IPv6 and QNames # IP format : (IPAddress, Action) # CIDR format : (IPAddress/cidr, Action) @@ -27,19 +25,26 @@ blocked_qnames = [("localhost", "A", DROP_ACTION), ("test.com", "*", TC_ACTION)] # Main -useXsk = True +parser = argparse.ArgumentParser(description='XDP helper for DNSDist') +parser.add_argument('--xsk', action='store_true', help='Enable XSK (AF_XDP) mode', default=False) +parser.add_argument('--interface', '-i', type=str, default='eth0', help='The interface on which the filter will be attached') + +parameters = parser.parse_args() cflag = [] -if useXsk: +if parameters.xsk: + print(f'Enabling XSK (AF_XDP) on {parameters.interface}..') cflag.append("-DUseXsk") else: Ports = [53] + portsStr = ', '.join(str(port) for port in Ports) + print(f'Enabling XDP on {parameters.interface} and ports {portsStr}..') IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x),"+str(i)+")" for i in Ports) cflag.append(r"-DIN_DNS_PORT_SET(x)=(" + IN_DNS_PORT_SET + r")") xdp = BPF(src_file="xdp-filter.ebpf.src", cflags=cflag) fn = xdp.load_func("xdp_dns_filter", BPF.XDP) -xdp.attach_xdp(DEV, fn, 0) +xdp.attach_xdp(parameters.interface, fn, 0) v4filter = xdp.get_table("v4filter") v6filter = xdp.get_table("v6filter") @@ -47,7 +52,7 @@ cidr6filter = xdp.get_table("cidr6filter") qnamefilter = xdp.get_table("qnamefilter") -if useXsk: +if parameters.xsk: xskDestinations = xdp.get_table("xskDestinationsV4") for ip in blocked_ipv4: @@ -108,7 +113,8 @@ leaf.action = qname[2] qnamefilter[key] = leaf -print("Filter is ready") +print(f"Filter is ready on {parameters.interface}") + try: xdp.trace_print() except KeyboardInterrupt: @@ -126,4 +132,4 @@ for item in qnamefilter.items(): print(f"{''.join(map(chr, item[0].qname)).strip()}/{INV_QTYPES[item[0].qtype]} ({ACTIONS[item[1].action]}): {item[1].counter}") -xdp.remove_xdp(DEV, 0) +xdp.remove_xdp(parameters.interface, 0) From 94e3db7eed0ecc330ffe4ce26fd8ac1500f56c40 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 13:03:35 +0100 Subject: [PATCH 57/65] dnsdist: Fix more clang-tidy warnings --- pdns/bpf-filter.cc | 5 +++-- pdns/dnsdist-tcp.cc | 2 +- pdns/dnsdist.cc | 8 ++++---- pdns/dnsdistdist/doh.cc | 2 +- pdns/dnsdistdist/doh3.cc | 2 +- pdns/dnsdistdist/doq.cc | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pdns/bpf-filter.cc b/pdns/bpf-filter.cc index 225432cc1c4a..32f6986d9763 100644 --- a/pdns/bpf-filter.cc +++ b/pdns/bpf-filter.cc @@ -83,6 +83,7 @@ static void bpf_check_map_sizes(int descriptor, uint32_t expectedKeySize, uint32 } } +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, int map_flags) { @@ -137,7 +138,7 @@ static int bpf_get_next_key(int descriptor, void *key, void *next_key) } static int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int prog_len, + const struct bpf_insn *insns, size_t prog_len, const char *license, int kern_version) { char log_buf[65535]; @@ -145,7 +146,7 @@ static int bpf_prog_load(enum bpf_prog_type prog_type, memset(&attr, 0, sizeof(attr)); attr.prog_type = prog_type; attr.insns = ptr_to_u64((void *) insns); - attr.insn_cnt = prog_len / sizeof(struct bpf_insn); + attr.insn_cnt = static_cast(prog_len / sizeof(struct bpf_insn)); attr.license = ptr_to_u64((void *) license); attr.log_buf = ptr_to_u64(log_buf); attr.log_size = sizeof(log_buf); diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index 9d8edaf572ff..e3eb68e1152c 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -674,7 +674,7 @@ IncomingTCPConnectionState::QueryProcessingResult IncomingTCPConnectionState::ha { /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */ const dnsheader_aligned dnsHeader(query.data()); - if (!checkQueryHeaders(*dnsHeader.get(), *d_ci.cs)) { + if (!checkQueryHeaders(*dnsHeader, *d_ci.cs)) { return QueryProcessingResult::InvalidHeaders; } diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 204db07f1413..c78fb5a650c4 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -1705,9 +1705,9 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& downstrea } try { - int fd = downstream->pickSocketForSending(); + int descriptor = downstream->pickSocketForSending(); if (actuallySend) { - dnsQuestion.ids.backendFD = fd; + dnsQuestion.ids.backendFD = descriptor; } dnsQuestion.ids.origID = queryID; dnsQuestion.ids.forwardedOverUDP = true; @@ -1726,7 +1726,7 @@ bool assignOutgoingUDPQueryToBackend(std::shared_ptr& downstrea /* you can't touch ids or du after this line, unless the call returned a non-negative value, because it might already have been freed */ - ssize_t ret = udpClientSendRequestToBackend(downstream, fd, query); + ssize_t ret = udpClientSendRequestToBackend(downstream, descriptor, query); if (ret < 0) { failed = true; @@ -1799,7 +1799,7 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct const dnsheader_aligned dnsHeader(query.data()); queryId = ntohs(dnsHeader->id); - if (!checkQueryHeaders(*dnsHeader.get(), cs)) { + if (!checkQueryHeaders(*dnsHeader, cs)) { return; } diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index f40923d385b1..5ecdd00f6fd6 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -719,7 +719,7 @@ static void processDOHQuery(DOHUnitUniquePtr&& unit, bool inMainThread = false) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader, clientState)) { unit->status_code = 400; handleImmediateResponse(std::move(unit), "DoH invalid headers"); return; diff --git a/pdns/dnsdistdist/doh3.cc b/pdns/dnsdistdist/doh3.cc index 66081eb08a6d..4c0823ab621e 100644 --- a/pdns/dnsdistdist/doh3.cc +++ b/pdns/dnsdistdist/doh3.cc @@ -513,7 +513,7 @@ static void processDOH3Query(DOH3UnitUniquePtr&& doh3Unit) /* don't keep that pointer around, it will be invalidated if the buffer is ever resized */ dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader, clientState)) { dnsdist::PacketMangling::editDNSHeaderFromPacket(unit->query, [](dnsheader& header) { header.rcode = RCode::ServFail; header.qr = true; diff --git a/pdns/dnsdistdist/doq.cc b/pdns/dnsdistdist/doq.cc index 3f52d33dc962..277a8c5985ac 100644 --- a/pdns/dnsdistdist/doq.cc +++ b/pdns/dnsdistdist/doq.cc @@ -425,7 +425,7 @@ static void processDOQQuery(DOQUnitUniquePtr&& doqUnit) /* don't keep that pointer around, it will be invalidated if the buffer is ever resized */ dnsheader_aligned dnsHeader(unit->query.data()); - if (!checkQueryHeaders(*dnsHeader.get(), clientState)) { + if (!checkQueryHeaders(*dnsHeader, clientState)) { dnsdist::PacketMangling::editDNSHeaderFromPacket(unit->query, [](dnsheader& header) { header.rcode = RCode::ServFail; header.qr = true; From ffbc306cefa96ba26055b27880bb28b37429300e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 14:54:29 +0100 Subject: [PATCH 58/65] dnsdist: Update the XSK documentation for recent changes --- pdns/dnsdistdist/docs/advanced/tuning.rst | 5 +++++ pdns/dnsdistdist/docs/advanced/xsk.rst | 23 ++++++++++++++++------ pdns/dnsdistdist/docs/install.rst | 3 ++- pdns/dnsdistdist/docs/reference/config.rst | 4 ++-- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/pdns/dnsdistdist/docs/advanced/tuning.rst b/pdns/dnsdistdist/docs/advanced/tuning.rst index e831485a7980..ab5f59e12fa4 100644 --- a/pdns/dnsdistdist/docs/advanced/tuning.rst +++ b/pdns/dnsdistdist/docs/advanced/tuning.rst @@ -70,6 +70,11 @@ For DNS over HTTPS, every :func:`addDOHLocal` directive adds a new thread dealin When dealing with a large traffic load, it might happen that the internal pipe used to pass queries between the threads handling the incoming connections and the one getting a response from the backend become full too quickly, degrading performance and causing timeouts. This can be prevented by increasing the size of the internal pipe buffer, via the `internalPipeBufferSize` option of :func:`addDOHLocal`. Setting a value of `1048576` is known to yield good results on Linux. +`AF_XDP` / `XSK` +---------------- + +On recent versions of Linux (`>= 4.18`), DNSDist supports receiving UDP datagrams directly from the kernel, bypassing the usual network stack, via `AF_XDP`/`XSK`. This yields much better performance but comes with some limitations. Please see :doc:`xsk` for more information. + UDP buffer sizes ---------------- diff --git a/pdns/dnsdistdist/docs/advanced/xsk.rst b/pdns/dnsdistdist/docs/advanced/xsk.rst index c61f88e9b229..3badb7d6fd72 100644 --- a/pdns/dnsdistdist/docs/advanced/xsk.rst +++ b/pdns/dnsdistdist/docs/advanced/xsk.rst @@ -10,9 +10,9 @@ Since 1.9.0, :program:`dnsdist` can use `AF_XDP `_ can be found in the ``contrib`` directory of the PowerDNS Git repository. The name of the network interface to use might have to be updated, though. Once it has been updated, the ``XDP`` program can be started:: +In addition to these, an ``eBPF`` ``XDP`` program needs to be loaded to decide which packets to distribute via the ``AF_XDP`` socket (and to which, as there are usually more than one). This program uses a ``BPF`` map of type ``XSKMAP`` (located at ``/sys/fs/bpf/dnsdist/xskmap`` by default) that is populated by :program:``dnsdist` at startup to locate the ``AF_XDP`` socket to use. :program:`dnsdist` also sets up two additional ``BPF`` maps (located at ``/sys/fs/bpf/dnsdist/xsk-destinations-v4`` and ``/sys/fs/bpf/dnsdist/xsk-destinations-v6``) to let the ``XDP`` program know which IP destinations are to be routed to the ``AF_XDP`` sockets and which are to be passed to the regular network stack (health-checks queries and responses, for example). A ready-to-use `XDP program `_ can be found in the ``contrib`` directory of the PowerDNS Git repository:: - $ python xdp.py + $ python xdp.py --xsk --interface eth0 Then :program:`dnsdist` needs to be configured to use ``AF_XDP``, first by creating a :class:`XskSocket` object that are tied to a specific queue of a specific network interface: @@ -52,17 +52,28 @@ The ``Combined`` lines tell us that the interface supports 8 queues, so we can d addLocal("192.0.2.1:53", {xskSocket=xsk, reusePort=true}) end -We can also instructs :program:`dnsdist` to use ``AF_XDP`` to send and receive UDP packets to a backend: +This will start one router thread per :class:`XskSocket` object, plus one worker thread per :func:`addLocal` using that :class:`XskSocket` object. + +We can instructs :program:`dnsdist` to use ``AF_XDP`` to send and receive UDP packets to a backend in addition to packets from clients: .. code-block:: lua - newServer("192.0.2.2:53", {xskSocket=xsk}) + local sockets = {} + for i=1,8 do + xsk = newXsk({ifName="enp1s0", NIC_queue_id=i-1, frameNums=65536, xskMapPath="/sys/fs/bpf/dnsdist/xskmap"}) + table.insert(sockets, xsk) + addLocal("192.0.2.1:53", {xskSocket=xsk, reusePort=true}) + end + + newServer("192.0.2.2:53", {xskSocket=sockets}) + +This will start one router thread per :class:`XskSocket` object, plus one worker thread per :func:`addLocal`/:func:`newServer` using that :class:`XskSocket` object. We are not passing the MAC address of the backend (or the gateway to reach it) directly, so :program:`dnsdist` will try to fetch it from the system MAC address cache. This may not work, in which case we might need to pass explicitly: .. code-block:: lua - newServer("192.0.2.2:53", {xskSocket=xsk, MACAddr='00:11:22:33:44:55'}) + newServer("192.0.2.2:53", {xskSocket=sockets, MACAddr='00:11:22:33:44:55'}) Performance @@ -70,7 +81,7 @@ Performance Using `kxdpgun `_, we can compare the performance of :program:`dnsdist` using the regular network stack and ``AF_XDP``. -This test was realized using two Intel E3-1270 with 4 cores (8 threads) running at 3.8 Ghz, using 10 Gbps network cards. On both the injector running ``kxdpgun`` and the box running :program:`dnsdist` there was no firewall, the governor was set to ``performance``, the UDP buffers were raised to ``16777216`` and the receive queue hash policy set to use the IP addresses and ports (see :doc:`tuning`). +This test was realized using two Intel E3-1270 with 4 cores (8 threads) running at 3.8 Ghz, using Intel 82599 10 Gbps network cards. On both the injector running ``kxdpgun`` and the box running :program:`dnsdist` there was no firewall, the governor was set to ``performance``, the UDP buffers were raised to ``16777216`` and the receive queue hash policy set to use the IP addresses and ports (see :doc:`tuning`). :program:`dnsdist` was configured to immediately respond to incoming queries with ``REFUSED``: diff --git a/pdns/dnsdistdist/docs/install.rst b/pdns/dnsdistdist/docs/install.rst index 13b672855a9c..34e3af1bfdb0 100644 --- a/pdns/dnsdistdist/docs/install.rst +++ b/pdns/dnsdistdist/docs/install.rst @@ -50,8 +50,9 @@ dnsdist depends on the following libraries: * `Editline (libedit) `_ * `libfstrm `_ (optional, dnstap support) * `GnuTLS `_ (optional, DoT and outgoing DoH support) -* `libh2o `_ (optional, incoming DoH support, deprecated in 1.9.0 in favor of ``nghttp2``) +* `libbpf `_ and `libxdp `_ (optional, `XSK`/`AF_XDP` support) * `libcap `_ (optional, capabilities support) +* `libh2o `_ (optional, incoming DoH support, deprecated in 1.9.0 in favor of ``nghttp2``) * `libsodium `_ (optional, DNSCrypt and console encryption support) * `LMDB `_ (optional, LMDB support) * `net-snmp `_ (optional, SNMP support) diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 7311fc81f555..b3e2f5c2dbca 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -631,7 +631,7 @@ Servers Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool``, ``maxConcurrentTCPConnections``, ``subjectAddr``, ``lazyHealthCheckSampleSize``, ``lazyHealthCheckMinSampleCount``, ``lazyHealthCheckThreshold``, ``lazyHealthCheckFailedInterval``, ``lazyHealthCheckMode``, ``lazyHealthCheckUseExponentialBackOff``, ``lazyHealthCheckMaxBackOff``, ``lazyHealthCheckWhenUpgraded``, ``healthCheckMode`` and ``ktls`` to server_table. .. versionchanged:: 1.9.0 - Added ``MACAddr``, ``proxyProtocolAdvertiseTLS`` and ``xskSocket`` to server_table. + Added ``MACAddr``, ``proxyProtocolAdvertiseTLS`` and ``xskSockets`` to server_table. :param str server_string: A simple IP:PORT string. :param table server_table: A table with at least an ``address`` key @@ -720,7 +720,7 @@ Servers ``lazyHealthCheckWhenUpgraded`` ``bool`` "Whether the auto-upgraded version of this backend (see ``autoUpgrade``) should use the lazy health-checking mode. Default is false, which means it will use the regular health-checking mode." ``ktls`` ``bool`` "Whether to enable the experimental kernel TLS support on Linux, if both the kernel and the OpenSSL library support it. Default is false. Currently both DoT and DoH backend support this option." ``proxyProtocolAdvertiseTLS`` ``bool`` "Whether to set the SSL Proxy Protocol TLV in the proxy protocol payload sent to the backend if the query was received over an encrypted channel (DNSCrypt, DoQ, DoH or DoT). Requires ``useProxyProtocol=true``. Default is false." - ``xskSocket`` :class:`XskSocket` "A socket to enable ``XSK`` / ``AF_XDP`` support for this backend. See :doc:`../advanced/xsk` for more information." + ``xskSockets`` ``array`` "An array of :class:`XskSocket` objects to enable ``XSK`` / ``AF_XDP`` support for this backend. See :doc:`../advanced/xsk` for more information." ``MACAddr`` ``str`` "When the ``xskSocket`` option is set, this parameter can be used to specify the destination MAC address to use to reach the backend. If this options is not specified, dnsdist will try to get it from the IP of the backend by looking into the system's MAC address table, but it will fail if the corresponding MAC address is not present." .. function:: getServer(index) -> Server From 2a42c6cba484f905df8697b4482dd344e98c6968 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 23 Jan 2024 14:57:55 +0100 Subject: [PATCH 59/65] spell-check: Allow libxdp --- .github/actions/spell-check/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index dda6d6b72df3..e88447ab9c12 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -705,6 +705,7 @@ libsodium libsofthsm libsystemd libtdsodbc +libxdp libyaml libzmq lightningstream From 265ece9c2de43073a9781596089925fe4f3fc80e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 25 Jan 2024 12:32:09 +0100 Subject: [PATCH 60/65] dnsdist: Properly detect whether `bpf_xdp_query` is available It was added in libbpf 0.7 and EL8 only has 0.5, sadly. --- pdns/dnsdistdist/m4/pdns_with_xsk.m4 | 7 +++++++ pdns/xsk.cc | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 index 32cee2707765..a8faf138063c 100644 --- a/pdns/dnsdistdist/m4/pdns_with_xsk.m4 +++ b/pdns/dnsdistdist/m4/pdns_with_xsk.m4 @@ -14,6 +14,13 @@ AC_DEFUN([PDNS_WITH_XSK],[ ], [:]) PKG_CHECK_MODULES([BPF], [libbpf], [ AC_DEFINE([HAVE_BPF], [1], [Define to 1 if you have the BPF library]) + save_CFLAGS=$CFLAGS + save_LIBS=$LIBS + CFLAGS="$BPF_CFLAGS $CFLAGS" + LIBS="$BPF_LIBS $LIBS" + AC_CHECK_FUNCS([bpf_xdp_query]) + CFLAGS=$save_CFLAGS + LIBS=$save_LIBS ], [:]) ]) ]) diff --git a/pdns/xsk.cc b/pdns/xsk.cc index 7c6d67e1bd3b..ee05b3a37a6c 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -472,9 +472,10 @@ std::string XskSocket::getMetrics() const [[nodiscard]] std::string XskSocket::getXDPMode() const { +#ifdef HAVE_BPF_XDP_QUERY unsigned int itfIdx = if_nametoindex(ifName.c_str()); if (itfIdx == 0) { - return {}; + return "unable to get interface index"; } bpf_xdp_query_opts info{}; info.sz = sizeof(info); @@ -491,6 +492,9 @@ std::string XskSocket::getMetrics() const default: return "unknown"; } +#else /* HAVE_BPF_XDP_QUERY */ + return "undetected"; +#endif /* HAVE_BPF_XDP_QUERY */ } void XskSocket::markAsFree(const XskPacket& packet) From a8c3569cee960fd94aa5b1f6b1fa3967723611b5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 29 Jan 2024 11:12:27 +0100 Subject: [PATCH 61/65] dnsdist: Add a fuzzing target for the XSK code --- fuzzing/corpus/raw-xsk-frames/v4-udp.raw | Bin 0 -> 95 bytes pdns/dnsdist.cc | 2 +- pdns/dnsdistdist/Makefile.am | 21 +++++++- pdns/dnsdistdist/fuzz_xsk.cc | 58 +++++++++++++++++++++++ pdns/xsk.cc | 29 ++++-------- pdns/xsk.hh | 6 +-- 6 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 fuzzing/corpus/raw-xsk-frames/v4-udp.raw create mode 100644 pdns/dnsdistdist/fuzz_xsk.cc diff --git a/fuzzing/corpus/raw-xsk-frames/v4-udp.raw b/fuzzing/corpus/raw-xsk-frames/v4-udp.raw new file mode 100644 index 0000000000000000000000000000000000000000..31084b58495120af26618ab5c8be43624b54e450 GIT binary patch literal 95 zcmXT;(;s5@Xhods^QTQ546Y0efz!Pi7#su><{ntVz;%FuiSbb@gDHb;Gn1nhqXGjX q0|+p36y%qu7Nz7BGbiWg0{I|uO_mE_Ngf6+1`b_*)7t25PdNcbwHLks literal 0 HcmV?d00001 diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index c78fb5a650c4..d83066e954b8 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -1896,7 +1896,7 @@ bool XskProcessQuery(ClientState& cs, LocalHolders& holders, XskPacket& packet) ids.origDest = dest; ids.hopLocal = dest; ids.protocol = dnsdist::Protocol::DoUDP; - ids.xskPacketHeader = packet.cloneHeadertoPacketBuffer(); + ids.xskPacketHeader = packet.cloneHeaderToPacketBuffer(); try { bool expectProxyProtocol = false; diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index 1a1e3080c1c3..60e76e0ff81e 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -519,6 +519,11 @@ standalone_fuzz_target_runner.o: standalone_fuzz_target_runner.cc fuzz_targets_programs = \ fuzz_target_dnsdistcache +if HAVE_XSK +fuzz_targets_programs += \ + fuzz_target_xsk +endif + fuzz_targets: $(ARC4RANDOM_LIBS) $(fuzz_targets_programs) bin_PROGRAMS += \ @@ -564,7 +569,21 @@ fuzz_target_dnsdistcache_DEPENDENCIES = $(fuzz_targets_deps) fuzz_target_dnsdistcache_LDFLAGS = $(fuzz_targets_ldflags) fuzz_target_dnsdistcache_LDADD = $(fuzz_targets_libs) -endif +if HAVE_XSK +fuzz_target_xsk_SOURCES = \ + dnslabeltext.cc \ + dnsname.cc dnsname.hh \ + fuzz_xsk.cc \ + gettime.cc gettime.hh \ + iputils.cc iputils.hh \ + misc.cc misc.hh \ + xsk.cc xsk.hh +fuzz_target_xsk_DEPENDENCIES = $(fuzz_targets_deps) +fuzz_target_xsk_LDFLAGS = $(fuzz_targets_ldflags) +fuzz_target_xsk_LDADD = $(fuzz_targets_libs) -lbpf -lxdp +endif # HAVE_XSK + +endif # FUZZ_TARGETS MANPAGES=dnsdist.1 diff --git a/pdns/dnsdistdist/fuzz_xsk.cc b/pdns/dnsdistdist/fuzz_xsk.cc new file mode 100644 index 000000000000..4c67cf45c2f7 --- /dev/null +++ b/pdns/dnsdistdist/fuzz_xsk.cc @@ -0,0 +1,58 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "xsk.hh" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ +#ifdef HAVE_XSK + if (size > XskSocket::getFrameSize()) { + return 0; + } + + try { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): packet data is usually mutable + XskPacket packet(const_cast(data), size, size); + if (packet.parse(false)) { + const auto& dest = packet.getToAddr(); + const auto& orig = packet.getFromAddr(); + const auto* payload = packet.getPayloadData(); + auto capacity = packet.getCapacity(); + auto length = packet.getDataLen(); + auto frameLen = packet.getFrameLen(); + auto header = packet.cloneHeaderToPacketBuffer(); + auto buffer = packet.clonePacketBuffer(); + (void) dest; + (void) orig; + (void) payload; + (void) capacity; + (void) length; + (void) frameLen; + } + } + catch (const std::exception& e) { + } +#endif /* HAVE_XSK */ + return 0; +} diff --git a/pdns/xsk.cc b/pdns/xsk.cc index ee05b3a37a6c..d0cb88513a78 100644 --- a/pdns/xsk.cc +++ b/pdns/xsk.cc @@ -150,12 +150,12 @@ XskSocket::XskSocket(size_t frameNum_, std::string ifName_, uint32_t queue_id, c uniqueEmptyFrameOffset.reserve(frameNum); { - for (uint64_t i = 0; i < frameNum; i++) { - uniqueEmptyFrameOffset.push_back(i * frameSize + XDP_PACKET_HEADROOM); + for (uint64_t idx = 0; idx < frameNum; idx++) { + uniqueEmptyFrameOffset.push_back(idx * frameSize + XDP_PACKET_HEADROOM); #ifdef DEBUG_UMEM { auto umems = s_umems.lock(); - (*umems)[i * frameSize + XDP_PACKET_HEADROOM] = UmemEntryStatus(); + (*umems)[idx * frameSize + XDP_PACKET_HEADROOM] = UmemEntryStatus(); } #endif /* DEBUG_UMEM */ } @@ -479,7 +479,7 @@ std::string XskSocket::getMetrics() const } bpf_xdp_query_opts info{}; info.sz = sizeof(info); - int ret = bpf_xdp_query(itfIdx, 0, &info); + int ret = bpf_xdp_query(static_cast(itfIdx), 0, &info); if (ret != 0) { return {}; } @@ -552,7 +552,6 @@ void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept [[nodiscard]] iphdr XskPacket::getIPv4Header() const noexcept { iphdr ipv4Header{}; - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv4Header))); assert(!v6); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) @@ -562,7 +561,6 @@ void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept { - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(iphdr))); assert(!v6); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) @@ -572,7 +570,6 @@ void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept [[nodiscard]] ipv6hdr XskPacket::getIPv6Header() const noexcept { ipv6hdr ipv6Header{}; - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); assert(v6); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) @@ -582,7 +579,6 @@ void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept { - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); assert(v6); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) @@ -592,7 +588,6 @@ void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept [[nodiscard]] udphdr XskPacket::getUDPHeader() const noexcept { udphdr udpHeader{}; - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) memcpy(&udpHeader, frame + getL4HeaderOffset(), sizeof(udpHeader)); @@ -784,19 +779,13 @@ PacketBuffer XskPacket::clonePacketBuffer() const { const auto size = getDataSize(); PacketBuffer tmp(size); - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - memcpy(tmp.data(), frame + getDataOffset(), size); + if (size > 0) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + memcpy(tmp.data(), frame + getDataOffset(), size); + } return tmp; } -void XskPacket::cloneIntoPacketBuffer(PacketBuffer& buffer) const -{ - const auto size = getDataSize(); - buffer.resize(size); - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - memcpy(buffer.data(), frame + getDataOffset(), size); -} - bool XskPacket::setPayload(const PacketBuffer& buf) { const auto bufSize = buf.size(); @@ -1094,7 +1083,7 @@ void XskPacket::setHeader(PacketBuffer& buf) } } -PacketBuffer XskPacket::cloneHeadertoPacketBuffer() const +PacketBuffer XskPacket::cloneHeaderToPacketBuffer() const { const auto size = getFrameLen() - getDataSize(); PacketBuffer tmp(size); diff --git a/pdns/xsk.hh b/pdns/xsk.hh index 3b16717989f9..53b884155a97 100644 --- a/pdns/xsk.hh +++ b/pdns/xsk.hh @@ -21,6 +21,7 @@ */ #pragma once +#include "config.h" #ifdef HAVE_XSK #include @@ -54,9 +55,9 @@ class XskPacket; class XskWorker; class XskSocket; +#ifdef HAVE_XSK using MACAddr = std::array; -#ifdef HAVE_XSK using XskPacketPtr = std::unique_ptr; // We use an XskSocket to manage an AF_XDP Socket corresponding to a NIC queue. @@ -251,8 +252,7 @@ public: [[nodiscard]] uint32_t getDataLen() const noexcept; [[nodiscard]] uint32_t getFrameLen() const noexcept; [[nodiscard]] PacketBuffer clonePacketBuffer() const; - void cloneIntoPacketBuffer(PacketBuffer& buffer) const; - [[nodiscard]] PacketBuffer cloneHeadertoPacketBuffer() const; + [[nodiscard]] PacketBuffer cloneHeaderToPacketBuffer() const; void setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept; bool setPayload(const PacketBuffer& buf); void rewrite() noexcept; From 7478036108eff737f163066ec23b3e2fd5051913 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 29 Jan 2024 11:15:16 +0100 Subject: [PATCH 62/65] dnsdist: Fix formatting of fuzz_xsk.cc --- pdns/dnsdistdist/fuzz_xsk.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pdns/dnsdistdist/fuzz_xsk.cc b/pdns/dnsdistdist/fuzz_xsk.cc index 4c67cf45c2f7..7066edc3e947 100644 --- a/pdns/dnsdistdist/fuzz_xsk.cc +++ b/pdns/dnsdistdist/fuzz_xsk.cc @@ -43,12 +43,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) auto frameLen = packet.getFrameLen(); auto header = packet.cloneHeaderToPacketBuffer(); auto buffer = packet.clonePacketBuffer(); - (void) dest; - (void) orig; - (void) payload; - (void) capacity; - (void) length; - (void) frameLen; + (void)dest; + (void)orig; + (void)payload; + (void)capacity; + (void)length; + (void)frameLen; } } catch (const std::exception& e) { From dd69e52f8a38779a62529640c2519bc45a417197 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 29 Jan 2024 16:41:10 +0100 Subject: [PATCH 63/65] dnsdist: Remove symbolic links --- pdns/dnsdistdist/dnsdist-cache.cc | 1 - pdns/dnsdistdist/dnsdist-cache.hh | 1 - pdns/dnsdistdist/dnsdist-carbon.cc | 1 - pdns/dnsdistdist/dnsdist-console.cc | 1 - pdns/dnsdistdist/dnsdist-console.hh | 1 - pdns/dnsdistdist/dnsdist-dnscrypt.cc | 1 - pdns/dnsdistdist/dnsdist-doh-common.hh | 1 - pdns/dnsdistdist/dnsdist-dynblocks.hh | 1 - pdns/dnsdistdist/dnsdist-dynbpf.cc | 1 - pdns/dnsdistdist/dnsdist-dynbpf.hh | 1 - pdns/dnsdistdist/dnsdist-ecs.cc | 1 - pdns/dnsdistdist/dnsdist-ecs.hh | 1 - pdns/dnsdistdist/dnsdist-idstate.hh | 1 - pdns/dnsdistdist/dnsdist-lbpolicies.hh | 1 - pdns/dnsdistdist/dnsdist-lua-actions.cc | 1 - pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc | 1 - pdns/dnsdistdist/dnsdist-lua-bindings.cc | 1 - pdns/dnsdistdist/dnsdist-lua-inspection.cc | 1 - pdns/dnsdistdist/dnsdist-lua-rules.cc | 1 - pdns/dnsdistdist/dnsdist-lua-vars.cc | 1 - pdns/dnsdistdist/dnsdist-lua.cc | 1 - pdns/dnsdistdist/dnsdist-lua.hh | 1 - pdns/dnsdistdist/dnsdist-protobuf.cc | 1 - pdns/dnsdistdist/dnsdist-protobuf.hh | 1 - pdns/dnsdistdist/dnsdist-protocols.cc | 1 - pdns/dnsdistdist/dnsdist-protocols.hh | 1 - pdns/dnsdistdist/dnsdist-rings.cc | 1 - pdns/dnsdistdist/dnsdist-rings.hh | 1 - pdns/dnsdistdist/dnsdist-snmp.cc | 1 - pdns/dnsdistdist/dnsdist-snmp.hh | 1 - pdns/dnsdistdist/dnsdist-tcp.cc | 1 - pdns/dnsdistdist/dnsdist-web.cc | 1 - pdns/dnsdistdist/dnsdist-xpf.cc | 1 - pdns/dnsdistdist/dnsdist-xpf.hh | 1 - pdns/dnsdistdist/dnsdist.cc | 1 - pdns/dnsdistdist/dnsdist.hh | 1 - pdns/dnsdistdist/test-dnsdist_cc.cc | 1 - pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc | 1 - 38 files changed, 38 deletions(-) delete mode 120000 pdns/dnsdistdist/dnsdist-cache.cc delete mode 120000 pdns/dnsdistdist/dnsdist-cache.hh delete mode 120000 pdns/dnsdistdist/dnsdist-carbon.cc delete mode 120000 pdns/dnsdistdist/dnsdist-console.cc delete mode 120000 pdns/dnsdistdist/dnsdist-console.hh delete mode 120000 pdns/dnsdistdist/dnsdist-dnscrypt.cc delete mode 120000 pdns/dnsdistdist/dnsdist-doh-common.hh delete mode 120000 pdns/dnsdistdist/dnsdist-dynblocks.hh delete mode 120000 pdns/dnsdistdist/dnsdist-dynbpf.cc delete mode 120000 pdns/dnsdistdist/dnsdist-dynbpf.hh delete mode 120000 pdns/dnsdistdist/dnsdist-ecs.cc delete mode 120000 pdns/dnsdistdist/dnsdist-ecs.hh delete mode 120000 pdns/dnsdistdist/dnsdist-idstate.hh delete mode 120000 pdns/dnsdistdist/dnsdist-lbpolicies.hh delete mode 120000 pdns/dnsdistdist/dnsdist-lua-actions.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua-bindings.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua-inspection.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua-rules.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua-vars.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua.cc delete mode 120000 pdns/dnsdistdist/dnsdist-lua.hh delete mode 120000 pdns/dnsdistdist/dnsdist-protobuf.cc delete mode 120000 pdns/dnsdistdist/dnsdist-protobuf.hh delete mode 120000 pdns/dnsdistdist/dnsdist-protocols.cc delete mode 120000 pdns/dnsdistdist/dnsdist-protocols.hh delete mode 120000 pdns/dnsdistdist/dnsdist-rings.cc delete mode 120000 pdns/dnsdistdist/dnsdist-rings.hh delete mode 120000 pdns/dnsdistdist/dnsdist-snmp.cc delete mode 120000 pdns/dnsdistdist/dnsdist-snmp.hh delete mode 120000 pdns/dnsdistdist/dnsdist-tcp.cc delete mode 120000 pdns/dnsdistdist/dnsdist-web.cc delete mode 120000 pdns/dnsdistdist/dnsdist-xpf.cc delete mode 120000 pdns/dnsdistdist/dnsdist-xpf.hh delete mode 120000 pdns/dnsdistdist/dnsdist.cc delete mode 120000 pdns/dnsdistdist/dnsdist.hh delete mode 120000 pdns/dnsdistdist/test-dnsdist_cc.cc delete mode 120000 pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc diff --git a/pdns/dnsdistdist/dnsdist-cache.cc b/pdns/dnsdistdist/dnsdist-cache.cc deleted file mode 120000 index 9730d7198b60..000000000000 --- a/pdns/dnsdistdist/dnsdist-cache.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-cache.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-cache.hh b/pdns/dnsdistdist/dnsdist-cache.hh deleted file mode 120000 index 84794d806927..000000000000 --- a/pdns/dnsdistdist/dnsdist-cache.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-cache.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-carbon.cc b/pdns/dnsdistdist/dnsdist-carbon.cc deleted file mode 120000 index dce7a30b311e..000000000000 --- a/pdns/dnsdistdist/dnsdist-carbon.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-carbon.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-console.cc b/pdns/dnsdistdist/dnsdist-console.cc deleted file mode 120000 index 402fcdc9d8c2..000000000000 --- a/pdns/dnsdistdist/dnsdist-console.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-console.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-console.hh b/pdns/dnsdistdist/dnsdist-console.hh deleted file mode 120000 index c92b2ad52737..000000000000 --- a/pdns/dnsdistdist/dnsdist-console.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-console.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-dnscrypt.cc b/pdns/dnsdistdist/dnsdist-dnscrypt.cc deleted file mode 120000 index 9f90566bff04..000000000000 --- a/pdns/dnsdistdist/dnsdist-dnscrypt.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-dnscrypt.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-doh-common.hh b/pdns/dnsdistdist/dnsdist-doh-common.hh deleted file mode 120000 index 56920844943f..000000000000 --- a/pdns/dnsdistdist/dnsdist-doh-common.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-doh-common.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-dynblocks.hh b/pdns/dnsdistdist/dnsdist-dynblocks.hh deleted file mode 120000 index 73fa1f7f19f3..000000000000 --- a/pdns/dnsdistdist/dnsdist-dynblocks.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-dynblocks.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-dynbpf.cc b/pdns/dnsdistdist/dnsdist-dynbpf.cc deleted file mode 120000 index 6463b56fc709..000000000000 --- a/pdns/dnsdistdist/dnsdist-dynbpf.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-dynbpf.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-dynbpf.hh b/pdns/dnsdistdist/dnsdist-dynbpf.hh deleted file mode 120000 index 9ac055ffc735..000000000000 --- a/pdns/dnsdistdist/dnsdist-dynbpf.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-dynbpf.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-ecs.cc b/pdns/dnsdistdist/dnsdist-ecs.cc deleted file mode 120000 index 9bf0156b00d1..000000000000 --- a/pdns/dnsdistdist/dnsdist-ecs.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-ecs.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-ecs.hh b/pdns/dnsdistdist/dnsdist-ecs.hh deleted file mode 120000 index bbd2156b59e4..000000000000 --- a/pdns/dnsdistdist/dnsdist-ecs.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-ecs.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-idstate.hh b/pdns/dnsdistdist/dnsdist-idstate.hh deleted file mode 120000 index 44f6de43450b..000000000000 --- a/pdns/dnsdistdist/dnsdist-idstate.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-idstate.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lbpolicies.hh b/pdns/dnsdistdist/dnsdist-lbpolicies.hh deleted file mode 120000 index 020353fc0d8a..000000000000 --- a/pdns/dnsdistdist/dnsdist-lbpolicies.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lbpolicies.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-actions.cc b/pdns/dnsdistdist/dnsdist-lua-actions.cc deleted file mode 120000 index 7ad46192b336..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-actions.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-actions.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc deleted file mode 120000 index 93b217138edc..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-bindings-dnsquestion.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings.cc b/pdns/dnsdistdist/dnsdist-lua-bindings.cc deleted file mode 120000 index 014a4be27466..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-bindings.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-bindings.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-inspection.cc b/pdns/dnsdistdist/dnsdist-lua-inspection.cc deleted file mode 120000 index ae053d658a0f..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-inspection.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-inspection.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-rules.cc b/pdns/dnsdistdist/dnsdist-lua-rules.cc deleted file mode 120000 index d01f6e223220..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-rules.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-rules.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua-vars.cc b/pdns/dnsdistdist/dnsdist-lua-vars.cc deleted file mode 120000 index ed3c358ea43e..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua-vars.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua-vars.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua.cc b/pdns/dnsdistdist/dnsdist-lua.cc deleted file mode 120000 index d3eb31e6bad7..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-lua.hh b/pdns/dnsdistdist/dnsdist-lua.hh deleted file mode 120000 index fab25c4c0c94..000000000000 --- a/pdns/dnsdistdist/dnsdist-lua.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-lua.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-protobuf.cc b/pdns/dnsdistdist/dnsdist-protobuf.cc deleted file mode 120000 index a10089548ebf..000000000000 --- a/pdns/dnsdistdist/dnsdist-protobuf.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-protobuf.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-protobuf.hh b/pdns/dnsdistdist/dnsdist-protobuf.hh deleted file mode 120000 index dd11fbf3db31..000000000000 --- a/pdns/dnsdistdist/dnsdist-protobuf.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-protobuf.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-protocols.cc b/pdns/dnsdistdist/dnsdist-protocols.cc deleted file mode 120000 index eb08cd3869eb..000000000000 --- a/pdns/dnsdistdist/dnsdist-protocols.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-protocols.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-protocols.hh b/pdns/dnsdistdist/dnsdist-protocols.hh deleted file mode 120000 index cb9d2fd79c7b..000000000000 --- a/pdns/dnsdistdist/dnsdist-protocols.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-protocols.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-rings.cc b/pdns/dnsdistdist/dnsdist-rings.cc deleted file mode 120000 index d6e222bea4a4..000000000000 --- a/pdns/dnsdistdist/dnsdist-rings.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-rings.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-rings.hh b/pdns/dnsdistdist/dnsdist-rings.hh deleted file mode 120000 index 4c33d6dbb173..000000000000 --- a/pdns/dnsdistdist/dnsdist-rings.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-rings.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-snmp.cc b/pdns/dnsdistdist/dnsdist-snmp.cc deleted file mode 120000 index 49f9feda64e6..000000000000 --- a/pdns/dnsdistdist/dnsdist-snmp.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-snmp.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-snmp.hh b/pdns/dnsdistdist/dnsdist-snmp.hh deleted file mode 120000 index ffa4710635e2..000000000000 --- a/pdns/dnsdistdist/dnsdist-snmp.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-snmp.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-tcp.cc b/pdns/dnsdistdist/dnsdist-tcp.cc deleted file mode 120000 index 58e398a5a0b3..000000000000 --- a/pdns/dnsdistdist/dnsdist-tcp.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-tcp.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-web.cc b/pdns/dnsdistdist/dnsdist-web.cc deleted file mode 120000 index 062182f6d4ee..000000000000 --- a/pdns/dnsdistdist/dnsdist-web.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-web.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-xpf.cc b/pdns/dnsdistdist/dnsdist-xpf.cc deleted file mode 120000 index 66fd88d61623..000000000000 --- a/pdns/dnsdistdist/dnsdist-xpf.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-xpf.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-xpf.hh b/pdns/dnsdistdist/dnsdist-xpf.hh deleted file mode 120000 index c2b75e2df4ef..000000000000 --- a/pdns/dnsdistdist/dnsdist-xpf.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist-xpf.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist.cc b/pdns/dnsdistdist/dnsdist.cc deleted file mode 120000 index dc104b27f9f1..000000000000 --- a/pdns/dnsdistdist/dnsdist.cc +++ /dev/null @@ -1 +0,0 @@ -../dnsdist.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist.hh b/pdns/dnsdistdist/dnsdist.hh deleted file mode 120000 index 2a87e4f9d67d..000000000000 --- a/pdns/dnsdistdist/dnsdist.hh +++ /dev/null @@ -1 +0,0 @@ -../dnsdist.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/test-dnsdist_cc.cc b/pdns/dnsdistdist/test-dnsdist_cc.cc deleted file mode 120000 index ae06da2233d4..000000000000 --- a/pdns/dnsdistdist/test-dnsdist_cc.cc +++ /dev/null @@ -1 +0,0 @@ -../test-dnsdist_cc.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc b/pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc deleted file mode 120000 index dde3be0e38d4..000000000000 --- a/pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc +++ /dev/null @@ -1 +0,0 @@ -../test-dnsdistpacketcache_cc.cc \ No newline at end of file From ac47e17754b8ea3aafd0d22bb1fca46d7d24184e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 29 Jan 2024 16:42:06 +0100 Subject: [PATCH 64/65] dnsdist: Move dnsdist-only files from pdns/ to pdns/dnsdistdist --- pdns/{ => dnsdistdist}/dnsdist-cache.cc | 0 pdns/{ => dnsdistdist}/dnsdist-cache.hh | 0 pdns/{ => dnsdistdist}/dnsdist-carbon.cc | 0 pdns/{ => dnsdistdist}/dnsdist-console.cc | 0 pdns/{ => dnsdistdist}/dnsdist-console.hh | 0 pdns/{ => dnsdistdist}/dnsdist-dnscrypt.cc | 0 pdns/{ => dnsdistdist}/dnsdist-doh-common.hh | 0 pdns/{ => dnsdistdist}/dnsdist-dynblocks.hh | 0 pdns/{ => dnsdistdist}/dnsdist-dynbpf.cc | 0 pdns/{ => dnsdistdist}/dnsdist-dynbpf.hh | 0 pdns/{ => dnsdistdist}/dnsdist-ecs.cc | 0 pdns/{ => dnsdistdist}/dnsdist-ecs.hh | 0 pdns/{ => dnsdistdist}/dnsdist-idstate.hh | 0 pdns/{ => dnsdistdist}/dnsdist-lbpolicies.hh | 0 pdns/{ => dnsdistdist}/dnsdist-lua-actions.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua-bindings-dnsquestion.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua-bindings.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua-inspection.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua-rules.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua-vars.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua.cc | 0 pdns/{ => dnsdistdist}/dnsdist-lua.hh | 0 pdns/{ => dnsdistdist}/dnsdist-protobuf.cc | 0 pdns/{ => dnsdistdist}/dnsdist-protobuf.hh | 0 pdns/{ => dnsdistdist}/dnsdist-protocols.cc | 0 pdns/{ => dnsdistdist}/dnsdist-protocols.hh | 0 pdns/{ => dnsdistdist}/dnsdist-rings.cc | 0 pdns/{ => dnsdistdist}/dnsdist-rings.hh | 0 pdns/{ => dnsdistdist}/dnsdist-snmp.cc | 0 pdns/{ => dnsdistdist}/dnsdist-snmp.hh | 0 pdns/{ => dnsdistdist}/dnsdist-tcp.cc | 0 pdns/{ => dnsdistdist}/dnsdist-web.cc | 0 pdns/{ => dnsdistdist}/dnsdist-xpf.cc | 0 pdns/{ => dnsdistdist}/dnsdist-xpf.hh | 0 pdns/{ => dnsdistdist}/dnsdist.cc | 0 pdns/{ => dnsdistdist}/dnsdist.hh | 0 pdns/{ => dnsdistdist}/test-dnsdist_cc.cc | 0 pdns/{ => dnsdistdist}/test-dnsdistpacketcache_cc.cc | 0 38 files changed, 0 insertions(+), 0 deletions(-) rename pdns/{ => dnsdistdist}/dnsdist-cache.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-cache.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-carbon.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-console.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-console.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-dnscrypt.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-doh-common.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-dynblocks.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-dynbpf.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-dynbpf.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-ecs.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-ecs.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-idstate.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-lbpolicies.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-actions.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-bindings-dnsquestion.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-bindings.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-inspection.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-rules.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua-vars.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-lua.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-protobuf.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-protobuf.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-protocols.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-protocols.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-rings.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-rings.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-snmp.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-snmp.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist-tcp.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-web.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-xpf.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist-xpf.hh (100%) rename pdns/{ => dnsdistdist}/dnsdist.cc (100%) rename pdns/{ => dnsdistdist}/dnsdist.hh (100%) rename pdns/{ => dnsdistdist}/test-dnsdist_cc.cc (100%) rename pdns/{ => dnsdistdist}/test-dnsdistpacketcache_cc.cc (100%) diff --git a/pdns/dnsdist-cache.cc b/pdns/dnsdistdist/dnsdist-cache.cc similarity index 100% rename from pdns/dnsdist-cache.cc rename to pdns/dnsdistdist/dnsdist-cache.cc diff --git a/pdns/dnsdist-cache.hh b/pdns/dnsdistdist/dnsdist-cache.hh similarity index 100% rename from pdns/dnsdist-cache.hh rename to pdns/dnsdistdist/dnsdist-cache.hh diff --git a/pdns/dnsdist-carbon.cc b/pdns/dnsdistdist/dnsdist-carbon.cc similarity index 100% rename from pdns/dnsdist-carbon.cc rename to pdns/dnsdistdist/dnsdist-carbon.cc diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdistdist/dnsdist-console.cc similarity index 100% rename from pdns/dnsdist-console.cc rename to pdns/dnsdistdist/dnsdist-console.cc diff --git a/pdns/dnsdist-console.hh b/pdns/dnsdistdist/dnsdist-console.hh similarity index 100% rename from pdns/dnsdist-console.hh rename to pdns/dnsdistdist/dnsdist-console.hh diff --git a/pdns/dnsdist-dnscrypt.cc b/pdns/dnsdistdist/dnsdist-dnscrypt.cc similarity index 100% rename from pdns/dnsdist-dnscrypt.cc rename to pdns/dnsdistdist/dnsdist-dnscrypt.cc diff --git a/pdns/dnsdist-doh-common.hh b/pdns/dnsdistdist/dnsdist-doh-common.hh similarity index 100% rename from pdns/dnsdist-doh-common.hh rename to pdns/dnsdistdist/dnsdist-doh-common.hh diff --git a/pdns/dnsdist-dynblocks.hh b/pdns/dnsdistdist/dnsdist-dynblocks.hh similarity index 100% rename from pdns/dnsdist-dynblocks.hh rename to pdns/dnsdistdist/dnsdist-dynblocks.hh diff --git a/pdns/dnsdist-dynbpf.cc b/pdns/dnsdistdist/dnsdist-dynbpf.cc similarity index 100% rename from pdns/dnsdist-dynbpf.cc rename to pdns/dnsdistdist/dnsdist-dynbpf.cc diff --git a/pdns/dnsdist-dynbpf.hh b/pdns/dnsdistdist/dnsdist-dynbpf.hh similarity index 100% rename from pdns/dnsdist-dynbpf.hh rename to pdns/dnsdistdist/dnsdist-dynbpf.hh diff --git a/pdns/dnsdist-ecs.cc b/pdns/dnsdistdist/dnsdist-ecs.cc similarity index 100% rename from pdns/dnsdist-ecs.cc rename to pdns/dnsdistdist/dnsdist-ecs.cc diff --git a/pdns/dnsdist-ecs.hh b/pdns/dnsdistdist/dnsdist-ecs.hh similarity index 100% rename from pdns/dnsdist-ecs.hh rename to pdns/dnsdistdist/dnsdist-ecs.hh diff --git a/pdns/dnsdist-idstate.hh b/pdns/dnsdistdist/dnsdist-idstate.hh similarity index 100% rename from pdns/dnsdist-idstate.hh rename to pdns/dnsdistdist/dnsdist-idstate.hh diff --git a/pdns/dnsdist-lbpolicies.hh b/pdns/dnsdistdist/dnsdist-lbpolicies.hh similarity index 100% rename from pdns/dnsdist-lbpolicies.hh rename to pdns/dnsdistdist/dnsdist-lbpolicies.hh diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdistdist/dnsdist-lua-actions.cc similarity index 100% rename from pdns/dnsdist-lua-actions.cc rename to pdns/dnsdistdist/dnsdist-lua-actions.cc diff --git a/pdns/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc similarity index 100% rename from pdns/dnsdist-lua-bindings-dnsquestion.cc rename to pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdistdist/dnsdist-lua-bindings.cc similarity index 100% rename from pdns/dnsdist-lua-bindings.cc rename to pdns/dnsdistdist/dnsdist-lua-bindings.cc diff --git a/pdns/dnsdist-lua-inspection.cc b/pdns/dnsdistdist/dnsdist-lua-inspection.cc similarity index 100% rename from pdns/dnsdist-lua-inspection.cc rename to pdns/dnsdistdist/dnsdist-lua-inspection.cc diff --git a/pdns/dnsdist-lua-rules.cc b/pdns/dnsdistdist/dnsdist-lua-rules.cc similarity index 100% rename from pdns/dnsdist-lua-rules.cc rename to pdns/dnsdistdist/dnsdist-lua-rules.cc diff --git a/pdns/dnsdist-lua-vars.cc b/pdns/dnsdistdist/dnsdist-lua-vars.cc similarity index 100% rename from pdns/dnsdist-lua-vars.cc rename to pdns/dnsdistdist/dnsdist-lua-vars.cc diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdistdist/dnsdist-lua.cc similarity index 100% rename from pdns/dnsdist-lua.cc rename to pdns/dnsdistdist/dnsdist-lua.cc diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdistdist/dnsdist-lua.hh similarity index 100% rename from pdns/dnsdist-lua.hh rename to pdns/dnsdistdist/dnsdist-lua.hh diff --git a/pdns/dnsdist-protobuf.cc b/pdns/dnsdistdist/dnsdist-protobuf.cc similarity index 100% rename from pdns/dnsdist-protobuf.cc rename to pdns/dnsdistdist/dnsdist-protobuf.cc diff --git a/pdns/dnsdist-protobuf.hh b/pdns/dnsdistdist/dnsdist-protobuf.hh similarity index 100% rename from pdns/dnsdist-protobuf.hh rename to pdns/dnsdistdist/dnsdist-protobuf.hh diff --git a/pdns/dnsdist-protocols.cc b/pdns/dnsdistdist/dnsdist-protocols.cc similarity index 100% rename from pdns/dnsdist-protocols.cc rename to pdns/dnsdistdist/dnsdist-protocols.cc diff --git a/pdns/dnsdist-protocols.hh b/pdns/dnsdistdist/dnsdist-protocols.hh similarity index 100% rename from pdns/dnsdist-protocols.hh rename to pdns/dnsdistdist/dnsdist-protocols.hh diff --git a/pdns/dnsdist-rings.cc b/pdns/dnsdistdist/dnsdist-rings.cc similarity index 100% rename from pdns/dnsdist-rings.cc rename to pdns/dnsdistdist/dnsdist-rings.cc diff --git a/pdns/dnsdist-rings.hh b/pdns/dnsdistdist/dnsdist-rings.hh similarity index 100% rename from pdns/dnsdist-rings.hh rename to pdns/dnsdistdist/dnsdist-rings.hh diff --git a/pdns/dnsdist-snmp.cc b/pdns/dnsdistdist/dnsdist-snmp.cc similarity index 100% rename from pdns/dnsdist-snmp.cc rename to pdns/dnsdistdist/dnsdist-snmp.cc diff --git a/pdns/dnsdist-snmp.hh b/pdns/dnsdistdist/dnsdist-snmp.hh similarity index 100% rename from pdns/dnsdist-snmp.hh rename to pdns/dnsdistdist/dnsdist-snmp.hh diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdistdist/dnsdist-tcp.cc similarity index 100% rename from pdns/dnsdist-tcp.cc rename to pdns/dnsdistdist/dnsdist-tcp.cc diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdistdist/dnsdist-web.cc similarity index 100% rename from pdns/dnsdist-web.cc rename to pdns/dnsdistdist/dnsdist-web.cc diff --git a/pdns/dnsdist-xpf.cc b/pdns/dnsdistdist/dnsdist-xpf.cc similarity index 100% rename from pdns/dnsdist-xpf.cc rename to pdns/dnsdistdist/dnsdist-xpf.cc diff --git a/pdns/dnsdist-xpf.hh b/pdns/dnsdistdist/dnsdist-xpf.hh similarity index 100% rename from pdns/dnsdist-xpf.hh rename to pdns/dnsdistdist/dnsdist-xpf.hh diff --git a/pdns/dnsdist.cc b/pdns/dnsdistdist/dnsdist.cc similarity index 100% rename from pdns/dnsdist.cc rename to pdns/dnsdistdist/dnsdist.cc diff --git a/pdns/dnsdist.hh b/pdns/dnsdistdist/dnsdist.hh similarity index 100% rename from pdns/dnsdist.hh rename to pdns/dnsdistdist/dnsdist.hh diff --git a/pdns/test-dnsdist_cc.cc b/pdns/dnsdistdist/test-dnsdist_cc.cc similarity index 100% rename from pdns/test-dnsdist_cc.cc rename to pdns/dnsdistdist/test-dnsdist_cc.cc diff --git a/pdns/test-dnsdistpacketcache_cc.cc b/pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc similarity index 100% rename from pdns/test-dnsdistpacketcache_cc.cc rename to pdns/dnsdistdist/test-dnsdistpacketcache_cc.cc From c40824f0b840efea4ea0c4adb54f1975bf1c3413 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 29 Jan 2024 16:46:50 +0100 Subject: [PATCH 65/65] dnsdist: Format code --- .not-formatted | 27 - pdns/dnsdistdist/dnsdist-cache.cc | 33 +- pdns/dnsdistdist/dnsdist-cache.hh | 11 +- pdns/dnsdistdist/dnsdist-console.cc | 859 +++++------ pdns/dnsdistdist/dnsdist-console.hh | 8 +- pdns/dnsdistdist/dnsdist-dynbpf.cc | 8 +- pdns/dnsdistdist/dnsdist-dynbpf.hh | 19 +- pdns/dnsdistdist/dnsdist-ecs.cc | 146 +- pdns/dnsdistdist/dnsdist-ecs.hh | 9 +- pdns/dnsdistdist/dnsdist-lbpolicies.hh | 15 +- .../dnsdist-lua-bindings-dnsquestion.cc | 631 ++++---- pdns/dnsdistdist/dnsdist-lua-bindings.cc | 929 ++++++------ pdns/dnsdistdist/dnsdist-lua-inspection.cc | 1147 +++++++------- pdns/dnsdistdist/dnsdist-lua-rules.cc | 476 +++--- pdns/dnsdistdist/dnsdist-lua-vars.cc | 97 +- pdns/dnsdistdist/dnsdist-lua.hh | 50 +- pdns/dnsdistdist/dnsdist-rings.cc | 42 +- pdns/dnsdistdist/dnsdist-rings.hh | 10 +- pdns/dnsdistdist/dnsdist-snmp.cc | 182 ++- pdns/dnsdistdist/dnsdist-snmp.hh | 4 +- pdns/dnsdistdist/dnsdist-web.cc | 1314 ++++++++++------- pdns/dnsdistdist/dnsdist-xpf.cc | 2 +- pdns/dnsdistdist/dnsdist-xpf.hh | 1 - pdns/dnsdistdist/dnsdist.cc | 577 ++++---- pdns/dnsdistdist/dnsdist.hh | 195 ++- pdns/dnsdistdist/test-dnsdist_cc.cc | 151 +- .../dnsdistdist/test-dnsdistpacketcache_cc.cc | 118 +- 27 files changed, 3690 insertions(+), 3371 deletions(-) diff --git a/.not-formatted b/.not-formatted index 759460e7fd5b..050ad15f107f 100644 --- a/.not-formatted +++ b/.not-formatted @@ -36,31 +36,6 @@ ./pdns/dnscrypt.cc ./pdns/dnscrypt.hh ./pdns/dnsdemog.cc -./pdns/dnsdist-cache.cc -./pdns/dnsdist-cache.hh -./pdns/dnsdist-console.cc -./pdns/dnsdist-console.hh -./pdns/dnsdist-dynbpf.cc -./pdns/dnsdist-dynbpf.hh -./pdns/dnsdist-ecs.cc -./pdns/dnsdist-ecs.hh -./pdns/dnsdist-lbpolicies.hh -./pdns/dnsdist-lua-bindings-dnsquestion.cc -./pdns/dnsdist-lua-bindings.cc -./pdns/dnsdist-lua-inspection.cc -./pdns/dnsdist-lua-rules.cc -./pdns/dnsdist-lua-vars.cc -./pdns/dnsdist-lua.hh -./pdns/dnsdist-rings.cc -./pdns/dnsdist-rings.hh -./pdns/dnsdist-snmp.cc -./pdns/dnsdist-snmp.hh -./pdns/dnsdist-tcp.cc -./pdns/dnsdist-web.cc -./pdns/dnsdist-xpf.cc -./pdns/dnsdist-xpf.hh -./pdns/dnsdist.cc -./pdns/dnsdist.hh ./pdns/dnsdistdist/connection-management.hh ./pdns/dnsdistdist/dnsdist-backend.cc ./pdns/dnsdistdist/dnsdist-kvs.cc @@ -232,8 +207,6 @@ ./pdns/test-common.hh ./pdns/test-distributor_hh.cc ./pdns/test-dnscrypt_cc.cc -./pdns/test-dnsdist_cc.cc -./pdns/test-dnsdistpacketcache_cc.cc ./pdns/test-dnsname_cc.cc ./pdns/test-dnsparser_cc.cc ./pdns/test-dnsparser_hh.cc diff --git a/pdns/dnsdistdist/dnsdist-cache.cc b/pdns/dnsdistdist/dnsdist-cache.cc index c3b0e75ef68d..62d84b7d631b 100644 --- a/pdns/dnsdistdist/dnsdist-cache.cc +++ b/pdns/dnsdistdist/dnsdist-cache.cc @@ -29,7 +29,8 @@ #include "ednssubnet.hh" #include "packetcache.hh" -DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t tempFailureTTL, uint32_t maxNegativeTTL, uint32_t staleTTL, bool dontAge, uint32_t shards, bool deferrableInsertLock, bool parseECS): d_maxEntries(maxEntries), d_shardCount(shards), d_maxTTL(maxTTL), d_tempFailureTTL(tempFailureTTL), d_maxNegativeTTL(maxNegativeTTL), d_minTTL(minTTL), d_staleTTL(staleTTL), d_dontAge(dontAge), d_deferrableInsertLock(deferrableInsertLock), d_parseECS(parseECS) +DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t tempFailureTTL, uint32_t maxNegativeTTL, uint32_t staleTTL, bool dontAge, uint32_t shards, bool deferrableInsertLock, bool parseECS) : + d_maxEntries(maxEntries), d_shardCount(shards), d_maxTTL(maxTTL), d_tempFailureTTL(tempFailureTTL), d_maxNegativeTTL(maxNegativeTTL), d_minTTL(minTTL), d_staleTTL(staleTTL), d_dontAge(dontAge), d_deferrableInsertLock(deferrableInsertLock), d_parseECS(parseECS) { if (d_maxEntries == 0) { throw std::runtime_error("Trying to create a 0-sized packet-cache"); @@ -83,14 +84,14 @@ bool DNSDistPacketCache::cachedValueMatches(const CacheValue& cachedValue, uint1 return true; } -void DNSDistPacketCache::insertLocked(CacheShard& shard, std::unordered_map& map, uint32_t key, CacheValue& newValue) +void DNSDistPacketCache::insertLocked(CacheShard& shard, std::unordered_map& map, uint32_t key, CacheValue& newValue) { /* check again now that we hold the lock to prevent a race */ if (map.size() >= (d_maxEntries / d_shardCount)) { return; } - std::unordered_map::iterator it; + std::unordered_map::iterator it; bool result; std::tie(it, result) = map.insert({key, newValue}); @@ -227,7 +228,7 @@ bool DNSDistPacketCache::get(DNSQuestion& dq, uint16_t queryId, uint32_t* keyOut return false; } - std::unordered_map::const_iterator it = map->find(key); + std::unordered_map::const_iterator it = map->find(key); if (it == map->end()) { if (recordMiss) { ++d_misses; @@ -298,7 +299,7 @@ bool DNSDistPacketCache::get(DNSQuestion& dq, uint16_t queryId, uint32_t* keyOut if (!stale) { // coverity[store_truncates_time_t] dnsheader_aligned dh_aligned(response.data()); - ageDNSPacket(reinterpret_cast(&response[0]), response.size(), age, dh_aligned); + ageDNSPacket(reinterpret_cast(&response[0]), response.size(), age, dh_aligned); } else { editDNSPacketTTL(reinterpret_cast(&response[0]), response.size(), @@ -330,7 +331,7 @@ size_t DNSDistPacketCache::purgeExpired(size_t upTo, const time_t now) size_t toRemove = map->size() - maxPerShard; - for (auto it = map->begin(); toRemove > 0 && it != map->end(); ) { + for (auto it = map->begin(); toRemove > 0 && it != map->end();) { const CacheValue& value = it->second; if (value.validity <= now) { @@ -338,7 +339,8 @@ size_t DNSDistPacketCache::purgeExpired(size_t upTo, const time_t now) --toRemove; --shard.d_entriesCount; ++removed; - } else { + } + else { ++it; } } @@ -393,14 +395,15 @@ size_t DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype, bo for (auto& shard : d_shards) { auto map = shard.d_map.write_lock(); - for(auto it = map->begin(); it != map->end(); ) { + for (auto it = map->begin(); it != map->end();) { const CacheValue& value = it->second; if ((value.qname == name || (suffixMatch && value.qname.isPartOf(name))) && (qtype == QType::ANY || qtype == value.qtype)) { it = map->erase(it); --shard.d_entriesCount; ++removed; - } else { + } + else { ++it; } } @@ -411,7 +414,7 @@ size_t DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype, bo bool DNSDistPacketCache::isFull() { - return (getSize() >= d_maxEntries); + return (getSize() >= d_maxEntries); } uint64_t DNSDistPacketCache::getSize() @@ -435,11 +438,11 @@ uint32_t DNSDistPacketCache::getKey(const DNSName::string_t& qname, size_t qname uint32_t result = 0; /* skip the query ID */ if (packet.size() < sizeof(dnsheader)) { - throw std::range_error("Computing packet cache key for an invalid packet size (" + std::to_string(packet.size()) +")"); + throw std::range_error("Computing packet cache key for an invalid packet size (" + std::to_string(packet.size()) + ")"); } result = burtle(&packet.at(2), sizeof(dnsheader) - 2, result); - result = burtleCI((const unsigned char*) qname.c_str(), qname.length(), result); + result = burtleCI((const unsigned char*)qname.c_str(), qname.length(), result); if (packet.size() < sizeof(dnsheader) + qnameWireLength) { throw std::range_error("Computing packet cache key for an invalid packet (" + std::to_string(packet.size()) + " < " + std::to_string(sizeof(dnsheader) + qnameWireLength) + ")"); } @@ -452,7 +455,7 @@ uint32_t DNSDistPacketCache::getKey(const DNSName::string_t& qname, size_t qname result = burtle(&packet.at(sizeof(dnsheader) + qnameWireLength), packet.size() - (sizeof(dnsheader) + qnameWireLength), result); } } - result = burtle((const unsigned char*) &receivedOverUDP, sizeof(receivedOverUDP), result); + result = burtle((const unsigned char*)&receivedOverUDP, sizeof(receivedOverUDP), result); return result; } @@ -473,7 +476,7 @@ uint64_t DNSDistPacketCache::getEntriesCount() uint64_t DNSDistPacketCache::dump(int fd) { - auto fp = std::unique_ptr(fdopen(dup(fd), "w"), fclose); + auto fp = std::unique_ptr(fdopen(dup(fd), "w"), fclose); if (fp == nullptr) { return 0; } @@ -499,7 +502,7 @@ uint64_t DNSDistPacketCache::dump(int fd) fprintf(fp.get(), "%s %" PRId64 " %s ; rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 "\n", value.qname.toString().c_str(), static_cast(value.validity - now), QType(value.qtype).toString().c_str(), rcode, entry.first, value.len, value.receivedOverUDP, static_cast(value.added)); } - catch(...) { + catch (...) { fprintf(fp.get(), "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str()); } } diff --git a/pdns/dnsdistdist/dnsdist-cache.hh b/pdns/dnsdistdist/dnsdist-cache.hh index 95667bd4cd56..3db5e6ab1ff4 100644 --- a/pdns/dnsdistdist/dnsdist-cache.hh +++ b/pdns/dnsdistdist/dnsdist-cache.hh @@ -35,13 +35,13 @@ struct DNSQuestion; class DNSDistPacketCache : boost::noncopyable { public: - DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL=86400, uint32_t minTTL=0, uint32_t tempFailureTTL=60, uint32_t maxNegativeTTL=3600, uint32_t staleTTL=60, bool dontAge=false, uint32_t shards=1, bool deferrableInsertLock=true, bool parseECS=false); + DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL = 86400, uint32_t minTTL = 0, uint32_t tempFailureTTL = 60, uint32_t maxNegativeTTL = 3600, uint32_t staleTTL = 60, bool dontAge = false, uint32_t shards = 1, bool deferrableInsertLock = true, bool parseECS = false); void insert(uint32_t key, const boost::optional& subnet, uint16_t queryFlags, bool dnssecOK, const DNSName& qname, uint16_t qtype, uint16_t qclass, const PacketBuffer& response, bool receivedOverUDP, uint8_t rcode, boost::optional tempFailureTTL); bool get(DNSQuestion& dq, uint16_t queryId, uint32_t* keyOut, boost::optional& subnet, bool dnssecOK, bool receivedOverUDP, uint32_t allowExpired = 0, bool skipAging = false, bool truncatedOK = true, bool recordMiss = true); size_t purgeExpired(size_t upTo, const time_t now); - size_t expunge(size_t upTo=0); - size_t expungeByName(const DNSName& name, uint16_t qtype=QType::ANY, bool suffixMatch=false); + size_t expunge(size_t upTo = 0); + size_t expungeByName(const DNSName& name, uint16_t qtype = QType::ANY, bool suffixMatch = false); bool isFull(); string toString(); uint64_t getSize(); @@ -89,7 +89,6 @@ public: static bool getClientSubnet(const PacketBuffer& packet, size_t qnameWireLength, boost::optional& subnet); private: - struct CacheValue { time_t getTTD() const { return validity; } @@ -121,13 +120,13 @@ private: d_map.write_lock()->reserve(maxSize); } - SharedLockGuarded> d_map; + SharedLockGuarded> d_map; std::atomic d_entriesCount{0}; }; bool cachedValueMatches(const CacheValue& cachedValue, uint16_t queryFlags, const DNSName& qname, uint16_t qtype, uint16_t qclass, bool receivedOverUDP, bool dnssecOK, const boost::optional& subnet) const; uint32_t getShardIndex(uint32_t key) const; - void insertLocked(CacheShard& shard, std::unordered_map& map, uint32_t key, CacheValue& newValue); + void insertLocked(CacheShard& shard, std::unordered_map& map, uint32_t key, CacheValue& newValue); std::vector d_shards; std::unordered_set d_optionsToSkip{EDNSOptionCode::COOKIE}; diff --git a/pdns/dnsdistdist/dnsdist-console.cc b/pdns/dnsdistdist/dnsdist-console.cc index 0f9e085735f9..5f338ca3346b 100644 --- a/pdns/dnsdistdist/dnsdist-console.cc +++ b/pdns/dnsdistdist/dnsdist-console.cc @@ -27,7 +27,7 @@ #include #ifdef HAVE_LIBEDIT -#if defined (__OpenBSD__) || defined(__NetBSD__) +#if defined(__OpenBSD__) || defined(__NetBSD__) // If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h #undef __STRICT_ANSI__ #include @@ -47,7 +47,7 @@ #include "threadname.hh" GlobalStateHolder g_consoleACL; -vector > g_confDelta; +vector> g_confDelta; std::string g_consoleKey; bool g_logConsoleConnections{true}; bool g_consoleEnabled{false}; @@ -58,13 +58,15 @@ static ConcurrentConnectionManager s_connManager(100); class ConsoleConnection { public: - ConsoleConnection(const ComboAddress& client, FDWrapper&& fileDesc): d_client(client), d_fileDesc(std::move(fileDesc)) + ConsoleConnection(const ComboAddress& client, FDWrapper&& fileDesc) : + d_client(client), d_fileDesc(std::move(fileDesc)) { if (!s_connManager.registerConnection()) { throw std::runtime_error("Too many concurrent console connections"); } } - ConsoleConnection(ConsoleConnection&& rhs) noexcept: d_client(rhs.d_client), d_fileDesc(std::move(rhs.d_fileDesc)) + ConsoleConnection(ConsoleConnection&& rhs) noexcept : + d_client(rhs.d_client), d_fileDesc(std::move(rhs.d_fileDesc)) { } @@ -111,17 +113,17 @@ static void feedConfigDelta(const std::string& line) } #ifdef HAVE_LIBEDIT -static string historyFile(const bool &ignoreHOME = false) +static string historyFile(const bool& ignoreHOME = false) { string ret; passwd pwd{}; - passwd *result{nullptr}; + passwd* result{nullptr}; std::array buf{}; getpwuid_r(geteuid(), &pwd, buf.data(), buf.size(), &result); // NOLINTNEXTLINE(concurrency-mt-unsafe): we are not modifying the environment - const char *homedir = getenv("HOME"); + const char* homedir = getenv("HOME"); if (result != nullptr) { ret = string(pwd.pw_dir); } @@ -136,7 +138,8 @@ static string historyFile(const bool &ignoreHOME = false) } #endif /* HAVE_LIBEDIT */ -enum class ConsoleCommandResult : uint8_t { +enum class ConsoleCommandResult : uint8_t +{ Valid = 0, ConnectionClosed, TooLarge @@ -166,13 +169,12 @@ static ConsoleCommandResult getMsgLen32(int fileDesc, uint32_t* len) static bool putMsgLen32(int fileDesc, uint32_t len) { - try - { + try { uint32_t raw = htonl(len); size_t ret = writen2(fileDesc, &raw, sizeof raw); return ret == sizeof raw; } - catch(...) { + catch (...) { return false; } } @@ -182,7 +184,7 @@ static ConsoleCommandResult sendMessageToServer(int fileDesc, const std::string& string msg = dnsdist::crypto::authenticated::encryptSym(line, g_consoleKey, writingNonce); const auto msgLen = msg.length(); if (msgLen > std::numeric_limits::max()) { - cerr << "Encrypted message is too long to be sent to the server, "<< std::to_string(msgLen) << " > " << std::numeric_limits::max() << endl; + cerr << "Encrypted message is too long to be sent to the server, " << std::to_string(msgLen) << " > " << std::numeric_limits::max() << endl; return ConsoleCommandResult::TooLarge; } @@ -229,12 +231,12 @@ void doClient(ComboAddress server, const std::string& command) } if (g_verbose) { - cout<<"Connecting to "< "); - rl_bind_key('\t',rl_complete); + rl_bind_key('\t', rl_complete); if (sline == nullptr) { break; } @@ -288,7 +290,7 @@ void doClient(ComboAddress server, const std::string& command) string line(sline); if (!line.empty() && line != lastline) { add_history(sline); - history << sline < getNextConsoleLine(ofstream& history, std::str string line(sline); if (!line.empty() && line != lastline) { add_history(sline); - history << sline <, ClientState*, - std::unordered_map - > - > - >(withReturn ? ("return "+*line) : *line); + std::unordered_map>>>(withReturn ? ("return " + *line) : *line); if (ret) { if (const auto* dsValue = boost::get>(&*ret)) { if (*dsValue) { - cout<<(*dsValue)->getName()<getName() << endl; } } else if (const auto* csValue = boost::get(&*ret)) { if (*csValue != nullptr) { - cout<<(*csValue)->local.toStringWithPort()<local.toStringWithPort() << endl; } } else if (const auto* strValue = boost::get(&*ret)) { - cout<<*strValue< >(&*ret)) { + else if (const auto* mapValue = boost::get>(&*ret)) { using namespace json11; Json::object obj; for (const auto& value : *mapValue) { obj[value.first] = value.second; } Json out = obj; - cout< g_consoleKeywords{ +const std::vector g_consoleKeywords +{ /* keyword, function, parameters, description */ - { "addACL", true, "netmask", "add to the ACL set who can use this server" }, - { "addAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "add a rule" }, - { "addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter" }, - { "addCapabilitiesToRetain", true, "capability or list of capabilities", "Linux capabilities to retain after startup, like CAP_BPF" }, - { "addConsoleACL", true, "netmask", "add a netmask to the console ACL" }, - { "addDNSCryptBind", true, R"('127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", {reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}})", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" }, - { "addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables" }, - { "addDOH3Local", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over HTTP/3 queries on the specified address using the specified certificate and key. The last parameter is a table" }, - { "addDOQLocal", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over QUIC queries on the specified address using the specified certificate and key. The last parameter is a table" }, - { "addDynamicBlock", true, "address, message[, action [, seconds [, clientIPMask [, clientIPPortMask]]]]", "block the supplied address with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, - { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, - { "addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, - { "addLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "add `addr` to the list of addresses we listen on" }, - { "addCacheHitResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache hit response rule" }, - { "addCacheInsertedResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache inserted response rule" }, - { "addResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a response rule" }, - { "addSelfAnsweredResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a self-answered response rule" }, - { "addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table" }, - { "AllowAction", true, "", "let these packets go through" }, - { "AllowResponseAction", true, "", "let these packets go through" }, - { "AllRule", true, "", "matches all traffic" }, - { "AndRule", true, "list of DNS rules", "matches if all sub-rules matches" }, - { "benchRule", true, "DNS Rule [, iterations [, suffix]]", "bench the specified DNS rule" }, - { "carbonServer", true, "serverIP, [ourname], [interval]", "report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds" }, - { "clearConsoleHistory", true, "", "clear the internal (in-memory) history of console commands" }, - { "clearDynBlocks", true, "", "clear all dynamic blocks" }, - { "clearQueryCounters", true, "", "clears the query counter buffer" }, - { "clearRules", true, "", "remove all current rules" }, - { "controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode" }, - { "ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action" }, - { "declareMetric", true, "name, type, description [, prometheusName]", "Declare a custom metric" }, - { "decMetric", true, "name", "Decrement a custom metric" }, - { "DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" }, - { "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" }, - { "delta", true, "", "shows all commands entered that changed the configuration" }, - { "DNSSECRule", true, "", "matches queries with the DO bit set" }, - { "DnstapLogAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this query to a FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSQuestion and a DnstapMessage, that can be used to modify the dnstap message" }, - { "DnstapLogResponseAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this response to a remote or FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSResponse and a DnstapMessage, that can be used to modify the dnstap message" }, - { "DropAction", true, "", "drop these packets" }, - { "DropResponseAction", true, "", "drop these packets" }, - { "DSTPortRule", true, "port", "matches questions received to the destination port specified" }, - { "dumpStats", true, "", "print all statistics we gather" }, - { "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" }, - { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" }, - { "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" }, - { "ERCodeAction", true, "ercode", "Reply immediately by turning the query into a response with the specified EDNS extended rcode" }, - { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" }, - { "exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds" }, - { "exceedQRate", true, "rate, seconds", "get set of address that exceed `rate` queries/s over `seconds` seconds" }, - { "exceedQTypeRate", true, "type, rate, seconds", "get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds" }, - { "exceedRespByterate", true, "rate, seconds", "get set of addresses that exceeded `rate` bytes/s answers over `seconds` seconds" }, - { "exceedServFails", true, "rate, seconds", "get set of addresses that exceed `rate` servfails/s over `seconds` seconds" }, - { "firstAvailable", false, "", "picks the server with the lowest `order` that has not exceeded its QPS limit" }, - { "fixupCase", true, "bool", "if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer" }, - { "generateDNSCryptCertificate", true, R"("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil)", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key" }, - { "generateDNSCryptProviderKeys", true, R"("/path/to/providerPublic.key", "/path/to/providerPrivate.key")", "generate a new provider keypair" }, - { "getAction", true, "n", "Returns the Action associated with rule n" }, - { "getBind", true, "n", "returns the listener at index n" }, - { "getBindCount", true, "", "returns the number of listeners all kinds" }, - { "getCacheHitResponseRule", true, "selector", "Return the cache-hit response rule corresponding to the selector, if any" }, - { "getCacheInsertedResponseRule", true, "selector", "Return the cache-inserted response rule corresponding to the selector, if any" }, - { "getCurrentTime", true, "", "returns the current time" }, - { "getDynamicBlocks", true, "", "returns a table of the current network-based dynamic blocks" }, - { "getDynamicBlocksSMT", true, "", "returns a table of the current suffix-based dynamic blocks" }, - { "getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`" }, - { "getDNSCryptBindCount", true, "", "returns the number of DNSCrypt listeners" }, - { "getDOHFrontend", true, "n", "returns the DOH frontend with index n" }, - { "getDOHFrontendCount", true, "", "returns the number of DoH listeners" }, - { "getListOfAddressesOfNetworkInterface", true, "itf", "returns the list of addresses configured on a given network interface, as strings" }, - { "getListOfNetworkInterfaces", true, "", "returns the list of network interfaces present on the system, as strings" }, - { "getListOfRangesOfNetworkInterface", true, "itf", "returns the list of network ranges configured on a given network interface, as strings" }, - { "getMACAddress", true, "IP addr", "return the link-level address (MAC) corresponding to the supplied neighbour IP address, if known by the kernel" }, - { "getMetric", true, "name", "Get the value of a custom metric" }, - { "getOutgoingTLSSessionCacheSize", true, "", "returns the number of TLS sessions (for outgoing connections) currently cached" }, - { "getPool", true, "name", "return the pool named `name`, or \"\" for the default pool" }, - { "getPoolServers", true, "pool", "return servers part of this pool" }, - { "getPoolNames", true, "", "returns a table with all the pool names" }, - { "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" }, - { "getResponseRing", true, "", "return the current content of the response ring" }, - { "getResponseRule", true, "selector", "Return the response rule corresponding to the selector, if any" }, - { "getRespRing", true, "", "return the qname/rcode content of the response ring" }, - { "getRule", true, "selector", "Return the rule corresponding to the selector, if any" }, - { "getSelfAnsweredResponseRule", true, "selector", "Return the self-answered response rule corresponding to the selector, if any" }, - { "getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string" }, - { "getServers", true, "", "returns a table with all defined servers" }, - { "getStatisticsCounters", true, "", "returns a map of statistic counters" }, - { "getTopCacheHitResponseRules", true, "[top]", "return the `top` cache-hit response rules" }, - { "getTopCacheInsertedResponseRules", true, "[top]", "return the `top` cache-inserted response rules" }, - { "getTopResponseRules", true, "[top]", "return the `top` response rules" }, - { "getTopRules", true, "[top]", "return the `top` rules" }, - { "getTopSelfAnsweredResponseRules", true, "[top]", "return the `top` self-answered response rules" }, - { "getTLSContext", true, "n", "returns the TLS context with index n" }, - { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" }, - { "getTLSFrontendCount", true, "", "returns the number of DoT listeners" }, - { "getVerbose", true, "", "get whether log messages at the verbose level will be logged" }, - { "grepq", true, R"(Netmask|DNS Name|100ms|{"::1", "powerdns.com", "100ms"} [, n] [,options])", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" }, - { "hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"}, - { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"}, - { "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"}, - { "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"}, - { "HTTPStatusAction", true, "status, reason, body", "return an HTTP response"}, - { "inClientStartup", true, "", "returns true during console client parsing of configuration" }, - { "includeDirectory", true, "path", "include configuration files from `path`" }, - { "incMetric", true, "name", "Increment a custom metric" }, - { "KeyValueLookupKeyQName", true, "[wireFormat]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the qname of the query, either in wire format (default) or in plain text if 'wireFormat' is false" }, - { "KeyValueLookupKeySourceIP", true, "[v4Mask [, v6Mask [, includePort]]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the (possibly bitmasked) source IP of the client in network byte-order." }, - { "KeyValueLookupKeySuffix", true, "[minLabels [,wireFormat]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return a vector of keys based on the labels of the qname in DNS wire format or plain text" }, - { "KeyValueLookupKeyTag", true, "tag", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the value of the corresponding tag for this query, if it exists" }, - { "KeyValueStoreLookupAction", true, "kvs, lookupKey, destinationTag", "does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" }, - { "KeyValueStoreRangeLookupAction", true, "kvs, lookupKey, destinationTag", "does a range-based lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" }, - { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" }, - { "KeyValueStoreRangeLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" }, - { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"}, + {"addACL", true, "netmask", "add to the ACL set who can use this server"}, + {"addAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "add a rule"}, + {"addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter"}, + {"addCapabilitiesToRetain", true, "capability or list of capabilities", "Linux capabilities to retain after startup, like CAP_BPF"}, + {"addConsoleACL", true, "netmask", "add a netmask to the console ACL"}, + {"addDNSCryptBind", true, R"('127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", {reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}})", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters"}, + {"addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables"}, + {"addDOH3Local", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over HTTP/3 queries on the specified address using the specified certificate and key. The last parameter is a table"}, + {"addDOQLocal", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over QUIC queries on the specified address using the specified certificate and key. The last parameter is a table"}, + {"addDynamicBlock", true, "address, message[, action [, seconds [, clientIPMask [, clientIPPortMask]]]]", "block the supplied address with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"}, + {"addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"}, + {"addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"}, + {"addLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "add `addr` to the list of addresses we listen on"}, + {"addCacheHitResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache hit response rule"}, + {"addCacheInsertedResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache inserted response rule"}, + {"addResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a response rule"}, + {"addSelfAnsweredResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a self-answered response rule"}, + {"addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table"}, + {"AllowAction", true, "", "let these packets go through"}, + {"AllowResponseAction", true, "", "let these packets go through"}, + {"AllRule", true, "", "matches all traffic"}, + {"AndRule", true, "list of DNS rules", "matches if all sub-rules matches"}, + {"benchRule", true, "DNS Rule [, iterations [, suffix]]", "bench the specified DNS rule"}, + {"carbonServer", true, "serverIP, [ourname], [interval]", "report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds"}, + {"clearConsoleHistory", true, "", "clear the internal (in-memory) history of console commands"}, + {"clearDynBlocks", true, "", "clear all dynamic blocks"}, + {"clearQueryCounters", true, "", "clears the query counter buffer"}, + {"clearRules", true, "", "remove all current rules"}, + {"controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode"}, + {"ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action"}, + {"declareMetric", true, "name, type, description [, prometheusName]", "Declare a custom metric"}, + {"decMetric", true, "name", "Decrement a custom metric"}, + {"DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)"}, + {"DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)"}, + {"delta", true, "", "shows all commands entered that changed the configuration"}, + {"DNSSECRule", true, "", "matches queries with the DO bit set"}, + {"DnstapLogAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this query to a FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSQuestion and a DnstapMessage, that can be used to modify the dnstap message"}, + {"DnstapLogResponseAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this response to a remote or FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSResponse and a DnstapMessage, that can be used to modify the dnstap message"}, + {"DropAction", true, "", "drop these packets"}, + {"DropResponseAction", true, "", "drop these packets"}, + {"DSTPortRule", true, "port", "matches questions received to the destination port specified"}, + {"dumpStats", true, "", "print all statistics we gather"}, + {"dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object"}, + {"EDNSVersionRule", true, "version", "matches queries with the specified EDNS version"}, + {"EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present"}, + {"ERCodeAction", true, "ercode", "Reply immediately by turning the query into a response with the specified EDNS extended rcode"}, + {"ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)"}, + {"exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds"}, + {"exceedQRate", true, "rate, seconds", "get set of address that exceed `rate` queries/s over `seconds` seconds"}, + {"exceedQTypeRate", true, "type, rate, seconds", "get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds"}, + {"exceedRespByterate", true, "rate, seconds", "get set of addresses that exceeded `rate` bytes/s answers over `seconds` seconds"}, + {"exceedServFails", true, "rate, seconds", "get set of addresses that exceed `rate` servfails/s over `seconds` seconds"}, + {"firstAvailable", false, "", "picks the server with the lowest `order` that has not exceeded its QPS limit"}, + {"fixupCase", true, "bool", "if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer"}, + {"generateDNSCryptCertificate", true, R"("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil)", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key"}, + {"generateDNSCryptProviderKeys", true, R"("/path/to/providerPublic.key", "/path/to/providerPrivate.key")", "generate a new provider keypair"}, + {"getAction", true, "n", "Returns the Action associated with rule n"}, + {"getBind", true, "n", "returns the listener at index n"}, + {"getBindCount", true, "", "returns the number of listeners all kinds"}, + {"getCacheHitResponseRule", true, "selector", "Return the cache-hit response rule corresponding to the selector, if any"}, + {"getCacheInsertedResponseRule", true, "selector", "Return the cache-inserted response rule corresponding to the selector, if any"}, + {"getCurrentTime", true, "", "returns the current time"}, + {"getDynamicBlocks", true, "", "returns a table of the current network-based dynamic blocks"}, + {"getDynamicBlocksSMT", true, "", "returns a table of the current suffix-based dynamic blocks"}, + {"getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`"}, + {"getDNSCryptBindCount", true, "", "returns the number of DNSCrypt listeners"}, + {"getDOHFrontend", true, "n", "returns the DOH frontend with index n"}, + {"getDOHFrontendCount", true, "", "returns the number of DoH listeners"}, + {"getListOfAddressesOfNetworkInterface", true, "itf", "returns the list of addresses configured on a given network interface, as strings"}, + {"getListOfNetworkInterfaces", true, "", "returns the list of network interfaces present on the system, as strings"}, + {"getListOfRangesOfNetworkInterface", true, "itf", "returns the list of network ranges configured on a given network interface, as strings"}, + {"getMACAddress", true, "IP addr", "return the link-level address (MAC) corresponding to the supplied neighbour IP address, if known by the kernel"}, + {"getMetric", true, "name", "Get the value of a custom metric"}, + {"getOutgoingTLSSessionCacheSize", true, "", "returns the number of TLS sessions (for outgoing connections) currently cached"}, + {"getPool", true, "name", "return the pool named `name`, or \"\" for the default pool"}, + {"getPoolServers", true, "pool", "return servers part of this pool"}, + {"getPoolNames", true, "", "returns a table with all the pool names"}, + {"getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided"}, + {"getResponseRing", true, "", "return the current content of the response ring"}, + {"getResponseRule", true, "selector", "Return the response rule corresponding to the selector, if any"}, + {"getRespRing", true, "", "return the qname/rcode content of the response ring"}, + {"getRule", true, "selector", "Return the rule corresponding to the selector, if any"}, + {"getSelfAnsweredResponseRule", true, "selector", "Return the self-answered response rule corresponding to the selector, if any"}, + {"getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string"}, + {"getServers", true, "", "returns a table with all defined servers"}, + {"getStatisticsCounters", true, "", "returns a map of statistic counters"}, + {"getTopCacheHitResponseRules", true, "[top]", "return the `top` cache-hit response rules"}, + {"getTopCacheInsertedResponseRules", true, "[top]", "return the `top` cache-inserted response rules"}, + {"getTopResponseRules", true, "[top]", "return the `top` response rules"}, + {"getTopRules", true, "[top]", "return the `top` rules"}, + {"getTopSelfAnsweredResponseRules", true, "[top]", "return the `top` self-answered response rules"}, + {"getTLSContext", true, "n", "returns the TLS context with index n"}, + {"getTLSFrontend", true, "n", "returns the TLS frontend with index n"}, + {"getTLSFrontendCount", true, "", "returns the number of DoT listeners"}, + {"getVerbose", true, "", "get whether log messages at the verbose level will be logged"}, + {"grepq", true, R"(Netmask|DNS Name|100ms|{"::1", "powerdns.com", "100ms"} [, n] [,options])", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms"}, + {"hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"}, + {"HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"}, + {"HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"}, + {"HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"}, + {"HTTPStatusAction", true, "status, reason, body", "return an HTTP response"}, + {"inClientStartup", true, "", "returns true during console client parsing of configuration"}, + {"includeDirectory", true, "path", "include configuration files from `path`"}, + {"incMetric", true, "name", "Increment a custom metric"}, + {"KeyValueLookupKeyQName", true, "[wireFormat]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the qname of the query, either in wire format (default) or in plain text if 'wireFormat' is false"}, + {"KeyValueLookupKeySourceIP", true, "[v4Mask [, v6Mask [, includePort]]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the (possibly bitmasked) source IP of the client in network byte-order."}, + {"KeyValueLookupKeySuffix", true, "[minLabels [,wireFormat]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return a vector of keys based on the labels of the qname in DNS wire format or plain text"}, + {"KeyValueLookupKeyTag", true, "tag", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the value of the corresponding tag for this query, if it exists"}, + {"KeyValueStoreLookupAction", true, "kvs, lookupKey, destinationTag", "does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'"}, + {"KeyValueStoreRangeLookupAction", true, "kvs, lookupKey, destinationTag", "does a range-based lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'"}, + {"KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store"}, + {"KeyValueStoreRangeLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store"}, + {"leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"}, #if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS) - { "loadTLSEngine", true, "engineName [, defaultString]", "Load the OpenSSL engine named 'engineName', setting the engine default string to 'defaultString' if supplied"}, + {"loadTLSEngine", true, "engineName [, defaultString]", "Load the OpenSSL engine named 'engineName', setting the engine default string to 'defaultString' if supplied"}, #endif #if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) - { "loadTLSProvider", true, "providerName", "Load the OpenSSL provider named 'providerName'"}, + {"loadTLSProvider", true, "providerName", "Load the OpenSSL provider named 'providerName'"}, #endif - { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." }, - { "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." }, - { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" }, - { "LuaFFIAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion" }, - { "LuaFFIPerThreadAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion, with a per-thread Lua context" }, - { "LuaFFIPerThreadResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse, with a per-thread Lua context" }, - { "LuaFFIResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse" }, - { "LuaFFIRule", true, "function", "Invoke a Lua FFI function that filters DNS questions" }, - { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" }, - { "LuaRule", true, "function", "Invoke a Lua function that filters DNS questions" }, + {"LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not."}, + {"LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not."}, + {"LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion"}, + {"LuaFFIAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion"}, + {"LuaFFIPerThreadAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion, with a per-thread Lua context"}, + {"LuaFFIPerThreadResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse, with a per-thread Lua context"}, + {"LuaFFIResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse"}, + {"LuaFFIRule", true, "function", "Invoke a Lua FFI function that filters DNS questions"}, + {"LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse"}, + {"LuaRule", true, "function", "Invoke a Lua function that filters DNS questions"}, #ifdef HAVE_IPCIPHER - { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" }, + {"makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher"}, #endif /* HAVE_IPCIPHER */ - { "makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting" }, - { "makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called" } , - { "MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60 [, scanFraction=10 [, shards=10]]]]]]]", "matches traffic exceeding the qps limit per subnet" }, - { "MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit" }, - { "mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" }, - { "mvCacheHitResponseRuleToTop", true, "", "move the last cache hit response rule to the first position" }, - { "mvCacheInsertedResponseRule", true, "from, to", "move cache inserted response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" }, - { "mvCacheInsertedResponseRuleToTop", true, "", "move the last cache inserted response rule to the first position" }, - { "mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" }, - { "mvResponseRuleToTop", true, "", "move the last response rule to the first position" }, - { "mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position" }, - { "mvRuleToTop", true, "", "move the last rule to the first position" }, - { "mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" }, - { "mvSelfAnsweredResponseRuleToTop", true, "", "move the last self-answered response rule to the first position" }, - { "NetmaskGroupRule", true, "nmg[, src]", "Matches traffic from/to the network range specified in nmg. Set the src parameter to false to match nmg against destination address instead of source address. This can be used to differentiate between clients" }, - { "newBPFFilter", true, "{ipv4MaxItems=int, ipv4PinnedPath=string, ipv6MaxItems=int, ipv6PinnedPath=string, cidr4MaxItems=int, cidr4PinnedPath=string, cidr6MaxItems=int, cidr6PinnedPath=string, qnamesMaxItems=int, qnamesPinnedPath=string, external=bool}", "Return a new eBPF socket filter with specified options." }, - { "newCA", true, "address", "Returns a ComboAddress based on `address`" }, + {"makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting"}, + {"makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called"}, + {"MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60 [, scanFraction=10 [, shards=10]]]]]]]", "matches traffic exceeding the qps limit per subnet"}, + {"MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit"}, + {"mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, + {"mvCacheHitResponseRuleToTop", true, "", "move the last cache hit response rule to the first position"}, + {"mvCacheInsertedResponseRule", true, "from, to", "move cache inserted response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, + {"mvCacheInsertedResponseRuleToTop", true, "", "move the last cache inserted response rule to the first position"}, + {"mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, + {"mvResponseRuleToTop", true, "", "move the last response rule to the first position"}, + {"mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position"}, + {"mvRuleToTop", true, "", "move the last rule to the first position"}, + {"mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, + {"mvSelfAnsweredResponseRuleToTop", true, "", "move the last self-answered response rule to the first position"}, + {"NetmaskGroupRule", true, "nmg[, src]", "Matches traffic from/to the network range specified in nmg. Set the src parameter to false to match nmg against destination address instead of source address. This can be used to differentiate between clients"}, + {"newBPFFilter", true, "{ipv4MaxItems=int, ipv4PinnedPath=string, ipv6MaxItems=int, ipv6PinnedPath=string, cidr4MaxItems=int, cidr4PinnedPath=string, cidr6MaxItems=int, cidr6PinnedPath=string, qnamesMaxItems=int, qnamesPinnedPath=string, external=bool}", "Return a new eBPF socket filter with specified options."}, + {"newCA", true, "address", "Returns a ComboAddress based on `address`"}, #ifdef HAVE_CDB - { "newCDBKVStore", true, "fname, refreshDelay", "Return a new KeyValueStore object associated to the corresponding CDB database" }, + {"newCDBKVStore", true, "fname, refreshDelay", "Return a new KeyValueStore object associated to the corresponding CDB database"}, #endif - { "newDNSName", true, "name", "make a DNSName based on this .-terminated name" }, - { "newDNSNameSet", true, "", "returns a new DNSNameSet" }, - { "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" }, - { "newFrameStreamTcpLogger", true, "addr [, options]", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" }, - { "newFrameStreamUnixLogger", true, "socket [, options]", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" }, + {"newDNSName", true, "name", "make a DNSName based on this .-terminated name"}, + {"newDNSNameSet", true, "", "returns a new DNSNameSet"}, + {"newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter"}, + {"newFrameStreamTcpLogger", true, "addr [, options]", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`"}, + {"newFrameStreamUnixLogger", true, "socket [, options]", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`"}, #ifdef HAVE_LMDB - { "newLMDBKVStore", true, "fname, dbName [, noLock]", "Return a new KeyValueStore object associated to the corresponding LMDB database" }, + {"newLMDBKVStore", true, "fname, dbName [, noLock]", "Return a new KeyValueStore object associated to the corresponding LMDB database"}, #endif - { "newNMG", true, "", "Returns a NetmaskGroup" }, - { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" }, - { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" }, - { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" }, - { "newRuleAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, - { "newServer", true, R"({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface", sockets=1, reconnectOnUp=false})", "instantiate a server" }, - { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" }, - { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" }, - { "newSVCRecordParameters", true, "priority, target, mandatoryParams, alpns, noDefaultAlpn [, port [, ech [, ipv4hints [, ipv6hints [, additionalParameters ]]]]]", "return a new SVCRecordParameters object, to use with SpoofSVCAction" }, - { "NegativeAndSOAAction", true, "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum [, options]", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section" }, - { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" }, - { "NotRule", true, "selector", "Matches the traffic if the selector rule does not match" }, - { "OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes" }, - { "OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match" }, - { "PoolAction", true, "poolname [, stop]", "set the packet into the specified pool" }, - { "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" }, - { "PoolOutstandingRule", true, "poolname, limit", "Check whether a pool has outstanding queries above limit" }, - { "printDNSCryptProviderFingerprint", true, R"("/path/to/providerPublic.key")", "display the fingerprint of the provided resolver public key" }, - { "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" }, - { "ProxyProtocolValueRule", true, "type [, value]", "matches queries with a specified Proxy Protocol TLV value of that type, optionally matching the content of the option as well" }, - { "QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass" }, - { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" }, - { "QNameRule", true, "qname", "matches queries with the specified qname" }, - { "QNameSetRule", true, "set", "Matches if the set contains exact qname" }, - { "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" }, - { "QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" }, - { "QPSPoolAction", true, "maxqps, poolname [, stop]", "Send the packet into the specified pool only if it does not exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" }, - { "QTypeRule", true, "qtype", "matches queries with the specified qtype" }, - { "RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode" }, - { "RCodeRule", true, "rcode", "matches responses with the specified rcode" }, - { "RDRule", true, "", "Matches queries with the RD flag set" }, - { "RecordsCountRule", true, "section, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records in the section section. section can be specified as an integer or as a DNS Packet Sections" }, - { "RecordsTypeCountRule", true, "section, qtype, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records of type type in the section section" }, - { "RegexRule", true, "regex", "matches the query name against the supplied regex" }, - { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" }, - { "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" }, - { "RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes. `serverID` is the server identifier." }, - { "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records. `serverID` is the server identifier." }, - { "requestTCPStatesDump", true, "", "Request a dump of the TCP states (incoming connections, outgoing connections) during the next scan. Useful for debugging purposes only" }, - { "rmACL", true, "netmask", "remove netmask from ACL" }, - { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, - { "rmCacheInsertedResponseRule", true, "id", "remove cache inserted response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, - { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, - { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, - { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, - { "rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string" }, - { "roundrobin", false, "", "Simple round robin over available servers" }, - { "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"}, - { "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" }, - { "setACLFromFile", true, "file", "replace the ACL set with netmasks in this file" }, - { "setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS" }, - { "setAllowEmptyResponse", true, "allow", "Set to true (defaults to false) to allow empty responses (qdcount=0) with a NoError or NXDomain rcode (default) from backends" }, - { "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" }, - { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" }, - { "setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove" }, - { "setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing" }, - { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" }, - { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" }, - { "setConsoleMaximumConcurrentConnections", true, "max", "Set the maximum number of concurrent console connections" }, - { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" }, - { "setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind" }, - { "setDoHDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle DoH downstream connections" }, - { "setDoHDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream DoH connection to a backend might stay idle" }, - { "setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported" }, - { "setDynBlocksPurgeInterval", true, "sec", "set how often the expired dynamic block entries should be removed" }, - { "setDropEmptyQueries", true, "drop", "Whether to drop empty queries right away instead of sending a NOTIMP response" }, - { "setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query" }, - { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" }, - { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" }, - { "setKey", true, "key", "set access key to that key" }, - { "setLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "reset the list of addresses we listen on to this address" }, - { "setMaxCachedDoHConnectionsPerDownstream", true, "max", "Set the maximum number of inactive DoH connections to a backend cached by each worker DoH thread" }, - { "setMaxCachedTCPConnectionsPerDownstream", true, "max", "Set the maximum number of inactive TCP connections to a backend cached by each worker TCP thread" }, - { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" }, - { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" }, - { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" }, - { "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" }, - { "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" }, - { "setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 65535" }, - { "setMetric", true, "name, value", "Set the value of a custom metric to the supplied value" }, - { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" }, - { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" }, - { "setPoolServerPolicyLua", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" }, - { "setPoolServerPolicyLuaFFI", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" }, - { "setPoolServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy for this pool to one named 'name' and returned by the Lua FFI code passed in 'code'" }, - { "setProxyProtocolACL", true, "{netmask, netmask}", "Set the netmasks who are allowed to send Proxy Protocol headers in front of queries/connections" }, - { "setProxyProtocolApplyACLToProxiedClients", true, "apply", "Whether the general ACL should be applied to the source IP address gathered from a Proxy Protocol header, in addition to being first applied to the source address seen by dnsdist" }, - { "setProxyProtocolMaximumPayloadSize", true, "max", "Set the maximum size of a Proxy Protocol payload, in bytes" }, - { "setQueryCount", true, "bool", "set whether queries should be counted" }, - { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" }, - { "SetReducedTTLResponseAction", true, "percentage", "Reduce the TTL of records in a response to a given percentage" }, - { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" }, - { "setRingBuffersOptions", true, "{ lockRetries=int, recordQueries=true, recordResponses=true }", "set ringbuffer options" }, - { "setRingBuffersSize", true, "n [, numberOfShards]", "set the capacity of the ringbuffers used for live traffic inspection to `n`, and optionally the number of shards to use to `numberOfShards`" }, - { "setRoundRobinFailOnNoServer", true, "value", "By default the roundrobin load-balancing policy will still try to select a backend even if all backends are currently down. Setting this to true will make the policy fail and return that no server is available instead" }, - { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" }, - { "setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds" }, - { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" }, - { "setServerPolicy", true, "policy", "set server selection policy to that policy" }, - { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" }, - { "setServerPolicyLuaFFI", true, "name, function", "set server selection policy to one named 'name' and provided by the Lua FFI 'function'" }, - { "setServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy to one named 'name' and returned by the Lua FFI code passed in 'code'" }, - { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" }, - { "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" }, - { "setStructuredLogging", true, "value [, options]", "set whether log messages should be in structured-logging-like format" }, - { "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" }, - { "setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections" }, - { "setTCPFastOpenKey", true, "string", "TCP Fast Open Key" }, - { "setTCPDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream TCP connection to a backend might stay idle" }, - { "setTCPInternalPipeBufferSize", true, "size", "Set the size in bytes of the internal buffer of the pipes used internally to distribute connections to TCP (and DoT) workers threads" }, - { "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" }, - { "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" }, - { "setUDPMultipleMessagesVectorSize", true, "n", "set the size of the vector passed to recvmmsg() to receive UDP messages. Default to 1 which means that the feature is disabled and recvmsg() is used instead" }, - { "setUDPSocketBufferSizes", true, "recv, send", "Set the size of the receive (SO_RCVBUF) and send (SO_SNDBUF) buffers for incoming UDP sockets" }, - { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" }, - { "setVerbose", true, "bool", "set whether log messages at the verbose level will be logged" }, - { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" }, - { "setVerboseLogDestination", true, "destination file", "Set a destination file to write the 'verbose' log messages to, instead of sending them to syslog and/or the standard output" }, - { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders, statsRequireAuthentication}]", "Updates webserver configuration" }, - { "setWeightedBalancingFactor", true, "factor", "Set the balancing factor for bounded-load weighted policies (whashed, wrandom)" }, - { "setWHashedPertubation", true, "value", "Set the hash perturbation value to be used in the whashed policy instead of a random one, allowing to have consistent whashed results on different instance" }, - { "show", true, "string", "outputs `string`" }, - { "showACL", true, "", "show our ACL set" }, - { "showBinds", true, "", "show listening addresses (frontends)" }, - { "showCacheHitResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined cache hit response rules, optionally with their UUIDs and optionally truncated to a given width" }, - { "showConsoleACL", true, "", "show our current console ACL set" }, - { "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" }, - { "showDOHFrontends", true, "", "list all the available DOH frontends" }, - { "showDOH3Frontends", true, "", "list all the available DOH3 frontends" }, - { "showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"}, - { "showDOQFrontends", true, "", "list all the available DOQ frontends" }, - { "showDynBlocks", true, "", "show dynamic blocks in force" }, - { "showPools", true, "", "show the available pools" }, - { "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" }, - { "showResponseLatency", true, "", "show a plot of the response time latency distribution" }, - { "showResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined response rules, optionally with their UUIDs and optionally truncated to a given width" }, - { "showRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined rules, optionally with their UUIDs and optionally truncated to a given width" }, - { "showSecurityStatus", true, "", "Show the security status"}, - { "showSelfAnsweredResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined self-answered response rules, optionally with their UUIDs and optionally truncated to a given width" }, - { "showServerPolicy", true, "", "show name of currently operational server selection policy" }, - { "showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs" }, - { "showTCPStats", true, "", "show some statistics regarding TCP" }, - { "showTLSContexts", true, "", "list all the available TLS contexts" }, - { "showTLSErrorCounters", true, "", "show metrics about TLS handshake failures" }, - { "showVersion", true, "", "show the current version" }, - { "showWebserverConfig", true, "", "Show the current webserver configuration" }, - { "shutdown", true, "", "shut down `dnsdist`" }, - { "snmpAgent", true, "enableTraps [, daemonSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `daemonSocket` an optional string specifying how to connect to the daemon agent"}, - { "SetAdditionalProxyProtocolValueAction", true, "type, value", "Add a Proxy Protocol TLV value of this type" }, - { "SetDisableECSAction", true, "", "Disable the sending of ECS to the backend. Subsequent rules are processed after this action." }, - { "SetDisableValidationAction", true, "", "set the CD bit in the question, let it go through" }, - { "SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value" }, - { "SetECSOverrideAction", true, "override", "Whether an existing EDNS Client Subnet value should be overridden (true) or not (false). Subsequent rules are processed after this action" }, - { "SetECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" }, - { "SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" }, - { "SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action" }, - { "SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action" }, - { "SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action" }, - { "SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through" }, - { "setOutgoingDoHWorkerThreads", true, "n", "Number of outgoing DoH worker threads" }, - { "SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'" }, - { "SetSkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" }, - { "SetSkipCacheResponseAction", true, "", "Don’t store this response into the cache" }, - { "SetTagAction", true, "name, value", "set the tag named 'name' to the given value" }, - { "SetTagResponseAction", true, "name, value", "set the tag named 'name' to the given value" }, - { "SetTempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" }, - { "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" }, - { "SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"}, - { "SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"}, - { "SpoofAction", true, "ip|list of ips [, options]", "forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in" }, - { "SpoofCNAMEAction", true, "cname [, options]", "Forge a response with the specified CNAME value" }, - { "SpoofRawAction", true, "raw|list of raws [, options]", "Forge a response with the specified record data as raw bytes. If you specify multiple raws (it is assumed they match the query type), all will get spoofed in" }, - { "SpoofSVCAction", true, "list of svcParams [, options]", "Forge a response with the specified SVC record data" } , - { "SuffixMatchNodeRule", true, "smn[, quiet]", "Matches based on a group of domain suffixes for rapid testing of membership. Pass true as second parameter to prevent listing of all domains matched" }, - { "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" }, - { "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" }, - { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" }, - { "TCResponseAction", true, "", "truncate a response" }, - { "TeeAction", true, "remote [, addECS [, local]]", "send copy of query to remote, optionally adding ECS info, optionally set local address" }, - { "testCrypto", true, "", "test of the crypto all works" }, - { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"}, - { "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" }, - { "topCacheHitResponseRules", true, "[top][, vars]", "show `top` cache-hit response rules" }, - { "topCacheInsertedResponseRules", true, "[top][, vars]", "show `top` cache-inserted response rules" }, - { "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" }, - { "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" }, - { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=NXDomain), as grouped when optionally cut down to 'labels' labels" }, - { "topResponseRules", true, "[top][, vars]", "show `top` response rules" }, - { "topRules", true, "[top][, vars]", "show `top` rules" }, - { "topSelfAnsweredResponseRules", true, "[top][, vars]", "show `top` self-answered response rules" }, - { "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" }, - { "TrailingDataRule", true, "", "Matches if the query has trailing data" }, - { "truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891." }, - { "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" }, - { "webserver", true, "address:port", "launch a webserver with stats on that address" }, - { "whashed", false, "", "Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter" }, - { "chashed", false, "", "Consistent hashed ('sticky') distribution over available servers, also based on the server 'weight' parameter" }, - { "wrandom", false, "", "Weighted random over available servers, based on the server 'weight' parameter" }, + {"newNMG", true, "", "Returns a NetmaskGroup"}, + {"newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache"}, + {"newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity"}, + {"newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`"}, + {"newRuleAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`"}, + {"newServer", true, R"({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface", sockets=1, reconnectOnUp=false})", "instantiate a server"}, + {"newServerPolicy", true, "name, function", "create a policy object from a Lua function"}, + {"newSuffixMatchNode", true, "", "returns a new SuffixMatchNode"}, + {"newSVCRecordParameters", true, "priority, target, mandatoryParams, alpns, noDefaultAlpn [, port [, ech [, ipv4hints [, ipv6hints [, additionalParameters ]]]]]", "return a new SVCRecordParameters object, to use with SpoofSVCAction"}, + {"NegativeAndSOAAction", true, "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum [, options]", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section"}, + {"NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action"}, + {"NotRule", true, "selector", "Matches the traffic if the selector rule does not match"}, + {"OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes"}, + {"OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match"}, + {"PoolAction", true, "poolname [, stop]", "set the packet into the specified pool"}, + {"PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries"}, + {"PoolOutstandingRule", true, "poolname, limit", "Check whether a pool has outstanding queries above limit"}, + {"printDNSCryptProviderFingerprint", true, R"("/path/to/providerPublic.key")", "display the fingerprint of the provided resolver public key"}, + {"ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always"}, + {"ProxyProtocolValueRule", true, "type [, value]", "matches queries with a specified Proxy Protocol TLV value of that type, optionally matching the content of the option as well"}, + {"QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass"}, + {"QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels"}, + {"QNameRule", true, "qname", "matches queries with the specified qname"}, + {"QNameSetRule", true, "set", "Matches if the set contains exact qname"}, + {"QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes"}, + {"QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise"}, + {"QPSPoolAction", true, "maxqps, poolname [, stop]", "Send the packet into the specified pool only if it does not exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise"}, + {"QTypeRule", true, "qtype", "matches queries with the specified qtype"}, + {"RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode"}, + {"RCodeRule", true, "rcode", "matches responses with the specified rcode"}, + {"RDRule", true, "", "Matches queries with the RD flag set"}, + {"RecordsCountRule", true, "section, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records in the section section. section can be specified as an integer or as a DNS Packet Sections"}, + {"RecordsTypeCountRule", true, "section, qtype, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records of type type in the section section"}, + {"RegexRule", true, "regex", "matches the query name against the supplied regex"}, + {"registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed"}, + {"reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys"}, + {"RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes. `serverID` is the server identifier."}, + {"RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records. `serverID` is the server identifier."}, + {"requestTCPStatesDump", true, "", "Request a dump of the TCP states (incoming connections, outgoing connections) during the next scan. Useful for debugging purposes only"}, + {"rmACL", true, "netmask", "remove netmask from ACL"}, + {"rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, + {"rmCacheInsertedResponseRule", true, "id", "remove cache inserted response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, + {"rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, + {"rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, + {"rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, + {"rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string"}, + {"roundrobin", false, "", "Simple round robin over available servers"}, + {"sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"}, + {"setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us"}, + {"setACLFromFile", true, "file", "replace the ACL set with netmasks in this file"}, + {"setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS"}, + {"setAllowEmptyResponse", true, "allow", "Set to true (defaults to false) to allow empty responses (qdcount=0) with a NoError or NXDomain rcode (default) from backends"}, + {"setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API"}, + {"setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries"}, + {"setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove"}, + {"setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing"}, + {"setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks"}, + {"setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections"}, + {"setConsoleMaximumConcurrentConnections", true, "max", "Set the maximum number of concurrent console connections"}, + {"setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB"}, + {"setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind"}, + {"setDoHDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle DoH downstream connections"}, + {"setDoHDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream DoH connection to a backend might stay idle"}, + {"setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported"}, + {"setDynBlocksPurgeInterval", true, "sec", "set how often the expired dynamic block entries should be removed"}, + {"setDropEmptyQueries", true, "drop", "Whether to drop empty queries right away instead of sending a NOTIMP response"}, + {"setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query"}, + {"setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries"}, + {"setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries"}, + {"setKey", true, "key", "set access key to that key"}, + {"setLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "reset the list of addresses we listen on to this address"}, + {"setMaxCachedDoHConnectionsPerDownstream", true, "max", "Set the maximum number of inactive DoH connections to a backend cached by each worker DoH thread"}, + {"setMaxCachedTCPConnectionsPerDownstream", true, "max", "Set the maximum number of inactive TCP connections to a backend cached by each worker TCP thread"}, + {"setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections"}, + {"setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited"}, + {"setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited"}, + {"setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited"}, + {"setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)"}, + {"setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 65535"}, + {"setMetric", true, "name, value", "Set the value of a custom metric to the supplied value"}, + {"setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses"}, + {"setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy"}, + {"setPoolServerPolicyLua", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'"}, + {"setPoolServerPolicyLuaFFI", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'"}, + {"setPoolServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy for this pool to one named 'name' and returned by the Lua FFI code passed in 'code'"}, + {"setProxyProtocolACL", true, "{netmask, netmask}", "Set the netmasks who are allowed to send Proxy Protocol headers in front of queries/connections"}, + {"setProxyProtocolApplyACLToProxiedClients", true, "apply", "Whether the general ACL should be applied to the source IP address gathered from a Proxy Protocol header, in addition to being first applied to the source address seen by dnsdist"}, + {"setProxyProtocolMaximumPayloadSize", true, "max", "Set the maximum size of a Proxy Protocol payload, in bytes"}, + {"setQueryCount", true, "bool", "set whether queries should be counted"}, + {"setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted"}, + {"SetReducedTTLResponseAction", true, "percentage", "Reduce the TTL of records in a response to a given percentage"}, + {"setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking"}, + {"setRingBuffersOptions", true, "{ lockRetries=int, recordQueries=true, recordResponses=true }", "set ringbuffer options"}, + {"setRingBuffersSize", true, "n [, numberOfShards]", "set the capacity of the ringbuffers used for live traffic inspection to `n`, and optionally the number of shards to use to `numberOfShards`"}, + {"setRoundRobinFailOnNoServer", true, "value", "By default the roundrobin load-balancing policy will still try to select a backend even if all backends are currently down. Setting this to true will make the policy fail and return that no server is available instead"}, + {"setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)"}, + {"setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds"}, + {"setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value"}, + {"setServerPolicy", true, "policy", "set server selection policy to that policy"}, + {"setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'"}, + {"setServerPolicyLuaFFI", true, "name, function", "set server selection policy to one named 'name' and provided by the Lua FFI 'function'"}, + {"setServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy to one named 'name' and returned by the Lua FFI code passed in 'code'"}, + {"setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query"}, + {"setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query"}, + {"setStructuredLogging", true, "value [, options]", "set whether log messages should be in structured-logging-like format"}, + {"setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON"}, + {"setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections"}, + {"setTCPFastOpenKey", true, "string", "TCP Fast Open Key"}, + {"setTCPDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream TCP connection to a backend might stay idle"}, + {"setTCPInternalPipeBufferSize", true, "size", "Set the size in bytes of the internal buffer of the pipes used internally to distribute connections to TCP (and DoT) workers threads"}, + {"setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds"}, + {"setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds"}, + {"setUDPMultipleMessagesVectorSize", true, "n", "set the size of the vector passed to recvmmsg() to receive UDP messages. Default to 1 which means that the feature is disabled and recvmsg() is used instead"}, + {"setUDPSocketBufferSizes", true, "recv, send", "Set the size of the receive (SO_RCVBUF) and send (SO_SNDBUF) buffers for incoming UDP sockets"}, + {"setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds"}, + {"setVerbose", true, "bool", "set whether log messages at the verbose level will be logged"}, + {"setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged"}, + {"setVerboseLogDestination", true, "destination file", "Set a destination file to write the 'verbose' log messages to, instead of sending them to syslog and/or the standard output"}, + {"setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders, statsRequireAuthentication}]", "Updates webserver configuration"}, + {"setWeightedBalancingFactor", true, "factor", "Set the balancing factor for bounded-load weighted policies (whashed, wrandom)"}, + {"setWHashedPertubation", true, "value", "Set the hash perturbation value to be used in the whashed policy instead of a random one, allowing to have consistent whashed results on different instance"}, + {"show", true, "string", "outputs `string`"}, + {"showACL", true, "", "show our ACL set"}, + {"showBinds", true, "", "show listening addresses (frontends)"}, + {"showCacheHitResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined cache hit response rules, optionally with their UUIDs and optionally truncated to a given width"}, + {"showConsoleACL", true, "", "show our current console ACL set"}, + {"showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds"}, + {"showDOHFrontends", true, "", "list all the available DOH frontends"}, + {"showDOH3Frontends", true, "", "list all the available DOH3 frontends"}, + {"showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"}, + {"showDOQFrontends", true, "", "list all the available DOQ frontends"}, + {"showDynBlocks", true, "", "show dynamic blocks in force"}, + {"showPools", true, "", "show the available pools"}, + {"showPoolServerPolicy", true, "pool", "show server selection policy for this pool"}, + {"showResponseLatency", true, "", "show a plot of the response time latency distribution"}, + {"showResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined response rules, optionally with their UUIDs and optionally truncated to a given width"}, + {"showRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined rules, optionally with their UUIDs and optionally truncated to a given width"}, + {"showSecurityStatus", true, "", "Show the security status"}, + {"showSelfAnsweredResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined self-answered response rules, optionally with their UUIDs and optionally truncated to a given width"}, + {"showServerPolicy", true, "", "show name of currently operational server selection policy"}, + {"showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs"}, + {"showTCPStats", true, "", "show some statistics regarding TCP"}, + {"showTLSContexts", true, "", "list all the available TLS contexts"}, + {"showTLSErrorCounters", true, "", "show metrics about TLS handshake failures"}, + {"showVersion", true, "", "show the current version"}, + {"showWebserverConfig", true, "", "Show the current webserver configuration"}, + {"shutdown", true, "", "shut down `dnsdist`"}, + {"snmpAgent", true, "enableTraps [, daemonSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `daemonSocket` an optional string specifying how to connect to the daemon agent"}, + {"SetAdditionalProxyProtocolValueAction", true, "type, value", "Add a Proxy Protocol TLV value of this type"}, + {"SetDisableECSAction", true, "", "Disable the sending of ECS to the backend. Subsequent rules are processed after this action."}, + {"SetDisableValidationAction", true, "", "set the CD bit in the question, let it go through"}, + {"SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value"}, + {"SetECSOverrideAction", true, "override", "Whether an existing EDNS Client Subnet value should be overridden (true) or not (false). Subsequent rules are processed after this action"}, + {"SetECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action"}, + {"SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action"}, + {"SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action"}, + {"SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action"}, + {"SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action"}, + {"SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through"}, + {"setOutgoingDoHWorkerThreads", true, "n", "Number of outgoing DoH worker threads"}, + {"SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'"}, + {"SetSkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer"}, + {"SetSkipCacheResponseAction", true, "", "Don’t store this response into the cache"}, + {"SetTagAction", true, "name, value", "set the tag named 'name' to the given value"}, + {"SetTagResponseAction", true, "name, value", "set the tag named 'name' to the given value"}, + {"SetTempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies"}, + {"SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)"}, + {"SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"}, + {"SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"}, + {"SpoofAction", true, "ip|list of ips [, options]", "forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in"}, + {"SpoofCNAMEAction", true, "cname [, options]", "Forge a response with the specified CNAME value"}, + {"SpoofRawAction", true, "raw|list of raws [, options]", "Forge a response with the specified record data as raw bytes. If you specify multiple raws (it is assumed they match the query type), all will get spoofed in"}, + {"SpoofSVCAction", true, "list of svcParams [, options]", "Forge a response with the specified SVC record data"}, + {"SuffixMatchNodeRule", true, "smn[, quiet]", "Matches based on a group of domain suffixes for rapid testing of membership. Pass true as second parameter to prevent listing of all domains matched"}, + {"TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any"}, + {"TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP"}, + {"TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise"}, + {"TCResponseAction", true, "", "truncate a response"}, + {"TeeAction", true, "remote [, addECS [, local]]", "send copy of query to remote, optionally adding ECS info, optionally set local address"}, + {"testCrypto", true, "", "test of the crypto all works"}, + {"TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"}, + {"topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer"}, + {"topCacheHitResponseRules", true, "[top][, vars]", "show `top` cache-hit response rules"}, + {"topCacheInsertedResponseRules", true, "[top][, vars]", "show `top` cache-inserted response rules"}, + {"topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer"}, + {"topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels"}, + {"topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=NXDomain), as grouped when optionally cut down to 'labels' labels"}, + {"topResponseRules", true, "[top][, vars]", "show `top` response rules"}, + {"topRules", true, "[top][, vars]", "show `top` rules"}, + {"topSelfAnsweredResponseRules", true, "[top][, vars]", "show `top` self-answered response rules"}, + {"topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels"}, + {"TrailingDataRule", true, "", "Matches if the query has trailing data"}, + {"truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891."}, + {"unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter"}, + {"webserver", true, "address:port", "launch a webserver with stats on that address"}, + {"whashed", false, "", "Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter"}, + {"chashed", false, "", "Consistent hashed ('sticky') distribution over available servers, also based on the server 'weight' parameter"}, + {"wrandom", false, "", "Weighted random over available servers, based on the server 'weight' parameter"}, }; #if defined(HAVE_LIBEDIT) -extern "C" { -static char* my_generator(const char* text, int state) +extern "C" { - string textStr(text); - /* to keep it readable, we try to keep only 4 keywords per line - and to start a new line when the first letter changes */ - static int s_counter = 0; - int counter = 0; - if (state == 0) { - s_counter = 0; - } + static char* my_generator(const char* text, int state) + { + string textStr(text); + /* to keep it readable, we try to keep only 4 keywords per line + and to start a new line when the first letter changes */ + static int s_counter = 0; + int counter = 0; + if (state == 0) { + s_counter = 0; + } - for (const auto& keyword : g_consoleKeywords) { - if (boost::starts_with(keyword.name, textStr) && counter++ == s_counter) { - std::string value(keyword.name); - s_counter++; - if (keyword.function) { - value += "("; - if (keyword.parameters.empty()) { - value += ")"; + for (const auto& keyword : g_consoleKeywords) { + if (boost::starts_with(keyword.name, textStr) && counter++ == s_counter) { + std::string value(keyword.name); + s_counter++; + if (keyword.function) { + value += "("; + if (keyword.parameters.empty()) { + value += ")"; + } } + return strdup(value.c_str()); } - return strdup(value.c_str()); } + return nullptr; } - return nullptr; -} -char** my_completion( const char * text , int start, int end) -{ - char **matches = nullptr; - if (start == 0) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): readline - matches = rl_completion_matches (const_cast(text), &my_generator); - } + char** my_completion(const char* text, int start, int end) + { + char** matches = nullptr; + if (start == 0) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): readline + matches = rl_completion_matches(const_cast(text), &my_generator); + } - // skip default filename completion. - rl_attempted_completion_over = 1; + // skip default filename completion. + rl_attempted_completion_over = 1; - return matches; -} + return matches; + } } #endif /* HAVE_LIBEDIT */ #endif /* DISABLE_COMPLETION */ @@ -933,37 +935,36 @@ static void controlClientThread(ConsoleConnection&& conn) string, shared_ptr, ClientState*, - std::unordered_map - > - > - >(withReturn ? ("return "+line) : line); + std::unordered_map>>>(withReturn ? ("return " + line) : line); if (ret) { if (const auto* dsValue = boost::get>(&*ret)) { if (*dsValue) { - response = (*dsValue)->getName()+"\n"; - } else { + response = (*dsValue)->getName() + "\n"; + } + else { response = ""; } } else if (const auto* csValue = boost::get(&*ret)) { if (*csValue != nullptr) { - response = (*csValue)->local.toStringWithPort()+"\n"; - } else { + response = (*csValue)->local.toStringWithPort() + "\n"; + } + else { response = ""; } } else if (const auto* strValue = boost::get(&*ret)) { - response = *strValue+"\n"; + response = *strValue + "\n"; } - else if (const auto* mapValue = boost::get >(&*ret)) { + else if (const auto* mapValue = boost::get>(&*ret)) { using namespace json11; Json::object obj; for (const auto& value : *mapValue) { obj[value.first] = value.second; } Json out = obj; - response = out.dump()+"\n"; + response = out.dump() + "\n"; } } else { @@ -982,12 +983,12 @@ static void controlClientThread(ConsoleConnection&& conn) throw; } } - catch(const LuaContext::WrongTypeException& e) { - response = "Command returned an object we can't print: " +std::string(e.what()) + "\n"; + catch (const LuaContext::WrongTypeException& e) { + response = "Command returned an object we can't print: " + std::string(e.what()) + "\n"; // tried to return something we don't understand } catch (const LuaContext::ExecutionErrorException& e) { - if (strcmp(e.what(),"invalid key to 'next'") == 0) { + if (strcmp(e.what(), "invalid key to 'next'") == 0) { response = "Error: Parsing function parameters, did you forget parameter name?"; } else { @@ -996,9 +997,10 @@ static void controlClientThread(ConsoleConnection&& conn) try { std::rethrow_if_nested(e); - } catch (const std::exception& ne) { + } + catch (const std::exception& ne) { // ne is the exception that was thrown from inside the lambda - response+= ": " + string(ne.what()); + response += ": " + string(ne.what()); } catch (const PDNSException& ne) { // ne is the exception that was thrown from inside the lambda @@ -1024,8 +1026,7 @@ static void controlClientThread(ConsoleConnection&& conn) // NOLINTNEXTLINE(performance-unnecessary-value-param): this is thread void controlThread(std::shared_ptr acceptFD, ComboAddress local) { - try - { + try { setThreadName("dnsdist/control"); ComboAddress client; int sock{-1}; diff --git a/pdns/dnsdistdist/dnsdist-console.hh b/pdns/dnsdistdist/dnsdist-console.hh index 6e227d47165d..dd833c49e473 100644 --- a/pdns/dnsdistdist/dnsdist-console.hh +++ b/pdns/dnsdistdist/dnsdist-console.hh @@ -25,7 +25,8 @@ #include "sstuff.hh" #ifndef DISABLE_COMPLETION -struct ConsoleKeyword { +struct ConsoleKeyword +{ std::string name; bool function; std::string parameters; @@ -42,8 +43,9 @@ struct ConsoleKeyword { } }; extern const std::vector g_consoleKeywords; -extern "C" { -char** my_completion( const char * text , int start, int end); +extern "C" +{ + char** my_completion(const char* text, int start, int end); } #endif /* DISABLE_COMPLETION */ diff --git a/pdns/dnsdistdist/dnsdist-dynbpf.cc b/pdns/dnsdistdist/dnsdist-dynbpf.cc index 9ede03fabf86..54902b367e6d 100644 --- a/pdns/dnsdistdist/dnsdist-dynbpf.cc +++ b/pdns/dnsdistdist/dnsdist-dynbpf.cc @@ -49,10 +49,10 @@ void DynBPFFilter::purgeExpired(const struct timespec& now) { auto data = d_data.lock(); - typedef boost::multi_index::nth_index::type ordered_until; + typedef boost::multi_index::nth_index::type ordered_until; ordered_until& ou = boost::multi_index::get<1>(data->d_entries); - for (ordered_until::iterator it = ou.begin(); it != ou.end(); ) { + for (ordered_until::iterator it = ou.begin(); it != ou.end();) { if (it->d_until < now) { ComboAddress addr = it->d_addr; it = ou.erase(it); @@ -64,9 +64,9 @@ void DynBPFFilter::purgeExpired(const struct timespec& now) } } -std::vector > DynBPFFilter::getAddrStats() +std::vector> DynBPFFilter::getAddrStats() { - std::vector > result; + std::vector> result; auto data = d_data.lock(); if (!data->d_bpf) { diff --git a/pdns/dnsdistdist/dnsdist-dynbpf.hh b/pdns/dnsdistdist/dnsdist-dynbpf.hh index 907a7300b9e8..cfb5d6b3c2ad 100644 --- a/pdns/dnsdistdist/dnsdist-dynbpf.hh +++ b/pdns/dnsdistdist/dnsdist-dynbpf.hh @@ -50,27 +50,28 @@ public: /* returns true if the addr wasn't already blocked, false otherwise */ bool block(const ComboAddress& addr, const struct timespec& until); void purgeExpired(const struct timespec& now); - std::vector > getAddrStats(); + std::vector> getAddrStats(); + private: struct BlockEntry { - BlockEntry(const ComboAddress& addr, const struct timespec until): d_addr(addr), d_until(until) + BlockEntry(const ComboAddress& addr, const struct timespec until) : + d_addr(addr), d_until(until) { } ComboAddress d_addr; struct timespec d_until; }; typedef boost::multi_index_container, ComboAddress::addressOnlyLessThan >, - boost::multi_index::ordered_non_unique< boost::multi_index::member > - > - > container_t; - struct Data { + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique, ComboAddress::addressOnlyLessThan>, + boost::multi_index::ordered_non_unique>>> + container_t; + struct Data + { container_t d_entries; std::shared_ptr d_bpf{nullptr}; NetmaskGroup d_excludedSubnets; }; LockGuarded d_data; }; - diff --git a/pdns/dnsdistdist/dnsdist-ecs.cc b/pdns/dnsdistdist/dnsdist-ecs.cc index 2cad1945bca8..5fa61c5b1992 100644 --- a/pdns/dnsdistdist/dnsdist-ecs.cc +++ b/pdns/dnsdistdist/dnsdist-ecs.cc @@ -73,24 +73,24 @@ int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& rrclass = pr.get16BitInt(); GenericDNSPacketWriter pw(newContent, rrname, rrtype, rrclass, dh->opcode); - pw.getHeader()->id=dh->id; - pw.getHeader()->qr=dh->qr; - pw.getHeader()->aa=dh->aa; - pw.getHeader()->tc=dh->tc; - pw.getHeader()->rd=dh->rd; - pw.getHeader()->ra=dh->ra; - pw.getHeader()->ad=dh->ad; - pw.getHeader()->cd=dh->cd; - pw.getHeader()->rcode=dh->rcode; + pw.getHeader()->id = dh->id; + pw.getHeader()->qr = dh->qr; + pw.getHeader()->aa = dh->aa; + pw.getHeader()->tc = dh->tc; + pw.getHeader()->rd = dh->rd; + pw.getHeader()->ra = dh->ra; + pw.getHeader()->ad = dh->ad; + pw.getHeader()->cd = dh->cd; + pw.getHeader()->rcode = dh->rcode; /* consume remaining qd if any */ if (qdcount > 1) { - for(idx = 1; idx < qdcount; idx++) { + for (idx = 1; idx < qdcount; idx++) { rrname = pr.getName(); rrtype = pr.get16BitInt(); rrclass = pr.get16BitInt(); - (void) rrtype; - (void) rrclass; + (void)rrtype; + (void)rrclass; } } @@ -121,7 +121,8 @@ int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true); pr.xfrBlob(blob); pw.xfrBlob(blob); - } else { + } + else { pr.skip(ah.d_clen); } @@ -133,7 +134,7 @@ int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& static bool addOrReplaceEDNSOption(std::vector>& options, uint16_t optionCode, bool& optionAdded, bool overrideExisting, const string& newOptionContent) { - for (auto it = options.begin(); it != options.end(); ) { + for (auto it = options.begin(); it != options.end();) { if (it->first == optionCode) { optionAdded = false; @@ -186,24 +187,24 @@ bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, rrclass = pr.get16BitInt(); GenericDNSPacketWriter pw(newContent, rrname, rrtype, rrclass, dh->opcode); - pw.getHeader()->id=dh->id; - pw.getHeader()->qr=dh->qr; - pw.getHeader()->aa=dh->aa; - pw.getHeader()->tc=dh->tc; - pw.getHeader()->rd=dh->rd; - pw.getHeader()->ra=dh->ra; - pw.getHeader()->ad=dh->ad; - pw.getHeader()->cd=dh->cd; - pw.getHeader()->rcode=dh->rcode; + pw.getHeader()->id = dh->id; + pw.getHeader()->qr = dh->qr; + pw.getHeader()->aa = dh->aa; + pw.getHeader()->tc = dh->tc; + pw.getHeader()->rd = dh->rd; + pw.getHeader()->ra = dh->ra; + pw.getHeader()->ad = dh->ad; + pw.getHeader()->cd = dh->cd; + pw.getHeader()->rcode = dh->rcode; /* consume remaining qd if any */ if (qdcount > 1) { - for(idx = 1; idx < qdcount; idx++) { + for (idx = 1; idx < qdcount; idx++) { rrname = pr.getName(); rrtype = pr.get16BitInt(); rrclass = pr.get16BitInt(); - (void) rrtype; - (void) rrclass; + (void)rrtype; + (void)rrclass; } } @@ -235,7 +236,8 @@ bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true); pr.xfrBlob(blob); pw.xfrBlob(blob); - } else { + } + else { ednsAdded = false; pr.xfrBlob(blob); @@ -286,13 +288,13 @@ static bool slowParseEDNSOptions(const PacketBuffer& packet, EDNSOptionViewMap& uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount); DNSPacketMangler dpm(const_cast(reinterpret_cast(&packet.at(0))), packet.size()); uint64_t n; - for(n=0; n < ntohs(dh->qdcount) ; ++n) { + for (n = 0; n < ntohs(dh->qdcount); ++n) { dpm.skipDomainName(); /* type and class */ dpm.skipBytes(4); } - for(n=0; n < numrecords; ++n) { + for (n = 0; n < numrecords; ++n) { dpm.skipDomainName(); uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3); @@ -300,7 +302,7 @@ static bool slowParseEDNSOptions(const PacketBuffer& packet, EDNSOptionViewMap& dpm.get16BitInt(); dpm.skipBytes(4); /* TTL */ - if(section == 3 && dnstype == QType::OPT) { + if (section == 3 && dnstype == QType::OPT) { uint32_t offset = dpm.getOffset(); if (offset >= packet.size()) { return false; @@ -314,15 +316,14 @@ static bool slowParseEDNSOptions(const PacketBuffer& packet, EDNSOptionViewMap& } } } - catch(...) - { + catch (...) { return false; } return true; } -int locateEDNSOptRR(const PacketBuffer& packet, uint16_t * optStart, size_t * optLen, bool * last) +int locateEDNSOptRR(const PacketBuffer& packet, uint16_t* optStart, size_t* optLen, bool* last) { assert(optStart != NULL); assert(optLen != NULL); @@ -346,12 +347,12 @@ int locateEDNSOptRR(const PacketBuffer& packet, uint16_t * optStart, size_t * op struct dnsrecordheader ah; /* consume qd */ - for(idx = 0; idx < qdcount; idx++) { + for (idx = 0; idx < qdcount; idx++) { rrname = pr.getName(); rrtype = pr.get16BitInt(); rrclass = pr.get16BitInt(); - (void) rrtype; - (void) rrclass; + (void)rrtype; + (void)rrclass; } /* consume AN and NS */ @@ -375,7 +376,7 @@ int locateEDNSOptRR(const PacketBuffer& packet, uint16_t * optStart, size_t * op throw std::range_error("Opt record overflow"); } - if (idx == ((size_t) arcount - 1)) { + if (idx == ((size_t)arcount - 1)) { *last = true; } else { @@ -420,7 +421,7 @@ int getEDNSOptionsStart(const PacketBuffer& packet, const size_t offset, uint16_ } pos += 1; - uint16_t qtype = packet.at(pos)*256 + packet.at(pos+1); + uint16_t qtype = packet.at(pos) * 256 + packet.at(pos + 1); pos += DNS_TYPE_SIZE; pos += DNS_CLASS_SIZE; @@ -647,7 +648,8 @@ bool handleEDNSClientSubnet(PacketBuffer& packet, const size_t maximumSize, cons } return replaceEDNSClientSubnetOption(packet, maximumSize, optRDPosition + ecsOptionStartPosition, ecsOptionSize, optRDPosition, newECSOption); - } else { + } + else { /* we have an EDNS OPT RR but no existing ECS option */ return addECSToExistingOPT(packet, maximumSize, newECSOption, optRDPosition, ecsAdded); } @@ -669,10 +671,10 @@ static int removeEDNSOptionFromOptions(unsigned char* optionsStart, const uint16 size_t pos = 0; while ((pos + 4) <= optionsLen) { unsigned char* optionBegin = p; - const uint16_t optionCode = 0x100*p[0] + p[1]; + const uint16_t optionCode = 0x100 * p[0] + p[1]; p += sizeof(optionCode); pos += sizeof(optionCode); - const uint16_t optionLen = 0x100*p[0] + p[1]; + const uint16_t optionLen = 0x100 * p[0] + p[1]; p += sizeof(optionLen); pos += sizeof(optionLen); if ((pos + optionLen) > optionsLen) { @@ -698,10 +700,10 @@ int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optio if (*optLen < optRecordMinimumSize) { return EINVAL; } - const unsigned char* end = (const unsigned char*) optStart + *optLen; - unsigned char* p = (unsigned char*) optStart + 9; + const unsigned char* end = (const unsigned char*)optStart + *optLen; + unsigned char* p = (unsigned char*)optStart + 9; unsigned char* rdLenPtr = p; - uint16_t rdLen = (0x100*p[0] + p[1]); + uint16_t rdLen = (0x100 * p[0] + p[1]); p += sizeof(rdLen); if (p + rdLen != end) { return EINVAL; @@ -723,7 +725,7 @@ bool isEDNSOptionInOpt(const PacketBuffer& packet, const size_t optStart, const return false; } size_t p = optStart + 9; - uint16_t rdLen = (0x100*static_cast(packet.at(p)) + static_cast(packet.at(p+1))); + uint16_t rdLen = (0x100 * static_cast(packet.at(p)) + static_cast(packet.at(p + 1))); p += sizeof(rdLen); if (rdLen > (optLen - optRecordMinimumSize)) { return false; @@ -731,9 +733,9 @@ bool isEDNSOptionInOpt(const PacketBuffer& packet, const size_t optStart, const size_t rdEnd = p + rdLen; while ((p + 4) <= rdEnd) { - const uint16_t optionCode = 0x100*static_cast(packet.at(p)) + static_cast(packet.at(p+1)); + const uint16_t optionCode = 0x100 * static_cast(packet.at(p)) + static_cast(packet.at(p + 1)); p += sizeof(optionCode); - const uint16_t optionLen = 0x100*static_cast(packet.at(p)) + static_cast(packet.at(p+1)); + const uint16_t optionLen = 0x100 * static_cast(packet.at(p)) + static_cast(packet.at(p + 1)); p += sizeof(optionLen); if ((p + optionLen) > rdEnd) { @@ -785,24 +787,24 @@ int rewriteResponseWithoutEDNSOption(const PacketBuffer& initialPacket, const ui rrclass = pr.get16BitInt(); GenericDNSPacketWriter pw(newContent, rrname, rrtype, rrclass, dh->opcode); - pw.getHeader()->id=dh->id; - pw.getHeader()->qr=dh->qr; - pw.getHeader()->aa=dh->aa; - pw.getHeader()->tc=dh->tc; - pw.getHeader()->rd=dh->rd; - pw.getHeader()->ra=dh->ra; - pw.getHeader()->ad=dh->ad; - pw.getHeader()->cd=dh->cd; - pw.getHeader()->rcode=dh->rcode; + pw.getHeader()->id = dh->id; + pw.getHeader()->qr = dh->qr; + pw.getHeader()->aa = dh->aa; + pw.getHeader()->tc = dh->tc; + pw.getHeader()->rd = dh->rd; + pw.getHeader()->ra = dh->ra; + pw.getHeader()->ad = dh->ad; + pw.getHeader()->cd = dh->cd; + pw.getHeader()->rcode = dh->rcode; /* consume remaining qd if any */ if (qdcount > 1) { - for(idx = 1; idx < qdcount; idx++) { + for (idx = 1; idx < qdcount; idx++) { rrname = pr.getName(); rrtype = pr.get16BitInt(); rrclass = pr.get16BitInt(); - (void) rrtype; - (void) rrclass; + (void)rrtype; + (void)rrclass; } } @@ -834,7 +836,8 @@ int rewriteResponseWithoutEDNSOption(const PacketBuffer& initialPacket, const ui pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true); pr.xfrBlob(blob); pw.xfrBlob(blob); - } else { + } + else { pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, false); pr.xfrBlob(blob); uint16_t rdLen = blob.length(); @@ -843,7 +846,8 @@ int rewriteResponseWithoutEDNSOption(const PacketBuffer& initialPacket, const ui if (rdLen > 0) { blob.resize((size_t)rdLen); pw.xfrBlob(blob); - } else { + } + else { pw.commit(); } } @@ -952,7 +956,8 @@ bool setNegativeAndAdditionalSOA(DNSQuestion& dq, bool nxd, const DNSName& zone, dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [soaInAuthoritySection](dnsheader& header) { if (soaInAuthoritySection) { header.nscount = htons(1); - } else { + } + else { header.arcount = htons(1); } return true; @@ -1012,8 +1017,7 @@ bool addEDNSToQueryTurnedResponse(DNSQuestion& dq) // goal in life - if you send us a reasonably normal packet, we'll get Z for you, otherwise 0 int getEDNSZ(const DNSQuestion& dq) { - try - { + try { const auto& dh = dq.getHeader(); if (ntohs(dh->qdcount) != 1 || dh->ancount != 0 || ntohs(dh->arcount) != 1 || dh->nscount != 0) { return 0; @@ -1038,7 +1042,7 @@ int getEDNSZ(const DNSQuestion& dq) pos++; - uint16_t qtype = packet.at(pos)*256 + packet.at(pos+1); + uint16_t qtype = packet.at(pos) * 256 + packet.at(pos + 1); pos += DNS_TYPE_SIZE; pos += DNS_CLASS_SIZE; @@ -1047,10 +1051,9 @@ int getEDNSZ(const DNSQuestion& dq) } const uint8_t* z = &packet.at(pos + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE); - return 0x100 * (*z) + *(z+1); + return 0x100 * (*z) + *(z + 1); } - catch(...) - { + catch (...) { return 0; } } @@ -1134,8 +1137,9 @@ bool setEDNSOption(DNSQuestion& dq, uint16_t ednsCode, const std::string& ednsDa return true; } -namespace dnsdist { -bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uint8_t rcode, bool clearAnswers) +namespace dnsdist +{ +bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uint8_t rcode, bool clearAnswers) { const auto qnameLength = state.qname.wirelength(); if (buffer.size() < sizeof(dnsheader) + qnameLength + sizeof(uint16_t) + sizeof(uint16_t)) { @@ -1148,7 +1152,7 @@ bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uin hadEDNS = getEDNS0Record(buffer, edns0); } - dnsdist::PacketMangling::editDNSHeaderFromPacket(buffer, [rcode,clearAnswers](dnsheader& header) { + dnsdist::PacketMangling::editDNSHeaderFromPacket(buffer, [rcode, clearAnswers](dnsheader& header) { header.rcode = rcode; header.ad = false; header.aa = false; diff --git a/pdns/dnsdistdist/dnsdist-ecs.hh b/pdns/dnsdistdist/dnsdist-ecs.hh index f5d215f1a065..3af754d8af42 100644 --- a/pdns/dnsdistdist/dnsdist-ecs.hh +++ b/pdns/dnsdistdist/dnsdist-ecs.hh @@ -36,12 +36,12 @@ extern uint16_t g_PayloadSizeSelfGenAnswers; int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& newContent); bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, const string& newOptionContent); -int locateEDNSOptRR(const PacketBuffer & packet, uint16_t * optStart, size_t * optLen, bool * last); +int locateEDNSOptRR(const PacketBuffer& packet, uint16_t* optStart, size_t* optLen, bool* last); bool generateOptRR(const std::string& optRData, PacketBuffer& res, size_t maximumSize, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK); void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength); int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove); int rewriteResponseWithoutEDNSOption(const PacketBuffer& initialPacket, const uint16_t optionCodeToSkip, PacketBuffer& newContent); -int getEDNSOptionsStart(const PacketBuffer& packet, const size_t offset, uint16_t* optRDPosition, size_t * remaining); +int getEDNSOptionsStart(const PacketBuffer& packet, const size_t offset, uint16_t* optRDPosition, size_t* remaining); bool isEDNSOptionInOpt(const PacketBuffer& packet, const size_t optStart, const size_t optLen, const uint16_t optionCodeToFind, size_t* optContentStart = nullptr, uint16_t* optContentLen = nullptr); bool addEDNS(PacketBuffer& packet, size_t maximumSize, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode); bool addEDNSToQueryTurnedResponse(DNSQuestion& dq); @@ -59,6 +59,7 @@ bool getEDNS0Record(const PacketBuffer& packet, EDNS0Record& edns0); bool setEDNSOption(DNSQuestion& dq, uint16_t ednsCode, const std::string& data); struct InternalQueryState; -namespace dnsdist { -bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uint8_t rcode, bool clearAnswers); +namespace dnsdist +{ +bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uint8_t rcode, bool clearAnswers); } diff --git a/pdns/dnsdistdist/dnsdist-lbpolicies.hh b/pdns/dnsdistdist/dnsdist-lbpolicies.hh index 72443402d10f..78fcb22016b2 100644 --- a/pdns/dnsdistdist/dnsdist-lbpolicies.hh +++ b/pdns/dnsdistdist/dnsdist-lbpolicies.hh @@ -32,16 +32,19 @@ struct PerThreadPoliciesState; class ServerPolicy { public: - template using NumberedVector = std::vector >; + template + using NumberedVector = std::vector>; using NumberedServerVector = NumberedVector>; typedef std::function(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t; typedef std::function ffipolicyfunc_t; - ServerPolicy(const std::string& name_, policyfunc_t policy_, bool isLua_): d_name(name_), d_policy(std::move(policy_)), d_isLua(isLua_) + ServerPolicy(const std::string& name_, policyfunc_t policy_, bool isLua_) : + d_name(name_), d_policy(std::move(policy_)), d_isLua(isLua_) { } - ServerPolicy(const std::string& name_, ffipolicyfunc_t policy_): d_name(name_), d_ffipolicy(std::move(policy_)), d_isLua(true), d_isFFI(true) + ServerPolicy(const std::string& name_, ffipolicyfunc_t policy_) : + d_name(name_), d_ffipolicy(std::move(policy_)), d_isLua(true), d_isFFI(true) { } @@ -59,7 +62,8 @@ public: return d_name; } - std::string toString() const { + std::string toString() const + { return string("ServerPolicy") + (d_isLua ? " (Lua)" : "") + " \"" + d_name + "\""; } @@ -74,7 +78,6 @@ private: const ffipolicyfunc_t& getPerThreadPolicy() const; static thread_local PerThreadState t_perThreadState; - public: std::string d_name; std::string d_perThreadPolicyCode; @@ -96,7 +99,7 @@ void setPoolPolicy(pools_t& pools, const string& poolName, std::shared_ptr server); void removeServerFromPool(pools_t& pools, const string& poolName, std::shared_ptr server); -const std::shared_ptr getDownstreamCandidates(const map>& pools, const std::string& poolName); +const std::shared_ptr getDownstreamCandidates(const map>& pools, const std::string& poolName); std::shared_ptr firstAvailable(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq); diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc index 4512fc5ef4f8..deb8c3f7aca5 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc @@ -33,80 +33,88 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) #ifndef DISABLE_NON_FFI_DQ_BINDINGS /* DNSQuestion */ /* PowerDNS DNSQuestion compat */ - luaCtx.registerMember("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; }); - luaCtx.registerMember("qname", [](const DNSQuestion& dq) -> const DNSName { return dq.ids.qname; }, [](DNSQuestion& dq, const DNSName& newName) { (void) newName; }); - luaCtx.registerMember("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; }); - luaCtx.registerMember("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; }); - luaCtx.registerMember("rcode", [](const DNSQuestion& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSQuestion& dq, int newRCode) { - dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { - header.rcode = static_cast(newRCode); - return true; - }); - }); - luaCtx.registerMember("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; }); + luaCtx.registerMember( + "localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void)newLocal; }); + luaCtx.registerMember( + "qname", [](const DNSQuestion& dq) -> const DNSName { return dq.ids.qname; }, [](DNSQuestion& dq, const DNSName& newName) { (void)newName; }); + luaCtx.registerMember( + "qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void)newType; }); + luaCtx.registerMember( + "qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void)newClass; }); + luaCtx.registerMember( + "rcode", [](const DNSQuestion& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSQuestion& dq, int newRCode) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { + header.rcode = static_cast(newRCode); + return true; + }); }); + luaCtx.registerMember( + "remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void)newRemote; }); /* DNSDist DNSQuestion */ - luaCtx.registerMember("dh", [](const DNSQuestion& dq) -> dnsheader* { return dq.getMutableHeader(); }, [](DNSQuestion& dq, const dnsheader* dh) { - dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [&dh](dnsheader& header) { - header = *dh; - return true; - }); - }); - luaCtx.registerMember("len", [](const DNSQuestion& dq) -> uint16_t { return dq.getData().size(); }, [](DNSQuestion& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); - luaCtx.registerMember("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; }); - luaCtx.registerMember("tcp", [](const DNSQuestion& dq) -> bool { return dq.overTCP(); }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; }); - luaCtx.registerMember("skipCache", [](const DNSQuestion& dq) -> bool { return dq.ids.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); - luaCtx.registerMember("pool", [](const DNSQuestion& dq) -> std::string { return dq.ids.poolName; }, [](DNSQuestion& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); - luaCtx.registerMember("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; }); - luaCtx.registerMember("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; }); - luaCtx.registerMember("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; }); - luaCtx.registerMember (DNSQuestion::*)>("tempFailureTTL", - [](const DNSQuestion& dq) -> boost::optional { - return dq.ids.tempFailureTTL; - }, - [](DNSQuestion& dq, boost::optional newValue) { - dq.ids.tempFailureTTL = newValue; - } - ); - luaCtx.registerMember("deviceID", [](const DNSQuestion& dq) -> std::string { + luaCtx.registerMember( + "dh", [](const DNSQuestion& dq) -> dnsheader* { return dq.getMutableHeader(); }, [](DNSQuestion& dq, const dnsheader* dh) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [&dh](dnsheader& header) { + header = *dh; + return true; + }); }); + luaCtx.registerMember( + "len", [](const DNSQuestion& dq) -> uint16_t { return dq.getData().size(); }, [](DNSQuestion& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); + luaCtx.registerMember( + "opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void)newOpcode; }); + luaCtx.registerMember( + "tcp", [](const DNSQuestion& dq) -> bool { return dq.overTCP(); }, [](DNSQuestion& dq, bool newTcp) { (void)newTcp; }); + luaCtx.registerMember( + "skipCache", [](const DNSQuestion& dq) -> bool { return dq.ids.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); + luaCtx.registerMember( + "pool", [](const DNSQuestion& dq) -> std::string { return dq.ids.poolName; }, [](DNSQuestion& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); + luaCtx.registerMember( + "useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; }); + luaCtx.registerMember( + "ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; }); + luaCtx.registerMember( + "ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; }); + luaCtx.registerMember(DNSQuestion::*)>( + "tempFailureTTL", + [](const DNSQuestion& dq) -> boost::optional { + return dq.ids.tempFailureTTL; + }, + [](DNSQuestion& dq, boost::optional newValue) { + dq.ids.tempFailureTTL = newValue; + }); + luaCtx.registerMember( + "deviceID", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_deviceID; } - return std::string(); - }, [](DNSQuestion& dq, const std::string& newValue) { + return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } - dq.ids.d_protoBufData->d_deviceID = newValue; - }); - luaCtx.registerMember("deviceName", [](const DNSQuestion& dq) -> std::string { + dq.ids.d_protoBufData->d_deviceID = newValue; }); + luaCtx.registerMember( + "deviceName", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_deviceName; } - return std::string(); - }, [](DNSQuestion& dq, const std::string& newValue) { + return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } - dq.ids.d_protoBufData->d_deviceName = newValue; - }); - luaCtx.registerMember("requestorID", [](const DNSQuestion& dq) -> std::string { + dq.ids.d_protoBufData->d_deviceName = newValue; }); + luaCtx.registerMember( + "requestorID", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_requestorID; } - return std::string(); - }, [](DNSQuestion& dq, const std::string& newValue) { + return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } - dq.ids.d_protoBufData->d_requestorID = newValue; - }); - luaCtx.registerFunction("getDO", [](const DNSQuestion& dq) { + dq.ids.d_protoBufData->d_requestorID = newValue; }); + luaCtx.registerFunction("getDO", [](const DNSQuestion& dq) { return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO; - }); - luaCtx.registerFunction("getContent", [](const DNSQuestion& dq) { + }); + luaCtx.registerFunction("getContent", [](const DNSQuestion& dq) { return std::string(reinterpret_cast(dq.getData().data()), dq.getData().size()); }); - luaCtx.registerFunction("setContent", [](DNSQuestion& dq, const std::string& raw) { + luaCtx.registerFunction("setContent", [](DNSQuestion& dq, const std::string& raw) { uint16_t oldID = dq.getHeader()->id; auto& buffer = dq.getMutableData(); buffer.clear(); @@ -117,73 +125,73 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) return true; }); }); - luaCtx.registerFunction(DNSQuestion::*)()const>("getEDNSOptions", [](const DNSQuestion& dq) { + luaCtx.registerFunction (DNSQuestion::*)() const>("getEDNSOptions", [](const DNSQuestion& dq) { + if (dq.ednsOptions == nullptr) { + parseEDNSOptions(dq); if (dq.ednsOptions == nullptr) { - parseEDNSOptions(dq); - if (dq.ednsOptions == nullptr) { - throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); - } + throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); } + } - return *dq.ednsOptions; - }); - luaCtx.registerFunction("getTrailingData", [](const DNSQuestion& dq) { - return dq.getTrailingData(); - }); - luaCtx.registerFunction("setTrailingData", [](DNSQuestion& dq, const std::string& tail) { - return dq.setTrailingData(tail); - }); + return *dq.ednsOptions; + }); + luaCtx.registerFunction("getTrailingData", [](const DNSQuestion& dq) { + return dq.getTrailingData(); + }); + luaCtx.registerFunction("setTrailingData", [](DNSQuestion& dq, const std::string& tail) { + return dq.setTrailingData(tail); + }); - luaCtx.registerFunction("getServerNameIndication", [](const DNSQuestion& dq) { - return dq.sni; - }); + luaCtx.registerFunction("getServerNameIndication", [](const DNSQuestion& dq) { + return dq.sni; + }); - luaCtx.registerFunction("getProtocol", [](const DNSQuestion& dq) { + luaCtx.registerFunction("getProtocol", [](const DNSQuestion& dq) { return dq.getProtocol().toPrettyString(); }); - luaCtx.registerFunction("getQueryTime", [](const DNSQuestion& dq) { + luaCtx.registerFunction("getQueryTime", [](const DNSQuestion& dq) { return dq.ids.queryRealTime.getStartTime(); }); - luaCtx.registerFunction("sendTrap", [](const DNSQuestion& dq, boost::optional reason) { + luaCtx.registerFunction("sendTrap", [](const DNSQuestion& dq, boost::optional reason) { #ifdef HAVE_NET_SNMP - if (g_snmpAgent && g_snmpTrapsEnabled) { - g_snmpAgent->sendDNSTrap(dq, reason ? *reason : ""); - } + if (g_snmpAgent && g_snmpTrapsEnabled) { + g_snmpAgent->sendDNSTrap(dq, reason ? *reason : ""); + } #endif /* HAVE_NET_SNMP */ - }); + }); - luaCtx.registerFunction("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) { - dq.setTag(strLabel, strValue); - }); - luaCtx.registerFunction)>("setTagArray", [](DNSQuestion& dq, const LuaAssociativeTable&tags) { - for (const auto& tag : tags) { - dq.setTag(tag.first, tag.second); - } - }); - luaCtx.registerFunction("getTag", [](const DNSQuestion& dq, const std::string& strLabel) { - if (!dq.ids.qTag) { - return string(); - } + luaCtx.registerFunction("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) { + dq.setTag(strLabel, strValue); + }); + luaCtx.registerFunction)>("setTagArray", [](DNSQuestion& dq, const LuaAssociativeTable& tags) { + for (const auto& tag : tags) { + dq.setTag(tag.first, tag.second); + } + }); + luaCtx.registerFunction("getTag", [](const DNSQuestion& dq, const std::string& strLabel) { + if (!dq.ids.qTag) { + return string(); + } - std::string strValue; - const auto it = dq.ids.qTag->find(strLabel); - if (it == dq.ids.qTag->cend()) { - return string(); - } - return it->second; - }); - luaCtx.registerFunction("getTagArray", [](const DNSQuestion& dq) { - if (!dq.ids.qTag) { - QTag empty; - return empty; - } + std::string strValue; + const auto it = dq.ids.qTag->find(strLabel); + if (it == dq.ids.qTag->cend()) { + return string(); + } + return it->second; + }); + luaCtx.registerFunction("getTagArray", [](const DNSQuestion& dq) { + if (!dq.ids.qTag) { + QTag empty; + return empty; + } - return *dq.ids.qTag; - }); + return *dq.ids.qTag; + }); - luaCtx.registerFunction)>("setProxyProtocolValues", [](DNSQuestion& dq, const LuaArray& values) { + luaCtx.registerFunction)>("setProxyProtocolValues", [](DNSQuestion& dq, const LuaArray& values) { if (!dq.proxyProtocolValues) { dq.proxyProtocolValues = make_unique>(); } @@ -196,7 +204,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) } }); - luaCtx.registerFunction("addProxyProtocolValue", [](DNSQuestion& dq, uint64_t type, std::string value) { + luaCtx.registerFunction("addProxyProtocolValue", [](DNSQuestion& dq, uint64_t type, std::string value) { checkParameterBound("addProxyProtocolValue", type, std::numeric_limits::max()); if (!dq.proxyProtocolValues) { dq.proxyProtocolValues = make_unique>(); @@ -205,7 +213,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) dq.proxyProtocolValues->push_back({std::move(value), static_cast(type)}); }); - luaCtx.registerFunction(DNSQuestion::*)()>("getProxyProtocolValues", [](const DNSQuestion& dq) { + luaCtx.registerFunction (DNSQuestion::*)()>("getProxyProtocolValues", [](const DNSQuestion& dq) { LuaArray result; if (!dq.proxyProtocolValues) { return result; @@ -213,13 +221,13 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) result.resize(dq.proxyProtocolValues->size()); for (const auto& value : *dq.proxyProtocolValues) { - result.push_back({ value.type, value.content }); + result.push_back({value.type, value.content}); } return result; }); - luaCtx.registerFunction("changeName", [](DNSQuestion& dq, const DNSName& newName) -> bool { + luaCtx.registerFunction("changeName", [](DNSQuestion& dq, const DNSName& newName) -> bool { if (!dnsdist::changeNameInDNSPacket(dq.getMutableData(), dq.ids.qname, newName)) { return false; } @@ -227,38 +235,38 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) return true; }); - luaCtx.registerFunction, LuaArray>&, boost::optional)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant, LuaArray>& response, boost::optional typeForAny) { - if (response.type() == typeid(LuaArray)) { - std::vector data; - auto responses = boost::get>(response); - data.reserve(responses.size()); - for (const auto& resp : responses) { - data.push_back(resp.second); - } - std::string result; - SpoofAction tempSpoofAction(data); - tempSpoofAction(&dnsQuestion, &result); - return; + luaCtx.registerFunction, LuaArray>&, boost::optional)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant, LuaArray>& response, boost::optional typeForAny) { + if (response.type() == typeid(LuaArray)) { + std::vector data; + auto responses = boost::get>(response); + data.reserve(responses.size()); + for (const auto& resp : responses) { + data.push_back(resp.second); } - if (response.type() == typeid(LuaArray)) { - std::vector data; - auto responses = boost::get>(response); - data.reserve(responses.size()); - for (const auto& resp : responses) { - data.push_back(resp.second); - } - std::string result; - SpoofAction tempSpoofAction(data, typeForAny ? *typeForAny : std::optional()); - tempSpoofAction(&dnsQuestion, &result); - return; + std::string result; + SpoofAction tempSpoofAction(data); + tempSpoofAction(&dnsQuestion, &result); + return; + } + if (response.type() == typeid(LuaArray)) { + std::vector data; + auto responses = boost::get>(response); + data.reserve(responses.size()); + for (const auto& resp : responses) { + data.push_back(resp.second); } + std::string result; + SpoofAction tempSpoofAction(data, typeForAny ? *typeForAny : std::optional()); + tempSpoofAction(&dnsQuestion, &result); + return; + } }); - luaCtx.registerFunction("setEDNSOption", [](DNSQuestion& dq, uint16_t code, const std::string& data) { + luaCtx.registerFunction("setEDNSOption", [](DNSQuestion& dq, uint16_t code, const std::string& data) { setEDNSOption(dq, code, data); }); - luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSQuestion& dnsQuestion, uint16_t infoCode, const boost::optional& extraText) { + luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSQuestion& dnsQuestion, uint16_t infoCode, const boost::optional& extraText) { EDNSExtendedError ede; ede.infoCode = infoCode; if (extraText) { @@ -267,77 +275,78 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) dnsQuestion.ids.d_extendedError = std::make_unique(ede); }); - luaCtx.registerFunction("suspend", [](DNSQuestion& dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { + luaCtx.registerFunction("suspend", [](DNSQuestion& dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { dq.asynchronous = true; return dnsdist::suspendQuery(dq, asyncID, queryID, timeoutMs); }); - luaCtx.registerFunction("setRestartable", [](DNSQuestion& dq) { + luaCtx.registerFunction("setRestartable", [](DNSQuestion& dq) { dq.ids.d_packet = std::make_unique(dq.getData()); return true; }); -class AsynchronousObject -{ -public: - AsynchronousObject(std::unique_ptr&& obj_): object(std::move(obj_)) - { - } - - DNSQuestion getDQ() const + class AsynchronousObject { - return object->getDQ(); - } + public: + AsynchronousObject(std::unique_ptr&& obj_) : + object(std::move(obj_)) + { + } - DNSResponse getDR() const - { - return object->getDR(); - } + DNSQuestion getDQ() const + { + return object->getDQ(); + } - bool resume() - { - return dnsdist::queueQueryResumptionEvent(std::move(object)); - } + DNSResponse getDR() const + { + return object->getDR(); + } - bool drop() - { - auto sender = object->getTCPQuerySender(); - if (!sender) { - return false; + bool resume() + { + return dnsdist::queueQueryResumptionEvent(std::move(object)); } - struct timeval now; - gettimeofday(&now, nullptr); - sender->notifyIOError(now, TCPResponse(std::move(object->query))); - return true; - } + bool drop() + { + auto sender = object->getTCPQuerySender(); + if (!sender) { + return false; + } - bool setRCode(uint8_t rcode, bool clearAnswers) - { - return dnsdist::setInternalQueryRCode(object->query.d_idstate, object->query.d_buffer, rcode, clearAnswers); - } + struct timeval now; + gettimeofday(&now, nullptr); + sender->notifyIOError(now, TCPResponse(std::move(object->query))); + return true; + } -private: - std::unique_ptr object; -}; + bool setRCode(uint8_t rcode, bool clearAnswers) + { + return dnsdist::setInternalQueryRCode(object->query.d_idstate, object->query.d_buffer, rcode, clearAnswers); + } - luaCtx.registerFunction("getDQ", [](const AsynchronousObject& obj) { - return obj.getDQ(); - }); + private: + std::unique_ptr object; + }; - luaCtx.registerFunction("getDR", [](const AsynchronousObject& obj) { - return obj.getDR(); - }); + luaCtx.registerFunction("getDQ", [](const AsynchronousObject& obj) { + return obj.getDQ(); + }); - luaCtx.registerFunction("resume", [](AsynchronousObject& obj) { - return obj.resume(); - }); + luaCtx.registerFunction("getDR", [](const AsynchronousObject& obj) { + return obj.getDR(); + }); - luaCtx.registerFunction("drop", [](AsynchronousObject& obj) { - return obj.drop(); - }); + luaCtx.registerFunction("resume", [](AsynchronousObject& obj) { + return obj.resume(); + }); + + luaCtx.registerFunction("drop", [](AsynchronousObject& obj) { + return obj.drop(); + }); - luaCtx.registerFunction("setRCode", [](AsynchronousObject& obj, uint8_t rcode, bool clearAnswers) { + luaCtx.registerFunction("setRCode", [](AsynchronousObject& obj, uint8_t rcode, bool clearAnswers) { return obj.setRCode(rcode, clearAnswers); }); @@ -353,38 +362,46 @@ class AsynchronousObject }); /* LuaWrapper doesn't support inheritance */ - luaCtx.registerMember("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; }); - luaCtx.registerMember("qname", [](const DNSResponse& dq) -> const DNSName { return dq.ids.qname; }, [](DNSResponse& dq, const DNSName& newName) { (void) newName; }); - luaCtx.registerMember("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; }); - luaCtx.registerMember("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; }); - luaCtx.registerMember("rcode", [](const DNSResponse& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSResponse& dq, int newRCode) { - dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { - header.rcode = static_cast(newRCode); - return true; - }); + luaCtx.registerMember( + "localaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void)newLocal; }); + luaCtx.registerMember( + "qname", [](const DNSResponse& dq) -> const DNSName { return dq.ids.qname; }, [](DNSResponse& dq, const DNSName& newName) { (void)newName; }); + luaCtx.registerMember( + "qtype", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void)newType; }); + luaCtx.registerMember( + "qclass", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void)newClass; }); + luaCtx.registerMember( + "rcode", [](const DNSResponse& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSResponse& dq, int newRCode) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { + header.rcode = static_cast(newRCode); + return true; + }); }); + luaCtx.registerMember( + "remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void)newRemote; }); + luaCtx.registerMember( + "dh", [](const DNSResponse& dr) -> dnsheader* { return dr.getMutableHeader(); }, [](DNSResponse& dr, const dnsheader* dh) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dr.getMutableData(), [&dh](dnsheader& header) { + header = *dh; + return true; + }); }); + luaCtx.registerMember( + "len", [](const DNSResponse& dq) -> uint16_t { return dq.getData().size(); }, [](DNSResponse& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); + luaCtx.registerMember( + "opcode", [](const DNSResponse& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void)newOpcode; }); + luaCtx.registerMember( + "tcp", [](const DNSResponse& dq) -> bool { return dq.overTCP(); }, [](DNSResponse& dq, bool newTcp) { (void)newTcp; }); + luaCtx.registerMember( + "skipCache", [](const DNSResponse& dq) -> bool { return dq.ids.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); + luaCtx.registerMember( + "pool", [](const DNSResponse& dq) -> std::string { return dq.ids.poolName; }, [](DNSResponse& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); + luaCtx.registerFunction editFunc)>("editTTLs", [](DNSResponse& dr, std::function editFunc) { + editDNSPacketTTL(reinterpret_cast(dr.getMutableData().data()), dr.getData().size(), editFunc); }); - luaCtx.registerMember("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; }); - luaCtx.registerMember("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.getMutableHeader(); }, [](DNSResponse& dr, const dnsheader* dh) { - dnsdist::PacketMangling::editDNSHeaderFromPacket(dr.getMutableData(), [&dh](dnsheader& header) { - header = *dh; - return true; - }); + luaCtx.registerFunction("getDO", [](const DNSResponse& dq) { + return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO; }); - luaCtx.registerMember("len", [](const DNSResponse& dq) -> uint16_t { return dq.getData().size(); }, [](DNSResponse& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); - luaCtx.registerMember("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; }); - luaCtx.registerMember("tcp", [](const DNSResponse& dq) -> bool { return dq.overTCP(); }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; }); - luaCtx.registerMember("skipCache", [](const DNSResponse& dq) -> bool { return dq.ids.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); - luaCtx.registerMember("pool", [](const DNSResponse& dq) -> std::string { return dq.ids.poolName; }, [](DNSResponse& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); - luaCtx.registerFunction editFunc)>("editTTLs", [](DNSResponse& dr, std::function editFunc) { - editDNSPacketTTL(reinterpret_cast(dr.getMutableData().data()), dr.getData().size(), editFunc); - }); - luaCtx.registerFunction("getDO", [](const DNSResponse& dq) { - return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO; - }); - luaCtx.registerFunction("getContent", [](const DNSResponse& dq) { + luaCtx.registerFunction("getContent", [](const DNSResponse& dq) { return std::string(reinterpret_cast(dq.getData().data()), dq.getData().size()); }); - luaCtx.registerFunction("setContent", [](DNSResponse& dr, const std::string& raw) { + luaCtx.registerFunction("setContent", [](DNSResponse& dr, const std::string& raw) { uint16_t oldID = dr.getHeader()->id; auto& buffer = dr.getMutableData(); buffer.clear(); @@ -395,127 +412,127 @@ class AsynchronousObject }); }); - luaCtx.registerFunction(DNSResponse::*)()const>("getEDNSOptions", [](const DNSResponse& dq) { + luaCtx.registerFunction (DNSResponse::*)() const>("getEDNSOptions", [](const DNSResponse& dq) { + if (dq.ednsOptions == nullptr) { + parseEDNSOptions(dq); if (dq.ednsOptions == nullptr) { - parseEDNSOptions(dq); - if (dq.ednsOptions == nullptr) { - throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); - } + throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); } + } - return *dq.ednsOptions; - }); - luaCtx.registerFunction("getTrailingData", [](const DNSResponse& dq) { - return dq.getTrailingData(); - }); - luaCtx.registerFunction("setTrailingData", [](DNSResponse& dq, const std::string& tail) { - return dq.setTrailingData(tail); - }); + return *dq.ednsOptions; + }); + luaCtx.registerFunction("getTrailingData", [](const DNSResponse& dq) { + return dq.getTrailingData(); + }); + luaCtx.registerFunction("setTrailingData", [](DNSResponse& dq, const std::string& tail) { + return dq.setTrailingData(tail); + }); - luaCtx.registerFunction("setTag", [](DNSResponse& dr, const std::string& strLabel, const std::string& strValue) { - dr.setTag(strLabel, strValue); - }); + luaCtx.registerFunction("setTag", [](DNSResponse& dr, const std::string& strLabel, const std::string& strValue) { + dr.setTag(strLabel, strValue); + }); - luaCtx.registerFunction)>("setTagArray", [](DNSResponse& dr, const LuaAssociativeTable&tags) { - for (const auto& tag : tags) { - dr.setTag(tag.first, tag.second); - } - }); - luaCtx.registerFunction("getTag", [](const DNSResponse& dr, const std::string& strLabel) { - if (!dr.ids.qTag) { - return string(); - } + luaCtx.registerFunction)>("setTagArray", [](DNSResponse& dr, const LuaAssociativeTable& tags) { + for (const auto& tag : tags) { + dr.setTag(tag.first, tag.second); + } + }); + luaCtx.registerFunction("getTag", [](const DNSResponse& dr, const std::string& strLabel) { + if (!dr.ids.qTag) { + return string(); + } - std::string strValue; - const auto it = dr.ids.qTag->find(strLabel); - if (it == dr.ids.qTag->cend()) { - return string(); - } - return it->second; - }); - luaCtx.registerFunction("getTagArray", [](const DNSResponse& dr) { - if (!dr.ids.qTag) { - QTag empty; - return empty; - } + std::string strValue; + const auto it = dr.ids.qTag->find(strLabel); + if (it == dr.ids.qTag->cend()) { + return string(); + } + return it->second; + }); + luaCtx.registerFunction("getTagArray", [](const DNSResponse& dr) { + if (!dr.ids.qTag) { + QTag empty; + return empty; + } - return *dr.ids.qTag; - }); + return *dr.ids.qTag; + }); - luaCtx.registerFunction("getProtocol", [](const DNSResponse& dr) { + luaCtx.registerFunction("getProtocol", [](const DNSResponse& dr) { return dr.getProtocol().toPrettyString(); }); - luaCtx.registerFunction("getQueryTime", [](const DNSResponse& dr) { + luaCtx.registerFunction("getQueryTime", [](const DNSResponse& dr) { return dr.ids.queryRealTime.getStartTime(); }); - luaCtx.registerFunction("sendTrap", [](const DNSResponse& dr, boost::optional reason) { + luaCtx.registerFunction("sendTrap", [](const DNSResponse& dr, boost::optional reason) { #ifdef HAVE_NET_SNMP - if (g_snmpAgent && g_snmpTrapsEnabled) { - g_snmpAgent->sendDNSTrap(dr, reason ? *reason : ""); - } + if (g_snmpAgent && g_snmpTrapsEnabled) { + g_snmpAgent->sendDNSTrap(dr, reason ? *reason : ""); + } #endif /* HAVE_NET_SNMP */ - }); + }); #ifdef HAVE_DNS_OVER_HTTPS - luaCtx.registerFunction("getHTTPPath", [](const DNSQuestion& dq) { - if (dq.ids.du == nullptr) { - return std::string(); - } - return dq.ids.du->getHTTPPath(); - }); + luaCtx.registerFunction("getHTTPPath", [](const DNSQuestion& dq) { + if (dq.ids.du == nullptr) { + return std::string(); + } + return dq.ids.du->getHTTPPath(); + }); - luaCtx.registerFunction("getHTTPQueryString", [](const DNSQuestion& dq) { - if (dq.ids.du == nullptr) { - return std::string(); - } - return dq.ids.du->getHTTPQueryString(); - }); + luaCtx.registerFunction("getHTTPQueryString", [](const DNSQuestion& dq) { + if (dq.ids.du == nullptr) { + return std::string(); + } + return dq.ids.du->getHTTPQueryString(); + }); - luaCtx.registerFunction("getHTTPHost", [](const DNSQuestion& dq) { - if (dq.ids.du == nullptr) { - return std::string(); - } - return dq.ids.du->getHTTPHost(); - }); + luaCtx.registerFunction("getHTTPHost", [](const DNSQuestion& dq) { + if (dq.ids.du == nullptr) { + return std::string(); + } + return dq.ids.du->getHTTPHost(); + }); - luaCtx.registerFunction("getHTTPScheme", [](const DNSQuestion& dq) { - if (dq.ids.du == nullptr) { - return std::string(); - } - return dq.ids.du->getHTTPScheme(); - }); + luaCtx.registerFunction("getHTTPScheme", [](const DNSQuestion& dq) { + if (dq.ids.du == nullptr) { + return std::string(); + } + return dq.ids.du->getHTTPScheme(); + }); - luaCtx.registerFunction(DNSQuestion::*)(void)const>("getHTTPHeaders", [](const DNSQuestion& dq) { - if (dq.ids.du == nullptr) { - return LuaAssociativeTable(); - } - return dq.ids.du->getHTTPHeaders(); - }); + luaCtx.registerFunction (DNSQuestion::*)(void) const>("getHTTPHeaders", [](const DNSQuestion& dq) { + if (dq.ids.du == nullptr) { + return LuaAssociativeTable(); + } + return dq.ids.du->getHTTPHeaders(); + }); - luaCtx.registerFunction contentType)>("setHTTPResponse", [](DNSQuestion& dq, uint64_t statusCode, const std::string& body, const boost::optional contentType) { - if (dq.ids.du == nullptr) { - return; - } - checkParameterBound("DNSQuestion::setHTTPResponse", statusCode, std::numeric_limits::max()); - PacketBuffer vect(body.begin(), body.end()); - dq.ids.du->setHTTPResponse(statusCode, std::move(vect), contentType ? *contentType : ""); - }); + luaCtx.registerFunction contentType)>("setHTTPResponse", [](DNSQuestion& dq, uint64_t statusCode, const std::string& body, const boost::optional contentType) { + if (dq.ids.du == nullptr) { + return; + } + checkParameterBound("DNSQuestion::setHTTPResponse", statusCode, std::numeric_limits::max()); + PacketBuffer vect(body.begin(), body.end()); + dq.ids.du->setHTTPResponse(statusCode, std::move(vect), contentType ? *contentType : ""); + }); #endif /* HAVE_DNS_OVER_HTTPS */ - luaCtx.registerFunction("setNegativeAndAdditionalSOA", [](DNSQuestion& dq, bool nxd, const std::string& zone, uint64_t ttl, const std::string& mname, const std::string& rname, uint64_t serial, uint64_t refresh, uint64_t retry, uint64_t expire, uint64_t minimum) { - checkParameterBound("setNegativeAndAdditionalSOA", ttl, std::numeric_limits::max()); - checkParameterBound("setNegativeAndAdditionalSOA", serial, std::numeric_limits::max()); - checkParameterBound("setNegativeAndAdditionalSOA", refresh, std::numeric_limits::max()); - checkParameterBound("setNegativeAndAdditionalSOA", retry, std::numeric_limits::max()); - checkParameterBound("setNegativeAndAdditionalSOA", expire, std::numeric_limits::max()); - checkParameterBound("setNegativeAndAdditionalSOA", minimum, std::numeric_limits::max()); + luaCtx.registerFunction("setNegativeAndAdditionalSOA", [](DNSQuestion& dq, bool nxd, const std::string& zone, uint64_t ttl, const std::string& mname, const std::string& rname, uint64_t serial, uint64_t refresh, uint64_t retry, uint64_t expire, uint64_t minimum) { + checkParameterBound("setNegativeAndAdditionalSOA", ttl, std::numeric_limits::max()); + checkParameterBound("setNegativeAndAdditionalSOA", serial, std::numeric_limits::max()); + checkParameterBound("setNegativeAndAdditionalSOA", refresh, std::numeric_limits::max()); + checkParameterBound("setNegativeAndAdditionalSOA", retry, std::numeric_limits::max()); + checkParameterBound("setNegativeAndAdditionalSOA", expire, std::numeric_limits::max()); + checkParameterBound("setNegativeAndAdditionalSOA", minimum, std::numeric_limits::max()); - return setNegativeAndAdditionalSOA(dq, nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum, false); - }); + return setNegativeAndAdditionalSOA(dq, nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum, false); + }); - luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSResponse& dnsResponse, uint16_t infoCode, const boost::optional& extraText) { + luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSResponse& dnsResponse, uint16_t infoCode, const boost::optional& extraText) { EDNSExtendedError ede; ede.infoCode = infoCode; if (extraText) { @@ -524,12 +541,12 @@ class AsynchronousObject dnsResponse.ids.d_extendedError = std::make_unique(ede); }); - luaCtx.registerFunction("suspend", [](DNSResponse& dr, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { + luaCtx.registerFunction("suspend", [](DNSResponse& dr, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { dr.asynchronous = true; return dnsdist::suspendResponse(dr, asyncID, queryID, timeoutMs); }); - luaCtx.registerFunction("changeName", [](DNSResponse& dr, const DNSName& newName) -> bool { + luaCtx.registerFunction("changeName", [](DNSResponse& dr, const DNSName& newName) -> bool { if (!dnsdist::changeNameInDNSPacket(dr.getMutableData(), dr.ids.qname, newName)) { return false; } @@ -537,7 +554,7 @@ class AsynchronousObject return true; }); - luaCtx.registerFunction("restart", [](DNSResponse& dr) { + luaCtx.registerFunction("restart", [](DNSResponse& dr) { if (!dr.ids.d_packet) { return false; } @@ -547,7 +564,7 @@ class AsynchronousObject return dnsdist::queueQueryResumptionEvent(std::move(query)); }); - luaCtx.registerFunction(DNSResponse::*)(void)const>("getSelectedBackend", [](const DNSResponse& dr) { + luaCtx.registerFunction (DNSResponse::*)(void) const>("getSelectedBackend", [](const DNSResponse& dr) { return dr.d_downstream; }); #endif /* DISABLE_NON_FFI_DQ_BINDINGS */ diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings.cc b/pdns/dnsdistdist/dnsdist-lua-bindings.cc index 12bf66526833..4dd5e1259f5b 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings.cc @@ -35,40 +35,43 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) { luaCtx.writeFunction("vinfolog", [](const string& arg) { - vinfolog("%s", arg); - }); + vinfolog("%s", arg); + }); luaCtx.writeFunction("infolog", [](const string& arg) { - infolog("%s", arg); - }); + infolog("%s", arg); + }); luaCtx.writeFunction("errlog", [](const string& arg) { - errlog("%s", arg); - }); + errlog("%s", arg); + }); luaCtx.writeFunction("warnlog", [](const string& arg) { - warnlog("%s", arg); - }); + warnlog("%s", arg); + }); luaCtx.writeFunction("show", [](const string& arg) { - g_outputBuffer+=arg; - g_outputBuffer+="\n"; - }); + g_outputBuffer += arg; + g_outputBuffer += "\n"; + }); /* Exceptions */ - luaCtx.registerFunction("__tostring", [](const std::exception_ptr& eptr) -> std::string { - try { - if (eptr) { - std::rethrow_exception(eptr); - } - } catch(const std::exception& e) { - return string(e.what()); - } catch(const PDNSException& e) { - return e.reason; - } catch(...) { - return string("Unknown exception"); + luaCtx.registerFunction("__tostring", [](const std::exception_ptr& eptr) -> std::string { + try { + if (eptr) { + std::rethrow_exception(eptr); } - return string("No exception"); - }); + } + catch (const std::exception& e) { + return string(e.what()); + } + catch (const PDNSException& e) { + return e.reason; + } + catch (...) { + return string("Unknown exception"); + } + return string("No exception"); + }); #ifndef DISABLE_POLICIES_BINDINGS /* ServerPolicy */ - luaCtx.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared(name, policy, true);}); + luaCtx.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared(name, policy, true); }); luaCtx.registerMember("name", &ServerPolicy::d_name); luaCtx.registerMember("policy", &ServerPolicy::d_policy); luaCtx.registerMember("ffipolicy", &ServerPolicy::d_ffipolicy); @@ -84,8 +87,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) ServerPolicy{"wrandom", wrandom, false}, ServerPolicy{"whashed", whashed, false}, ServerPolicy{"chashed", chashed, false}, - ServerPolicy{"leastOutstanding", leastOutstanding, false} - }; + ServerPolicy{"leastOutstanding", leastOutstanding, false}}; for (auto& policy : policies) { luaCtx.writeVariable(policy.d_name, policy); } @@ -93,255 +95,257 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) #endif /* DISABLE_POLICIES_BINDINGS */ /* ServerPool */ - luaCtx.registerFunction::*)(std::shared_ptr)>("setCache", [](std::shared_ptr pool, std::shared_ptr cache) { - if (pool) { - pool->packetCache = std::move(cache); - } - }); + luaCtx.registerFunction::*)(std::shared_ptr)>("setCache", [](std::shared_ptr pool, std::shared_ptr cache) { + if (pool) { + pool->packetCache = std::move(cache); + } + }); luaCtx.registerFunction("getCache", &ServerPool::getCache); - luaCtx.registerFunction::*)()>("unsetCache", [](std::shared_ptr pool) { - if (pool) { - pool->packetCache = nullptr; - } - }); + luaCtx.registerFunction::*)()>("unsetCache", [](std::shared_ptr pool) { + if (pool) { + pool->packetCache = nullptr; + } + }); luaCtx.registerFunction("getECS", &ServerPool::getECS); luaCtx.registerFunction("setECS", &ServerPool::setECS); #ifndef DISABLE_DOWNSTREAM_BINDINGS /* DownstreamState */ - luaCtx.registerFunction("setQPS", [](DownstreamState& state, int lim) { state.qps = lim > 0 ? QPSLimiter(lim, lim) : QPSLimiter(); }); - luaCtx.registerFunction::*)(string)>("addPool", [](const std::shared_ptr& state, const string& pool) { - auto localPools = g_pools.getCopy(); - addServerToPool(localPools, pool, state); - g_pools.setState(localPools); - state->d_config.pools.insert(pool); - }); - luaCtx.registerFunction::*)(string)>("rmPool", [](const std::shared_ptr& state, const string& pool) { - auto localPools = g_pools.getCopy(); - removeServerFromPool(localPools, pool, state); - g_pools.setState(localPools); - state->d_config.pools.erase(pool); - }); - luaCtx.registerFunction("getOutstanding", [](const DownstreamState& state) { return state.outstanding.load(); }); - luaCtx.registerFunction("getDrops", [](const DownstreamState& state) { return state.reuseds.load(); }); - luaCtx.registerFunction("getLatency", [](const DownstreamState& state) { return state.getRelevantLatencyUsec(); }); + luaCtx.registerFunction("setQPS", [](DownstreamState& state, int lim) { state.qps = lim > 0 ? QPSLimiter(lim, lim) : QPSLimiter(); }); + luaCtx.registerFunction::*)(string)>("addPool", [](const std::shared_ptr& state, const string& pool) { + auto localPools = g_pools.getCopy(); + addServerToPool(localPools, pool, state); + g_pools.setState(localPools); + state->d_config.pools.insert(pool); + }); + luaCtx.registerFunction::*)(string)>("rmPool", [](const std::shared_ptr& state, const string& pool) { + auto localPools = g_pools.getCopy(); + removeServerFromPool(localPools, pool, state); + g_pools.setState(localPools); + state->d_config.pools.erase(pool); + }); + luaCtx.registerFunction("getOutstanding", [](const DownstreamState& state) { return state.outstanding.load(); }); + luaCtx.registerFunction("getDrops", [](const DownstreamState& state) { return state.reuseds.load(); }); + luaCtx.registerFunction("getLatency", [](const DownstreamState& state) { return state.getRelevantLatencyUsec(); }); luaCtx.registerFunction("isUp", &DownstreamState::isUp); luaCtx.registerFunction("setDown", &DownstreamState::setDown); luaCtx.registerFunction("setUp", &DownstreamState::setUp); - luaCtx.registerFunction newStatus)>("setAuto", [](DownstreamState& state, boost::optional newStatus) { - if (newStatus) { - state.setUpStatus(*newStatus); - } - state.setAuto(); - }); - luaCtx.registerFunction newStatus)>("setLazyAuto", [](DownstreamState& state, boost::optional newStatus) { - if (newStatus) { - state.setUpStatus(*newStatus); - } - state.setLazyAuto(); - }); - luaCtx.registerFunction("getName", [](const DownstreamState& state) -> const std::string& { return state.getName(); }); - luaCtx.registerFunction("getNameWithAddr", [](const DownstreamState& state) -> const std::string& { return state.getNameWithAddr(); }); + luaCtx.registerFunction newStatus)>("setAuto", [](DownstreamState& state, boost::optional newStatus) { + if (newStatus) { + state.setUpStatus(*newStatus); + } + state.setAuto(); + }); + luaCtx.registerFunction newStatus)>("setLazyAuto", [](DownstreamState& state, boost::optional newStatus) { + if (newStatus) { + state.setUpStatus(*newStatus); + } + state.setLazyAuto(); + }); + luaCtx.registerFunction("getName", [](const DownstreamState& state) -> const std::string& { return state.getName(); }); + luaCtx.registerFunction("getNameWithAddr", [](const DownstreamState& state) -> const std::string& { return state.getNameWithAddr(); }); luaCtx.registerMember("upStatus", &DownstreamState::upStatus); - luaCtx.registerMember("weight", - [](const DownstreamState& state) -> int {return state.d_config.d_weight;}, - [](DownstreamState& state, int newWeight) { state.setWeight(newWeight); } - ); - luaCtx.registerMember("order", - [](const DownstreamState& state) -> int {return state.d_config.order; }, - [](DownstreamState& state, int newOrder) { state.d_config.order = newOrder; } - ); - luaCtx.registerMember("name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); }); - luaCtx.registerFunction("getID", [](const DownstreamState& state) { return boost::uuids::to_string(*state.d_config.id); }); + luaCtx.registerMember( + "weight", + [](const DownstreamState& state) -> int { return state.d_config.d_weight; }, + [](DownstreamState& state, int newWeight) { state.setWeight(newWeight); }); + luaCtx.registerMember( + "order", + [](const DownstreamState& state) -> int { return state.d_config.order; }, + [](DownstreamState& state, int newOrder) { state.d_config.order = newOrder; }); + luaCtx.registerMember( + "name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); }); + luaCtx.registerFunction("getID", [](const DownstreamState& state) { return boost::uuids::to_string(*state.d_config.id); }); #endif /* DISABLE_DOWNSTREAM_BINDINGS */ #ifndef DISABLE_DNSHEADER_BINDINGS /* dnsheader */ - luaCtx.registerFunction("setRD", [](dnsheader& dh, bool v) { - dh.rd=v; - }); + luaCtx.registerFunction("setRD", [](dnsheader& dh, bool v) { + dh.rd = v; + }); - luaCtx.registerFunction("getRD", [](const dnsheader& dh) { - return (bool)dh.rd; - }); + luaCtx.registerFunction("getRD", [](const dnsheader& dh) { + return (bool)dh.rd; + }); - luaCtx.registerFunction("setRA", [](dnsheader& dh, bool v) { - dh.ra=v; - }); + luaCtx.registerFunction("setRA", [](dnsheader& dh, bool v) { + dh.ra = v; + }); - luaCtx.registerFunction("getRA", [](const dnsheader& dh) { - return (bool)dh.ra; - }); + luaCtx.registerFunction("getRA", [](const dnsheader& dh) { + return (bool)dh.ra; + }); - luaCtx.registerFunction("setAD", [](dnsheader& dh, bool v) { - dh.ad=v; - }); + luaCtx.registerFunction("setAD", [](dnsheader& dh, bool v) { + dh.ad = v; + }); - luaCtx.registerFunction("getAD", [](const dnsheader& dh) { - return (bool)dh.ad; - }); + luaCtx.registerFunction("getAD", [](const dnsheader& dh) { + return (bool)dh.ad; + }); - luaCtx.registerFunction("setAA", [](dnsheader& dh, bool v) { - dh.aa=v; - }); + luaCtx.registerFunction("setAA", [](dnsheader& dh, bool v) { + dh.aa = v; + }); - luaCtx.registerFunction("getAA", [](const dnsheader& dh) { - return (bool)dh.aa; - }); + luaCtx.registerFunction("getAA", [](const dnsheader& dh) { + return (bool)dh.aa; + }); - luaCtx.registerFunction("setCD", [](dnsheader& dh, bool v) { - dh.cd=v; - }); + luaCtx.registerFunction("setCD", [](dnsheader& dh, bool v) { + dh.cd = v; + }); - luaCtx.registerFunction("getCD", [](const dnsheader& dh) { - return (bool)dh.cd; - }); + luaCtx.registerFunction("getCD", [](const dnsheader& dh) { + return (bool)dh.cd; + }); - luaCtx.registerFunction("getID", [](const dnsheader& dh) { - return ntohs(dh.id); - }); + luaCtx.registerFunction("getID", [](const dnsheader& dh) { + return ntohs(dh.id); + }); - luaCtx.registerFunction("getTC", [](const dnsheader& dh) { - return (bool)dh.tc; - }); + luaCtx.registerFunction("getTC", [](const dnsheader& dh) { + return (bool)dh.tc; + }); - luaCtx.registerFunction("setTC", [](dnsheader& dh, bool v) { - dh.tc=v; - if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored - }); + luaCtx.registerFunction("setTC", [](dnsheader& dh, bool v) { + dh.tc = v; + if (v) + dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored + }); - luaCtx.registerFunction("setQR", [](dnsheader& dh, bool v) { - dh.qr=v; - }); + luaCtx.registerFunction("setQR", [](dnsheader& dh, bool v) { + dh.qr = v; + }); #endif /* DISABLE_DNSHEADER_BINDINGS */ #ifndef DISABLE_COMBO_ADDR_BINDINGS /* ComboAddress */ luaCtx.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); }); luaCtx.writeFunction("newCAFromRaw", [](const std::string& raw, boost::optional port) { - if (raw.size() == 4) { - struct sockaddr_in sin4; - memset(&sin4, 0, sizeof(sin4)); - sin4.sin_family = AF_INET; - memcpy(&sin4.sin_addr.s_addr, raw.c_str(), raw.size()); - if (port) { - sin4.sin_port = htons(*port); - } - return ComboAddress(&sin4); - } - else if (raw.size() == 16) { - struct sockaddr_in6 sin6; - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr.s6_addr, raw.c_str(), raw.size()); - if (port) { - sin6.sin6_port = htons(*port); - } - return ComboAddress(&sin6); - } - return ComboAddress(); - }); - luaCtx.registerFunction("tostring", [](const ComboAddress& ca) { return ca.toString(); }); - luaCtx.registerFunction("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); - luaCtx.registerFunction("__tostring", [](const ComboAddress& ca) { return ca.toString(); }); - luaCtx.registerFunction("toString", [](const ComboAddress& ca) { return ca.toString(); }); - luaCtx.registerFunction("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); - luaCtx.registerFunction("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } ); - luaCtx.registerFunction("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); }); - luaCtx.registerFunction("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; }); - luaCtx.registerFunction("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; }); - luaCtx.registerFunction("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); }); - luaCtx.registerFunction("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); }); - luaCtx.registerFunction("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); }); + if (raw.size() == 4) { + struct sockaddr_in sin4; + memset(&sin4, 0, sizeof(sin4)); + sin4.sin_family = AF_INET; + memcpy(&sin4.sin_addr.s_addr, raw.c_str(), raw.size()); + if (port) { + sin4.sin_port = htons(*port); + } + return ComboAddress(&sin4); + } + else if (raw.size() == 16) { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + memcpy(&sin6.sin6_addr.s6_addr, raw.c_str(), raw.size()); + if (port) { + sin6.sin6_port = htons(*port); + } + return ComboAddress(&sin6); + } + return ComboAddress(); + }); + luaCtx.registerFunction("tostring", [](const ComboAddress& ca) { return ca.toString(); }); + luaCtx.registerFunction("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); + luaCtx.registerFunction("__tostring", [](const ComboAddress& ca) { return ca.toString(); }); + luaCtx.registerFunction("toString", [](const ComboAddress& ca) { return ca.toString(); }); + luaCtx.registerFunction("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); + luaCtx.registerFunction("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); }); + luaCtx.registerFunction("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); }); + luaCtx.registerFunction("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; }); + luaCtx.registerFunction("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; }); + luaCtx.registerFunction("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); }); + luaCtx.registerFunction("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); }); + luaCtx.registerFunction("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); }); #endif /* DISABLE_COMBO_ADDR_BINDINGS */ #ifndef DISABLE_DNSNAME_BINDINGS /* DNSName */ luaCtx.registerFunction("isPartOf", &DNSName::isPartOf); - luaCtx.registerFunction("chopOff", [](DNSName&dn ) { return dn.chopOff(); }); - luaCtx.registerFunction("countLabels", [](const DNSName& name) { return name.countLabels(); }); - luaCtx.registerFunction("hash", [](const DNSName& name) { return name.hash(); }); - luaCtx.registerFunction("wirelength", [](const DNSName& name) { return name.wirelength(); }); - luaCtx.registerFunction("tostring", [](const DNSName&dn ) { return dn.toString(); }); - luaCtx.registerFunction("toString", [](const DNSName&dn ) { return dn.toString(); }); - luaCtx.registerFunction("toStringNoDot", [](const DNSName&dn ) { return dn.toStringNoDot(); }); - luaCtx.registerFunction("__tostring", [](const DNSName&dn ) { return dn.toString(); }); - luaCtx.registerFunction("toDNSString", [](const DNSName&dn ) { return dn.toDNSString(); }); - luaCtx.registerFunction("makeRelative", [](const DNSName& dn, const DNSName& to) { return dn.makeRelative(to); }); + luaCtx.registerFunction("chopOff", [](DNSName& dn) { return dn.chopOff(); }); + luaCtx.registerFunction("countLabels", [](const DNSName& name) { return name.countLabels(); }); + luaCtx.registerFunction("hash", [](const DNSName& name) { return name.hash(); }); + luaCtx.registerFunction("wirelength", [](const DNSName& name) { return name.wirelength(); }); + luaCtx.registerFunction("tostring", [](const DNSName& dn) { return dn.toString(); }); + luaCtx.registerFunction("toString", [](const DNSName& dn) { return dn.toString(); }); + luaCtx.registerFunction("toStringNoDot", [](const DNSName& dn) { return dn.toStringNoDot(); }); + luaCtx.registerFunction("__tostring", [](const DNSName& dn) { return dn.toString(); }); + luaCtx.registerFunction("toDNSString", [](const DNSName& dn) { return dn.toDNSString(); }); + luaCtx.registerFunction("makeRelative", [](const DNSName& dn, const DNSName& to) { return dn.makeRelative(to); }); luaCtx.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); }); luaCtx.writeFunction("newDNSNameFromRaw", [](const std::string& name) { return DNSName(name.c_str(), name.size(), 0, false); }); luaCtx.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); }); luaCtx.writeFunction("newDNSNameSet", []() { return DNSNameSet(); }); /* DNSNameSet */ - luaCtx.registerFunction("toString", [](const DNSNameSet&dns ) { return dns.toString(); }); - luaCtx.registerFunction("__tostring", [](const DNSNameSet&dns ) { return dns.toString(); }); - luaCtx.registerFunction("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); }); - luaCtx.registerFunction("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); }); - luaCtx.registerFunction("delete",(size_t (DNSNameSet::*)(const DNSName&)) &DNSNameSet::erase); - luaCtx.registerFunction("size",(size_t (DNSNameSet::*)() const) &DNSNameSet::size); - luaCtx.registerFunction("clear",(void (DNSNameSet::*)()) &DNSNameSet::clear); - luaCtx.registerFunction("empty",(bool (DNSNameSet::*)() const) &DNSNameSet::empty); + luaCtx.registerFunction("toString", [](const DNSNameSet& dns) { return dns.toString(); }); + luaCtx.registerFunction("__tostring", [](const DNSNameSet& dns) { return dns.toString(); }); + luaCtx.registerFunction("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); }); + luaCtx.registerFunction("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); }); + luaCtx.registerFunction("delete", (size_t(DNSNameSet::*)(const DNSName&)) & DNSNameSet::erase); + luaCtx.registerFunction("size", (size_t(DNSNameSet::*)() const) & DNSNameSet::size); + luaCtx.registerFunction("clear", (void(DNSNameSet::*)()) & DNSNameSet::clear); + luaCtx.registerFunction("empty", (bool(DNSNameSet::*)() const) & DNSNameSet::empty); #endif /* DISABLE_DNSNAME_BINDINGS */ #ifndef DISABLE_SUFFIX_MATCH_BINDINGS /* SuffixMatchNode */ - luaCtx.registerFunction, LuaArray> &name)>("add", [](SuffixMatchNode &smn, const boost::variant, LuaArray> &name) { - if (name.type() == typeid(DNSName)) { - const auto& actualName = boost::get(name); - smn.add(actualName); - return; - } - if (name.type() == typeid(std::string)) { - const auto& actualName = boost::get(name); - smn.add(actualName); - return; - } - if (name.type() == typeid(LuaArray)) { - const auto& names = boost::get>(name); - for (const auto& actualName : names) { - smn.add(actualName.second); - } - return; + luaCtx.registerFunction, LuaArray>& name)>("add", [](SuffixMatchNode& smn, const boost::variant, LuaArray>& name) { + if (name.type() == typeid(DNSName)) { + const auto& actualName = boost::get(name); + smn.add(actualName); + return; + } + if (name.type() == typeid(std::string)) { + const auto& actualName = boost::get(name); + smn.add(actualName); + return; + } + if (name.type() == typeid(LuaArray)) { + const auto& names = boost::get>(name); + for (const auto& actualName : names) { + smn.add(actualName.second); } - if (name.type() == typeid(LuaArray)) { - const auto& names = boost::get>(name); - for (const auto& actualName : names) { - smn.add(actualName.second); - } - return; + return; + } + if (name.type() == typeid(LuaArray)) { + const auto& names = boost::get>(name); + for (const auto& actualName : names) { + smn.add(actualName.second); } + return; + } }); - luaCtx.registerFunction, LuaArray> &name)>("remove", [](SuffixMatchNode &smn, const boost::variant, LuaArray> &name) { - if (name.type() == typeid(DNSName)) { - const auto& actualName = boost::get(name); - smn.remove(actualName); - return; - } - if (name.type() == typeid(string)) { - const auto& actualName = boost::get(name); - DNSName dnsName(actualName); - smn.remove(dnsName); - return; - } - if (name.type() == typeid(LuaArray)) { - const auto& names = boost::get>(name); - for (const auto& actualName : names) { - smn.remove(actualName.second); - } - return; + luaCtx.registerFunction, LuaArray>& name)>("remove", [](SuffixMatchNode& smn, const boost::variant, LuaArray>& name) { + if (name.type() == typeid(DNSName)) { + const auto& actualName = boost::get(name); + smn.remove(actualName); + return; + } + if (name.type() == typeid(string)) { + const auto& actualName = boost::get(name); + DNSName dnsName(actualName); + smn.remove(dnsName); + return; + } + if (name.type() == typeid(LuaArray)) { + const auto& names = boost::get>(name); + for (const auto& actualName : names) { + smn.remove(actualName.second); } - if (name.type() == typeid(LuaArray)) { - const auto& names = boost::get>(name); - for (const auto& actualName : names) { - DNSName dnsName(actualName.second); - smn.remove(dnsName); - } - return; + return; + } + if (name.type() == typeid(LuaArray)) { + const auto& names = boost::get>(name); + for (const auto& actualName : names) { + DNSName dnsName(actualName.second); + smn.remove(dnsName); } + return; + } }); - luaCtx.registerFunction("check", (bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check); + luaCtx.registerFunction("check", (bool(SuffixMatchNode::*)(const DNSName&) const) & SuffixMatchNode::check); luaCtx.registerFunction (SuffixMatchNode::*)(const DNSName&) const>("getBestMatch", [](const SuffixMatchNode& smn, const DNSName& needle) { boost::optional result{boost::none}; auto res = smn.getBestMatch(needle); @@ -354,7 +358,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) #ifndef DISABLE_NETMASK_BINDINGS /* Netmask */ - luaCtx.writeFunction("newNetmask", [](boost::variant addrOrStr, boost::optional bits) { + luaCtx.writeFunction("newNetmask", [](boost::variant addrOrStr, boost::optional bits) { if (addrOrStr.type() == typeid(ComboAddress)) { const auto& comboAddr = boost::get(addrOrStr); if (bits) { @@ -370,13 +374,13 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) }); luaCtx.registerFunction("empty", &Netmask::empty); luaCtx.registerFunction("getBits", &Netmask::getBits); - luaCtx.registerFunction("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); } ); // const reference makes this necessary - luaCtx.registerFunction("getMaskedNetwork", [](const Netmask& nm) { return nm.getMaskedNetwork(); } ); + luaCtx.registerFunction("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); }); // const reference makes this necessary + luaCtx.registerFunction("getMaskedNetwork", [](const Netmask& nm) { return nm.getMaskedNetwork(); }); luaCtx.registerFunction("isIpv4", &Netmask::isIPv4); luaCtx.registerFunction("isIPv4", &Netmask::isIPv4); luaCtx.registerFunction("isIpv6", &Netmask::isIPv6); luaCtx.registerFunction("isIPv6", &Netmask::isIPv6); - luaCtx.registerFunction("match", (bool (Netmask::*)(const string&) const)&Netmask::match); + luaCtx.registerFunction("match", (bool(Netmask::*)(const string&) const) & Netmask::match); luaCtx.registerFunction("toString", &Netmask::toString); luaCtx.registerFunction("__tostring", &Netmask::toString); luaCtx.registerEqFunction(&Netmask::operator==); @@ -384,29 +388,27 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) /* NetmaskGroup */ luaCtx.writeFunction("newNMG", []() { return NetmaskGroup(); }); - luaCtx.registerFunction("addMask", [](NetmaskGroup& nmg, const std::string& mask) - { - nmg.addMask(mask); - }); - luaCtx.registerFunction("addNMG", [](NetmaskGroup& nmg, const NetmaskGroup& otherNMG) { + luaCtx.registerFunction("addMask", [](NetmaskGroup& nmg, const std::string& mask) { + nmg.addMask(mask); + }); + luaCtx.registerFunction("addNMG", [](NetmaskGroup& nmg, const NetmaskGroup& otherNMG) { /* this is not going to be very efficient, sorry */ auto entries = otherNMG.toStringVector(); for (const auto& entry : entries) { nmg.addMask(entry); } }); - luaCtx.registerFunction& map)>("addMasks", [](NetmaskGroup&nmg, const std::map& map) - { - for (const auto& entry : map) { - nmg.addMask(Netmask(entry.first)); - } - }); + luaCtx.registerFunction& map)>("addMasks", [](NetmaskGroup& nmg, const std::map& map) { + for (const auto& entry : map) { + nmg.addMask(Netmask(entry.first)); + } + }); - luaCtx.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match); + luaCtx.registerFunction("match", (bool(NetmaskGroup::*)(const ComboAddress&) const) & NetmaskGroup::match); luaCtx.registerFunction("size", &NetmaskGroup::size); luaCtx.registerFunction("clear", &NetmaskGroup::clear); - luaCtx.registerFunction("toString", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); }); - luaCtx.registerFunction("__tostring", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); }); + luaCtx.registerFunction("toString", [](const NetmaskGroup& nmg) { return "NetmaskGroup " + nmg.toString(); }); + luaCtx.registerFunction("__tostring", [](const NetmaskGroup& nmg) { return "NetmaskGroup " + nmg.toString(); }); #endif /* DISABLE_NETMASK_BINDINGS */ #ifndef DISABLE_QPS_LIMITER_BINDINGS @@ -417,48 +419,48 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) #ifndef DISABLE_CLIENT_STATE_BINDINGS /* ClientState */ - luaCtx.registerFunction("toString", [](const ClientState& fe) { - setLuaNoSideEffect(); - return fe.local.toStringWithPort(); - }); - luaCtx.registerFunction("__tostring", [](const ClientState& fe) { - setLuaNoSideEffect(); - return fe.local.toStringWithPort(); - }); - luaCtx.registerFunction("getType", [](const ClientState& fe) { - setLuaNoSideEffect(); - return fe.getType(); - }); - luaCtx.registerFunction("getConfiguredTLSProvider", [](const ClientState& fe) { - setLuaNoSideEffect(); - if (fe.tlsFrontend != nullptr) { - return fe.tlsFrontend->getRequestedProvider(); - } - else if (fe.dohFrontend != nullptr) { - return std::string("openssl"); - } - return std::string(); + luaCtx.registerFunction("toString", [](const ClientState& fe) { + setLuaNoSideEffect(); + return fe.local.toStringWithPort(); }); - luaCtx.registerFunction("getEffectiveTLSProvider", [](const ClientState& fe) { - setLuaNoSideEffect(); - if (fe.tlsFrontend != nullptr) { - return fe.tlsFrontend->getEffectiveProvider(); - } - else if (fe.dohFrontend != nullptr) { - return std::string("openssl"); - } - return std::string(); + luaCtx.registerFunction("__tostring", [](const ClientState& fe) { + setLuaNoSideEffect(); + return fe.local.toStringWithPort(); + }); + luaCtx.registerFunction("getType", [](const ClientState& fe) { + setLuaNoSideEffect(); + return fe.getType(); + }); + luaCtx.registerFunction("getConfiguredTLSProvider", [](const ClientState& fe) { + setLuaNoSideEffect(); + if (fe.tlsFrontend != nullptr) { + return fe.tlsFrontend->getRequestedProvider(); + } + else if (fe.dohFrontend != nullptr) { + return std::string("openssl"); + } + return std::string(); + }); + luaCtx.registerFunction("getEffectiveTLSProvider", [](const ClientState& fe) { + setLuaNoSideEffect(); + if (fe.tlsFrontend != nullptr) { + return fe.tlsFrontend->getEffectiveProvider(); + } + else if (fe.dohFrontend != nullptr) { + return std::string("openssl"); + } + return std::string(); }); luaCtx.registerMember("muted", &ClientState::muted); #ifdef HAVE_EBPF - luaCtx.registerFunction)>("attachFilter", [](ClientState& frontend, std::shared_ptr bpf) { - if (bpf) { - frontend.attachFilter(bpf, frontend.getSocket()); - } - }); - luaCtx.registerFunction("detachFilter", [](ClientState& frontend) { - frontend.detachFilter(frontend.getSocket()); - }); + luaCtx.registerFunction)>("attachFilter", [](ClientState& frontend, std::shared_ptr bpf) { + if (bpf) { + frontend.attachFilter(bpf, frontend.getSocket()); + } + }); + luaCtx.registerFunction("detachFilter", [](ClientState& frontend) { + frontend.detachFilter(frontend.getSocket()); + }); #endif /* HAVE_EBPF */ #endif /* DISABLE_CLIENT_STATE_BINDINGS */ @@ -466,80 +468,80 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) #ifdef HAVE_EBPF using bpfopts_t = LuaAssociativeTable>; luaCtx.writeFunction("newBPFFilter", [client](bpfopts_t opts) { - if (client) { - return std::shared_ptr(nullptr); - } - std::unordered_map mapsConfig; - - const auto convertParamsToConfig = [&](const std::string& name, BPFFilter::MapType type) { - BPFFilter::MapConfiguration config; - config.d_type = type; - if (const string key = name + "MaxItems"; opts.count(key)) { - const auto& tmp = opts.at(key); - if (tmp.type() != typeid(uint32_t)) { - throw std::runtime_error("params is invalid"); - } - const auto& params = boost::get(tmp); - config.d_maxItems = params; + if (client) { + return std::shared_ptr(nullptr); + } + std::unordered_map mapsConfig; + + const auto convertParamsToConfig = [&](const std::string& name, BPFFilter::MapType type) { + BPFFilter::MapConfiguration config; + config.d_type = type; + if (const string key = name + "MaxItems"; opts.count(key)) { + const auto& tmp = opts.at(key); + if (tmp.type() != typeid(uint32_t)) { + throw std::runtime_error("params is invalid"); } + const auto& params = boost::get(tmp); + config.d_maxItems = params; + } - if (const string key = name + "PinnedPath"; opts.count(key)) { - auto& tmp = opts.at(key); - if (tmp.type() != typeid(string)) { - throw std::runtime_error("params is invalid"); - } - auto& params = boost::get(tmp); - config.d_pinnedPath = std::move(params); - } - mapsConfig[name] = std::move(config); - }; - - convertParamsToConfig("ipv4", BPFFilter::MapType::IPv4); - convertParamsToConfig("ipv6", BPFFilter::MapType::IPv6); - convertParamsToConfig("qnames", BPFFilter::MapType::QNames); - convertParamsToConfig("cidr4", BPFFilter::MapType::CIDR4); - convertParamsToConfig("cidr6", BPFFilter::MapType::CIDR6); - - BPFFilter::MapFormat format = BPFFilter::MapFormat::Legacy; - bool external = false; - if (opts.count("external")) { - const auto& tmp = opts.at("external"); - if (tmp.type() != typeid(bool)) { + if (const string key = name + "PinnedPath"; opts.count(key)) { + auto& tmp = opts.at(key); + if (tmp.type() != typeid(string)) { throw std::runtime_error("params is invalid"); } - if ((external = boost::get(tmp))) { - format = BPFFilter::MapFormat::WithActions; - } + auto& params = boost::get(tmp); + config.d_pinnedPath = std::move(params); + } + mapsConfig[name] = std::move(config); + }; + + convertParamsToConfig("ipv4", BPFFilter::MapType::IPv4); + convertParamsToConfig("ipv6", BPFFilter::MapType::IPv6); + convertParamsToConfig("qnames", BPFFilter::MapType::QNames); + convertParamsToConfig("cidr4", BPFFilter::MapType::CIDR4); + convertParamsToConfig("cidr6", BPFFilter::MapType::CIDR6); + + BPFFilter::MapFormat format = BPFFilter::MapFormat::Legacy; + bool external = false; + if (opts.count("external")) { + const auto& tmp = opts.at("external"); + if (tmp.type() != typeid(bool)) { + throw std::runtime_error("params is invalid"); + } + if ((external = boost::get(tmp))) { + format = BPFFilter::MapFormat::WithActions; } + } - return std::make_shared(mapsConfig, format, external); + return std::make_shared(mapsConfig, format, external); }); - luaCtx.registerFunction::*)(const ComboAddress& ca, boost::optional action)>("block", [](std::shared_ptr bpf, const ComboAddress& ca, boost::optional action) { - if (bpf) { - if (!action) { - return bpf->block(ca, BPFFilter::MatchAction::Drop); - } - else { - BPFFilter::MatchAction match; - - switch (*action) { - case 0: - match = BPFFilter::MatchAction::Pass; - break; - case 1: - match = BPFFilter::MatchAction::Drop; - break; - case 2: - match = BPFFilter::MatchAction::Truncate; - break; - default: - throw std::runtime_error("Unsupported action for BPFFilter::block"); - } - return bpf->block(ca, match); + luaCtx.registerFunction::*)(const ComboAddress& ca, boost::optional action)>("block", [](std::shared_ptr bpf, const ComboAddress& ca, boost::optional action) { + if (bpf) { + if (!action) { + return bpf->block(ca, BPFFilter::MatchAction::Drop); + } + else { + BPFFilter::MatchAction match; + + switch (*action) { + case 0: + match = BPFFilter::MatchAction::Pass; + break; + case 1: + match = BPFFilter::MatchAction::Drop; + break; + case 2: + match = BPFFilter::MatchAction::Truncate; + break; + default: + throw std::runtime_error("Unsupported action for BPFFilter::block"); } + return bpf->block(ca, match); } - }); + } + }); luaCtx.registerFunction::*)(const string& range, uint32_t action, boost::optional force)>("addRangeRule", [](std::shared_ptr bpf, const string& range, uint32_t action, boost::optional force) { if (!bpf) { return; @@ -560,37 +562,37 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) } return bpf->addRangeRule(Netmask(range), force ? *force : false, match); }); - luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype, boost::optional action)>("blockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype, boost::optional action) { - if (bpf) { - if (!action) { - return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype ? *qtype : 255); - } - else { - BPFFilter::MatchAction match; - - switch (*action) { - case 0: - match = BPFFilter::MatchAction::Pass; - break; - case 1: - match = BPFFilter::MatchAction::Drop; - break; - case 2: - match = BPFFilter::MatchAction::Truncate; - break; - default: - throw std::runtime_error("Unsupported action for BPFFilter::blockQName"); - } - return bpf->block(qname, match, qtype ? *qtype : 255); + luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype, boost::optional action)>("blockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype, boost::optional action) { + if (bpf) { + if (!action) { + return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype ? *qtype : 255); + } + else { + BPFFilter::MatchAction match; + + switch (*action) { + case 0: + match = BPFFilter::MatchAction::Pass; + break; + case 1: + match = BPFFilter::MatchAction::Drop; + break; + case 2: + match = BPFFilter::MatchAction::Truncate; + break; + default: + throw std::runtime_error("Unsupported action for BPFFilter::blockQName"); } + return bpf->block(qname, match, qtype ? *qtype : 255); } - }); + } + }); - luaCtx.registerFunction::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr bpf, const ComboAddress& ca) { - if (bpf) { - return bpf->unblock(ca); - } - }); + luaCtx.registerFunction::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr bpf, const ComboAddress& ca) { + if (bpf) { + return bpf->unblock(ca); + } + }); luaCtx.registerFunction::*)(const string& range)>("rmRangeRule", [](std::shared_ptr bpf, const string& range) { if (!bpf) { return; @@ -614,108 +616,108 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) } return res; }); - luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype)>("unblockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype) { - if (bpf) { - return bpf->unblock(qname, qtype ? *qtype : 255); - } - }); + luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype)>("unblockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype) { + if (bpf) { + return bpf->unblock(qname, qtype ? *qtype : 255); + } + }); - luaCtx.registerFunction::*)()const>("getStats", [](const std::shared_ptr bpf) { - setLuaNoSideEffect(); - std::string res; - if (bpf) { - auto stats = bpf->getAddrStats(); - for (const auto& value : stats) { - if (value.first.sin4.sin_family == AF_INET) { - res += value.first.toString() + ": " + std::to_string(value.second) + "\n"; - } - else if (value.first.sin4.sin_family == AF_INET6) { - res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n"; - } + luaCtx.registerFunction::*)() const>("getStats", [](const std::shared_ptr bpf) { + setLuaNoSideEffect(); + std::string res; + if (bpf) { + auto stats = bpf->getAddrStats(); + for (const auto& value : stats) { + if (value.first.sin4.sin_family == AF_INET) { + res += value.first.toString() + ": " + std::to_string(value.second) + "\n"; } - const auto rangeStat = bpf->getRangeRule(); - for (const auto& value : rangeStat) { - if (value.first.isIPv4()) { - res += BPFFilter::toString(value.second.action) + "\t " + value.first.toString() + ": " + std::to_string(value.second.counter) + "\n"; - } - else if (value.first.isIPv6()) { - res += BPFFilter::toString(value.second.action) + "\t[" + value.first.toString() + "]: " + std::to_string(value.second.counter) + "\n"; - } + else if (value.first.sin4.sin_family == AF_INET6) { + res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n"; } - auto qstats = bpf->getQNameStats(); - for (const auto& value : qstats) { - res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n"; + } + const auto rangeStat = bpf->getRangeRule(); + for (const auto& value : rangeStat) { + if (value.first.isIPv4()) { + res += BPFFilter::toString(value.second.action) + "\t " + value.first.toString() + ": " + std::to_string(value.second.counter) + "\n"; + } + else if (value.first.isIPv6()) { + res += BPFFilter::toString(value.second.action) + "\t[" + value.first.toString() + "]: " + std::to_string(value.second.counter) + "\n"; } } - return res; - }); - - luaCtx.registerFunction::*)()>("attachToAllBinds", [](std::shared_ptr bpf) { - std::string res; - if (!g_configurationDone) { - throw std::runtime_error("attachToAllBinds() cannot be used at configuration time!"); - return; + auto qstats = bpf->getQNameStats(); + for (const auto& value : qstats) { + res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n"; } - if (bpf) { - for (const auto& frontend : g_frontends) { - frontend->attachFilter(bpf, frontend->getSocket()); - } + } + return res; + }); + + luaCtx.registerFunction::*)()>("attachToAllBinds", [](std::shared_ptr bpf) { + std::string res; + if (!g_configurationDone) { + throw std::runtime_error("attachToAllBinds() cannot be used at configuration time!"); + return; + } + if (bpf) { + for (const auto& frontend : g_frontends) { + frontend->attachFilter(bpf, frontend->getSocket()); } - }); + } + }); - luaCtx.writeFunction("newDynBPFFilter", [client](std::shared_ptr bpf) { - if (client) { - return std::shared_ptr(nullptr); - } - return std::make_shared(bpf); - }); - - luaCtx.registerFunction::*)(const ComboAddress& addr, boost::optional seconds)>("block", [](std::shared_ptr dbpf, const ComboAddress& addr, boost::optional seconds) { - if (dbpf) { - struct timespec until; - clock_gettime(CLOCK_MONOTONIC, &until); - until.tv_sec += seconds ? *seconds : 10; - dbpf->block(addr, until); - } - }); + luaCtx.writeFunction("newDynBPFFilter", [client](std::shared_ptr bpf) { + if (client) { + return std::shared_ptr(nullptr); + } + return std::make_shared(bpf); + }); - luaCtx.registerFunction::*)()>("purgeExpired", [](std::shared_ptr dbpf) { - if (dbpf) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - dbpf->purgeExpired(now); - } - }); + luaCtx.registerFunction::*)(const ComboAddress& addr, boost::optional seconds)>("block", [](std::shared_ptr dbpf, const ComboAddress& addr, boost::optional seconds) { + if (dbpf) { + struct timespec until; + clock_gettime(CLOCK_MONOTONIC, &until); + until.tv_sec += seconds ? *seconds : 10; + dbpf->block(addr, until); + } + }); - luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("excludeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { - if (!dbpf) { - return; - } + luaCtx.registerFunction::*)()>("purgeExpired", [](std::shared_ptr dbpf) { + if (dbpf) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + dbpf->purgeExpired(now); + } + }); - if (ranges.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&ranges)) { - dbpf->excludeRange(Netmask(range.second)); - } - } - else { - dbpf->excludeRange(Netmask(*boost::get(&ranges))); - } - }); + luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("excludeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { + if (!dbpf) { + return; + } - luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("includeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { - if (!dbpf) { - return; + if (ranges.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&ranges)) { + dbpf->excludeRange(Netmask(range.second)); } + } + else { + dbpf->excludeRange(Netmask(*boost::get(&ranges))); + } + }); - if (ranges.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&ranges)) { - dbpf->includeRange(Netmask(range.second)); - } - } - else { - dbpf->includeRange(Netmask(*boost::get(&ranges))); + luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("includeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { + if (!dbpf) { + return; + } + + if (ranges.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&ranges)) { + dbpf->includeRange(Netmask(range.second)); } - }); + } + else { + dbpf->includeRange(Netmask(*boost::get(&ranges))); + } + }); #endif /* HAVE_EBPF */ #ifdef HAVE_XSK using xskopt_t = LuaAssociativeTable>; @@ -752,7 +754,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) dnsdist::xsk::g_xsk.push_back(socket); return socket; }); - luaCtx.registerFunction::*)()const>("getMetrics", [](const std::shared_ptr& xsk) { + luaCtx.registerFunction::*)() const>("getMetrics", [](const std::shared_ptr& xsk) { std::string result; if (!xsk) { return result; @@ -761,10 +763,10 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) }); #endif /* HAVE_XSK */ /* EDNSOptionView */ - luaCtx.registerFunction("count", [](const EDNSOptionView& option) { - return option.values.size(); - }); - luaCtx.registerFunction(EDNSOptionView::*)()const>("getValues", [] (const EDNSOptionView& option) { + luaCtx.registerFunction("count", [](const EDNSOptionView& option) { + return option.values.size(); + }); + luaCtx.registerFunction (EDNSOptionView::*)() const>("getValues", [](const EDNSOptionView& option) { std::vector values; for (const auto& value : option.values) { values.push_back(std::string(value.content, value.size)); @@ -784,8 +786,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) return std::make_shared(regex, status, PacketBuffer(content.begin(), content.end()), headers); }); - luaCtx.writeFunction("newSVCRecordParameters", [](uint64_t priority, const std::string& target, boost::optional additionalParameters) - { + luaCtx.writeFunction("newSVCRecordParameters", [](uint64_t priority, const std::string& target, boost::optional additionalParameters) { checkParameterBound("newSVCRecordParameters", priority, std::numeric_limits::max()); SVCRecordParameters parameters; if (additionalParameters) { @@ -843,7 +844,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) if (client || configCheck) { return; } - std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback=std::move(callback)](const std::string& resolvedHostname, std::vector& ips) { + std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback = std::move(callback)](const std::string& resolvedHostname, std::vector& ips) { LuaArray result; result.reserve(ips.size()); for (const auto& entry : ips) { diff --git a/pdns/dnsdistdist/dnsdist-lua-inspection.cc b/pdns/dnsdistdist/dnsdist-lua-inspection.cc index 35b5c8b9b344..304a05af5d01 100644 --- a/pdns/dnsdistdist/dnsdist-lua-inspection.cc +++ b/pdns/dnsdistdist/dnsdist-lua-inspection.cc @@ -31,17 +31,17 @@ #include "statnode.hh" #ifndef DISABLE_TOP_N_BINDINGS -static LuaArray>> getGenResponses(uint64_t top, boost::optional labels, std::function pred) +static LuaArray>> getGenResponses(uint64_t top, boost::optional labels, std::function pred) { setLuaNoSideEffect(); map counts; - unsigned int total=0; + unsigned int total = 0; { for (const auto& shard : g_rings.d_shards) { auto rl = shard->respRing.lock(); if (!labels) { - for(const auto& a : *rl) { - if(!pred(a)) + for (const auto& a : *rl) { + if (!pred(a)) continue; counts[a.name]++; total++; @@ -49,8 +49,8 @@ static LuaArray>> getGenResponses(uint } else { unsigned int lab = *labels; - for(const auto& a : *rl) { - if(!pred(a)) + for (const auto& a : *rl) { + if (!pred(a)) continue; DNSName temp(a.name); @@ -67,29 +67,28 @@ static LuaArray>> getGenResponses(uint for (const auto& c : counts) rcounts.emplace_back(c.second, c.first.makeLowerCase()); - sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, - const decltype(rcounts)::value_type& b) { - return b.first < a.first; - }); + sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, const decltype(rcounts)::value_type& b) { + return b.first < a.first; + }); - LuaArray>> ret; + LuaArray>> ret; ret.reserve(std::min(rcounts.size(), static_cast(top + 1U))); int count = 1; unsigned int rest = 0; for (const auto& rc : rcounts) { if (count == static_cast(top + 1)) { - rest+=rc.first; + rest += rc.first; } else { - ret.push_back({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); + ret.push_back({count++, {rc.second.toString(), rc.first, 100.0 * rc.first / total}}); } } if (total > 0) { - ret.push_back({count, {"Rest", rest, 100.0*rest/total}}); + ret.push_back({count, {"Rest", rest, 100.0 * rest / total}}); } else { - ret.push_back({count, {"Rest", rest, 100.0 }}); + ret.push_back({count, {"Rest", rest, 100.0}}); } return ret; @@ -102,12 +101,12 @@ static LuaArray>> getGenResponses(uint typedef std::unordered_map counts_t; static counts_t filterScore(const counts_t& counts, - double delta, unsigned int rate) + double delta, unsigned int rate) { counts_t ret; - double lim = delta*rate; - for(const auto& c : counts) { + double lim = delta * rate; + for (const auto& c : counts) { if (c.second > lim) { ret[c.first] = c.second; } @@ -129,8 +128,8 @@ static void statNodeRespRing(statvisitor_t visitor, uint64_t seconds) for (const auto& shard : g_rings.d_shards) { auto rl = shard->respRing.lock(); - for(const auto& c : *rl) { - if (now < c.when){ + for (const auto& c : *rl) { + if (now < c.when) { continue; } @@ -144,8 +143,7 @@ static void statNodeRespRing(statvisitor_t visitor, uint64_t seconds) } StatNode::Stat node; - root.visit([visitor = std::move(visitor)](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) { - visitor(*node_, self, children);}, node); + root.visit([visitor = std::move(visitor)](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) { visitor(*node_, self, children); }, node); } static LuaArray> getRespRing(boost::optional rcode) @@ -184,15 +182,15 @@ static counts_t exceedRespGen(unsigned int rate, int seconds, std::functionrespRing.lock(); - for(const auto& c : *rl) { + for (const auto& c : *rl) { - if(seconds && c.when < cutoff) + if (seconds && c.when < cutoff) continue; - if(now < c.when) + if (now < c.when) continue; T(counts, c); - if(c.when < mintime) + if (c.when < mintime) mintime = c.when; } } @@ -213,13 +211,13 @@ static counts_t exceedQueryGen(unsigned int rate, int seconds, std::functionqueryRing.lock(); - for(const auto& c : *rl) { - if(seconds && c.when < cutoff) + for (const auto& c : *rl) { + if (seconds && c.when < cutoff) continue; - if(now < c.when) + if (now < c.when) continue; T(counts, c); - if(c.when < mintime) + if (c.when < mintime) mintime = c.when; } } @@ -228,22 +226,19 @@ static counts_t exceedQueryGen(unsigned int rate, int seconds, std::function top_) { - setLuaNoSideEffect(); - uint64_t top = top_ ? *top_ : 10U; - map counts; - unsigned int total=0; - { - for (const auto& shard : g_rings.d_shards) { - auto rl = shard->queryRing.lock(); - for(const auto& c : *rl) { - counts[c.requestor]++; - total++; - } + setLuaNoSideEffect(); + uint64_t top = top_ ? *top_ : 10U; + map counts; + unsigned int total = 0; + { + for (const auto& shard : g_rings.d_shards) { + auto rl = shard->queryRing.lock(); + for (const auto& c : *rl) { + counts[c.requestor]++; + total++; } } - vector> rcounts; - rcounts.reserve(counts.size()); - for(const auto& c : counts) - rcounts.emplace_back(c.second, c.first); - - sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, - const decltype(rcounts)::value_type& b) { - return b.first < a.first; - }); - unsigned int count=1, rest=0; - boost::format fmt("%4d %-40s %4d %4.1f%%\n"); - for(const auto& rc : rcounts) { - if(count==top+1) - rest+=rc.first; - else - g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str(); - } - g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str(); - }); + } + vector> rcounts; + rcounts.reserve(counts.size()); + for (const auto& c : counts) + rcounts.emplace_back(c.second, c.first); + + sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, const decltype(rcounts)::value_type& b) { + return b.first < a.first; + }); + unsigned int count = 1, rest = 0; + boost::format fmt("%4d %-40s %4d %4.1f%%\n"); + for (const auto& rc : rcounts) { + if (count == top + 1) + rest += rc.first; + else + g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0 * rc.first / total)).str(); + } + g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0 * rest / total : 100.0)).str(); + }); luaCtx.writeFunction("getTopQueries", [](uint64_t top, boost::optional labels) { - setLuaNoSideEffect(); - map counts; - unsigned int total=0; - if(!labels) { - for (const auto& shard : g_rings.d_shards) { - auto rl = shard->queryRing.lock(); - for(const auto& a : *rl) { - counts[a.name]++; - total++; - } + setLuaNoSideEffect(); + map counts; + unsigned int total = 0; + if (!labels) { + for (const auto& shard : g_rings.d_shards) { + auto rl = shard->queryRing.lock(); + for (const auto& a : *rl) { + counts[a.name]++; + total++; } } - else { - unsigned int lab = *labels; - for (const auto& shard : g_rings.d_shards) { - auto rl = shard->queryRing.lock(); - // coverity[auto_causes_copy] - for (auto a : *rl) { - a.name.trimToLabels(lab); - counts[a.name]++; - total++; - } + } + else { + unsigned int lab = *labels; + for (const auto& shard : g_rings.d_shards) { + auto rl = shard->queryRing.lock(); + // coverity[auto_causes_copy] + for (auto a : *rl) { + a.name.trimToLabels(lab); + counts[a.name]++; + total++; } } - // cout<<"Looked at "<> rcounts; - rcounts.reserve(counts.size()); - for(const auto& c : counts) - rcounts.emplace_back(c.second, c.first.makeLowerCase()); - - sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, - const decltype(rcounts)::value_type& b) { - return b.first < a.first; - }); - - std::unordered_map>> ret; - unsigned int count=1, rest=0; - for(const auto& rc : rcounts) { - if(count==top+1) - rest+=rc.first; - else - ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); - } - - if (total > 0) { - ret.insert({count, {"Rest", rest, 100.0*rest/total}}); - } - else { - ret.insert({count, {"Rest", rest, 100.0}}); - } + } + // cout<<"Looked at "<> rcounts; + rcounts.reserve(counts.size()); + for (const auto& c : counts) + rcounts.emplace_back(c.second, c.first.makeLowerCase()); + + sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, const decltype(rcounts)::value_type& b) { + return b.first < a.first; + }); + + std::unordered_map>> ret; + unsigned int count = 1, rest = 0; + for (const auto& rc : rcounts) { + if (count == top + 1) + rest += rc.first; + else + ret.insert({count++, {rc.second.toString(), rc.first, 100.0 * rc.first / total}}); + } - return ret; + if (total > 0) { + ret.insert({count, {"Rest", rest, 100.0 * rest / total}}); + } + else { + ret.insert({count, {"Rest", rest, 100.0}}); + } - }); + return ret; + }); luaCtx.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)"); luaCtx.writeFunction("getResponseRing", []() { - setLuaNoSideEffect(); - size_t totalEntries = 0; - std::vector> rings; - rings.reserve(g_rings.getNumberOfShards()); - for (const auto& shard : g_rings.d_shards) { - { - auto rl = shard->respRing.lock(); - rings.push_back(*rl); - } - totalEntries += rings.back().size(); - } - vector > > ret; - ret.reserve(totalEntries); - decltype(ret)::value_type item; - for (size_t idx = 0; idx < rings.size(); idx++) { - for(const auto& r : rings[idx]) { - item["name"]=r.name.toString(); - item["qtype"]=r.qtype; - item["rcode"]=r.dh.rcode; - item["usec"]=r.usec; - ret.push_back(item); - } + setLuaNoSideEffect(); + size_t totalEntries = 0; + std::vector> rings; + rings.reserve(g_rings.getNumberOfShards()); + for (const auto& shard : g_rings.d_shards) { + { + auto rl = shard->respRing.lock(); + rings.push_back(*rl); } - return ret; - }); + totalEntries += rings.back().size(); + } + vector>> ret; + ret.reserve(totalEntries); + decltype(ret)::value_type item; + for (size_t idx = 0; idx < rings.size(); idx++) { + for (const auto& r : rings[idx]) { + item["name"] = r.name.toString(); + item["qtype"] = r.qtype; + item["rcode"] = r.dh.rcode; + item["usec"] = r.usec; + ret.push_back(item); + } + } + return ret; + }); luaCtx.writeFunction("getTopResponses", [](uint64_t top, uint64_t kind, boost::optional labels) { - return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; }); - }); + return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; }); + }); luaCtx.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); - luaCtx.writeFunction("getSlowResponses", [](uint64_t top, uint64_t msec, boost::optional labels) { - return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; }); - }); - + return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec * 1000; }); + }); luaCtx.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); luaCtx.writeFunction("getTopBandwidth", [](uint64_t top) { - setLuaNoSideEffect(); - return g_rings.getTopBandwidth(top); - }); + setLuaNoSideEffect(); + return g_rings.getTopBandwidth(top); + }); luaCtx.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)"); #endif /* DISABLE_TOP_N_BINDINGS */ luaCtx.writeFunction("delta", []() { - setLuaNoSideEffect(); - // we hold the lua lock already! - for(const auto& d : g_confDelta) { - struct tm tm; - localtime_r(&d.first.tv_sec, &tm); - char date[80]; - strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm); - g_outputBuffer += date; - g_outputBuffer += d.second + "\n"; - } - }); + setLuaNoSideEffect(); + // we hold the lua lock already! + for (const auto& d : g_confDelta) { + struct tm tm; + localtime_r(&d.first.tv_sec, &tm); + char date[80]; + strftime(date, sizeof(date) - 1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm); + g_outputBuffer += date; + g_outputBuffer += d.second + "\n"; + } + }); luaCtx.writeFunction("grepq", [](LuaTypeOrArrayOf inp, boost::optional limit, boost::optional> options) { - setLuaNoSideEffect(); - boost::optional nm; - boost::optional dn; - int msec = -1; - std::unique_ptr outputFile{nullptr, fclose}; - - if (options) { - std::string outputFileName; - if (getOptionalValue(options, "outputFile", outputFileName) > 0) { - int fd = open(outputFileName.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0600); - if (fd < 0) { - g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n"; - return; - } - outputFile = std::unique_ptr(fdopen(fd, "w"), fclose); - if (outputFile == nullptr) { - g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n"; - close(fd); - return; - } - } - checkAllParametersConsumed("grepq", options); - } + setLuaNoSideEffect(); + boost::optional nm; + boost::optional dn; + int msec = -1; + std::unique_ptr outputFile{nullptr, fclose}; + + if (options) { + std::string outputFileName; + if (getOptionalValue(options, "outputFile", outputFileName) > 0) { + int fd = open(outputFileName.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) { + g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n"; + return; + } + outputFile = std::unique_ptr(fdopen(fd, "w"), fclose); + if (outputFile == nullptr) { + g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n"; + close(fd); + return; + } + } + checkAllParametersConsumed("grepq", options); + } - vector vec; - auto str = boost::get(&inp); - if (str) { - vec.push_back(*str); - } - else { - auto v = boost::get>(inp); - for (const auto& a: v) { - vec.push_back(a.second); - } + vector vec; + auto str = boost::get(&inp); + if (str) { + vec.push_back(*str); + } + else { + auto v = boost::get>(inp); + for (const auto& a : v) { + vec.push_back(a.second); } + } - for (const auto& s : vec) { - try { - nm = Netmask(s); + for (const auto& s : vec) { + try { + nm = Netmask(s); + } + catch (...) { + if (boost::ends_with(s, "ms") && sscanf(s.c_str(), "%ums", &msec)) { + ; } - catch (...) { - if (boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) { - ; + else { + try { + dn = DNSName(s); } - else { - try { - dn = DNSName(s); - } - catch (...) { - g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask"; - return; - } + catch (...) { + g_outputBuffer = "Could not parse '" + s + "' as domain name or netmask"; + return; } } } + } - std::vector qr; - std::vector rr; - qr.reserve(g_rings.getNumberOfQueryEntries()); - rr.reserve(g_rings.getNumberOfResponseEntries()); - for (const auto& shard : g_rings.d_shards) { - { - auto rl = shard->queryRing.lock(); - for (const auto& entry : *rl) { - qr.push_back(entry); - } + std::vector qr; + std::vector rr; + qr.reserve(g_rings.getNumberOfQueryEntries()); + rr.reserve(g_rings.getNumberOfResponseEntries()); + for (const auto& shard : g_rings.d_shards) { + { + auto rl = shard->queryRing.lock(); + for (const auto& entry : *rl) { + qr.push_back(entry); } - { - auto rl = shard->respRing.lock(); - for (const auto& entry : *rl) { - rr.push_back(entry); - } + } + { + auto rl = shard->respRing.lock(); + for (const auto& entry : *rl) { + rr.push_back(entry); } } + } - sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) { - return b.when < a.when; - }); - - sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) { - return b.when < a.when; - }); + sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) { + return b.when < a.when; + }); - unsigned int num=0; - struct timespec now; - gettime(&now); + sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) { + return b.when < a.when; + }); - std::multimap out; + unsigned int num = 0; + struct timespec now; + gettime(&now); - boost::format fmt("%-7.1f %-47s %-12s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n"); - const auto headLine = (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str(); - if (!outputFile) { - g_outputBuffer += headLine; - } - else { - fprintf(outputFile.get(), "%s", headLine.c_str()); - } + std::multimap out; - if (msec == -1) { - for (const auto& c : qr) { - bool nmmatch = true; - bool dnmatch = true; - if (nm) { - nmmatch = nm->match(c.requestor); - } - if (dn) { - if (c.name.empty()) { - dnmatch = false; - } - else { - dnmatch = c.name.isPartOf(*dn); - } - } - if (nmmatch && dnmatch) { - QType qt(c.qtype); - std::string extra; - if (c.dh.opcode != 0) { - extra = " (" + Opcode::to_s(c.dh.opcode) + ")"; - } - out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % dnsdist::Protocol(c.protocol).toString() % "" % htons(c.dh.id) % c.name.toString() % qt.toString() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % ("Question" + extra)).str()); - - if (limit && *limit == ++num) { - break; - } - } - } - } - num = 0; + boost::format fmt("%-7.1f %-47s %-12s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n"); + const auto headLine = (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str(); + if (!outputFile) { + g_outputBuffer += headLine; + } + else { + fprintf(outputFile.get(), "%s", headLine.c_str()); + } - string extra; - for (const auto& c : rr) { + if (msec == -1) { + for (const auto& c : qr) { bool nmmatch = true; bool dnmatch = true; - bool msecmatch = true; if (nm) { nmmatch = nm->match(c.requestor); } @@ -552,169 +510,201 @@ void setupLuaInspection(LuaContext& luaCtx) dnmatch = c.name.isPartOf(*dn); } } - if (msec != -1) { - msecmatch = (c.usec/1000 > (unsigned int)msec); - } - - if (nmmatch && dnmatch && msecmatch) { + if (nmmatch && dnmatch) { QType qt(c.qtype); - if (!c.dh.rcode) { - extra = ". " +std::to_string(htons(c.dh.ancount)) + " answers"; - } - else { - extra.clear(); - } - - std::string server = c.ds.toStringWithPort(); - std::string protocol = dnsdist::Protocol(c.protocol).toString(); - if (server == "0.0.0.0:0") { - server = "Cache"; - protocol = "-"; - } - if (c.usec != std::numeric_limits::max()) { - out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % (c.usec / 1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str()); - } - else { - out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str()); + std::string extra; + if (c.dh.opcode != 0) { + extra = " (" + Opcode::to_s(c.dh.opcode) + ")"; } + out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % dnsdist::Protocol(c.protocol).toString() % "" % htons(c.dh.id) % c.name.toString() % qt.toString() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % ("Question" + extra)).str()); if (limit && *limit == ++num) { break; } } } + } + num = 0; - for (const auto& p : out) { - if (!outputFile) { - g_outputBuffer += p.second; + string extra; + for (const auto& c : rr) { + bool nmmatch = true; + bool dnmatch = true; + bool msecmatch = true; + if (nm) { + nmmatch = nm->match(c.requestor); + } + if (dn) { + if (c.name.empty()) { + dnmatch = false; } else { - fprintf(outputFile.get(), "%s", p.second.c_str()); + dnmatch = c.name.isPartOf(*dn); } } - }); - - luaCtx.writeFunction("showResponseLatency", []() { - setLuaNoSideEffect(); - map histo; - double bin=100; - for(int i=0; i < 15; ++i) { - histo[bin]; - bin*=2; + if (msec != -1) { + msecmatch = (c.usec / 1000 > (unsigned int)msec); } - double totlat=0; - unsigned int size=0; - { - for (const auto& shard : g_rings.d_shards) { - auto rl = shard->respRing.lock(); - for(const auto& r : *rl) { - /* skip actively discovered timeouts */ - if (r.usec == std::numeric_limits::max()) - continue; - - ++size; - auto iter = histo.lower_bound(r.usec); - if(iter != histo.end()) - iter->second++; - else - histo.rbegin()++; - totlat+=r.usec; - } + if (nmmatch && dnmatch && msecmatch) { + QType qt(c.qtype); + if (!c.dh.rcode) { + extra = ". " + std::to_string(htons(c.dh.ancount)) + " answers"; + } + else { + extra.clear(); + } + + std::string server = c.ds.toStringWithPort(); + std::string protocol = dnsdist::Protocol(c.protocol).toString(); + if (server == "0.0.0.0:0") { + server = "Cache"; + protocol = "-"; + } + if (c.usec != std::numeric_limits::max()) { + out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % (c.usec / 1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str()); + } + else { + out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str()); + } + + if (limit && *limit == ++num) { + break; } } + } - if (size == 0) { - g_outputBuffer = "No traffic yet.\n"; - return; + for (const auto& p : out) { + if (!outputFile) { + g_outputBuffer += p.second; + } + else { + fprintf(outputFile.get(), "%s", p.second.c_str()); } + } + }); - g_outputBuffer = (boost::format("Average response latency: %.02f ms\n") % (0.001*totlat/size)).str(); - double highest=0; + luaCtx.writeFunction("showResponseLatency", []() { + setLuaNoSideEffect(); + map histo; + double bin = 100; + for (int i = 0; i < 15; ++i) { + histo[bin]; + bin *= 2; + } - for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { - highest=std::max(highest, iter->second*1.0); - } - boost::format fmt("%7.2f\t%s\n"); - g_outputBuffer += (fmt % "ms" % "").str(); + double totlat = 0; + unsigned int size = 0; + { + for (const auto& shard : g_rings.d_shards) { + auto rl = shard->respRing.lock(); + for (const auto& r : *rl) { + /* skip actively discovered timeouts */ + if (r.usec == std::numeric_limits::max()) + continue; - for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { - int stars = (70.0 * iter->second/highest); - char c='*'; - if(!stars && iter->second) { - stars=1; // you get 1 . to show something is there.. - if(70.0*iter->second/highest > 0.5) - c=':'; - else - c='.'; - } - g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str(); + ++size; + auto iter = histo.lower_bound(r.usec); + if (iter != histo.end()) + iter->second++; + else + histo.rbegin()++; + totlat += r.usec; + } } - }); + } - luaCtx.writeFunction("showTCPStats", [] { - setLuaNoSideEffect(); - ostringstream ret; - boost::format fmt("%-12d %-12d %-12d %-12d"); - ret << (fmt % "Workers" % "Max Workers" % "Queued" % "Max Queued") << endl; - ret << (fmt % g_tcpclientthreads->getThreadsCount() % (g_maxTCPClientThreads ? *g_maxTCPClientThreads : 0) % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl; - ret << endl; + if (size == 0) { + g_outputBuffer = "No traffic yet.\n"; + return; + } - ret << "Frontends:" << endl; - fmt = boost::format("%-3d %-20.20s %-20d %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f %-20d %-20d %-25d %-25d %-15d %-15d %-15d %-15d %-15d"); - ret << (fmt % "#" % "Address" % "Connections" % "Max concurrent conn" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration" % "TLS new sessions" % "TLS Resumptions" % "TLS unknown ticket keys" % "TLS inactive ticket keys" % "TLS 1.0" % "TLS 1.1" % "TLS 1.2" % "TLS 1.3" % "TLS other") << endl; + g_outputBuffer = (boost::format("Average response latency: %.02f ms\n") % (0.001 * totlat / size)).str(); + double highest = 0; - size_t counter = 0; - for(const auto& f : g_frontends) { - ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpMaxConcurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration % f->tlsNewSessions % f->tlsResumptions % f->tlsUnknownTicketKey % f->tlsInactiveTicketKey % f->tls10queries % f->tls11queries % f->tls12queries % f->tls13queries % f->tlsUnknownqueries) << endl; - ++counter; - } - ret << endl; + for (auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { + highest = std::max(highest, iter->second * 1.0); + } + boost::format fmt("%7.2f\t%s\n"); + g_outputBuffer += (fmt % "ms" % "").str(); + + for (auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { + int stars = (70.0 * iter->second / highest); + char c = '*'; + if (!stars && iter->second) { + stars = 1; // you get 1 . to show something is there.. + if (70.0 * iter->second / highest > 0.5) + c = ':'; + else + c = '.'; + } + g_outputBuffer += (fmt % (iter->first / 1000.0) % string(stars, c)).str(); + } + }); - ret << "Backends:" << endl; - fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-25d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20f %-20f"); - ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Max concurrent conn" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Connect timeouts" % "Too many conn" % "Total connections" % "Reused connections" % "TLS resumptions" % "Avg queries/conn" % "Avg duration") << endl; + luaCtx.writeFunction("showTCPStats", [] { + setLuaNoSideEffect(); + ostringstream ret; + boost::format fmt("%-12d %-12d %-12d %-12d"); + ret << (fmt % "Workers" % "Max Workers" % "Queued" % "Max Queued") << endl; + ret << (fmt % g_tcpclientthreads->getThreadsCount() % (g_maxTCPClientThreads ? *g_maxTCPClientThreads : 0) % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl; + ret << endl; + + ret << "Frontends:" << endl; + fmt = boost::format("%-3d %-20.20s %-20d %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f %-20d %-20d %-25d %-25d %-15d %-15d %-15d %-15d %-15d"); + ret << (fmt % "#" % "Address" % "Connections" % "Max concurrent conn" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration" % "TLS new sessions" % "TLS Resumptions" % "TLS unknown ticket keys" % "TLS inactive ticket keys" % "TLS 1.0" % "TLS 1.1" % "TLS 1.2" % "TLS 1.3" % "TLS other") << endl; + + size_t counter = 0; + for (const auto& f : g_frontends) { + ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpMaxConcurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration % f->tlsNewSessions % f->tlsResumptions % f->tlsUnknownTicketKey % f->tlsInactiveTicketKey % f->tls10queries % f->tls11queries % f->tls12queries % f->tls13queries % f->tlsUnknownqueries) << endl; + ++counter; + } + ret << endl; - auto states = g_dstates.getLocal(); - counter = 0; - for(const auto& s : *states) { - ret << (fmt % counter % s->getName() % s->d_config.remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpMaxConcurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpConnectTimeouts % s->tcpTooManyConcurrentConnections % s->tcpNewConnections % s->tcpReusedConnections % s->tlsResumptions % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl; - ++counter; - } + ret << "Backends:" << endl; + fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-25d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20f %-20f"); + ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Max concurrent conn" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Connect timeouts" % "Too many conn" % "Total connections" % "Reused connections" % "TLS resumptions" % "Avg queries/conn" % "Avg duration") << endl; - g_outputBuffer=ret.str(); - }); + auto states = g_dstates.getLocal(); + counter = 0; + for (const auto& s : *states) { + ret << (fmt % counter % s->getName() % s->d_config.remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpMaxConcurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpConnectTimeouts % s->tcpTooManyConcurrentConnections % s->tcpNewConnections % s->tcpReusedConnections % s->tlsResumptions % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl; + ++counter; + } + + g_outputBuffer = ret.str(); + }); luaCtx.writeFunction("showTLSErrorCounters", [] { - setLuaNoSideEffect(); - ostringstream ret; - boost::format fmt("%-3d %-20.20s %-23d %-23d %-23d %-23d %-23d %-23d %-23d %-23d"); + setLuaNoSideEffect(); + ostringstream ret; + boost::format fmt("%-3d %-20.20s %-23d %-23d %-23d %-23d %-23d %-23d %-23d %-23d"); - ret << (fmt % "#" % "Address" % "DH key too small" % "Inappropriate fallback" % "No shared cipher" % "Unknown cipher type" % "Unknown exchange type" % "Unknown protocol" % "Unsupported EC" % "Unsupported protocol") << endl; + ret << (fmt % "#" % "Address" % "DH key too small" % "Inappropriate fallback" % "No shared cipher" % "Unknown cipher type" % "Unknown exchange type" % "Unknown protocol" % "Unsupported EC" % "Unsupported protocol") << endl; - size_t counter = 0; - for(const auto& f : g_frontends) { - if (!f->hasTLS()) { - continue; - } - const TLSErrorCounters* errorCounters = nullptr; - if (f->tlsFrontend != nullptr) { - errorCounters = &f->tlsFrontend->d_tlsCounters; - } - else if (f->dohFrontend != nullptr) { - errorCounters = &f->dohFrontend->d_tlsContext.d_tlsCounters; - } - if (errorCounters == nullptr) { - continue; - } - - ret << (fmt % counter % f->local.toStringWithPort() % errorCounters->d_dhKeyTooSmall % errorCounters->d_inappropriateFallBack % errorCounters->d_noSharedCipher % errorCounters->d_unknownCipherType % errorCounters->d_unknownKeyExchangeType % errorCounters->d_unknownProtocol % errorCounters->d_unsupportedEC % errorCounters->d_unsupportedProtocol) << endl; - ++counter; + size_t counter = 0; + for (const auto& f : g_frontends) { + if (!f->hasTLS()) { + continue; + } + const TLSErrorCounters* errorCounters = nullptr; + if (f->tlsFrontend != nullptr) { + errorCounters = &f->tlsFrontend->d_tlsCounters; + } + else if (f->dohFrontend != nullptr) { + errorCounters = &f->dohFrontend->d_tlsContext.d_tlsCounters; + } + if (errorCounters == nullptr) { + continue; } - ret << endl; - g_outputBuffer=ret.str(); - }); + ret << (fmt % counter % f->local.toStringWithPort() % errorCounters->d_dhKeyTooSmall % errorCounters->d_inappropriateFallBack % errorCounters->d_noSharedCipher % errorCounters->d_unknownCipherType % errorCounters->d_unknownKeyExchangeType % errorCounters->d_unknownProtocol % errorCounters->d_unsupportedEC % errorCounters->d_unsupportedProtocol) << endl; + ++counter; + } + ret << endl; + + g_outputBuffer = ret.str(); + }); luaCtx.writeFunction("requestTCPStatesDump", [] { setLuaNoSideEffect(); @@ -730,95 +720,95 @@ void setupLuaInspection(LuaContext& luaCtx) }); luaCtx.writeFunction("dumpStats", [] { - setLuaNoSideEffect(); - vector leftcolumn, rightcolumn; - - boost::format fmt("%-35s\t%+11s"); - g_outputBuffer.clear(); - auto entries = *dnsdist::metrics::g_stats.entries.read_lock(); - sort(entries.begin(), entries.end(), - [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) { - return a.d_name < b.d_name; - }); - boost::format flt(" %9.1f"); - for (const auto& entry : entries) { - string second; - if (const auto& val = std::get_if(&entry.d_value)) { - second = std::to_string((*val)->load()); - } - else if (const auto& adval = std::get_if*>(&entry.d_value)) { - second = (flt % (*adval)->load()).str(); - } - else if (const auto& dval = std::get_if(&entry.d_value)) { - second = (flt % (**dval)).str(); - } - else if (const auto& func = std::get_if(&entry.d_value)) { - second = std::to_string((*func)(entry.d_name)); - } + setLuaNoSideEffect(); + vector leftcolumn, rightcolumn; - if (leftcolumn.size() < entries.size() / 2) { - leftcolumn.push_back((fmt % entry.d_name % second).str()); - } - else { - rightcolumn.push_back((fmt % entry.d_name % second).str()); - } + boost::format fmt("%-35s\t%+11s"); + g_outputBuffer.clear(); + auto entries = *dnsdist::metrics::g_stats.entries.read_lock(); + sort(entries.begin(), entries.end(), + [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) { + return a.d_name < b.d_name; + }); + boost::format flt(" %9.1f"); + for (const auto& entry : entries) { + string second; + if (const auto& val = std::get_if(&entry.d_value)) { + second = std::to_string((*val)->load()); + } + else if (const auto& adval = std::get_if*>(&entry.d_value)) { + second = (flt % (*adval)->load()).str(); + } + else if (const auto& dval = std::get_if(&entry.d_value)) { + second = (flt % (**dval)).str(); + } + else if (const auto& func = std::get_if(&entry.d_value)) { + second = std::to_string((*func)(entry.d_name)); + } + + if (leftcolumn.size() < entries.size() / 2) { + leftcolumn.push_back((fmt % entry.d_name % second).str()); + } + else { + rightcolumn.push_back((fmt % entry.d_name % second).str()); } + } - auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin(); - boost::format clmn("%|0t|%1% %|51t|%2%\n"); + auto leftiter = leftcolumn.begin(), rightiter = rightcolumn.begin(); + boost::format clmn("%|0t|%1% %|51t|%2%\n"); - for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) { - string lentry, rentry; - if(leftiter!= leftcolumn.end()) { - lentry = *leftiter; - leftiter++; - } - if(rightiter!= rightcolumn.end()) { - rentry = *rightiter; - rightiter++; - } - g_outputBuffer += (clmn % lentry % rentry).str(); + for (; leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) { + string lentry, rentry; + if (leftiter != leftcolumn.end()) { + lentry = *leftiter; + leftiter++; } - }); + if (rightiter != rightcolumn.end()) { + rentry = *rightiter; + rightiter++; + } + g_outputBuffer += (clmn % lentry % rentry).str(); + } + }); #ifndef DISABLE_DYNBLOCKS #ifndef DISABLE_DEPRECATED_DYNBLOCK luaCtx.writeFunction("exceedServFails", [](unsigned int rate, int seconds) { - setLuaNoSideEffect(); - return exceedRCode(rate, seconds, RCode::ServFail); - }); + setLuaNoSideEffect(); + return exceedRCode(rate, seconds, RCode::ServFail); + }); luaCtx.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) { - setLuaNoSideEffect(); - return exceedRCode(rate, seconds, RCode::NXDomain); - }); + setLuaNoSideEffect(); + return exceedRCode(rate, seconds, RCode::NXDomain); + }); luaCtx.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) { - setLuaNoSideEffect(); - return exceedRespByterate(rate, seconds); - }); + setLuaNoSideEffect(); + return exceedRespByterate(rate, seconds); + }); luaCtx.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) { - setLuaNoSideEffect(); - return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) { - if(q.qtype==type) - counts[q.requestor]++; - }); + setLuaNoSideEffect(); + return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) { + if (q.qtype == type) + counts[q.requestor]++; }); + }); luaCtx.writeFunction("exceedQRate", [](unsigned int rate, int seconds) { - setLuaNoSideEffect(); - return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) { - counts[q.requestor]++; - }); + setLuaNoSideEffect(); + return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) { + counts[q.requestor]++; }); + }); luaCtx.writeFunction("getRespRing", getRespRing); /* StatNode */ - luaCtx.registerFunction("numChildren", - [](const StatNode& sn) -> unsigned int { - return sn.children.size(); - } ); + luaCtx.registerFunction("numChildren", + [](const StatNode& sn) -> unsigned int { + return sn.children.size(); + }); luaCtx.registerMember("fullname", &StatNode::fullname); luaCtx.registerMember("labelsCount", &StatNode::labelsCount); luaCtx.registerMember("servfails", &StatNode::Stat::servfails); @@ -830,124 +820,124 @@ void setupLuaInspection(LuaContext& luaCtx) luaCtx.registerMember("hits", &StatNode::Stat::hits); luaCtx.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional seconds) { - statNodeRespRing(std::move(visitor), seconds ? *seconds : 0U); - }); + statNodeRespRing(std::move(visitor), seconds ? *seconds : 0U); + }); #endif /* DISABLE_DEPRECATED_DYNBLOCK */ /* DynBlockRulesGroup */ luaCtx.writeFunction("dynBlockRulesGroup", []() { return std::make_shared(); }); - luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQueryRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { - if (group) { - group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); - } - }); - luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setResponseByteRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { - if (group) { - group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); - } - }); - luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, DynBlockRulesGroup::smtVisitor_t visitor) { - if (group) { - group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); - } - }); - luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, dnsdist_ffi_stat_node_visitor_t visitor) { - if (group) { - group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); - } - }); - luaCtx.registerFunction::*)(const dnsdist_ffi_dynamic_block_inserted_hook&)>("setNewBlockInsertedHook", [](std::shared_ptr& group, const dnsdist_ffi_dynamic_block_inserted_hook& hook) { - if (group) { - group->setNewBlockHook(hook); - } - }); - luaCtx.registerFunction::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setRCodeRate", [](std::shared_ptr& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { - if (group) { - group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); - } - }); - luaCtx.registerFunction::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional, boost::optional)>("setRCodeRatio", [](std::shared_ptr& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional action, boost::optional warningRatio) { - if (group) { - group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses); - } - }); - luaCtx.registerFunction::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQTypeRate", [](std::shared_ptr& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { - if (group) { - group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); - } - }); - luaCtx.registerFunction::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional, boost::optional)>("setCacheMissRatio", [](std::shared_ptr& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional action, boost::optional warningRatio) { - if (group) { - group->setCacheMissRatio(ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses, minimumGlobalCacheHitRatio); - } - }); - luaCtx.registerFunction::*)(uint8_t, uint8_t, uint8_t)>("setMasks", [](std::shared_ptr& group, uint8_t v4, uint8_t v6, uint8_t port) { - if (group) { - if (v4 > 32) { - throw std::runtime_error("Trying to set an invalid IPv4 mask (" + std::to_string(v4) + ") to a Dynamic Block object"); - } - if (v6 > 128) { - throw std::runtime_error("Trying to set an invalid IPv6 mask (" + std::to_string(v6) + ") to a Dynamic Block object"); - } - if (port > 16) { - throw std::runtime_error("Trying to set an invalid port mask (" + std::to_string(port) + ") to a Dynamic Block object"); - } - if (port > 0 && v4 != 32) { - throw std::runtime_error("Setting a non-zero port mask for Dynamic Blocks while only considering parts of IPv4 addresses does not make sense"); - } - group->setMasks(v4, v6, port); - } - }); - luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("excludeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { - if (ranges.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&ranges)) { - group->excludeRange(Netmask(range.second)); - } - } - else if (ranges.type() == typeid(NetmaskGroup)) { - group->excludeRange(*boost::get(&ranges)); - } - else { - group->excludeRange(Netmask(*boost::get(&ranges))); - } - }); - luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("includeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { - if (ranges.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&ranges)) { - group->includeRange(Netmask(range.second)); - } + luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQueryRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + if (group) { + group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + } + }); + luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setResponseByteRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + if (group) { + group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + } + }); + luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, DynBlockRulesGroup::smtVisitor_t visitor) { + if (group) { + group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); + } + }); + luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, dnsdist_ffi_stat_node_visitor_t visitor) { + if (group) { + group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); + } + }); + luaCtx.registerFunction::*)(const dnsdist_ffi_dynamic_block_inserted_hook&)>("setNewBlockInsertedHook", [](std::shared_ptr& group, const dnsdist_ffi_dynamic_block_inserted_hook& hook) { + if (group) { + group->setNewBlockHook(hook); + } + }); + luaCtx.registerFunction::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setRCodeRate", [](std::shared_ptr& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + if (group) { + group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + } + }); + luaCtx.registerFunction::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional, boost::optional)>("setRCodeRatio", [](std::shared_ptr& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional action, boost::optional warningRatio) { + if (group) { + group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses); + } + }); + luaCtx.registerFunction::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQTypeRate", [](std::shared_ptr& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + if (group) { + group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + } + }); + luaCtx.registerFunction::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional, boost::optional)>("setCacheMissRatio", [](std::shared_ptr& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional action, boost::optional warningRatio) { + if (group) { + group->setCacheMissRatio(ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses, minimumGlobalCacheHitRatio); + } + }); + luaCtx.registerFunction::*)(uint8_t, uint8_t, uint8_t)>("setMasks", [](std::shared_ptr& group, uint8_t v4, uint8_t v6, uint8_t port) { + if (group) { + if (v4 > 32) { + throw std::runtime_error("Trying to set an invalid IPv4 mask (" + std::to_string(v4) + ") to a Dynamic Block object"); } - else if (ranges.type() == typeid(NetmaskGroup)) { - group->includeRange(*boost::get(&ranges)); + if (v6 > 128) { + throw std::runtime_error("Trying to set an invalid IPv6 mask (" + std::to_string(v6) + ") to a Dynamic Block object"); } - else { - group->includeRange(Netmask(*boost::get(&ranges))); + if (port > 16) { + throw std::runtime_error("Trying to set an invalid port mask (" + std::to_string(port) + ") to a Dynamic Block object"); } - }); - luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("removeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { - if (ranges.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&ranges)) { - group->removeRange(Netmask(range.second)); - } + if (port > 0 && v4 != 32) { + throw std::runtime_error("Setting a non-zero port mask for Dynamic Blocks while only considering parts of IPv4 addresses does not make sense"); } - else if (ranges.type() == typeid(NetmaskGroup)) { - group->removeRange(*boost::get(&ranges)); + group->setMasks(v4, v6, port); + } + }); + luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("excludeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { + if (ranges.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&ranges)) { + group->excludeRange(Netmask(range.second)); } - else { - group->removeRange(Netmask(*boost::get(&ranges))); + } + else if (ranges.type() == typeid(NetmaskGroup)) { + group->excludeRange(*boost::get(&ranges)); + } + else { + group->excludeRange(Netmask(*boost::get(&ranges))); + } + }); + luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("includeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { + if (ranges.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&ranges)) { + group->includeRange(Netmask(range.second)); } - }); - luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("excludeDomains", [](std::shared_ptr& group, LuaTypeOrArrayOf domains) { - if (domains.type() == typeid(LuaArray)) { - for (const auto& range : *boost::get>(&domains)) { - group->excludeDomain(DNSName(range.second)); - } + } + else if (ranges.type() == typeid(NetmaskGroup)) { + group->includeRange(*boost::get(&ranges)); + } + else { + group->includeRange(Netmask(*boost::get(&ranges))); + } + }); + luaCtx.registerFunction::*)(boost::variant, NetmaskGroup>)>("removeRange", [](std::shared_ptr& group, boost::variant, NetmaskGroup> ranges) { + if (ranges.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&ranges)) { + group->removeRange(Netmask(range.second)); } - else { - group->excludeDomain(DNSName(*boost::get(&domains))); + } + else if (ranges.type() == typeid(NetmaskGroup)) { + group->removeRange(*boost::get(&ranges)); + } + else { + group->removeRange(Netmask(*boost::get(&ranges))); + } + }); + luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("excludeDomains", [](std::shared_ptr& group, LuaTypeOrArrayOf domains) { + if (domains.type() == typeid(LuaArray)) { + for (const auto& range : *boost::get>(&domains)) { + group->excludeDomain(DNSName(range.second)); } - }); - luaCtx.registerFunction::*)()>("apply", [](std::shared_ptr& group) { + } + else { + group->excludeDomain(DNSName(*boost::get(&domains))); + } + }); + luaCtx.registerFunction::*)()>("apply", [](std::shared_ptr& group) { group->apply(); }); luaCtx.registerFunction("setQuiet", &DynBlockRulesGroup::setQuiet); @@ -957,7 +947,8 @@ void setupLuaInspection(LuaContext& luaCtx) luaCtx.registerMember("reason", &DynBlock::reason); luaCtx.registerMember("domain", &DynBlock::domain); luaCtx.registerMember("until", &DynBlock::until); - luaCtx.registerMember("blocks", [](const DynBlock& block) { return block.blocks.load(); }, [](DynBlock& block, [[maybe_unused]] unsigned int blocks) { }); + luaCtx.registerMember( + "blocks", [](const DynBlock& block) { return block.blocks.load(); }, [](DynBlock& block, [[maybe_unused]] unsigned int blocks) {}); luaCtx.registerMember("action", &DynBlock::action); luaCtx.registerMember("warning", &DynBlock::warning); luaCtx.registerMember("bpf", &DynBlock::bpf); diff --git a/pdns/dnsdistdist/dnsdist-lua-rules.cc b/pdns/dnsdistdist/dnsdist-lua-rules.cc index f53b98be3932..d58bedba765f 100644 --- a/pdns/dnsdistdist/dnsdist-lua-rules.cc +++ b/pdns/dnsdistdist/dnsdist-lua-rules.cc @@ -36,7 +36,8 @@ std::shared_ptr makeRule(const luadnsrule_t& var, const std::string& ca auto add = [&nmg, &smn, &suffixSeen](const string& src) { try { nmg.addMask(src); // need to try mask first, all masks are domain names! - } catch (...) { + } + catch (...) { suffixSeen = true; smn.add(DNSName(src)); } @@ -91,9 +92,9 @@ void parseRuleParams(boost::optional& params, boost::uuids::uui creationOrder = s_creationOrder++; } -typedef LuaAssociativeTable > > ruleparams_t; +typedef LuaAssociativeTable>> ruleparams_t; -template +template static std::string rulesToString(const std::vector& rules, boost::optional& vars) { int num = 0; @@ -108,7 +109,7 @@ static std::string rulesToString(const std::vector& rules, boost::optionaltoString().substr(0, truncateRuleWidth); result += (fmt % num % lim.d_name % boost::uuids::to_string(lim.d_id) % lim.d_creationOrder % lim.d_rule->d_matches % desc % lim.d_action->toString()).str(); ++num; @@ -117,25 +118,27 @@ static std::string rulesToString(const std::vector& rules, boost::optionaltoString().substr(0, truncateRuleWidth); - result += (fmt % num % lim.d_name % lim.d_rule->d_matches % desc % lim.d_action->toString()).str(); + result += (fmt % num % lim.d_name % lim.d_rule->d_matches % desc % lim.d_action->toString()).str(); ++num; } } return result; } -template -static void showRules(GlobalStateHolder > *someRuleActions, boost::optional& vars) { +template +static void showRules(GlobalStateHolder>* someRuleActions, boost::optional& vars) +{ setLuaNoSideEffect(); auto rules = someRuleActions->getLocal(); g_outputBuffer += rulesToString(*rules, vars); } -template -static void rmRule(GlobalStateHolder > *someRuleActions, const boost::variant& id) { +template +static void rmRule(GlobalStateHolder>* someRuleActions, const boost::variant& id) +{ setLuaSideEffect(); auto rules = someRuleActions->getCopy(); if (auto str = boost::get(&id)) { @@ -169,16 +172,17 @@ static void rmRule(GlobalStateHolder > *someRuleActions, const boost:: g_outputBuffer = "Error: attempt to delete non-existing rule\n"; return; } - rules.erase(rules.begin()+*pos); + rules.erase(rules.begin() + *pos); } someRuleActions->setState(std::move(rules)); } -template -static void moveRuleToTop(GlobalStateHolder > *someRuleActions) { +template +static void moveRuleToTop(GlobalStateHolder>* someRuleActions) +{ setLuaSideEffect(); auto rules = someRuleActions->getCopy(); - if(rules.empty()) + if (rules.empty()) return; auto subject = *rules.rbegin(); rules.erase(std::prev(rules.end())); @@ -186,27 +190,28 @@ static void moveRuleToTop(GlobalStateHolder > *someRuleActions) { someRuleActions->setState(std::move(rules)); } -template -static void mvRule(GlobalStateHolder > *someRespRuleActions, unsigned int from, unsigned int to) { +template +static void mvRule(GlobalStateHolder>* someRespRuleActions, unsigned int from, unsigned int to) +{ setLuaSideEffect(); auto rules = someRespRuleActions->getCopy(); - if(from >= rules.size() || to > rules.size()) { + if (from >= rules.size() || to > rules.size()) { g_outputBuffer = "Error: attempt to move rules from/to invalid index\n"; return; } auto subject = rules[from]; - rules.erase(rules.begin()+from); - if(to > rules.size()) + rules.erase(rules.begin() + from); + if (to > rules.size()) rules.push_back(subject); else { - if(from < to) + if (from < to) --to; - rules.insert(rules.begin()+to, subject); + rules.insert(rules.begin() + to, subject); } someRespRuleActions->setState(std::move(rules)); } -template +template static std::vector getTopRules(const std::vector& rules, unsigned int top) { std::vector> counts; @@ -218,8 +223,7 @@ static std::vector getTopRules(const std::vector& rules, unsigned int top) pos++; } - sort(counts.begin(), counts.end(), [](const decltype(counts)::value_type& a, - const decltype(counts)::value_type& b) { + sort(counts.begin(), counts.end(), [](const decltype(counts)::value_type& a, const decltype(counts)::value_type& b) { return b.first < a.first; }); @@ -238,7 +242,7 @@ static std::vector getTopRules(const std::vector& rules, unsigned int top) return results; } -template +template static LuaArray toLuaArray(std::vector&& rules) { LuaArray results; @@ -313,49 +317,49 @@ void setupLuaRules(LuaContext& luaCtx) return makeRule(var, "makeRule"); }); - luaCtx.registerFunction::*)()const>("toString", [](const std::shared_ptr& rule) { return rule->toString(); }); + luaCtx.registerFunction::*)() const>("toString", [](const std::shared_ptr& rule) { return rule->toString(); }); - luaCtx.registerFunction::*)()const>("getMatches", [](const std::shared_ptr& rule) { return rule->d_matches.load(); }); + luaCtx.registerFunction::*)() const>("getMatches", [](const std::shared_ptr& rule) { return rule->d_matches.load(); }); - luaCtx.registerFunction(DNSDistRuleAction::*)()const>("getSelector", [](const DNSDistRuleAction& rule) { return rule.d_rule; }); + luaCtx.registerFunction (DNSDistRuleAction::*)() const>("getSelector", [](const DNSDistRuleAction& rule) { return rule.d_rule; }); - luaCtx.registerFunction(DNSDistRuleAction::*)()const>("getAction", [](const DNSDistRuleAction& rule) { return rule.d_action; }); + luaCtx.registerFunction (DNSDistRuleAction::*)() const>("getAction", [](const DNSDistRuleAction& rule) { return rule.d_action; }); - luaCtx.registerFunction(DNSDistResponseRuleAction::*)()const>("getSelector", [](const DNSDistResponseRuleAction& rule) { return rule.d_rule; }); + luaCtx.registerFunction (DNSDistResponseRuleAction::*)() const>("getSelector", [](const DNSDistResponseRuleAction& rule) { return rule.d_rule; }); - luaCtx.registerFunction(DNSDistResponseRuleAction::*)()const>("getAction", [](const DNSDistResponseRuleAction& rule) { return rule.d_action; }); + luaCtx.registerFunction (DNSDistResponseRuleAction::*)() const>("getAction", [](const DNSDistResponseRuleAction& rule) { return rule.d_action; }); luaCtx.writeFunction("showResponseRules", [](boost::optional vars) { - showRules(&g_respruleactions, vars); - }); + showRules(&g_respruleactions, vars); + }); luaCtx.writeFunction("rmResponseRule", [](boost::variant id) { - rmRule(&g_respruleactions, id); - }); + rmRule(&g_respruleactions, id); + }); luaCtx.writeFunction("mvResponseRuleToTop", []() { - moveRuleToTop(&g_respruleactions); - }); + moveRuleToTop(&g_respruleactions); + }); luaCtx.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) { - mvRule(&g_respruleactions, from, to); - }); + mvRule(&g_respruleactions, from, to); + }); luaCtx.writeFunction("showCacheHitResponseRules", [](boost::optional vars) { - showRules(&g_cachehitrespruleactions, vars); - }); + showRules(&g_cachehitrespruleactions, vars); + }); luaCtx.writeFunction("rmCacheHitResponseRule", [](boost::variant id) { - rmRule(&g_cachehitrespruleactions, id); - }); + rmRule(&g_cachehitrespruleactions, id); + }); luaCtx.writeFunction("mvCacheHitResponseRuleToTop", []() { - moveRuleToTop(&g_cachehitrespruleactions); - }); + moveRuleToTop(&g_cachehitrespruleactions); + }); luaCtx.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) { - mvRule(&g_cachehitrespruleactions, from, to); - }); + mvRule(&g_cachehitrespruleactions, from, to); + }); luaCtx.writeFunction("showCacheInsertedResponseRules", [](boost::optional vars) { showRules(&g_cacheInsertedRespRuleActions, vars); @@ -374,53 +378,53 @@ void setupLuaRules(LuaContext& luaCtx) }); luaCtx.writeFunction("showSelfAnsweredResponseRules", [](boost::optional vars) { - showRules(&g_selfansweredrespruleactions, vars); - }); + showRules(&g_selfansweredrespruleactions, vars); + }); luaCtx.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant id) { - rmRule(&g_selfansweredrespruleactions, id); - }); + rmRule(&g_selfansweredrespruleactions, id); + }); luaCtx.writeFunction("mvSelfAnsweredResponseRuleToTop", []() { - moveRuleToTop(&g_selfansweredrespruleactions); - }); + moveRuleToTop(&g_selfansweredrespruleactions); + }); luaCtx.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) { - mvRule(&g_selfansweredrespruleactions, from, to); - }); + mvRule(&g_selfansweredrespruleactions, from, to); + }); luaCtx.writeFunction("rmRule", [](boost::variant id) { - rmRule(&g_ruleactions, id); - }); + rmRule(&g_ruleactions, id); + }); luaCtx.writeFunction("mvRuleToTop", []() { - moveRuleToTop(&g_ruleactions); - }); + moveRuleToTop(&g_ruleactions); + }); luaCtx.writeFunction("mvRule", [](unsigned int from, unsigned int to) { - mvRule(&g_ruleactions, from, to); - }); + mvRule(&g_ruleactions, from, to); + }); luaCtx.writeFunction("clearRules", []() { - setLuaSideEffect(); - g_ruleactions.modify([](decltype(g_ruleactions)::value_type& ruleactions) { - ruleactions.clear(); - }); + setLuaSideEffect(); + g_ruleactions.modify([](decltype(g_ruleactions)::value_type& ruleactions) { + ruleactions.clear(); }); + }); luaCtx.writeFunction("setRules", [](const LuaArray>& newruleactions) { - setLuaSideEffect(); - g_ruleactions.modify([newruleactions](decltype(g_ruleactions)::value_type& gruleactions) { - gruleactions.clear(); - for (const auto& pair : newruleactions) { - const auto& newruleaction = pair.second; - if (newruleaction->d_action) { - auto rule = newruleaction->d_rule; - gruleactions.push_back({std::move(rule), newruleaction->d_action, newruleaction->d_name, newruleaction->d_id, newruleaction->d_creationOrder}); - } - } - }); + setLuaSideEffect(); + g_ruleactions.modify([newruleactions](decltype(g_ruleactions)::value_type& gruleactions) { + gruleactions.clear(); + for (const auto& pair : newruleactions) { + const auto& newruleaction = pair.second; + if (newruleaction->d_action) { + auto rule = newruleaction->d_rule; + gruleactions.push_back({std::move(rule), newruleaction->d_action, newruleaction->d_name, newruleaction->d_id, newruleaction->d_creationOrder}); + } + } }); + }); luaCtx.writeFunction("getRule", [](boost::variant selector) -> boost::optional { auto rules = g_ruleactions.getLocal(); @@ -509,39 +513,39 @@ void setupLuaRules(LuaContext& luaCtx) luaCtx.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional ipv4trunc, boost::optional ipv6trunc, boost::optional burst, boost::optional expiration, boost::optional cleanupDelay, boost::optional scanFraction, boost::optional shards) { return std::shared_ptr(new MaxQPSIPRule(qps, (burst ? *burst : qps), (ipv4trunc ? *ipv4trunc : 32), (ipv6trunc ? *ipv6trunc : 64), (expiration ? *expiration : 300), (cleanupDelay ? *cleanupDelay : 60), (scanFraction ? *scanFraction : 10), (shards ? *shards : 10))); - }); + }); luaCtx.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional burst) { - if(!burst) - return std::shared_ptr(new MaxQPSRule(qps)); - else - return std::shared_ptr(new MaxQPSRule(qps, *burst)); - }); + if (!burst) + return std::shared_ptr(new MaxQPSRule(qps)); + else + return std::shared_ptr(new MaxQPSRule(qps, *burst)); + }); luaCtx.writeFunction("RegexRule", [](const std::string& str) { - return std::shared_ptr(new RegexRule(str)); - }); + return std::shared_ptr(new RegexRule(str)); + }); #ifdef HAVE_DNS_OVER_HTTPS luaCtx.writeFunction("HTTPHeaderRule", [](const std::string& header, const std::string& regex) { - return std::shared_ptr(new HTTPHeaderRule(header, regex)); - }); + return std::shared_ptr(new HTTPHeaderRule(header, regex)); + }); luaCtx.writeFunction("HTTPPathRule", [](const std::string& path) { - return std::shared_ptr(new HTTPPathRule(path)); - }); + return std::shared_ptr(new HTTPPathRule(path)); + }); luaCtx.writeFunction("HTTPPathRegexRule", [](const std::string& regex) { - return std::shared_ptr(new HTTPPathRegexRule(regex)); - }); + return std::shared_ptr(new HTTPPathRegexRule(regex)); + }); #endif #ifdef HAVE_RE2 luaCtx.writeFunction("RE2Rule", [](const std::string& str) { - return std::shared_ptr(new RE2Rule(str)); - }); + return std::shared_ptr(new RE2Rule(str)); + }); #endif luaCtx.writeFunction("SNIRule", [](const std::string& name) { - return std::shared_ptr(new SNIRule(name)); + return std::shared_ptr(new SNIRule(name)); }); luaCtx.writeFunction("SuffixMatchNodeRule", qnameSuffixRule); @@ -565,175 +569,175 @@ void setupLuaRules(LuaContext& luaCtx) return std::shared_ptr(new NetmaskGroupRule(nmg, src ? *src : true, quiet ? *quiet : false)); }); - luaCtx.writeFunction("benchRule", [](std::shared_ptr rule, boost::optional times_, boost::optional suffix_) { - setLuaNoSideEffect(); - unsigned int times = times_ ? *times_ : 100000; - DNSName suffix(suffix_ ? *suffix_ : "powerdns.com"); - struct item { - PacketBuffer packet; - InternalQueryState ids; - }; - vector items; - items.reserve(1000); - for (int n = 0; n < 1000; ++n) { - struct item i; - i.ids.qname = DNSName(std::to_string(dns_random_uint32())); - i.ids.qname += suffix; - i.ids.qtype = dns_random(0xff); - i.ids.qclass = QClass::IN; - i.ids.protocol = dnsdist::Protocol::DoUDP; - i.ids.origRemote = ComboAddress("127.0.0.1"); - i.ids.origRemote.sin4.sin_addr.s_addr = random(); - i.ids.queryRealTime.start(); - GenericDNSPacketWriter pw(i.packet, i.ids.qname, i.ids.qtype); - items.push_back(std::move(i)); - } + luaCtx.writeFunction("benchRule", [](std::shared_ptr rule, boost::optional times_, boost::optional suffix_) { + setLuaNoSideEffect(); + unsigned int times = times_ ? *times_ : 100000; + DNSName suffix(suffix_ ? *suffix_ : "powerdns.com"); + struct item + { + PacketBuffer packet; + InternalQueryState ids; + }; + vector items; + items.reserve(1000); + for (int n = 0; n < 1000; ++n) { + struct item i; + i.ids.qname = DNSName(std::to_string(dns_random_uint32())); + i.ids.qname += suffix; + i.ids.qtype = dns_random(0xff); + i.ids.qclass = QClass::IN; + i.ids.protocol = dnsdist::Protocol::DoUDP; + i.ids.origRemote = ComboAddress("127.0.0.1"); + i.ids.origRemote.sin4.sin_addr.s_addr = random(); + i.ids.queryRealTime.start(); + GenericDNSPacketWriter pw(i.packet, i.ids.qname, i.ids.qtype); + items.push_back(std::move(i)); + } - int matches = 0; - ComboAddress dummy("127.0.0.1"); - StopWatch sw; - sw.start(); - for (unsigned int n = 0; n < times; ++n) { - item& i = items[n % items.size()]; - DNSQuestion dq(i.ids, i.packet); + int matches = 0; + ComboAddress dummy("127.0.0.1"); + StopWatch sw; + sw.start(); + for (unsigned int n = 0; n < times; ++n) { + item& i = items[n % items.size()]; + DNSQuestion dq(i.ids, i.packet); - if (rule->matches(&dq)) { - matches++; - } + if (rule->matches(&dq)) { + matches++; } - double udiff = sw.udiff(); - g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f us\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str(); - - }); + } + double udiff = sw.udiff(); + g_outputBuffer = (boost::format("Had %d matches out of %d, %.1f qps, in %.1f us\n") % matches % times % (1000000 * (1.0 * times / udiff)) % udiff).str(); + }); luaCtx.writeFunction("AllRule", []() { - return std::shared_ptr(new AllRule()); - }); + return std::shared_ptr(new AllRule()); + }); luaCtx.writeFunction("ProbaRule", [](double proba) { - return std::shared_ptr(new ProbaRule(proba)); - }); + return std::shared_ptr(new ProbaRule(proba)); + }); luaCtx.writeFunction("QNameRule", [](const std::string& qname) { - return std::shared_ptr(new QNameRule(DNSName(qname))); - }); + return std::shared_ptr(new QNameRule(DNSName(qname))); + }); luaCtx.writeFunction("QNameSuffixRule", qnameSuffixRule); luaCtx.writeFunction("QTypeRule", [](boost::variant str) { - uint16_t qtype; - if (auto dir = boost::get(&str)) { - qtype = *dir; - } - else { - string val = boost::get(str); - qtype = QType::chartocode(val.c_str()); - if (!qtype) { - throw std::runtime_error("Unable to convert '"+val+"' to a DNS type"); - } + uint16_t qtype; + if (auto dir = boost::get(&str)) { + qtype = *dir; + } + else { + string val = boost::get(str); + qtype = QType::chartocode(val.c_str()); + if (!qtype) { + throw std::runtime_error("Unable to convert '" + val + "' to a DNS type"); } - return std::shared_ptr(new QTypeRule(qtype)); - }); + } + return std::shared_ptr(new QTypeRule(qtype)); + }); luaCtx.writeFunction("QClassRule", [](uint64_t c) { - checkParameterBound("QClassRule", c, std::numeric_limits::max()); - return std::shared_ptr(new QClassRule(c)); - }); + checkParameterBound("QClassRule", c, std::numeric_limits::max()); + return std::shared_ptr(new QClassRule(c)); + }); luaCtx.writeFunction("OpcodeRule", [](uint64_t code) { - checkParameterBound("OpcodeRule", code, std::numeric_limits::max()); - return std::shared_ptr(new OpcodeRule(code)); - }); + checkParameterBound("OpcodeRule", code, std::numeric_limits::max()); + return std::shared_ptr(new OpcodeRule(code)); + }); luaCtx.writeFunction("AndRule", [](const LuaArray>& a) { - return std::shared_ptr(new AndRule(a)); - }); + return std::shared_ptr(new AndRule(a)); + }); luaCtx.writeFunction("OrRule", [](const LuaArray>& a) { - return std::shared_ptr(new OrRule(a)); - }); + return std::shared_ptr(new OrRule(a)); + }); luaCtx.writeFunction("DSTPortRule", [](uint64_t port) { - checkParameterBound("DSTPortRule", port, std::numeric_limits::max()); - return std::shared_ptr(new DSTPortRule(port)); - }); + checkParameterBound("DSTPortRule", port, std::numeric_limits::max()); + return std::shared_ptr(new DSTPortRule(port)); + }); luaCtx.writeFunction("TCPRule", [](bool tcp) { - return std::shared_ptr(new TCPRule(tcp)); - }); + return std::shared_ptr(new TCPRule(tcp)); + }); luaCtx.writeFunction("DNSSECRule", []() { - return std::shared_ptr(new DNSSECRule()); - }); + return std::shared_ptr(new DNSSECRule()); + }); luaCtx.writeFunction("NotRule", [](const std::shared_ptr& rule) { - return std::shared_ptr(new NotRule(rule)); - }); + return std::shared_ptr(new NotRule(rule)); + }); luaCtx.writeFunction("RecordsCountRule", [](uint64_t section, uint64_t minCount, uint64_t maxCount) { - checkParameterBound("RecordsCountRule", section, std::numeric_limits::max()); - checkParameterBound("RecordsCountRule", minCount, std::numeric_limits::max()); - checkParameterBound("RecordsCountRule", maxCount, std::numeric_limits::max()); - return std::shared_ptr(new RecordsCountRule(section, minCount, maxCount)); - }); + checkParameterBound("RecordsCountRule", section, std::numeric_limits::max()); + checkParameterBound("RecordsCountRule", minCount, std::numeric_limits::max()); + checkParameterBound("RecordsCountRule", maxCount, std::numeric_limits::max()); + return std::shared_ptr(new RecordsCountRule(section, minCount, maxCount)); + }); luaCtx.writeFunction("RecordsTypeCountRule", [](uint64_t section, uint64_t type, uint64_t minCount, uint64_t maxCount) { - checkParameterBound("RecordsTypeCountRule", section, std::numeric_limits::max()); - checkParameterBound("RecordsTypeCountRule", type, std::numeric_limits::max()); - checkParameterBound("RecordsTypeCountRule", minCount, std::numeric_limits::max()); - checkParameterBound("RecordsTypeCountRule", maxCount, std::numeric_limits::max()); - return std::shared_ptr(new RecordsTypeCountRule(section, type, minCount, maxCount)); - }); + checkParameterBound("RecordsTypeCountRule", section, std::numeric_limits::max()); + checkParameterBound("RecordsTypeCountRule", type, std::numeric_limits::max()); + checkParameterBound("RecordsTypeCountRule", minCount, std::numeric_limits::max()); + checkParameterBound("RecordsTypeCountRule", maxCount, std::numeric_limits::max()); + return std::shared_ptr(new RecordsTypeCountRule(section, type, minCount, maxCount)); + }); luaCtx.writeFunction("TrailingDataRule", []() { - return std::shared_ptr(new TrailingDataRule()); - }); + return std::shared_ptr(new TrailingDataRule()); + }); luaCtx.writeFunction("QNameLabelsCountRule", [](uint64_t minLabelsCount, uint64_t maxLabelsCount) { - checkParameterBound("QNameLabelsCountRule", minLabelsCount, std::numeric_limits::max()); - checkParameterBound("QNameLabelsCountRule", maxLabelsCount, std::numeric_limits::max()); - return std::shared_ptr(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount)); - }); + checkParameterBound("QNameLabelsCountRule", minLabelsCount, std::numeric_limits::max()); + checkParameterBound("QNameLabelsCountRule", maxLabelsCount, std::numeric_limits::max()); + return std::shared_ptr(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount)); + }); luaCtx.writeFunction("QNameWireLengthRule", [](uint64_t min, uint64_t max) { - return std::shared_ptr(new QNameWireLengthRule(min, max)); - }); + return std::shared_ptr(new QNameWireLengthRule(min, max)); + }); luaCtx.writeFunction("RCodeRule", [](uint64_t rcode) { - checkParameterBound("RCodeRule", rcode, std::numeric_limits::max()); - return std::shared_ptr(new RCodeRule(rcode)); - }); + checkParameterBound("RCodeRule", rcode, std::numeric_limits::max()); + return std::shared_ptr(new RCodeRule(rcode)); + }); luaCtx.writeFunction("ERCodeRule", [](uint64_t rcode) { - checkParameterBound("ERCodeRule", rcode, std::numeric_limits::max()); - return std::shared_ptr(new ERCodeRule(rcode)); - }); + checkParameterBound("ERCodeRule", rcode, std::numeric_limits::max()); + return std::shared_ptr(new ERCodeRule(rcode)); + }); luaCtx.writeFunction("EDNSVersionRule", [](uint64_t version) { - checkParameterBound("EDNSVersionRule", version, std::numeric_limits::max()); - return std::shared_ptr(new EDNSVersionRule(version)); - }); + checkParameterBound("EDNSVersionRule", version, std::numeric_limits::max()); + return std::shared_ptr(new EDNSVersionRule(version)); + }); luaCtx.writeFunction("EDNSOptionRule", [](uint64_t optcode) { - checkParameterBound("EDNSOptionRule", optcode, std::numeric_limits::max()); - return std::shared_ptr(new EDNSOptionRule(optcode)); - }); + checkParameterBound("EDNSOptionRule", optcode, std::numeric_limits::max()); + return std::shared_ptr(new EDNSOptionRule(optcode)); + }); luaCtx.writeFunction("showRules", [](boost::optional vars) { - showRules(&g_ruleactions, vars); - }); + showRules(&g_ruleactions, vars); + }); luaCtx.writeFunction("RDRule", []() { - return std::shared_ptr(new RDRule()); - }); + return std::shared_ptr(new RDRule()); + }); luaCtx.writeFunction("TagRule", [](const std::string& tag, boost::optional value) { - return std::shared_ptr(new TagRule(tag, std::move(value))); - }); + return std::shared_ptr(new TagRule(tag, std::move(value))); + }); luaCtx.writeFunction("TimedIPSetRule", []() { - return std::shared_ptr(new TimedIPSetRule()); - }); + return std::shared_ptr(new TimedIPSetRule()); + }); luaCtx.writeFunction("PoolAvailableRule", [](const std::string& poolname) { return std::shared_ptr(new PoolAvailableRule(poolname)); @@ -743,56 +747,56 @@ void setupLuaRules(LuaContext& luaCtx) return std::shared_ptr(new PoolOutstandingRule(poolname, limit)); }); - luaCtx.registerFunction::*)()>("clear", [](std::shared_ptr tisr) { - tisr->clear(); - }); + luaCtx.registerFunction::*)()>("clear", [](std::shared_ptr tisr) { + tisr->clear(); + }); - luaCtx.registerFunction::*)()>("cleanup", [](std::shared_ptr tisr) { - tisr->cleanup(); - }); + luaCtx.registerFunction::*)()>("cleanup", [](std::shared_ptr tisr) { + tisr->cleanup(); + }); - luaCtx.registerFunction::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr tisr, const ComboAddress& ca, int t) { - tisr->add(ca, time(0)+t); - }); + luaCtx.registerFunction::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr tisr, const ComboAddress& ca, int t) { + tisr->add(ca, time(0) + t); + }); - luaCtx.registerFunction(std::shared_ptr::*)()>("slice", [](std::shared_ptr tisr) { - return std::dynamic_pointer_cast(tisr); - }); - luaCtx.registerFunction::*)()>("__tostring", [](std::shared_ptr tisr) { - tisr->toString(); - }); + luaCtx.registerFunction (std::shared_ptr::*)()>("slice", [](std::shared_ptr tisr) { + return std::dynamic_pointer_cast(tisr); + }); + luaCtx.registerFunction::*)()>("__tostring", [](std::shared_ptr tisr) { + tisr->toString(); + }); luaCtx.writeFunction("QNameSetRule", [](const DNSNameSet& names) { - return std::shared_ptr(new QNameSetRule(names)); - }); + return std::shared_ptr(new QNameSetRule(names)); + }); #if defined(HAVE_LMDB) || defined(HAVE_CDB) luaCtx.writeFunction("KeyValueStoreLookupRule", [](std::shared_ptr& kvs, std::shared_ptr& lookupKey) { - return std::shared_ptr(new KeyValueStoreLookupRule(kvs, lookupKey)); - }); + return std::shared_ptr(new KeyValueStoreLookupRule(kvs, lookupKey)); + }); luaCtx.writeFunction("KeyValueStoreRangeLookupRule", [](std::shared_ptr& kvs, std::shared_ptr& lookupKey) { - return std::shared_ptr(new KeyValueStoreRangeLookupRule(kvs, lookupKey)); - }); + return std::shared_ptr(new KeyValueStoreRangeLookupRule(kvs, lookupKey)); + }); #endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */ luaCtx.writeFunction("LuaRule", [](LuaRule::func_t func) { - return std::shared_ptr(new LuaRule(func)); - }); + return std::shared_ptr(new LuaRule(func)); + }); luaCtx.writeFunction("LuaFFIRule", [](LuaFFIRule::func_t func) { - return std::shared_ptr(new LuaFFIRule(func)); - }); + return std::shared_ptr(new LuaFFIRule(func)); + }); luaCtx.writeFunction("LuaFFIPerThreadRule", [](const std::string& code) { - return std::shared_ptr(new LuaFFIPerThreadRule(code)); - }); + return std::shared_ptr(new LuaFFIPerThreadRule(code)); + }); luaCtx.writeFunction("ProxyProtocolValueRule", [](uint8_t type, boost::optional value) { - return std::shared_ptr(new ProxyProtocolValueRule(type, std::move(value))); - }); + return std::shared_ptr(new ProxyProtocolValueRule(type, std::move(value))); + }); luaCtx.writeFunction("PayloadSizeRule", [](const std::string& comparison, uint16_t size) { return std::shared_ptr(new PayloadSizeRule(comparison, size)); - }); + }); } diff --git a/pdns/dnsdistdist/dnsdist-lua-vars.cc b/pdns/dnsdistdist/dnsdist-lua-vars.cc index 89927b72456a..e237204220f1 100644 --- a/pdns/dnsdistdist/dnsdist-lua-vars.cc +++ b/pdns/dnsdistdist/dnsdist-lua-vars.cc @@ -23,96 +23,23 @@ #include "dnsdist-lua.hh" #include "ednsoptions.hh" -#undef BADSIG // signal.h SIG_ERR +#undef BADSIG // signal.h SIG_ERR void setupLuaVars(LuaContext& luaCtx) { - luaCtx.writeVariable("DNSAction", LuaAssociativeTable{ - {"Drop", (int)DNSAction::Action::Drop}, - {"Nxdomain", (int)DNSAction::Action::Nxdomain}, - {"Refused", (int)DNSAction::Action::Refused}, - {"Spoof", (int)DNSAction::Action::Spoof}, - {"SpoofPacket", (int)DNSAction::Action::SpoofPacket}, - {"SpoofRaw", (int)DNSAction::Action::SpoofRaw}, - {"Allow", (int)DNSAction::Action::Allow}, - {"HeaderModify", (int)DNSAction::Action::HeaderModify}, - {"Pool", (int)DNSAction::Action::Pool}, - {"None",(int)DNSAction::Action::None}, - {"NoOp",(int)DNSAction::Action::NoOp}, - {"Delay", (int)DNSAction::Action::Delay}, - {"Truncate", (int)DNSAction::Action::Truncate}, - {"ServFail", (int)DNSAction::Action::ServFail}, - {"NoRecurse", (int)DNSAction::Action::NoRecurse} - }); + luaCtx.writeVariable("DNSAction", LuaAssociativeTable{{"Drop", (int)DNSAction::Action::Drop}, {"Nxdomain", (int)DNSAction::Action::Nxdomain}, {"Refused", (int)DNSAction::Action::Refused}, {"Spoof", (int)DNSAction::Action::Spoof}, {"SpoofPacket", (int)DNSAction::Action::SpoofPacket}, {"SpoofRaw", (int)DNSAction::Action::SpoofRaw}, {"Allow", (int)DNSAction::Action::Allow}, {"HeaderModify", (int)DNSAction::Action::HeaderModify}, {"Pool", (int)DNSAction::Action::Pool}, {"None", (int)DNSAction::Action::None}, {"NoOp", (int)DNSAction::Action::NoOp}, {"Delay", (int)DNSAction::Action::Delay}, {"Truncate", (int)DNSAction::Action::Truncate}, {"ServFail", (int)DNSAction::Action::ServFail}, {"NoRecurse", (int)DNSAction::Action::NoRecurse}}); - luaCtx.writeVariable("DNSResponseAction", LuaAssociativeTable{ - {"Allow", (int)DNSResponseAction::Action::Allow }, - {"Delay", (int)DNSResponseAction::Action::Delay }, - {"Drop", (int)DNSResponseAction::Action::Drop }, - {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify }, - {"ServFail", (int)DNSResponseAction::Action::ServFail }, - {"Truncate", (int)DNSResponseAction::Action::Truncate }, - {"None", (int)DNSResponseAction::Action::None } - }); + luaCtx.writeVariable("DNSResponseAction", LuaAssociativeTable{{"Allow", (int)DNSResponseAction::Action::Allow}, {"Delay", (int)DNSResponseAction::Action::Delay}, {"Drop", (int)DNSResponseAction::Action::Drop}, {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify}, {"ServFail", (int)DNSResponseAction::Action::ServFail}, {"Truncate", (int)DNSResponseAction::Action::Truncate}, {"None", (int)DNSResponseAction::Action::None}}); - luaCtx.writeVariable("DNSClass", LuaAssociativeTable{ - {"IN", QClass::IN }, - {"CHAOS", QClass::CHAOS }, - {"NONE", QClass::NONE }, - {"ANY", QClass::ANY } - }); + luaCtx.writeVariable("DNSClass", LuaAssociativeTable{{"IN", QClass::IN}, {"CHAOS", QClass::CHAOS}, {"NONE", QClass::NONE}, {"ANY", QClass::ANY}}); - luaCtx.writeVariable("DNSOpcode", LuaAssociativeTable{ - {"Query", Opcode::Query }, - {"IQuery", Opcode::IQuery }, - {"Status", Opcode::Status }, - {"Notify", Opcode::Notify }, - {"Update", Opcode::Update } - }); + luaCtx.writeVariable("DNSOpcode", LuaAssociativeTable{{"Query", Opcode::Query}, {"IQuery", Opcode::IQuery}, {"Status", Opcode::Status}, {"Notify", Opcode::Notify}, {"Update", Opcode::Update}}); - luaCtx.writeVariable("DNSSection", LuaAssociativeTable{ - {"Question", 0 }, - {"Answer", 1 }, - {"Authority", 2 }, - {"Additional",3 } - }); + luaCtx.writeVariable("DNSSection", LuaAssociativeTable{{"Question", 0}, {"Answer", 1}, {"Authority", 2}, {"Additional", 3}}); - luaCtx.writeVariable("EDNSOptionCode", LuaAssociativeTable{ - {"NSID", EDNSOptionCode::NSID }, - {"DAU", EDNSOptionCode::DAU }, - {"DHU", EDNSOptionCode::DHU }, - {"N3U", EDNSOptionCode::N3U }, - {"ECS", EDNSOptionCode::ECS }, - {"EXPIRE", EDNSOptionCode::EXPIRE }, - {"COOKIE", EDNSOptionCode::COOKIE }, - {"TCPKEEPALIVE", EDNSOptionCode::TCPKEEPALIVE }, - {"PADDING", EDNSOptionCode::PADDING }, - {"CHAIN", EDNSOptionCode::CHAIN }, - {"KEYTAG", EDNSOptionCode::KEYTAG } - }); + luaCtx.writeVariable("EDNSOptionCode", LuaAssociativeTable{{"NSID", EDNSOptionCode::NSID}, {"DAU", EDNSOptionCode::DAU}, {"DHU", EDNSOptionCode::DHU}, {"N3U", EDNSOptionCode::N3U}, {"ECS", EDNSOptionCode::ECS}, {"EXPIRE", EDNSOptionCode::EXPIRE}, {"COOKIE", EDNSOptionCode::COOKIE}, {"TCPKEEPALIVE", EDNSOptionCode::TCPKEEPALIVE}, {"PADDING", EDNSOptionCode::PADDING}, {"CHAIN", EDNSOptionCode::CHAIN}, {"KEYTAG", EDNSOptionCode::KEYTAG}}); - luaCtx.writeVariable("DNSRCode", LuaAssociativeTable{ - {"NOERROR", RCode::NoError }, - {"FORMERR", RCode::FormErr }, - {"SERVFAIL", RCode::ServFail }, - {"NXDOMAIN", RCode::NXDomain }, - {"NOTIMP", RCode::NotImp }, - {"REFUSED", RCode::Refused }, - {"YXDOMAIN", RCode::YXDomain }, - {"YXRRSET", RCode::YXRRSet }, - {"NXRRSET", RCode::NXRRSet }, - {"NOTAUTH", RCode::NotAuth }, - {"NOTZONE", RCode::NotZone }, - {"BADVERS", ERCode::BADVERS }, - {"BADSIG", ERCode::BADSIG }, - {"BADKEY", ERCode::BADKEY }, - {"BADTIME", ERCode::BADTIME }, - {"BADMODE", ERCode::BADMODE }, - {"BADNAME", ERCode::BADNAME }, - {"BADALG", ERCode::BADALG }, - {"BADTRUNC", ERCode::BADTRUNC }, - {"BADCOOKIE",ERCode::BADCOOKIE } - }); + luaCtx.writeVariable("DNSRCode", LuaAssociativeTable{{"NOERROR", RCode::NoError}, {"FORMERR", RCode::FormErr}, {"SERVFAIL", RCode::ServFail}, {"NXDOMAIN", RCode::NXDomain}, {"NOTIMP", RCode::NotImp}, {"REFUSED", RCode::Refused}, {"YXDOMAIN", RCode::YXDomain}, {"YXRRSET", RCode::YXRRSet}, {"NXRRSET", RCode::NXRRSet}, {"NOTAUTH", RCode::NotAuth}, {"NOTZONE", RCode::NotZone}, {"BADVERS", ERCode::BADVERS}, {"BADSIG", ERCode::BADSIG}, {"BADKEY", ERCode::BADKEY}, {"BADTIME", ERCode::BADTIME}, {"BADMODE", ERCode::BADMODE}, {"BADNAME", ERCode::BADNAME}, {"BADALG", ERCode::BADALG}, {"BADTRUNC", ERCode::BADTRUNC}, {"BADCOOKIE", ERCode::BADCOOKIE}}); LuaAssociativeTable dd; for (const auto& n : QType::names) { @@ -121,9 +48,9 @@ void setupLuaVars(LuaContext& luaCtx) luaCtx.writeVariable("DNSQType", dd); #ifdef HAVE_DNSCRYPT - luaCtx.writeVariable("DNSCryptExchangeVersion", LuaAssociativeTable{ - { "VERSION1", DNSCryptExchangeVersion::VERSION1 }, - { "VERSION2", DNSCryptExchangeVersion::VERSION2 }, - }); + luaCtx.writeVariable("DNSCryptExchangeVersion", LuaAssociativeTable{ + {"VERSION1", DNSCryptExchangeVersion::VERSION1}, + {"VERSION2", DNSCryptExchangeVersion::VERSION2}, + }); #endif } diff --git a/pdns/dnsdistdist/dnsdist-lua.hh b/pdns/dnsdistdist/dnsdist-lua.hh index 5c35c3fb9d02..8a6363300a5c 100644 --- a/pdns/dnsdistdist/dnsdist-lua.hh +++ b/pdns/dnsdistdist/dnsdist-lua.hh @@ -38,7 +38,8 @@ void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& co class SpoofAction : public DNSAction { public: - SpoofAction(const vector& addrs): d_addrs(addrs) + SpoofAction(const vector& addrs) : + d_addrs(addrs) { for (const auto& addr : d_addrs) { if (addr.isIPv4()) { @@ -54,15 +55,18 @@ public: } } - SpoofAction(const DNSName& cname): d_cname(cname) + SpoofAction(const DNSName& cname) : + d_cname(cname) { } - SpoofAction(const char* rawresponse, size_t len): d_raw(rawresponse, rawresponse + len) + SpoofAction(const char* rawresponse, size_t len) : + d_raw(rawresponse, rawresponse + len) { } - SpoofAction(const vector& raws, std::optional typeForAny): d_rawResponses(raws), d_rawTypeForAny(typeForAny) + SpoofAction(const vector& raws, std::optional typeForAny) : + d_rawResponses(raws), d_rawTypeForAny(typeForAny) { } @@ -78,8 +82,8 @@ public: ret += "raw bytes "; } else { - for(const auto& a : d_addrs) - ret += a.toString()+" "; + for (const auto& a : d_addrs) + ret += a.toString() + " "; } return ret; } @@ -105,7 +109,8 @@ class LimitTTLResponseAction : public DNSResponseAction, public boost::noncopyab public: LimitTTLResponseAction() {} - LimitTTLResponseAction(uint32_t min, uint32_t max = std::numeric_limits::max(), const std::unordered_set& types = {}) : d_types(types), d_min(min), d_max(max) + LimitTTLResponseAction(uint32_t min, uint32_t max = std::numeric_limits::max(), const std::unordered_set& types = {}) : + d_types(types), d_min(min), d_max(max) { } @@ -126,7 +131,7 @@ public: } return ttl; }; - editDNSPacketTTL(reinterpret_cast(dr->getMutableData().data()), dr->getData().size(), visitor); + editDNSPacketTTL(reinterpret_cast(dr->getMutableData().data()), dr->getData().size(), visitor); return DNSResponseAction::Action::None; } @@ -147,7 +152,7 @@ public: } result += "]"; } - result += + ")"; + result += +")"; return result; } @@ -157,9 +162,12 @@ private: uint32_t d_max{std::numeric_limits::max()}; }; -template using LuaArray = std::vector>; -template using LuaAssociativeTable = std::unordered_map; -template using LuaTypeOrArrayOf = boost::variant>; +template +using LuaArray = std::vector>; +template +using LuaAssociativeTable = std::unordered_map; +template +using LuaTypeOrArrayOf = boost::variant>; using luaruleparams_t = LuaAssociativeTable; using nmts_t = NetmaskTree; @@ -195,8 +203,9 @@ void setupLuaLoadBalancingContext(LuaContext& luaCtx); * * returns: -1 if type wasn't compatible, 0 if not found or number of element(s) found */ -template -static inline int getOptionalValue(boost::optional& vars, const std::string& key, T& value, bool warnOnWrongType = true) { +template +static inline int getOptionalValue(boost::optional& vars, const std::string& key, T& value, bool warnOnWrongType = true) +{ /* nothing found, nothing to return */ if (!vars) { return 0; @@ -205,7 +214,8 @@ static inline int getOptionalValue(boost::optional& vars, const std::string& if (vars->count(key)) { try { value = boost::get((*vars)[key]); - } catch (const boost::bad_get& e) { + } + catch (const boost::bad_get& e) { /* key is there but isn't compatible */ if (warnOnWrongType) { warnlog("Invalid type for key '%s' - ignored", key); @@ -217,8 +227,9 @@ static inline int getOptionalValue(boost::optional& vars, const std::string& return vars->erase(key); } -template -static inline int getOptionalIntegerValue(const std::string& func, boost::optional& vars, const std::string& key, T& value) { +template +static inline int getOptionalIntegerValue(const std::string& func, boost::optional& vars, const std::string& key, T& value) +{ std::string valueStr; auto ret = getOptionalValue(vars, key, valueStr, true); if (ret == 1) { @@ -233,8 +244,9 @@ static inline int getOptionalIntegerValue(const std::string& func, boost::option return ret; } -template -static inline void checkAllParametersConsumed(const std::string& func, const boost::optional& vars) { +template +static inline void checkAllParametersConsumed(const std::string& func, const boost::optional& vars) +{ /* no vars */ if (!vars) { return; diff --git a/pdns/dnsdistdist/dnsdist-rings.cc b/pdns/dnsdistdist/dnsdist-rings.cc index b97b44e6459d..7ab00f1b7ba4 100644 --- a/pdns/dnsdistdist/dnsdist-rings.cc +++ b/pdns/dnsdistdist/dnsdist-rings.cc @@ -65,7 +65,8 @@ void Rings::setNumberOfLockRetries(size_t retries) { if (d_numberOfShards <= 1) { d_nbLockTries = 0; - } else { + } + else { d_nbLockTries = retries; } } @@ -92,23 +93,23 @@ size_t Rings::numDistinctRequestors() return s.size(); } -std::unordered_map>> Rings::getTopBandwidth(unsigned int numentries) +std::unordered_map>> Rings::getTopBandwidth(unsigned int numentries) { map counts; - uint64_t total=0; + uint64_t total = 0; for (const auto& shard : d_shards) { { auto rl = shard->queryRing.lock(); - for(const auto& q : *rl) { + for (const auto& q : *rl) { counts[q.requestor] += q.size; - total+=q.size; + total += q.size; } } { auto rl = shard->respRing.lock(); - for(const auto& r : *rl) { + for (const auto& r : *rl) { counts[r.requestor] += r.size; - total+=r.size; + total += r.size; } } } @@ -116,30 +117,29 @@ std::unordered_map>> Rings::getTopBand typedef vector> ret_t; ret_t rcounts; rcounts.reserve(counts.size()); - for(const auto& p : counts) + for (const auto& p : counts) rcounts.push_back({p.second, p.first}); numentries = rcounts.size() < numentries ? rcounts.size() : numentries; - partial_sort(rcounts.begin(), rcounts.begin()+numentries, rcounts.end(), [](const ret_t::value_type&a, const ret_t::value_type&b) - { - return(b.first < a.first); - }); - std::unordered_map>> ret; + partial_sort(rcounts.begin(), rcounts.begin() + numentries, rcounts.end(), [](const ret_t::value_type& a, const ret_t::value_type& b) { + return (b.first < a.first); + }); + std::unordered_map>> ret; uint64_t rest = 0; int count = 1; - for(const auto& rc : rcounts) { + for (const auto& rc : rcounts) { if (count == static_cast(numentries + 1)) { - rest+=rc.first; + rest += rc.first; } else { - ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); + ret.insert({count++, {rc.second.toString(), rc.first, 100.0 * rc.first / total}}); } } if (total > 0) { - ret.insert({count, {"Rest", rest, 100.0*rest/total}}); + ret.insert({count, {"Rest", rest, 100.0 * rest / total}}); } else { - ret.insert({count, {"Rest", rest, 100.0 }}); + ret.insert({count, {"Rest", rest, 100.0}}); } return ret; @@ -170,7 +170,7 @@ size_t Rings::loadFromFile(const std::string& filepath, const struct timespec& n isResponse = true; } else { - cerr<<"skipping line with "< timeStr; stringtok(timeStr, parts.at(idx++), "."); if (timeStr.size() != 2) { - cerr<<"skipping invalid time "<> respRing; }; - Rings(size_t capacity=10000, size_t numberOfShards=10, size_t nbLockTries=5, bool keepLockingStats=false): d_blockingQueryInserts(0), d_blockingResponseInserts(0), d_deferredQueryInserts(0), d_deferredResponseInserts(0), d_nbQueryEntries(0), d_nbResponseEntries(0), d_currentShardId(0), d_capacity(capacity), d_numberOfShards(numberOfShards), d_nbLockTries(nbLockTries), d_keepLockingStats(keepLockingStats) + Rings(size_t capacity = 10000, size_t numberOfShards = 10, size_t nbLockTries = 5, bool keepLockingStats = false) : + d_blockingQueryInserts(0), d_blockingResponseInserts(0), d_deferredQueryInserts(0), d_deferredResponseInserts(0), d_nbQueryEntries(0), d_nbResponseEntries(0), d_currentShardId(0), d_capacity(capacity), d_numberOfShards(numberOfShards), d_nbLockTries(nbLockTries), d_keepLockingStats(keepLockingStats) { } - std::unordered_map > > getTopBandwidth(unsigned int numentries); + std::unordered_map>> getTopBandwidth(unsigned int numentries); size_t numDistinctRequestors(); /* this function should not be called after init() has been called */ void setCapacity(size_t newCapacity, size_t numberOfShards); @@ -201,7 +203,7 @@ struct Rings { return d_recordResponses; } - std::vector > d_shards; + std::vector> d_shards; pdns::stat_t d_blockingQueryInserts; pdns::stat_t d_blockingResponseInserts; pdns::stat_t d_deferredQueryInserts; diff --git a/pdns/dnsdistdist/dnsdist-snmp.cc b/pdns/dnsdistdist/dnsdist-snmp.cc index 856fccb111d1..6081d11f9ec5 100644 --- a/pdns/dnsdistdist/dnsdist-snmp.cc +++ b/pdns/dnsdistdist/dnsdist-snmp.cc @@ -15,46 +15,46 @@ DNSDistSNMPAgent* g_snmpAgent{nullptr}; #define DNSDIST_TRAPS_OID DNSDIST_OID, 10, 0 #define DNSDIST_TRAP_OBJECTS_OID DNSDIST_OID, 11 -static const oid queriesOID[] = { DNSDIST_STATS_OID, 1 }; -static const oid responsesOID[] = { DNSDIST_STATS_OID, 2 }; -static const oid servfailResponsesOID[] = { DNSDIST_STATS_OID, 3 }; -static const oid aclDropsOID[] = { DNSDIST_STATS_OID, 4 }; +static const oid queriesOID[] = {DNSDIST_STATS_OID, 1}; +static const oid responsesOID[] = {DNSDIST_STATS_OID, 2}; +static const oid servfailResponsesOID[] = {DNSDIST_STATS_OID, 3}; +static const oid aclDropsOID[] = {DNSDIST_STATS_OID, 4}; // 5 was BlockFilter, removed in 1.2.0 -static const oid ruleDropOID[] = { DNSDIST_STATS_OID, 6 }; -static const oid ruleNXDomainOID[] = { DNSDIST_STATS_OID, 7 }; -static const oid ruleRefusedOID[] = { DNSDIST_STATS_OID, 8 }; -static const oid selfAnsweredOID[] = { DNSDIST_STATS_OID, 9 }; -static const oid downstreamTimeoutsOID[] = { DNSDIST_STATS_OID, 10 }; -static const oid downstreamSendErrorsOID[] = { DNSDIST_STATS_OID, 11 }; -static const oid truncFailOID[] = { DNSDIST_STATS_OID, 12 }; -static const oid noPolicyOID[] = { DNSDIST_STATS_OID, 13 }; -static const oid latency0_1OID[] = { DNSDIST_STATS_OID, 14 }; -static const oid latency1_10OID[] = { DNSDIST_STATS_OID, 15 }; -static const oid latency10_50OID[] = { DNSDIST_STATS_OID, 16 }; -static const oid latency50_100OID[] = { DNSDIST_STATS_OID, 17 }; -static const oid latency100_1000OID[] = { DNSDIST_STATS_OID, 18 }; -static const oid latencySlowOID[] = { DNSDIST_STATS_OID, 19 }; -static const oid latencyAvg100OID[] = { DNSDIST_STATS_OID, 20 }; -static const oid latencyAvg1000OID[] = { DNSDIST_STATS_OID, 21 }; -static const oid latencyAvg10000OID[] = { DNSDIST_STATS_OID, 22 }; -static const oid latencyAvg1000000OID[] = { DNSDIST_STATS_OID, 23 }; -static const oid uptimeOID[] = { DNSDIST_STATS_OID, 24 }; -static const oid realMemoryUsageOID[] = { DNSDIST_STATS_OID, 25 }; -static const oid nonCompliantQueriesOID[] = { DNSDIST_STATS_OID, 26 }; -static const oid nonCompliantResponsesOID[] = { DNSDIST_STATS_OID, 27 }; -static const oid rdQueriesOID[] = { DNSDIST_STATS_OID, 28 }; -static const oid emptyQueriesOID[] = { DNSDIST_STATS_OID, 29 }; -static const oid cacheHitsOID[] = { DNSDIST_STATS_OID, 30 }; -static const oid cacheMissesOID[] = { DNSDIST_STATS_OID, 31 }; -static const oid cpuUserMSecOID[] = { DNSDIST_STATS_OID, 32 }; -static const oid cpuSysMSecOID[] = { DNSDIST_STATS_OID, 33 }; -static const oid fdUsageOID[] = { DNSDIST_STATS_OID, 34 }; -static const oid dynBlockedOID[] = { DNSDIST_STATS_OID, 35 }; -static const oid dynBlockedNMGSizeOID[] = { DNSDIST_STATS_OID, 36 }; -static const oid ruleServFailOID[] = { DNSDIST_STATS_OID, 37 }; -static const oid securityStatusOID[] = { DNSDIST_STATS_OID, 38 }; -static const oid specialMemoryUsageOID[] = { DNSDIST_STATS_OID, 39 }; -static const oid ruleTruncatedOID[] = { DNSDIST_STATS_OID, 40 }; +static const oid ruleDropOID[] = {DNSDIST_STATS_OID, 6}; +static const oid ruleNXDomainOID[] = {DNSDIST_STATS_OID, 7}; +static const oid ruleRefusedOID[] = {DNSDIST_STATS_OID, 8}; +static const oid selfAnsweredOID[] = {DNSDIST_STATS_OID, 9}; +static const oid downstreamTimeoutsOID[] = {DNSDIST_STATS_OID, 10}; +static const oid downstreamSendErrorsOID[] = {DNSDIST_STATS_OID, 11}; +static const oid truncFailOID[] = {DNSDIST_STATS_OID, 12}; +static const oid noPolicyOID[] = {DNSDIST_STATS_OID, 13}; +static const oid latency0_1OID[] = {DNSDIST_STATS_OID, 14}; +static const oid latency1_10OID[] = {DNSDIST_STATS_OID, 15}; +static const oid latency10_50OID[] = {DNSDIST_STATS_OID, 16}; +static const oid latency50_100OID[] = {DNSDIST_STATS_OID, 17}; +static const oid latency100_1000OID[] = {DNSDIST_STATS_OID, 18}; +static const oid latencySlowOID[] = {DNSDIST_STATS_OID, 19}; +static const oid latencyAvg100OID[] = {DNSDIST_STATS_OID, 20}; +static const oid latencyAvg1000OID[] = {DNSDIST_STATS_OID, 21}; +static const oid latencyAvg10000OID[] = {DNSDIST_STATS_OID, 22}; +static const oid latencyAvg1000000OID[] = {DNSDIST_STATS_OID, 23}; +static const oid uptimeOID[] = {DNSDIST_STATS_OID, 24}; +static const oid realMemoryUsageOID[] = {DNSDIST_STATS_OID, 25}; +static const oid nonCompliantQueriesOID[] = {DNSDIST_STATS_OID, 26}; +static const oid nonCompliantResponsesOID[] = {DNSDIST_STATS_OID, 27}; +static const oid rdQueriesOID[] = {DNSDIST_STATS_OID, 28}; +static const oid emptyQueriesOID[] = {DNSDIST_STATS_OID, 29}; +static const oid cacheHitsOID[] = {DNSDIST_STATS_OID, 30}; +static const oid cacheMissesOID[] = {DNSDIST_STATS_OID, 31}; +static const oid cpuUserMSecOID[] = {DNSDIST_STATS_OID, 32}; +static const oid cpuSysMSecOID[] = {DNSDIST_STATS_OID, 33}; +static const oid fdUsageOID[] = {DNSDIST_STATS_OID, 34}; +static const oid dynBlockedOID[] = {DNSDIST_STATS_OID, 35}; +static const oid dynBlockedNMGSizeOID[] = {DNSDIST_STATS_OID, 36}; +static const oid ruleServFailOID[] = {DNSDIST_STATS_OID, 37}; +static const oid securityStatusOID[] = {DNSDIST_STATS_OID, 38}; +static const oid specialMemoryUsageOID[] = {DNSDIST_STATS_OID, 39}; +static const oid ruleTruncatedOID[] = {DNSDIST_STATS_OID, 40}; static std::unordered_map s_statsMap; @@ -202,40 +202,40 @@ static void registerGauge64Stat(const char* name, const oid statOID[], size_t st } /* column number definitions for table backendStatTable */ -#define COLUMN_BACKENDID 1 -#define COLUMN_BACKENDNAME 2 -#define COLUMN_BACKENDLATENCY 3 -#define COLUMN_BACKENDWEIGHT 4 -#define COLUMN_BACKENDOUTSTANDING 5 -#define COLUMN_BACKENDQPSLIMIT 6 -#define COLUMN_BACKENDREUSED 7 -#define COLUMN_BACKENDSTATE 8 -#define COLUMN_BACKENDADDRESS 9 -#define COLUMN_BACKENDPOOLS 10 -#define COLUMN_BACKENDQPS 11 -#define COLUMN_BACKENDQUERIES 12 -#define COLUMN_BACKENDORDER 13 - -static const oid backendStatTableOID[] = { DNSDIST_STATS_TABLE_OID }; -static const oid backendNameOID[] = { DNSDIST_STATS_TABLE_OID, 1, 2 }; -static const oid backendStateOID[] = { DNSDIST_STATS_TABLE_OID, 1, 8}; -static const oid backendAddressOID[] = { DNSDIST_STATS_TABLE_OID, 1, 9}; - -static const oid socketFamilyOID[] = { DNSDIST_TRAP_OBJECTS_OID, 1, 0 }; -static const oid socketProtocolOID[] = { DNSDIST_TRAP_OBJECTS_OID, 2, 0 }; -static const oid fromAddressOID[] = { DNSDIST_TRAP_OBJECTS_OID, 3, 0 }; -static const oid toAddressOID[] = { DNSDIST_TRAP_OBJECTS_OID, 4, 0 }; -static const oid queryTypeOID[] = { DNSDIST_TRAP_OBJECTS_OID, 5, 0 }; -static const oid querySizeOID[] = { DNSDIST_TRAP_OBJECTS_OID, 6, 0 }; -static const oid queryIDOID[] = { DNSDIST_TRAP_OBJECTS_OID, 7, 0 }; -static const oid qNameOID[] = { DNSDIST_TRAP_OBJECTS_OID, 8, 0 }; -static const oid qClassOID[] = { DNSDIST_TRAP_OBJECTS_OID, 9, 0 }; -static const oid qTypeOID[] = { DNSDIST_TRAP_OBJECTS_OID, 10, 0 }; -static const oid trapReasonOID[] = { DNSDIST_TRAP_OBJECTS_OID, 11, 0 }; - -static const oid backendStatusChangeTrapOID[] = { DNSDIST_TRAPS_OID, 1 }; -static const oid actionTrapOID[] = { DNSDIST_TRAPS_OID, 2 }; -static const oid customTrapOID[] = { DNSDIST_TRAPS_OID, 3 }; +#define COLUMN_BACKENDID 1 +#define COLUMN_BACKENDNAME 2 +#define COLUMN_BACKENDLATENCY 3 +#define COLUMN_BACKENDWEIGHT 4 +#define COLUMN_BACKENDOUTSTANDING 5 +#define COLUMN_BACKENDQPSLIMIT 6 +#define COLUMN_BACKENDREUSED 7 +#define COLUMN_BACKENDSTATE 8 +#define COLUMN_BACKENDADDRESS 9 +#define COLUMN_BACKENDPOOLS 10 +#define COLUMN_BACKENDQPS 11 +#define COLUMN_BACKENDQUERIES 12 +#define COLUMN_BACKENDORDER 13 + +static const oid backendStatTableOID[] = {DNSDIST_STATS_TABLE_OID}; +static const oid backendNameOID[] = {DNSDIST_STATS_TABLE_OID, 1, 2}; +static const oid backendStateOID[] = {DNSDIST_STATS_TABLE_OID, 1, 8}; +static const oid backendAddressOID[] = {DNSDIST_STATS_TABLE_OID, 1, 9}; + +static const oid socketFamilyOID[] = {DNSDIST_TRAP_OBJECTS_OID, 1, 0}; +static const oid socketProtocolOID[] = {DNSDIST_TRAP_OBJECTS_OID, 2, 0}; +static const oid fromAddressOID[] = {DNSDIST_TRAP_OBJECTS_OID, 3, 0}; +static const oid toAddressOID[] = {DNSDIST_TRAP_OBJECTS_OID, 4, 0}; +static const oid queryTypeOID[] = {DNSDIST_TRAP_OBJECTS_OID, 5, 0}; +static const oid querySizeOID[] = {DNSDIST_TRAP_OBJECTS_OID, 6, 0}; +static const oid queryIDOID[] = {DNSDIST_TRAP_OBJECTS_OID, 7, 0}; +static const oid qNameOID[] = {DNSDIST_TRAP_OBJECTS_OID, 8, 0}; +static const oid qClassOID[] = {DNSDIST_TRAP_OBJECTS_OID, 9, 0}; +static const oid qTypeOID[] = {DNSDIST_TRAP_OBJECTS_OID, 10, 0}; +static const oid trapReasonOID[] = {DNSDIST_TRAP_OBJECTS_OID, 11, 0}; + +static const oid backendStatusChangeTrapOID[] = {DNSDIST_TRAPS_OID, 1}; +static const oid actionTrapOID[] = {DNSDIST_TRAPS_OID, 2}; +static const oid customTrapOID[] = {DNSDIST_TRAPS_OID, 3}; static servers_t s_servers; static size_t s_currentServerIdx = 0; @@ -249,7 +249,7 @@ static netsnmp_variable_list* backendStatTable_get_next_data_point(void** loop_c return NULL; } - *my_data_context = (void*) (s_servers[s_currentServerIdx]).get(); + *my_data_context = (void*)(s_servers[s_currentServerIdx]).get(); snmp_set_var_typed_integer(put_index_data, ASN_UNSIGNED, s_currentServerIdx); s_currentServerIdx++; @@ -289,7 +289,7 @@ static int backendStatTable_handler(netsnmp_mib_handler* handler, case MODE_GET: for (request = requests; request; request = request->next) { netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); - const DownstreamState* server = (const DownstreamState*) netsnmp_extract_iterator_context(request); + const DownstreamState* server = (const DownstreamState*)netsnmp_extract_iterator_context(request); if (!server) { continue; @@ -321,8 +321,7 @@ static int backendStatTable_handler(netsnmp_mib_handler* handler, case COLUMN_BACKENDREUSED: DNSDistSNMPAgent::setCounter64Value(request, server->reuseds.load()); break; - case COLUMN_BACKENDSTATE: - { + case COLUMN_BACKENDSTATE: { std::string state(server->getStatus()); snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, @@ -330,8 +329,7 @@ static int backendStatTable_handler(netsnmp_mib_handler* handler, state.size()); break; } - case COLUMN_BACKENDADDRESS: - { + case COLUMN_BACKENDADDRESS: { std::string addr(server->d_config.remote.toStringWithPort()); snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, @@ -339,12 +337,11 @@ static int backendStatTable_handler(netsnmp_mib_handler* handler, addr.size()); break; } - case COLUMN_BACKENDPOOLS: - { + case COLUMN_BACKENDPOOLS: { std::string pools; for (const auto& p : server->d_config.pools) { if (!pools.empty()) { - pools+=" "; + pools += " "; } pools += p; } @@ -388,8 +385,7 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(const DownstreamState& dss) snmpTrapOID.size(), ASN_OBJECT_ID, backendStatusChangeTrapOID, - OID_LENGTH(backendStatusChangeTrapOID) * sizeof(oid)); - + OID_LENGTH(backendStatusChangeTrapOID) * sizeof(oid)); snmp_varlist_add_variable(&varList, backendNameOID, @@ -428,7 +424,7 @@ bool DNSDistSNMPAgent::sendCustomTrap(const std::string& reason) snmpTrapOID.size(), ASN_OBJECT_ID, customTrapOID, - OID_LENGTH(customTrapOID) * sizeof(oid)); + OID_LENGTH(customTrapOID) * sizeof(oid)); snmp_varlist_add_variable(&varList, trapReasonOID, @@ -452,10 +448,10 @@ bool DNSDistSNMPAgent::sendDNSTrap(const DNSQuestion& dq, const std::string& rea const uint32_t socketFamily = dq.ids.origRemote.isIPv4() ? 1 : 2; const uint32_t socketProtocol = dq.overTCP() ? 2 : 1; const uint32_t queryType = dq.getHeader()->qr ? 2 : 1; - const uint32_t querySize = (uint32_t) dq.getData().size(); - const uint32_t queryID = (uint32_t) ntohs(dq.getHeader()->id); - const uint32_t qType = (uint32_t) dq.ids.qtype; - const uint32_t qClass = (uint32_t) dq.ids.qclass; + const uint32_t querySize = (uint32_t)dq.getData().size(); + const uint32_t queryID = (uint32_t)ntohs(dq.getHeader()->id); + const uint32_t qType = (uint32_t)dq.ids.qtype; + const uint32_t qClass = (uint32_t)dq.ids.qclass; netsnmp_variable_list* varList = nullptr; @@ -464,7 +460,7 @@ bool DNSDistSNMPAgent::sendDNSTrap(const DNSQuestion& dq, const std::string& rea snmpTrapOID.size(), ASN_OBJECT_ID, actionTrapOID, - OID_LENGTH(actionTrapOID) * sizeof(oid)); + OID_LENGTH(actionTrapOID) * sizeof(oid)); snmp_varlist_add_variable(&varList, socketFamilyOID, @@ -549,7 +545,8 @@ bool DNSDistSNMPAgent::sendDNSTrap(const DNSQuestion& dq, const std::string& rea #endif /* HAVE_NET_SNMP */ } -DNSDistSNMPAgent::DNSDistSNMPAgent(const std::string& name, const std::string& daemonSocket): SNMPAgent(name, daemonSocket) +DNSDistSNMPAgent::DNSDistSNMPAgent(const std::string& name, const std::string& daemonSocket) : + SNMPAgent(name, daemonSocket) { #ifdef HAVE_NET_SNMP @@ -593,10 +590,9 @@ DNSDistSNMPAgent::DNSDistSNMPAgent(const std::string& name, const std::string& d registerGauge64Stat("securityStatus", securityStatusOID, OID_LENGTH(securityStatusOID), [](const std::string&) { return dnsdist::metrics::g_stats.securityStatus.load(); }); registerGauge64Stat("realMemoryUsage", realMemoryUsageOID, OID_LENGTH(realMemoryUsageOID), &getRealMemoryUsage); - netsnmp_table_registration_info* table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); netsnmp_table_helper_add_indexes(table_info, - ASN_GAUGE, /* index: backendId */ + ASN_GAUGE, /* index: backendId */ 0); table_info->min_column = COLUMN_BACKENDNAME; table_info->max_column = COLUMN_BACKENDORDER; diff --git a/pdns/dnsdistdist/dnsdist-snmp.hh b/pdns/dnsdistdist/dnsdist-snmp.hh index 283d43c6a579..7c1deff9b321 100644 --- a/pdns/dnsdistdist/dnsdist-snmp.hh +++ b/pdns/dnsdistdist/dnsdist-snmp.hh @@ -27,11 +27,11 @@ class DNSDistSNMPAgent; #include "dnsdist.hh" -class DNSDistSNMPAgent: public SNMPAgent +class DNSDistSNMPAgent : public SNMPAgent { public: DNSDistSNMPAgent(const std::string& name, const std::string& daemonSocket); bool sendBackendStatusChangeTrap(const DownstreamState&); bool sendCustomTrap(const std::string& reason); - bool sendDNSTrap(const DNSQuestion&, const std::string& reason=""); + bool sendDNSTrap(const DNSQuestion&, const std::string& reason = ""); }; diff --git a/pdns/dnsdistdist/dnsdist-web.cc b/pdns/dnsdistdist/dnsdist-web.cc index 066b5c177f54..58118e7db110 100644 --- a/pdns/dnsdistdist/dnsdist-web.cc +++ b/pdns/dnsdistdist/dnsdist-web.cc @@ -53,7 +53,7 @@ struct WebserverConfig NetmaskGroup acl; std::unique_ptr password; std::unique_ptr apiKey; - boost::optional > customHeaders; + boost::optional> customHeaders; bool apiRequiresAuthentication{true}; bool dashboardRequiresAuthentication{true}; bool statsRequireAuthentication{true}; @@ -99,13 +99,15 @@ std::string getWebserverConfig() class WebClientConnection { public: - WebClientConnection(const ComboAddress& client, int fd): d_client(client), d_socket(fd) + WebClientConnection(const ComboAddress& client, int fd) : + d_client(client), d_socket(fd) { if (!s_connManager.registerConnection()) { throw std::runtime_error("Too many concurrent web client connections"); } } - WebClientConnection(WebClientConnection&& rhs): d_client(rhs.d_client), d_socket(std::move(rhs.d_socket)) + WebClientConnection(WebClientConnection&& rhs) : + d_client(rhs.d_client), d_socket(std::move(rhs.d_socket)) { } @@ -138,87 +140,88 @@ class WebClientConnection static MetricDefinitionStorage s_metricDefinitions; std::map MetricDefinitionStorage::metrics{ - { "responses", MetricDefinition(PrometheusMetricType::counter, "Number of responses received from backends") }, - { "servfail-responses", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers received from backends") }, - { "queries", MetricDefinition(PrometheusMetricType::counter, "Number of received queries")}, - { "frontend-nxdomain", MetricDefinition(PrometheusMetricType::counter, "Number of NXDomain answers sent to clients")}, - { "frontend-servfail", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers sent to clients")}, - { "frontend-noerror", MetricDefinition(PrometheusMetricType::counter, "Number of NoError answers sent to clients")}, - { "acl-drops", MetricDefinition(PrometheusMetricType::counter, "Number of packets dropped because of the ACL")}, - { "rule-drop", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a rule")}, - { "rule-nxdomain", MetricDefinition(PrometheusMetricType::counter, "Number of NXDomain answers returned because of a rule")}, - { "rule-refused", MetricDefinition(PrometheusMetricType::counter, "Number of Refused answers returned because of a rule")}, - { "rule-servfail", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers received because of a rule")}, - { "rule-truncated", MetricDefinition(PrometheusMetricType::counter, "Number of truncated answers returned because of a rule")}, - { "self-answered", MetricDefinition(PrometheusMetricType::counter, "Number of self-answered responses")}, - { "downstream-timeouts", MetricDefinition(PrometheusMetricType::counter, "Number of queries not answered in time by a backend")}, - { "downstream-send-errors", MetricDefinition(PrometheusMetricType::counter, "Number of errors when sending a query to a backend")}, - { "trunc-failures", MetricDefinition(PrometheusMetricType::counter, "Number of errors encountered while truncating an answer")}, - { "no-policy", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because no server was available")}, - { "latency0-1", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in less than 1ms")}, - { "latency1-10", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 1-10 ms")}, - { "latency10-50", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 10-50 ms")}, - { "latency50-100", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 50-100 ms")}, - { "latency100-1000", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 100-1000 ms")}, - { "latency-slow", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in more than 1 second")}, - { "latency-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 100 packets")}, - { "latency-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 1000 packets")}, - { "latency-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 10000 packets")}, - { "latency-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 1000000 packets")}, - { "latency-tcp-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over TCP")}, - { "latency-tcp-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over TCP")}, - { "latency-tcp-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over TCP")}, - { "latency-tcp-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over TCP")}, - { "latency-dot-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoT")}, - { "latency-dot-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoT")}, - { "latency-dot-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoT")}, - { "latency-dot-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoT")}, - { "latency-doh-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoH")}, - { "latency-doh-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoH")}, - { "latency-doh-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoH")}, - { "latency-doh-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoH")}, - { "latency-doq-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoQ")}, - { "latency-doq-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoQ")}, - { "latency-doq-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoQ")}, - { "latency-doq-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoQ")}, - { "uptime", MetricDefinition(PrometheusMetricType::gauge, "Uptime of the dnsdist process in seconds")}, - { "real-memory-usage", MetricDefinition(PrometheusMetricType::gauge, "Current memory usage in bytes")}, - { "noncompliant-queries", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped as non-compliant")}, - { "noncompliant-responses", MetricDefinition(PrometheusMetricType::counter, "Number of answers from a backend dropped as non-compliant")}, - { "rdqueries", MetricDefinition(PrometheusMetricType::counter, "Number of received queries with the recursion desired bit set")}, - { "empty-queries", MetricDefinition(PrometheusMetricType::counter, "Number of empty queries received from clients")}, - { "cache-hits", MetricDefinition(PrometheusMetricType::counter, "Number of times an answer was retrieved from cache")}, - { "cache-misses", MetricDefinition(PrometheusMetricType::counter, "Number of times an answer not found in the cache")}, - { "cpu-iowait", MetricDefinition(PrometheusMetricType::counter, "Time waiting for I/O to complete by the whole system, in units of USER_HZ")}, - { "cpu-user-msec", MetricDefinition(PrometheusMetricType::counter, "Milliseconds spent by dnsdist in the user state")}, - { "cpu-steal", MetricDefinition(PrometheusMetricType::counter, "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ")}, - { "cpu-sys-msec", MetricDefinition(PrometheusMetricType::counter, "Milliseconds spent by dnsdist in the system state")}, - { "fd-usage", MetricDefinition(PrometheusMetricType::gauge, "Number of currently used file descriptors")}, - { "dyn-blocked", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a dynamic block")}, - { "dyn-block-nmg-size", MetricDefinition(PrometheusMetricType::gauge, "Number of dynamic blocks entries") }, - { "security-status", MetricDefinition(PrometheusMetricType::gauge, "Security status of this software. 0=unknown, 1=OK, 2=upgrade recommended, 3=upgrade mandatory") }, - { "doh-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of DoH queries dropped because the internal pipe used to distribute queries was full") }, - { "doh-response-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of DoH responses dropped because the internal pipe used to distribute responses was full") }, - { "outgoing-doh-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of outgoing DoH queries dropped because the internal pipe used to distribute queries was full") }, - { "tcp-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP queries dropped because the internal pipe used to distribute queries was full") }, - { "tcp-cross-protocol-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP cross-protocol queries dropped because the internal pipe used to distribute queries was full") }, - { "tcp-cross-protocol-response-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP cross-protocol responses dropped because the internal pipe used to distribute queries was full") }, - { "udp-in-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InErrors") }, - { "udp-noport-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp NoPorts") }, - { "udp-recvbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp RcvbufErrors") }, - { "udp-sndbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp SndbufErrors") }, - { "udp-in-csum-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InCsumErrors") }, - { "udp6-in-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6InErrors") }, - { "udp6-recvbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6RcvbufErrors") }, - { "udp6-sndbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6SndbufErrors") }, - { "udp6-noport-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6NoPorts") }, - { "udp6-in-csum-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6InCsumErrors") }, - { "tcp-listen-overflows", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/netstat ListenOverflows") }, - { "proxy-protocol-invalid", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of an invalid Proxy Protocol header") }, + {"responses", MetricDefinition(PrometheusMetricType::counter, "Number of responses received from backends")}, + {"servfail-responses", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers received from backends")}, + {"queries", MetricDefinition(PrometheusMetricType::counter, "Number of received queries")}, + {"frontend-nxdomain", MetricDefinition(PrometheusMetricType::counter, "Number of NXDomain answers sent to clients")}, + {"frontend-servfail", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers sent to clients")}, + {"frontend-noerror", MetricDefinition(PrometheusMetricType::counter, "Number of NoError answers sent to clients")}, + {"acl-drops", MetricDefinition(PrometheusMetricType::counter, "Number of packets dropped because of the ACL")}, + {"rule-drop", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a rule")}, + {"rule-nxdomain", MetricDefinition(PrometheusMetricType::counter, "Number of NXDomain answers returned because of a rule")}, + {"rule-refused", MetricDefinition(PrometheusMetricType::counter, "Number of Refused answers returned because of a rule")}, + {"rule-servfail", MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers received because of a rule")}, + {"rule-truncated", MetricDefinition(PrometheusMetricType::counter, "Number of truncated answers returned because of a rule")}, + {"self-answered", MetricDefinition(PrometheusMetricType::counter, "Number of self-answered responses")}, + {"downstream-timeouts", MetricDefinition(PrometheusMetricType::counter, "Number of queries not answered in time by a backend")}, + {"downstream-send-errors", MetricDefinition(PrometheusMetricType::counter, "Number of errors when sending a query to a backend")}, + {"trunc-failures", MetricDefinition(PrometheusMetricType::counter, "Number of errors encountered while truncating an answer")}, + {"no-policy", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because no server was available")}, + {"latency0-1", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in less than 1ms")}, + {"latency1-10", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 1-10 ms")}, + {"latency10-50", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 10-50 ms")}, + {"latency50-100", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 50-100 ms")}, + {"latency100-1000", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in 100-1000 ms")}, + {"latency-slow", MetricDefinition(PrometheusMetricType::counter, "Number of queries answered in more than 1 second")}, + {"latency-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 100 packets")}, + {"latency-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 1000 packets")}, + {"latency-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 10000 packets")}, + {"latency-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency in microseconds of the last 1000000 packets")}, + {"latency-tcp-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over TCP")}, + {"latency-tcp-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over TCP")}, + {"latency-tcp-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over TCP")}, + {"latency-tcp-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over TCP")}, + {"latency-dot-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoT")}, + {"latency-dot-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoT")}, + {"latency-dot-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoT")}, + {"latency-dot-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoT")}, + {"latency-doh-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoH")}, + {"latency-doh-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoH")}, + {"latency-doh-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoH")}, + {"latency-doh-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoH")}, + {"latency-doq-avg100", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 100 packets received over DoQ")}, + {"latency-doq-avg1000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000 packets received over DoQ")}, + {"latency-doq-avg10000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 10000 packets received over DoQ")}, + {"latency-doq-avg1000000", MetricDefinition(PrometheusMetricType::gauge, "Average response latency, in microseconds, of the last 1000000 packets received over DoQ")}, + {"uptime", MetricDefinition(PrometheusMetricType::gauge, "Uptime of the dnsdist process in seconds")}, + {"real-memory-usage", MetricDefinition(PrometheusMetricType::gauge, "Current memory usage in bytes")}, + {"noncompliant-queries", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped as non-compliant")}, + {"noncompliant-responses", MetricDefinition(PrometheusMetricType::counter, "Number of answers from a backend dropped as non-compliant")}, + {"rdqueries", MetricDefinition(PrometheusMetricType::counter, "Number of received queries with the recursion desired bit set")}, + {"empty-queries", MetricDefinition(PrometheusMetricType::counter, "Number of empty queries received from clients")}, + {"cache-hits", MetricDefinition(PrometheusMetricType::counter, "Number of times an answer was retrieved from cache")}, + {"cache-misses", MetricDefinition(PrometheusMetricType::counter, "Number of times an answer not found in the cache")}, + {"cpu-iowait", MetricDefinition(PrometheusMetricType::counter, "Time waiting for I/O to complete by the whole system, in units of USER_HZ")}, + {"cpu-user-msec", MetricDefinition(PrometheusMetricType::counter, "Milliseconds spent by dnsdist in the user state")}, + {"cpu-steal", MetricDefinition(PrometheusMetricType::counter, "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ")}, + {"cpu-sys-msec", MetricDefinition(PrometheusMetricType::counter, "Milliseconds spent by dnsdist in the system state")}, + {"fd-usage", MetricDefinition(PrometheusMetricType::gauge, "Number of currently used file descriptors")}, + {"dyn-blocked", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a dynamic block")}, + {"dyn-block-nmg-size", MetricDefinition(PrometheusMetricType::gauge, "Number of dynamic blocks entries")}, + {"security-status", MetricDefinition(PrometheusMetricType::gauge, "Security status of this software. 0=unknown, 1=OK, 2=upgrade recommended, 3=upgrade mandatory")}, + {"doh-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of DoH queries dropped because the internal pipe used to distribute queries was full")}, + {"doh-response-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of DoH responses dropped because the internal pipe used to distribute responses was full")}, + {"outgoing-doh-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of outgoing DoH queries dropped because the internal pipe used to distribute queries was full")}, + {"tcp-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP queries dropped because the internal pipe used to distribute queries was full")}, + {"tcp-cross-protocol-query-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP cross-protocol queries dropped because the internal pipe used to distribute queries was full")}, + {"tcp-cross-protocol-response-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of TCP cross-protocol responses dropped because the internal pipe used to distribute queries was full")}, + {"udp-in-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InErrors")}, + {"udp-noport-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp NoPorts")}, + {"udp-recvbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp RcvbufErrors")}, + {"udp-sndbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp SndbufErrors")}, + {"udp-in-csum-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InCsumErrors")}, + {"udp6-in-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6InErrors")}, + {"udp6-recvbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6RcvbufErrors")}, + {"udp6-sndbuf-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6SndbufErrors")}, + {"udp6-noport-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6NoPorts")}, + {"udp6-in-csum-errors", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp6 Udp6InCsumErrors")}, + {"tcp-listen-overflows", MetricDefinition(PrometheusMetricType::counter, "From /proc/net/netstat ListenOverflows")}, + {"proxy-protocol-invalid", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of an invalid Proxy Protocol header")}, }; #endif /* DISABLE_PROMETHEUS */ -bool addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def) { +bool addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def) +{ #ifndef DISABLE_PROMETHEUS return MetricDefinitionStorage::addMetricDefinition(def); #else @@ -341,7 +344,7 @@ static bool handleAuthorization(const YaHTTP::Request& req) if (isAnAPIRequest(req)) { /* Access to the API requires a valid API key */ - if (!config->apiRequiresAuthentication || checkAPIKey(req, config->apiKey)) { + if (!config->apiRequiresAuthentication || checkAPIKey(req, config->apiKey)) { return true; } @@ -399,14 +402,14 @@ static void handleCORS(const YaHTTP::Request& req, YaHTTP::Response& resp) } } -static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional >& customHeaders) +static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional>& customHeaders) { - static const std::vector > headers = { - { "X-Content-Type-Options", "nosniff" }, - { "X-Frame-Options", "deny" }, - { "X-Permitted-Cross-Domain-Policies", "none" }, - { "X-XSS-Protection", "1; mode=block" }, - { "Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'" }, + static const std::vector> headers = { + {"X-Content-Type-Options", "nosniff"}, + {"X-Frame-Options", "deny"}, + {"X-Permitted-Cross-Domain-Policies", "none"}, + {"X-XSS-Protection", "1; mode=block"}, + {"Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'"}, }; for (const auto& h : headers) { @@ -420,7 +423,7 @@ static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional >& customHeaders) +static void addCustomHeaders(YaHTTP::Response& resp, const boost::optional>& customHeaders) { if (!customHeaders) return; @@ -432,31 +435,31 @@ static void addCustomHeaders(YaHTTP::Response& resp, const boost::optional +template static json11::Json::array someResponseRulesToJson(GlobalStateHolder>* someResponseRules) { using namespace json11; Json::array responseRules; - int num=0; + int num = 0; auto localResponseRules = someResponseRules->getLocal(); responseRules.reserve(localResponseRules->size()); for (const auto& a : *localResponseRules) { responseRules.push_back(Json::object{ - {"id", num++}, - {"creationOrder", (double)a.d_creationOrder}, - {"uuid", boost::uuids::to_string(a.d_id)}, - {"name", a.d_name}, - {"matches", (double)a.d_rule->d_matches}, - {"rule", a.d_rule->toString()}, - {"action", a.d_action->toString()}, - }); + {"id", num++}, + {"creationOrder", (double)a.d_creationOrder}, + {"uuid", boost::uuids::to_string(a.d_id)}, + {"name", a.d_name}, + {"matches", (double)a.d_rule->d_matches}, + {"rule", a.d_rule->toString()}, + {"action", a.d_action->toString()}, + }); } return responseRules; } #ifndef DISABLE_PROMETHEUS -template -static void addRulesToPrometheusOutput(std::ostringstream& output, GlobalStateHolder >& rules) +template +static void addRulesToPrometheusOutput(std::ostringstream& output, GlobalStateHolder>& rules) { auto localRules = rules.getLocal(); for (const auto& entry : *localRules) { @@ -471,7 +474,7 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) resp.status = 200; std::ostringstream output; - static const std::set metricBlacklist = { "special-memory-usage", "latency-count", "latency-sum" }; + static const std::set metricBlacklist = {"special-memory-usage", "latency-count", "latency-sum"}; { auto entries = dnsdist::metrics::g_stats.entries.read_lock(); for (const auto& entry : *entries) { @@ -505,7 +508,7 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) // for these we have the help and types encoded in the sources // but we need to be careful about labels in custom metrics std::string helpName = prometheusMetricName.substr(0, prometheusMetricName.find('{')); - output << "# HELP " << helpName << " " << metricDetails.description << "\n"; + output << "# HELP " << helpName << " " << metricDetails.description << "\n"; output << "# TYPE " << helpName << " " << prometheusTypeName << "\n"; output << prometheusMetricName << " "; @@ -547,68 +550,192 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) auto states = g_dstates.getLocal(); const string statesbase = "dnsdist_server_"; - output << "# HELP " << statesbase << "status " << "Whether this backend is up (1) or down (0)" << "\n"; - output << "# TYPE " << statesbase << "status " << "gauge" << "\n"; - output << "# HELP " << statesbase << "queries " << "Amount of queries relayed to server" << "\n"; - output << "# TYPE " << statesbase << "queries " << "counter" << "\n"; - output << "# HELP " << statesbase << "responses " << "Amount of responses received from this server" << "\n"; - output << "# TYPE " << statesbase << "responses " << "counter" << "\n"; - output << "# HELP " << statesbase << "noncompliantresponses " << "Amount of non-compliant responses received from this server" << "\n"; - output << "# TYPE " << statesbase << "noncompliantresponses " << "counter" << "\n"; - output << "# HELP " << statesbase << "drops " << "Amount of queries not answered by server" << "\n"; - output << "# TYPE " << statesbase << "drops " << "counter" << "\n"; - output << "# HELP " << statesbase << "latency " << "Server's latency when answering questions in milliseconds" << "\n"; - output << "# TYPE " << statesbase << "latency " << "gauge" << "\n"; - output << "# HELP " << statesbase << "senderrors " << "Total number of OS send errors while relaying queries" << "\n"; - output << "# TYPE " << statesbase << "senderrors " << "counter" << "\n"; - output << "# HELP " << statesbase << "outstanding " << "Current number of queries that are waiting for a backend response" << "\n"; - output << "# TYPE " << statesbase << "outstanding " << "gauge" << "\n"; - output << "# HELP " << statesbase << "order " << "The order in which this server is picked" << "\n"; - output << "# TYPE " << statesbase << "order " << "gauge" << "\n"; - output << "# HELP " << statesbase << "weight " << "The weight within the order in which this server is picked" << "\n"; - output << "# TYPE " << statesbase << "weight " << "gauge" << "\n"; - output << "# HELP " << statesbase << "tcpdiedsendingquery " << "The number of TCP I/O errors while sending the query" << "\n"; - output << "# TYPE " << statesbase << "tcpdiedsendingquery " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpdiedreadingresponse " << "The number of TCP I/O errors while reading the response" << "\n"; - output << "# TYPE " << statesbase << "tcpdiedreadingresponse " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpgaveup " << "The number of TCP connections failing after too many attempts" << "\n"; - output << "# TYPE " << statesbase << "tcpgaveup " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpconnecttimeouts " << "The number of TCP connect timeouts" << "\n"; - output << "# TYPE " << statesbase << "tcpconnecttimeouts " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpreadtimeouts " << "The number of TCP read timeouts" << "\n"; - output << "# TYPE " << statesbase << "tcpreadtimeouts " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpwritetimeouts " << "The number of TCP write timeouts" << "\n"; - output << "# TYPE " << statesbase << "tcpwritetimeouts " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpcurrentconnections " << "The number of current TCP connections" << "\n"; - output << "# TYPE " << statesbase << "tcpcurrentconnections " << "gauge" << "\n"; - output << "# HELP " << statesbase << "tcpmaxconcurrentconnections " << "The maximum number of concurrent TCP connections" << "\n"; - output << "# TYPE " << statesbase << "tcpmaxconcurrentconnections " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcptoomanyconcurrentconnections " << "Number of times we had to enforce the maximum number of concurrent TCP connections" << "\n"; - output << "# TYPE " << statesbase << "tcptoomanyconcurrentconnections " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpnewconnections " << "The number of established TCP connections in total" << "\n"; - output << "# TYPE " << statesbase << "tcpnewconnections " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpreusedconnections " << "The number of times a TCP connection has been reused" << "\n"; - output << "# TYPE " << statesbase << "tcpreusedconnections " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcpavgqueriesperconn " << "The average number of queries per TCP connection" << "\n"; - output << "# TYPE " << statesbase << "tcpavgqueriesperconn " << "gauge" << "\n"; - output << "# HELP " << statesbase << "tcpavgconnduration " << "The average duration of a TCP connection (ms)" << "\n"; - output << "# TYPE " << statesbase << "tcpavgconnduration " << "gauge" << "\n"; - output << "# HELP " << statesbase << "tlsresumptions " << "The number of times a TLS session has been resumed" << "\n"; - output << "# TYPE " << statesbase << "tlsresumptions " << "counter" << "\n"; - output << "# HELP " << statesbase << "tcplatency " << "Server's latency when answering TCP questions in milliseconds" << "\n"; - output << "# TYPE " << statesbase << "tcplatency " << "gauge" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailures " << "Number of health check attempts that failed (total)" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailures " << "counter" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailuresparsing " << "Number of health check attempts where the response could not be parsed" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailuresparsing " << "counter" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailurestimeout " << "Number of health check attempts where the response did not arrive in time" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailurestimeout " << "counter" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailuresnetwork " << "Number of health check attempts that experienced a network issue" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailuresnetwork " << "counter" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailuresmismatch " << "Number of health check attempts where the response did not match the query" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailuresmismatch " << "counter" << "\n"; - output << "# HELP " << statesbase << "healthcheckfailuresinvalid " << "Number of health check attempts where the DNS response was invalid" << "\n"; - output << "# TYPE " << statesbase << "healthcheckfailuresinvalid " << "counter" << "\n"; + output << "# HELP " << statesbase << "status " + << "Whether this backend is up (1) or down (0)" + << "\n"; + output << "# TYPE " << statesbase << "status " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "queries " + << "Amount of queries relayed to server" + << "\n"; + output << "# TYPE " << statesbase << "queries " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "responses " + << "Amount of responses received from this server" + << "\n"; + output << "# TYPE " << statesbase << "responses " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "noncompliantresponses " + << "Amount of non-compliant responses received from this server" + << "\n"; + output << "# TYPE " << statesbase << "noncompliantresponses " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "drops " + << "Amount of queries not answered by server" + << "\n"; + output << "# TYPE " << statesbase << "drops " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "latency " + << "Server's latency when answering questions in milliseconds" + << "\n"; + output << "# TYPE " << statesbase << "latency " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "senderrors " + << "Total number of OS send errors while relaying queries" + << "\n"; + output << "# TYPE " << statesbase << "senderrors " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "outstanding " + << "Current number of queries that are waiting for a backend response" + << "\n"; + output << "# TYPE " << statesbase << "outstanding " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "order " + << "The order in which this server is picked" + << "\n"; + output << "# TYPE " << statesbase << "order " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "weight " + << "The weight within the order in which this server is picked" + << "\n"; + output << "# TYPE " << statesbase << "weight " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "tcpdiedsendingquery " + << "The number of TCP I/O errors while sending the query" + << "\n"; + output << "# TYPE " << statesbase << "tcpdiedsendingquery " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpdiedreadingresponse " + << "The number of TCP I/O errors while reading the response" + << "\n"; + output << "# TYPE " << statesbase << "tcpdiedreadingresponse " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpgaveup " + << "The number of TCP connections failing after too many attempts" + << "\n"; + output << "# TYPE " << statesbase << "tcpgaveup " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpconnecttimeouts " + << "The number of TCP connect timeouts" + << "\n"; + output << "# TYPE " << statesbase << "tcpconnecttimeouts " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpreadtimeouts " + << "The number of TCP read timeouts" + << "\n"; + output << "# TYPE " << statesbase << "tcpreadtimeouts " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpwritetimeouts " + << "The number of TCP write timeouts" + << "\n"; + output << "# TYPE " << statesbase << "tcpwritetimeouts " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpcurrentconnections " + << "The number of current TCP connections" + << "\n"; + output << "# TYPE " << statesbase << "tcpcurrentconnections " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "tcpmaxconcurrentconnections " + << "The maximum number of concurrent TCP connections" + << "\n"; + output << "# TYPE " << statesbase << "tcpmaxconcurrentconnections " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcptoomanyconcurrentconnections " + << "Number of times we had to enforce the maximum number of concurrent TCP connections" + << "\n"; + output << "# TYPE " << statesbase << "tcptoomanyconcurrentconnections " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpnewconnections " + << "The number of established TCP connections in total" + << "\n"; + output << "# TYPE " << statesbase << "tcpnewconnections " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpreusedconnections " + << "The number of times a TCP connection has been reused" + << "\n"; + output << "# TYPE " << statesbase << "tcpreusedconnections " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcpavgqueriesperconn " + << "The average number of queries per TCP connection" + << "\n"; + output << "# TYPE " << statesbase << "tcpavgqueriesperconn " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "tcpavgconnduration " + << "The average duration of a TCP connection (ms)" + << "\n"; + output << "# TYPE " << statesbase << "tcpavgconnduration " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "tlsresumptions " + << "The number of times a TLS session has been resumed" + << "\n"; + output << "# TYPE " << statesbase << "tlsresumptions " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "tcplatency " + << "Server's latency when answering TCP questions in milliseconds" + << "\n"; + output << "# TYPE " << statesbase << "tcplatency " + << "gauge" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailures " + << "Number of health check attempts that failed (total)" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailures " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailuresparsing " + << "Number of health check attempts where the response could not be parsed" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailuresparsing " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailurestimeout " + << "Number of health check attempts where the response did not arrive in time" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailurestimeout " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailuresnetwork " + << "Number of health check attempts that experienced a network issue" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailuresnetwork " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailuresmismatch " + << "Number of health check attempts where the response did not match the query" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailuresmismatch " + << "counter" + << "\n"; + output << "# HELP " << statesbase << "healthcheckfailuresinvalid " + << "Number of health check attempts where the DNS response was invalid" + << "\n"; + output << "# TYPE " << statesbase << "healthcheckfailuresinvalid " + << "counter" + << "\n"; for (const auto& state : *states) { string serverName; @@ -625,80 +752,152 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) const std::string label = boost::str(boost::format("{server=\"%1%\",address=\"%2%\"}") % serverName % state->d_config.remote.toStringWithPort()); - output << statesbase << "status" << label << " " << (state->isUp() ? "1" : "0") << "\n"; - output << statesbase << "queries" << label << " " << state->queries.load() << "\n"; - output << statesbase << "responses" << label << " " << state->responses.load() << "\n"; - output << statesbase << "noncompliantresponses" << label << " " << state->nonCompliantResponses.load() << "\n"; - output << statesbase << "drops" << label << " " << state->reuseds.load() << "\n"; + output << statesbase << "status" << label << " " << (state->isUp() ? "1" : "0") << "\n"; + output << statesbase << "queries" << label << " " << state->queries.load() << "\n"; + output << statesbase << "responses" << label << " " << state->responses.load() << "\n"; + output << statesbase << "noncompliantresponses" << label << " " << state->nonCompliantResponses.load() << "\n"; + output << statesbase << "drops" << label << " " << state->reuseds.load() << "\n"; if (state->isUp()) { - output << statesbase << "latency" << label << " " << state->latencyUsec/1000.0 << "\n"; - output << statesbase << "tcplatency" << label << " " << state->latencyUsecTCP/1000.0 << "\n"; - } - output << statesbase << "senderrors" << label << " " << state->sendErrors.load() << "\n"; - output << statesbase << "outstanding" << label << " " << state->outstanding.load() << "\n"; - output << statesbase << "order" << label << " " << state->d_config.order << "\n"; - output << statesbase << "weight" << label << " " << state->d_config.d_weight << "\n"; - output << statesbase << "tcpdiedsendingquery" << label << " " << state->tcpDiedSendingQuery << "\n"; - output << statesbase << "tcpdiedreadingresponse" << label << " " << state->tcpDiedReadingResponse << "\n"; - output << statesbase << "tcpgaveup" << label << " " << state->tcpGaveUp << "\n"; - output << statesbase << "tcpreadtimeouts" << label << " " << state->tcpReadTimeouts << "\n"; - output << statesbase << "tcpwritetimeouts" << label << " " << state->tcpWriteTimeouts << "\n"; - output << statesbase << "tcpconnecttimeouts" << label << " " << state->tcpConnectTimeouts << "\n"; - output << statesbase << "tcpcurrentconnections" << label << " " << state->tcpCurrentConnections << "\n"; - output << statesbase << "tcpmaxconcurrentconnections" << label << " " << state->tcpMaxConcurrentConnections << "\n"; - output << statesbase << "tcptoomanyconcurrentconnections" << label << " " << state->tcpTooManyConcurrentConnections << "\n"; - output << statesbase << "tcpnewconnections" << label << " " << state->tcpNewConnections << "\n"; - output << statesbase << "tcpreusedconnections" << label << " " << state->tcpReusedConnections << "\n"; - output << statesbase << "tcpavgqueriesperconn" << label << " " << state->tcpAvgQueriesPerConnection << "\n"; - output << statesbase << "tcpavgconnduration" << label << " " << state->tcpAvgConnectionDuration << "\n"; - output << statesbase << "tlsresumptions" << label << " " << state->tlsResumptions << "\n"; - output << statesbase << "healthcheckfailures" << label << " " << state->d_healthCheckMetrics.d_failures << "\n"; - output << statesbase << "healthcheckfailuresparsing" << label << " " << state->d_healthCheckMetrics.d_parseErrors << "\n"; - output << statesbase << "healthcheckfailurestimeout" << label << " " << state->d_healthCheckMetrics.d_timeOuts << "\n"; - output << statesbase << "healthcheckfailuresnetwork" << label << " " << state->d_healthCheckMetrics.d_networkErrors << "\n"; - output << statesbase << "healthcheckfailuresmismatch" << label << " " << state->d_healthCheckMetrics.d_mismatchErrors << "\n"; - output << statesbase << "healthcheckfailuresinvalid" << label << " " << state->d_healthCheckMetrics.d_invalidResponseErrors << "\n"; + output << statesbase << "latency" << label << " " << state->latencyUsec / 1000.0 << "\n"; + output << statesbase << "tcplatency" << label << " " << state->latencyUsecTCP / 1000.0 << "\n"; + } + output << statesbase << "senderrors" << label << " " << state->sendErrors.load() << "\n"; + output << statesbase << "outstanding" << label << " " << state->outstanding.load() << "\n"; + output << statesbase << "order" << label << " " << state->d_config.order << "\n"; + output << statesbase << "weight" << label << " " << state->d_config.d_weight << "\n"; + output << statesbase << "tcpdiedsendingquery" << label << " " << state->tcpDiedSendingQuery << "\n"; + output << statesbase << "tcpdiedreadingresponse" << label << " " << state->tcpDiedReadingResponse << "\n"; + output << statesbase << "tcpgaveup" << label << " " << state->tcpGaveUp << "\n"; + output << statesbase << "tcpreadtimeouts" << label << " " << state->tcpReadTimeouts << "\n"; + output << statesbase << "tcpwritetimeouts" << label << " " << state->tcpWriteTimeouts << "\n"; + output << statesbase << "tcpconnecttimeouts" << label << " " << state->tcpConnectTimeouts << "\n"; + output << statesbase << "tcpcurrentconnections" << label << " " << state->tcpCurrentConnections << "\n"; + output << statesbase << "tcpmaxconcurrentconnections" << label << " " << state->tcpMaxConcurrentConnections << "\n"; + output << statesbase << "tcptoomanyconcurrentconnections" << label << " " << state->tcpTooManyConcurrentConnections << "\n"; + output << statesbase << "tcpnewconnections" << label << " " << state->tcpNewConnections << "\n"; + output << statesbase << "tcpreusedconnections" << label << " " << state->tcpReusedConnections << "\n"; + output << statesbase << "tcpavgqueriesperconn" << label << " " << state->tcpAvgQueriesPerConnection << "\n"; + output << statesbase << "tcpavgconnduration" << label << " " << state->tcpAvgConnectionDuration << "\n"; + output << statesbase << "tlsresumptions" << label << " " << state->tlsResumptions << "\n"; + output << statesbase << "healthcheckfailures" << label << " " << state->d_healthCheckMetrics.d_failures << "\n"; + output << statesbase << "healthcheckfailuresparsing" << label << " " << state->d_healthCheckMetrics.d_parseErrors << "\n"; + output << statesbase << "healthcheckfailurestimeout" << label << " " << state->d_healthCheckMetrics.d_timeOuts << "\n"; + output << statesbase << "healthcheckfailuresnetwork" << label << " " << state->d_healthCheckMetrics.d_networkErrors << "\n"; + output << statesbase << "healthcheckfailuresmismatch" << label << " " << state->d_healthCheckMetrics.d_mismatchErrors << "\n"; + output << statesbase << "healthcheckfailuresinvalid" << label << " " << state->d_healthCheckMetrics.d_invalidResponseErrors << "\n"; } const string frontsbase = "dnsdist_frontend_"; - output << "# HELP " << frontsbase << "queries " << "Amount of queries received by this frontend" << "\n"; - output << "# TYPE " << frontsbase << "queries " << "counter" << "\n"; - output << "# HELP " << frontsbase << "noncompliantqueries " << "Amount of non-compliant queries received by this frontend" << "\n"; - output << "# TYPE " << frontsbase << "noncompliantqueries " << "counter" << "\n"; - output << "# HELP " << frontsbase << "responses " << "Amount of responses sent by this frontend" << "\n"; - output << "# TYPE " << frontsbase << "responses " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpdiedreadingquery " << "Amount of TCP connections terminated while reading the query from the client" << "\n"; - output << "# TYPE " << frontsbase << "tcpdiedreadingquery " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpdiedsendingresponse " << "Amount of TCP connections terminated while sending a response to the client" << "\n"; - output << "# TYPE " << frontsbase << "tcpdiedsendingresponse " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpgaveup " << "Amount of TCP connections terminated after too many attempts to get a connection to the backend" << "\n"; - output << "# TYPE " << frontsbase << "tcpgaveup " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpclienttimeouts " << "Amount of TCP connections terminated by a timeout while reading from the client" << "\n"; - output << "# TYPE " << frontsbase << "tcpclienttimeouts " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpdownstreamtimeouts " << "Amount of TCP connections terminated by a timeout while reading from the backend" << "\n"; - output << "# TYPE " << frontsbase << "tcpdownstreamtimeouts " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpcurrentconnections " << "Amount of current incoming TCP connections from clients" << "\n"; - output << "# TYPE " << frontsbase << "tcpcurrentconnections " << "gauge" << "\n"; - output << "# HELP " << frontsbase << "tcpmaxconcurrentconnections " << "Maximum number of concurrent incoming TCP connections from clients" << "\n"; - output << "# TYPE " << frontsbase << "tcpmaxconcurrentconnections " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tcpavgqueriesperconnection " << "The average number of queries per TCP connection" << "\n"; - output << "# TYPE " << frontsbase << "tcpavgqueriesperconnection " << "gauge" << "\n"; - output << "# HELP " << frontsbase << "tcpavgconnectionduration " << "The average duration of a TCP connection (ms)" << "\n"; - output << "# TYPE " << frontsbase << "tcpavgconnectionduration " << "gauge" << "\n"; - output << "# HELP " << frontsbase << "tlsqueries " << "Number of queries received by dnsdist over TLS, by TLS version" << "\n"; - output << "# TYPE " << frontsbase << "tlsqueries " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tlsnewsessions " << "Amount of new TLS sessions negotiated" << "\n"; - output << "# TYPE " << frontsbase << "tlsnewsessions " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tlsresumptions " << "Amount of TLS sessions resumed" << "\n"; - output << "# TYPE " << frontsbase << "tlsresumptions " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tlsunknownticketkeys " << "Amount of attempts to resume TLS session from an unknown key (possibly expired)" << "\n"; - output << "# TYPE " << frontsbase << "tlsunknownticketkeys " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tlsinactiveticketkeys " << "Amount of TLS sessions resumed from an inactive key" << "\n"; - output << "# TYPE " << frontsbase << "tlsinactiveticketkeys " << "counter" << "\n"; - output << "# HELP " << frontsbase << "tlshandshakefailures " << "Amount of TLS handshake failures" << "\n"; - output << "# TYPE " << frontsbase << "tlshandshakefailures " << "counter" << "\n"; - - std::map frontendDuplicates; + output << "# HELP " << frontsbase << "queries " + << "Amount of queries received by this frontend" + << "\n"; + output << "# TYPE " << frontsbase << "queries " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "noncompliantqueries " + << "Amount of non-compliant queries received by this frontend" + << "\n"; + output << "# TYPE " << frontsbase << "noncompliantqueries " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "responses " + << "Amount of responses sent by this frontend" + << "\n"; + output << "# TYPE " << frontsbase << "responses " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpdiedreadingquery " + << "Amount of TCP connections terminated while reading the query from the client" + << "\n"; + output << "# TYPE " << frontsbase << "tcpdiedreadingquery " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpdiedsendingresponse " + << "Amount of TCP connections terminated while sending a response to the client" + << "\n"; + output << "# TYPE " << frontsbase << "tcpdiedsendingresponse " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpgaveup " + << "Amount of TCP connections terminated after too many attempts to get a connection to the backend" + << "\n"; + output << "# TYPE " << frontsbase << "tcpgaveup " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpclienttimeouts " + << "Amount of TCP connections terminated by a timeout while reading from the client" + << "\n"; + output << "# TYPE " << frontsbase << "tcpclienttimeouts " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpdownstreamtimeouts " + << "Amount of TCP connections terminated by a timeout while reading from the backend" + << "\n"; + output << "# TYPE " << frontsbase << "tcpdownstreamtimeouts " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpcurrentconnections " + << "Amount of current incoming TCP connections from clients" + << "\n"; + output << "# TYPE " << frontsbase << "tcpcurrentconnections " + << "gauge" + << "\n"; + output << "# HELP " << frontsbase << "tcpmaxconcurrentconnections " + << "Maximum number of concurrent incoming TCP connections from clients" + << "\n"; + output << "# TYPE " << frontsbase << "tcpmaxconcurrentconnections " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tcpavgqueriesperconnection " + << "The average number of queries per TCP connection" + << "\n"; + output << "# TYPE " << frontsbase << "tcpavgqueriesperconnection " + << "gauge" + << "\n"; + output << "# HELP " << frontsbase << "tcpavgconnectionduration " + << "The average duration of a TCP connection (ms)" + << "\n"; + output << "# TYPE " << frontsbase << "tcpavgconnectionduration " + << "gauge" + << "\n"; + output << "# HELP " << frontsbase << "tlsqueries " + << "Number of queries received by dnsdist over TLS, by TLS version" + << "\n"; + output << "# TYPE " << frontsbase << "tlsqueries " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tlsnewsessions " + << "Amount of new TLS sessions negotiated" + << "\n"; + output << "# TYPE " << frontsbase << "tlsnewsessions " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tlsresumptions " + << "Amount of TLS sessions resumed" + << "\n"; + output << "# TYPE " << frontsbase << "tlsresumptions " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tlsunknownticketkeys " + << "Amount of attempts to resume TLS session from an unknown key (possibly expired)" + << "\n"; + output << "# TYPE " << frontsbase << "tlsunknownticketkeys " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tlsinactiveticketkeys " + << "Amount of TLS sessions resumed from an inactive key" + << "\n"; + output << "# TYPE " << frontsbase << "tlsinactiveticketkeys " + << "counter" + << "\n"; + output << "# HELP " << frontsbase << "tlshandshakefailures " + << "Amount of TLS handshake failures" + << "\n"; + output << "# TYPE " << frontsbase << "tlshandshakefailures " + << "counter" + << "\n"; + + std::map frontendDuplicates; for (const auto& front : g_frontends) { if (front->udpFD == -1 && front->tcpFD == -1) continue; @@ -762,27 +961,51 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) } } - output << "# HELP " << frontsbase << "http_connects " << "Number of DoH TCP connections established to this frontend" << "\n"; - output << "# TYPE " << frontsbase << "http_connects " << "counter" << "\n"; - - output << "# HELP " << frontsbase << "doh_http_method_queries " << "Number of DoH queries received by dnsdist, by HTTP method" << "\n"; - output << "# TYPE " << frontsbase << "doh_http_method_queries " << "counter" << "\n"; - - output << "# HELP " << frontsbase << "doh_http_version_queries " << "Number of DoH queries received by dnsdist, by HTTP version" << "\n"; - output << "# TYPE " << frontsbase << "doh_http_version_queries " << "counter" << "\n"; - - output << "# HELP " << frontsbase << "doh_bad_requests " << "Number of requests that could not be converted to a DNS query" << "\n"; - output << "# TYPE " << frontsbase << "doh_bad_requests " << "counter" << "\n"; - - output << "# HELP " << frontsbase << "doh_responses " << "Number of responses sent, by type" << "\n"; - output << "# TYPE " << frontsbase << "doh_responses " << "counter" << "\n"; - - output << "# HELP " << frontsbase << "doh_version_status_responses " << "Number of requests that could not be converted to a DNS query" << "\n"; - output << "# TYPE " << frontsbase << "doh_version_status_responses " << "counter" << "\n"; + output << "# HELP " << frontsbase << "http_connects " + << "Number of DoH TCP connections established to this frontend" + << "\n"; + output << "# TYPE " << frontsbase << "http_connects " + << "counter" + << "\n"; + + output << "# HELP " << frontsbase << "doh_http_method_queries " + << "Number of DoH queries received by dnsdist, by HTTP method" + << "\n"; + output << "# TYPE " << frontsbase << "doh_http_method_queries " + << "counter" + << "\n"; + + output << "# HELP " << frontsbase << "doh_http_version_queries " + << "Number of DoH queries received by dnsdist, by HTTP version" + << "\n"; + output << "# TYPE " << frontsbase << "doh_http_version_queries " + << "counter" + << "\n"; + + output << "# HELP " << frontsbase << "doh_bad_requests " + << "Number of requests that could not be converted to a DNS query" + << "\n"; + output << "# TYPE " << frontsbase << "doh_bad_requests " + << "counter" + << "\n"; + + output << "# HELP " << frontsbase << "doh_responses " + << "Number of responses sent, by type" + << "\n"; + output << "# TYPE " << frontsbase << "doh_responses " + << "counter" + << "\n"; + + output << "# HELP " << frontsbase << "doh_version_status_responses " + << "Number of requests that could not be converted to a DNS query" + << "\n"; + output << "# TYPE " << frontsbase << "doh_version_status_responses " + << "counter" + << "\n"; #ifdef HAVE_DNS_OVER_HTTPS - std::map dohFrontendDuplicates; - for(const auto& doh : g_dohlocals) { + std::map dohFrontendDuplicates; + for (const auto& doh : g_dohlocals) { const string frontName = doh->d_tlsContext.d_addr.toStringWithPort(); uint64_t threadNumber = 0; auto dupPair = frontendDuplicates.emplace(frontName, 1); @@ -823,31 +1046,79 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) auto localPools = g_pools.getLocal(); const string cachebase = "dnsdist_pool_"; - output << "# HELP dnsdist_pool_servers " << "Number of servers in that pool" << "\n"; - output << "# TYPE dnsdist_pool_servers " << "gauge" << "\n"; - output << "# HELP dnsdist_pool_active_servers " << "Number of available servers in that pool" << "\n"; - output << "# TYPE dnsdist_pool_active_servers " << "gauge" << "\n"; - - output << "# HELP dnsdist_pool_cache_size " << "Maximum number of entries that this cache can hold" << "\n"; - output << "# TYPE dnsdist_pool_cache_size " << "gauge" << "\n"; - output << "# HELP dnsdist_pool_cache_entries " << "Number of entries currently present in that cache" << "\n"; - output << "# TYPE dnsdist_pool_cache_entries " << "gauge" << "\n"; - output << "# HELP dnsdist_pool_cache_hits " << "Number of hits from that cache" << "\n"; - output << "# TYPE dnsdist_pool_cache_hits " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_misses " << "Number of misses from that cache" << "\n"; - output << "# TYPE dnsdist_pool_cache_misses " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_deferred_inserts " << "Number of insertions into that cache skipped because it was already locked" << "\n"; - output << "# TYPE dnsdist_pool_cache_deferred_inserts " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_deferred_lookups " << "Number of lookups into that cache skipped because it was already locked" << "\n"; - output << "# TYPE dnsdist_pool_cache_deferred_lookups " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_lookup_collisions " << "Number of lookups into that cache that triggered a collision (same hash but different entry)" << "\n"; - output << "# TYPE dnsdist_pool_cache_lookup_collisions " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_insert_collisions " << "Number of insertions into that cache that triggered a collision (same hash but different entry)" << "\n"; - output << "# TYPE dnsdist_pool_cache_insert_collisions " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_ttl_too_shorts " << "Number of insertions into that cache skipped because the TTL of the answer was not long enough" << "\n"; - output << "# TYPE dnsdist_pool_cache_ttl_too_shorts " << "counter" << "\n"; - output << "# HELP dnsdist_pool_cache_cleanup_count_total " << "Number of times the cache has been scanned to remove expired entries, if any" << "\n"; - output << "# TYPE dnsdist_pool_cache_cleanup_count_total " << "counter" << "\n"; + output << "# HELP dnsdist_pool_servers " + << "Number of servers in that pool" + << "\n"; + output << "# TYPE dnsdist_pool_servers " + << "gauge" + << "\n"; + output << "# HELP dnsdist_pool_active_servers " + << "Number of available servers in that pool" + << "\n"; + output << "# TYPE dnsdist_pool_active_servers " + << "gauge" + << "\n"; + + output << "# HELP dnsdist_pool_cache_size " + << "Maximum number of entries that this cache can hold" + << "\n"; + output << "# TYPE dnsdist_pool_cache_size " + << "gauge" + << "\n"; + output << "# HELP dnsdist_pool_cache_entries " + << "Number of entries currently present in that cache" + << "\n"; + output << "# TYPE dnsdist_pool_cache_entries " + << "gauge" + << "\n"; + output << "# HELP dnsdist_pool_cache_hits " + << "Number of hits from that cache" + << "\n"; + output << "# TYPE dnsdist_pool_cache_hits " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_misses " + << "Number of misses from that cache" + << "\n"; + output << "# TYPE dnsdist_pool_cache_misses " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_deferred_inserts " + << "Number of insertions into that cache skipped because it was already locked" + << "\n"; + output << "# TYPE dnsdist_pool_cache_deferred_inserts " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_deferred_lookups " + << "Number of lookups into that cache skipped because it was already locked" + << "\n"; + output << "# TYPE dnsdist_pool_cache_deferred_lookups " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_lookup_collisions " + << "Number of lookups into that cache that triggered a collision (same hash but different entry)" + << "\n"; + output << "# TYPE dnsdist_pool_cache_lookup_collisions " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_insert_collisions " + << "Number of insertions into that cache that triggered a collision (same hash but different entry)" + << "\n"; + output << "# TYPE dnsdist_pool_cache_insert_collisions " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_ttl_too_shorts " + << "Number of insertions into that cache skipped because the TTL of the answer was not long enough" + << "\n"; + output << "# TYPE dnsdist_pool_cache_ttl_too_shorts " + << "counter" + << "\n"; + output << "# HELP dnsdist_pool_cache_cleanup_count_total " + << "Number of times the cache has been scanned to remove expired entries, if any" + << "\n"; + output << "# TYPE dnsdist_pool_cache_cleanup_count_total " + << "counter" + << "\n"; for (const auto& entry : *localPools) { string poolName = entry.first; @@ -863,21 +1134,25 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) if (pool->packetCache != nullptr) { const auto& cache = pool->packetCache; - output << cachebase << "cache_size" <