Skip to content

Commit

Permalink
dnsdist: Protect the runtime-modifiable configuration via RCU
Browse files Browse the repository at this point in the history
  • Loading branch information
rgacogne committed Jul 5, 2024
1 parent 1a79e4f commit b8ea3cc
Show file tree
Hide file tree
Showing 53 changed files with 1,136 additions and 939 deletions.
4 changes: 0 additions & 4 deletions pdns/credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ static std::string const pwhash_prefix = "$scrypt$";
static size_t const pwhash_prefix_size = pwhash_prefix.size();
#endif

uint64_t const CredentialsHolder::s_defaultWorkFactor{1024U}; /* N */
uint64_t const CredentialsHolder::s_defaultParallelFactor{1U}; /* p */
uint64_t const CredentialsHolder::s_defaultBlockSize{8U}; /* r */

SensitiveData::SensitiveData(std::string&& data) :
d_data(std::move(data))
{
Expand Down
6 changes: 3 additions & 3 deletions pdns/credentials.hh
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ public:
static bool isHashingAvailable();
static SensitiveData readFromTerminal();

static uint64_t const s_defaultWorkFactor;
static uint64_t const s_defaultParallelFactor;
static uint64_t const s_defaultBlockSize;
static uint64_t constexpr s_defaultWorkFactor{1024U}; /* N */;
static uint64_t constexpr s_defaultParallelFactor{1U}; /* p */;
static uint64_t constexpr s_defaultBlockSize{8U}; /* r */;

private:
SensitiveData d_credentials;
Expand Down
4 changes: 4 additions & 0 deletions pdns/dnsdistdist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ dnsdist_SOURCES = \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-carbon.cc dnsdist-carbon.hh \
dnsdist-concurrent-connections.hh \
dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-console.cc dnsdist-console.hh \
dnsdist-crypto.cc dnsdist-crypto.hh \
dnsdist-discovery.cc dnsdist-discovery.hh \
Expand Down Expand Up @@ -194,6 +195,7 @@ dnsdist_SOURCES = \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
dnsdist-query-count.hh dnsdist-query-count.cc \
dnsdist-random.cc dnsdist-random.hh \
dnsdist-resolver.cc dnsdist-resolver.hh \
dnsdist-rings.cc dnsdist-rings.hh \
Expand Down Expand Up @@ -274,6 +276,7 @@ testrunner_SOURCES = \
dnsdist-backoff.hh \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-concurrent-connections.hh \
dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-crypto.cc dnsdist-crypto.hh \
dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
dnsdist-doh-common.cc dnsdist-doh-common.hh \
Expand Down Expand Up @@ -547,6 +550,7 @@ fuzz_target_dnsdistcache_SOURCES = \
channel.hh channel.cc \
dns.cc dns.hh \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
dnsdist-ecs.cc dnsdist-ecs.hh \
dnsdist-idstate.hh \
Expand Down
2 changes: 1 addition & 1 deletion pdns/dnsdistdist/dnscrypt.hh
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public:
void getCertificateResponse(time_t now, PacketBuffer& response) const;
int encryptResponse(PacketBuffer& response, size_t maxResponseSize, bool tcp);

static const size_t s_minUDPLength = 256;
static constexpr size_t s_minUDPLength = 256;

private:
static void fillServerNonce(DNSCryptNonceType& nonce);
Expand Down
48 changes: 26 additions & 22 deletions pdns/dnsdistdist/dnsdist-backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "dnsdist-nghttp2.hh"
#include "dnsdist-random.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-snmp.hh"
#include "dnsdist-tcp.hh"
#include "dnsdist-xsk.hh"
#include "dolog.hh"
Expand Down Expand Up @@ -143,7 +144,7 @@ bool DownstreamState::reconnect(bool initialAttempt)
connected = true;
}
catch (const std::runtime_error& error) {
if (initialAttempt || g_verbose) {
if (initialAttempt || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
infolog("Error connecting to new server with address %s: %s", d_config.remote.toStringWithPort(), error.what());
}
connected = false;
Expand Down Expand Up @@ -236,14 +237,16 @@ void DownstreamState::stop()
void DownstreamState::hash()
{
vinfolog("Computing hashes for id=%s and weight=%d", *d_config.id, d_config.d_weight);
const auto hashPerturbation = dnsdist::configuration::getImmutableConfiguration().d_hashPerturbation;
auto w = d_config.d_weight;
auto idStr = boost::str(boost::format("%s") % *d_config.id);
auto lockedHashes = hashes.write_lock();
lockedHashes->clear();
lockedHashes->reserve(w);
while (w > 0) {
std::string uuid = boost::str(boost::format("%s-%d") % idStr % w);
unsigned int wshash = burtleCI(reinterpret_cast<const unsigned char*>(uuid.c_str()), uuid.size(), g_hashperturb);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): sorry, it's the burtle API
unsigned int wshash = burtleCI(reinterpret_cast<const unsigned char*>(uuid.c_str()), uuid.size(), hashPerturbation);
lockedHashes->push_back(wshash);
--w;
}
Expand Down Expand Up @@ -305,7 +308,7 @@ DownstreamState::DownstreamState(DownstreamState::Config&& config, std::shared_p
#ifdef HAVE_NGHTTP2
setupDoHClientProtocolNegotiation(d_tlsCtx);

if (g_configurationDone && g_outgoingDoHWorkerThreads && *g_outgoingDoHWorkerThreads == 0) {
if (dnsdist::configuration::isConfigurationDone() && g_outgoingDoHWorkerThreads && *g_outgoingDoHWorkerThreads == 0) {
throw std::runtime_error("Error: setOutgoingDoHWorkerThreads() is set to 0 so no outgoing DoH worker thread is available to serve queries");
}

Expand Down Expand Up @@ -352,11 +355,12 @@ void DownstreamState::start()

void DownstreamState::connectUDPSockets()
{
if (s_randomizeIDs) {
const auto& config = dnsdist::configuration::getImmutableConfiguration();
if (config.d_randomizeIDsToBackend) {
idStates.clear();
}
else {
idStates.resize(g_maxOutstanding);
idStates.resize(config.d_maxUDPOutstanding);
}
sockets.resize(d_config.d_numberOfSockets);

Expand Down Expand Up @@ -396,8 +400,8 @@ int DownstreamState::pickSocketForSending()
return sockets[0];
}

size_t idx;
if (s_randomizeSockets) {
size_t idx{0};
if (dnsdist::configuration::getImmutableConfiguration().d_randomizeUDPSocketsToBackend) {
idx = dnsdist::getRandomValue(numberOfSockets);
}
else {
Expand All @@ -419,14 +423,10 @@ void DownstreamState::pickSocketsReadyForReceiving(std::vector<int>& ready)
(*mplexer.lock())->getAvailableFDs(ready, 1000);
}

bool DownstreamState::s_randomizeSockets{false};
bool DownstreamState::s_randomizeIDs{false};
int DownstreamState::s_udpTimeout{2};

static bool isIDSExpired(const IDState& ids)
static bool isIDSExpired(const IDState& ids, uint8_t udpTimeout)
{
auto age = ids.age.load();
return age > DownstreamState::s_udpTimeout;
return age > udpTimeout;
}

void DownstreamState::handleUDPTimeout(IDState& ids)
Expand Down Expand Up @@ -478,11 +478,13 @@ void DownstreamState::handleUDPTimeouts()
return;
}

if (s_randomizeIDs) {
const auto& config = dnsdist::configuration::getImmutableConfiguration();
const auto udpTimeout = config.d_udpTimeout;
if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();
for (auto it = map->begin(); it != map->end(); ) {
auto& ids = it->second;
if (isIDSExpired(ids)) {
if (isIDSExpired(ids, udpTimeout)) {
handleUDPTimeout(ids);
it = map->erase(it);
continue;
Expand All @@ -497,7 +499,7 @@ void DownstreamState::handleUDPTimeouts()
if (!ids.isInUse()) {
continue;
}
if (!isIDSExpired(ids)) {
if (!isIDSExpired(ids, udpTimeout)) {
++ids.age;
continue;
}
Expand All @@ -506,7 +508,7 @@ void DownstreamState::handleUDPTimeouts()
continue;
}
/* check again, now that we have locked this state */
if (ids.isInUse() && isIDSExpired(ids)) {
if (ids.isInUse() && isIDSExpired(ids, udpTimeout)) {
handleUDPTimeout(ids);
}
}
Expand All @@ -516,7 +518,8 @@ void DownstreamState::handleUDPTimeouts()

uint16_t DownstreamState::saveState(InternalQueryState&& state)
{
if (s_randomizeIDs) {
const auto& config = dnsdist::configuration::getImmutableConfiguration();
if (config.d_randomizeIDsToBackend) {
/* if the state is already in use we will retry,
up to 5 five times. The last selected one is used
even if it was already in use */
Expand Down Expand Up @@ -578,7 +581,8 @@ uint16_t DownstreamState::saveState(InternalQueryState&& state)

void DownstreamState::restoreState(uint16_t id, InternalQueryState&& state)
{
if (s_randomizeIDs) {
const auto& config = dnsdist::configuration::getImmutableConfiguration();
if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();

auto [it, inserted] = map->emplace(id, IDState());
Expand Down Expand Up @@ -619,8 +623,8 @@ void DownstreamState::restoreState(uint16_t id, InternalQueryState&& state)
std::optional<InternalQueryState> DownstreamState::getState(uint16_t id)
{
std::optional<InternalQueryState> result = std::nullopt;

if (s_randomizeIDs) {
const auto& config = dnsdist::configuration::getImmutableConfiguration();
if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();

auto it = map->find(id);
Expand Down Expand Up @@ -864,7 +868,7 @@ void DownstreamState::submitHealthCheckResult(bool initial, bool newResult)
}

setUpStatus(newState);
if (g_snmpAgent && g_snmpTrapsEnabled) {
if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendBackendStatusChangeTrap(*this);
}
}
Expand Down
3 changes: 2 additions & 1 deletion pdns/dnsdistdist/dnsdist-carbon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "dnsdist-carbon.hh"
#include "dnsdist.hh"
#include "dnsdist-backoff.hh"
#include "dnsdist-configuration.hh"
#include "dnsdist-metrics.hh"

#ifndef DISABLE_CARBON
Expand Down Expand Up @@ -270,7 +271,7 @@ static bool doOneCarbonExport(const Carbon::Endpoint& endpoint)

{
std::string qname;
auto records = g_qcount.records.write_lock();
auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
for (const auto& record : *records) {
qname = record.first;
boost::replace_all(qname, ".", "_");
Expand Down
15 changes: 6 additions & 9 deletions pdns/dnsdistdist/dnsdist-concurrent-connections.hh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <map>
#include "iputils.hh"
#include "lock.hh"
#include "dnsdist-configuration.hh"

namespace dnsdist
{
Expand All @@ -32,12 +33,13 @@ class IncomingConcurrentTCPConnectionsManager
public:
static bool accountNewTCPConnection(const ComboAddress& from)
{
if (s_maxTCPConnectionsPerClient == 0) {
const auto maxConnsPerClient = dnsdist::configuration::getImmutableConfiguration().d_maxTCPConnectionsPerClient;
if (maxConnsPerClient == 0) {
return true;
}
auto db = s_tcpClientsConcurrentConnectionsCount.lock();
auto& count = (*db)[from];
if (count >= s_maxTCPConnectionsPerClient) {
if (count >= maxConnsPerClient) {
return false;
}
++count;
Expand All @@ -46,7 +48,8 @@ public:

static void accountClosedTCPConnection(const ComboAddress& from)
{
if (s_maxTCPConnectionsPerClient == 0) {
const auto maxConnsPerClient = dnsdist::configuration::getImmutableConfiguration().d_maxTCPConnectionsPerClient;
if (maxConnsPerClient == 0) {
return;
}
auto db = s_tcpClientsConcurrentConnectionsCount.lock();
Expand All @@ -57,14 +60,8 @@ public:
}
}

static void setMaxTCPConnectionsPerClient(size_t max)
{
s_maxTCPConnectionsPerClient = max;
}

private:
static LockGuarded<std::map<ComboAddress, size_t, ComboAddress::addressOnlyLessThan>> s_tcpClientsConcurrentConnectionsCount;
static size_t s_maxTCPConnectionsPerClient;
};

}
66 changes: 66 additions & 0 deletions pdns/dnsdistdist/dnsdist-configuration.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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-configuration.hh"
#include "sholder.hh"

namespace dnsdist::configuration
{
static GlobalStateHolder<RuntimeConfiguration> s_currentRuntimeConfiguration;
static Configuration s_configuration;
static std::atomic<bool> s_configurationDone{false};

const RuntimeConfiguration& getCurrentRuntimeConfiguration()
{
static thread_local auto t_threadLocalConfiguration = s_currentRuntimeConfiguration.getLocal();
return *t_threadLocalConfiguration;
}

void updateRuntimeConfiguration(const std::function<void(RuntimeConfiguration&)>& mutator)
{
s_currentRuntimeConfiguration.modify(mutator);
}

void updateImmutableConfiguration(const std::function<void(Configuration&)>& mutator)
{
if (isConfigurationDone()) {
throw std::runtime_error("Trying to update an immutable setting at runtime!");
}

mutator(s_configuration);
}

const Configuration& getImmutableConfiguration()
{
return s_configuration;
}

bool isConfigurationDone()
{
return s_configurationDone.load();
}

void setConfigurationDone()
{
s_configurationDone.store(true);
}
}
Loading

0 comments on commit b8ea3cc

Please sign in to comment.