diff options
Diffstat (limited to 'dnsdist.hh')
-rw-r--r-- | dnsdist.hh | 288 |
1 files changed, 110 insertions, 178 deletions
@@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once + #include "config.h" #include "ext/luawrapper/include/LuaContext.hpp" @@ -42,7 +43,9 @@ #include "dnsdist-lbpolicies.hh" #include "dnsdist-protocols.hh" #include "dnsname.hh" -#include "doh.hh" +#include "dnsdist-doh-common.hh" +#include "doq.hh" +#include "doh3.hh" #include "ednsoptions.hh" #include "iputils.hh" #include "misc.hh" @@ -87,20 +90,26 @@ struct DNSQuestion return data; } - dnsheader* getHeader() + bool editHeader(const std::function<bool(dnsheader&)>& editFunction); + + const dnsheader_aligned getHeader() const { if (data.size() < sizeof(dnsheader)) { throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer"); } - return reinterpret_cast<dnsheader*>(&data.at(0)); + dnsheader_aligned dh(data.data()); + return dh; } - const dnsheader* getHeader() const + /* this function is not safe against unaligned access, you should + use editHeader() instead, but we need it for the Lua bindings */ + dnsheader* getMutableHeader() const { if (data.size() < sizeof(dnsheader)) { throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer"); } - return reinterpret_cast<const dnsheader*>(&data.at(0)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast<dnsheader*>(data.data()); } bool hasRoomFor(size_t more) const @@ -140,6 +149,13 @@ struct DNSQuestion ids.qTag->insert_or_assign(key, value); } + void setTag(const std::string& key, std::string&& value) { + if (!ids.qTag) { + ids.qTag = std::make_unique<QTag>(); + } + ids.qTag->insert_or_assign(key, std::move(value)); + } + const struct timespec& getQueryRealTime() const { return ids.queryRealTime.d_start; @@ -258,7 +274,7 @@ public: class DNSResponseAction { public: - enum class Action : uint8_t { Allow, Delay, Drop, HeaderModify, ServFail, None }; + enum class Action : uint8_t { Allow, Delay, Drop, HeaderModify, ServFail, Truncate, None }; virtual Action operator()(DNSResponse*, string* ruleresult) const =0; virtual ~DNSResponseAction() { @@ -330,156 +346,6 @@ extern vector<pair<struct timeval, std::string> > g_confDelta; using pdns::stat_t; -struct DNSDistStats -{ - stat_t responses{0}; - stat_t servfailResponses{0}; - stat_t queries{0}; - stat_t frontendNXDomain{0}; - stat_t frontendServFail{0}; - stat_t frontendNoError{0}; - stat_t nonCompliantQueries{0}; - stat_t nonCompliantResponses{0}; - stat_t rdQueries{0}; - stat_t emptyQueries{0}; - stat_t aclDrops{0}; - stat_t dynBlocked{0}; - stat_t ruleDrop{0}; - stat_t ruleNXDomain{0}; - stat_t ruleRefused{0}; - stat_t ruleServFail{0}; - stat_t ruleTruncated{0}; - stat_t selfAnswered{0}; - stat_t downstreamTimeouts{0}; - stat_t downstreamSendErrors{0}; - stat_t truncFail{0}; - stat_t noPolicy{0}; - stat_t cacheHits{0}; - stat_t cacheMisses{0}; - stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0}, latencyCount{0}; - stat_t securityStatus{0}; - stat_t dohQueryPipeFull{0}; - stat_t dohResponsePipeFull{0}; - stat_t outgoingDoHQueryPipeFull{0}; - stat_t proxyProtocolInvalid{0}; - stat_t tcpQueryPipeFull{0}; - stat_t tcpCrossProtocolQueryPipeFull{0}; - stat_t tcpCrossProtocolResponsePipeFull{0}; - double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0}; - double latencyTCPAvg100{0}, latencyTCPAvg1000{0}, latencyTCPAvg10000{0}, latencyTCPAvg1000000{0}; - double latencyDoTAvg100{0}, latencyDoTAvg1000{0}, latencyDoTAvg10000{0}, latencyDoTAvg1000000{0}; - double latencyDoHAvg100{0}, latencyDoHAvg1000{0}, latencyDoHAvg10000{0}, latencyDoHAvg1000000{0}; - using statfunction_t = std::function<uint64_t(const std::string&)>; - using entry_t = boost::variant<stat_t*, pdns::stat_t_trait<double>*, double*, statfunction_t>; - struct EntryPair - { - std::string d_name; - entry_t d_value; - }; - - SharedLockGuarded<std::vector<EntryPair>> entries{std::vector<EntryPair>{ - {"responses", &responses}, - {"servfail-responses", &servfailResponses}, - {"queries", &queries}, - {"frontend-nxdomain", &frontendNXDomain}, - {"frontend-servfail", &frontendServFail}, - {"frontend-noerror", &frontendNoError}, - {"acl-drops", &aclDrops}, - {"rule-drop", &ruleDrop}, - {"rule-nxdomain", &ruleNXDomain}, - {"rule-refused", &ruleRefused}, - {"rule-servfail", &ruleServFail}, - {"rule-truncated", &ruleTruncated}, - {"self-answered", &selfAnswered}, - {"downstream-timeouts", &downstreamTimeouts}, - {"downstream-send-errors", &downstreamSendErrors}, - {"trunc-failures", &truncFail}, - {"no-policy", &noPolicy}, - {"latency0-1", &latency0_1}, - {"latency1-10", &latency1_10}, - {"latency10-50", &latency10_50}, - {"latency50-100", &latency50_100}, - {"latency100-1000", &latency100_1000}, - {"latency-slow", &latencySlow}, - {"latency-avg100", &latencyAvg100}, - {"latency-avg1000", &latencyAvg1000}, - {"latency-avg10000", &latencyAvg10000}, - {"latency-avg1000000", &latencyAvg1000000}, - {"latency-tcp-avg100", &latencyTCPAvg100}, - {"latency-tcp-avg1000", &latencyTCPAvg1000}, - {"latency-tcp-avg10000", &latencyTCPAvg10000}, - {"latency-tcp-avg1000000", &latencyTCPAvg1000000}, - {"latency-dot-avg100", &latencyDoTAvg100}, - {"latency-dot-avg1000", &latencyDoTAvg1000}, - {"latency-dot-avg10000", &latencyDoTAvg10000}, - {"latency-dot-avg1000000", &latencyDoTAvg1000000}, - {"latency-doh-avg100", &latencyDoHAvg100}, - {"latency-doh-avg1000", &latencyDoHAvg1000}, - {"latency-doh-avg10000", &latencyDoHAvg10000}, - {"latency-doh-avg1000000", &latencyDoHAvg1000000}, - {"uptime", uptimeOfProcess}, - {"real-memory-usage", getRealMemoryUsage}, - {"special-memory-usage", getSpecialMemoryUsage}, - {"udp-in-errors", std::bind(udpErrorStats, "udp-in-errors")}, - {"udp-noport-errors", std::bind(udpErrorStats, "udp-noport-errors")}, - {"udp-recvbuf-errors", std::bind(udpErrorStats, "udp-recvbuf-errors")}, - {"udp-sndbuf-errors", std::bind(udpErrorStats, "udp-sndbuf-errors")}, - {"udp-in-csum-errors", std::bind(udpErrorStats, "udp-in-csum-errors")}, - {"udp6-in-errors", std::bind(udp6ErrorStats, "udp6-in-errors")}, - {"udp6-recvbuf-errors", std::bind(udp6ErrorStats, "udp6-recvbuf-errors")}, - {"udp6-sndbuf-errors", std::bind(udp6ErrorStats, "udp6-sndbuf-errors")}, - {"udp6-noport-errors", std::bind(udp6ErrorStats, "udp6-noport-errors")}, - {"udp6-in-csum-errors", std::bind(udp6ErrorStats, "udp6-in-csum-errors")}, - {"tcp-listen-overflows", std::bind(tcpErrorStats, "ListenOverflows")}, - {"noncompliant-queries", &nonCompliantQueries}, - {"noncompliant-responses", &nonCompliantResponses}, - {"proxy-protocol-invalid", &proxyProtocolInvalid}, - {"rdqueries", &rdQueries}, - {"empty-queries", &emptyQueries}, - {"cache-hits", &cacheHits}, - {"cache-misses", &cacheMisses}, - {"cpu-iowait", getCPUIOWait}, - {"cpu-steal", getCPUSteal}, - {"cpu-sys-msec", getCPUTimeSystem}, - {"cpu-user-msec", getCPUTimeUser}, - {"fd-usage", getOpenFileDescriptors}, - {"dyn-blocked", &dynBlocked}, - {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }}, - {"security-status", &securityStatus}, - {"doh-query-pipe-full", &dohQueryPipeFull}, - {"doh-response-pipe-full", &dohResponsePipeFull}, - {"outgoing-doh-query-pipe-full", &outgoingDoHQueryPipeFull}, - {"tcp-query-pipe-full", &tcpQueryPipeFull}, - {"tcp-cross-protocol-query-pipe-full", &tcpCrossProtocolQueryPipeFull}, - {"tcp-cross-protocol-response-pipe-full", &tcpCrossProtocolResponsePipeFull}, - // Latency histogram - {"latency-sum", &latencySum}, - {"latency-count", &latencyCount}, - }}; - struct MutableCounter - { - MutableCounter() = default; - MutableCounter(MutableCounter&& rhs): d_value(rhs.d_value.load()) - { - } - - mutable stat_t d_value{0}; - }; - struct MutableGauge - { - MutableGauge() = default; - MutableGauge(MutableGauge&& rhs): d_value(rhs.d_value.load()) - { - } - - mutable pdns::stat_t_trait<double> d_value{0}; - }; - SharedLockGuarded<std::map<std::string, MutableCounter, std::less<>>> customCounters; - SharedLockGuarded<std::map<std::string, MutableGauge, std::less<>>> customGauges; -}; - -extern struct DNSDistStats g_stats; - class BasicQPSLimiter { public: @@ -605,9 +471,13 @@ 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<int>& cpus_): cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort) + ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_, bool enableProxyProtocol): cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol) { } @@ -642,7 +512,10 @@ struct ClientState std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr}; std::shared_ptr<TLSFrontend> tlsFrontend{nullptr}; std::shared_ptr<DOHFrontend> dohFrontend{nullptr}; + std::shared_ptr<DOQFrontend> doqFrontend{nullptr}; + std::shared_ptr<DOH3Frontend> doh3Frontend{nullptr}; std::shared_ptr<BPFFilter> d_filter{nullptr}; + std::shared_ptr<XskWorker> xskInfo{nullptr}; size_t d_maxInFlightQueriesPerConn{1}; size_t d_tcpConcurrentConnectionsLimit{0}; int udpFD{-1}; @@ -652,6 +525,7 @@ struct ClientState bool muted{false}; bool tcp; bool reuseport; + bool d_enableProxyProtocol{true}; // the global proxy protocol ACL still applies bool ready{false}; int getSocket() const @@ -679,6 +553,17 @@ struct ClientState return tlsFrontend != nullptr || (dohFrontend != nullptr && dohFrontend->isHTTPS()); } + const TLSFrontend& getTLSFrontend() const + { + if (tlsFrontend != nullptr) { + return *tlsFrontend; + } + if (dohFrontend) { + return dohFrontend->d_tlsContext; + } + throw std::runtime_error("Trying to get a TLS frontend from a non-TLS ClientState"); + } + dnsdist::Protocol getProtocol() const { if (dnscryptCtx) { @@ -693,6 +578,12 @@ struct ClientState else if (hasTLS()) { return dnsdist::Protocol::DoT; } + else if (doqFrontend != nullptr) { + return dnsdist::Protocol::DoQ; + } + else if (doh3Frontend != nullptr) { + return dnsdist::Protocol::DoH3; + } else if (udpFD != -1) { return dnsdist::Protocol::DoUDP; } @@ -705,7 +596,13 @@ struct ClientState { std::string result = udpFD != -1 ? "UDP" : "TCP"; - if (dohFrontend) { + if (doqFrontend) { + result += " (DNS over QUIC)"; + } + else if (doh3Frontend) { + result += " (DNS over HTTP/3)"; + } + else if (dohFrontend) { if (dohFrontend->isHTTPS()) { result += " (DNS over HTTPS)"; } @@ -731,7 +628,7 @@ struct ClientState } } - void attachFilter(shared_ptr<BPFFilter> bpf, int socket) + void attachFilter(shared_ptr<BPFFilter>& bpf, int socket) { detachFilter(socket); @@ -754,7 +651,7 @@ struct ClientState } } - void attachFilter(shared_ptr<BPFFilter> bpf) + void attachFilter(shared_ptr<BPFFilter>& bpf) { detachFilter(); @@ -810,6 +707,10 @@ struct DownstreamState: public std::enable_shared_from_this<DownstreamState> std::string d_dohPath; std::string name; std::string nameWithAddr; +#ifdef HAVE_XSK + std::array<uint8_t, 6> sourceMACAddr; + std::array<uint8_t, 6> destMACAddr; +#endif /* HAVE_XSK */ size_t d_numberOfSockets{1}; size_t d_maxInFlightQueriesPerConn{1}; size_t d_tcpConcurrentConnectionsLimit{0}; @@ -839,6 +740,7 @@ struct DownstreamState: public std::enable_shared_from_this<DownstreamState> bool mustResolve{false}; bool useECS{false}; bool useProxyProtocol{false}; + bool d_proxyProtocolAdvertiseTLS{false}; bool setCD{false}; bool disableZeroScope{false}; bool tcpFastOpen{false}; @@ -851,6 +753,16 @@ struct DownstreamState: public std::enable_shared_from_this<DownstreamState> bool d_upgradeToLazyHealthChecks{false}; }; + struct HealthCheckMetrics + { + stat_t d_failures{0}; + stat_t d_timeOuts{0}; + stat_t d_parseErrors{0}; + stat_t d_networkErrors{0}; + stat_t d_mismatchErrors{0}; + stat_t d_invalidResponseErrors{0}; + }; + DownstreamState(DownstreamState::Config&& config, std::shared_ptr<TLSCtx> tlsCtx, bool connect); DownstreamState(const ComboAddress& remote): DownstreamState(DownstreamState::Config(remote), nullptr, false) { @@ -859,6 +771,7 @@ struct DownstreamState: public std::enable_shared_from_this<DownstreamState> ~DownstreamState(); Config d_config; + HealthCheckMetrics d_healthCheckMetrics; stat_t sendErrors{0}; stat_t outstanding{0}; stat_t reuseds{0}; @@ -911,27 +824,46 @@ public: std::vector<int> sockets; StopWatch sw; QPSLimiter qps; +#ifdef HAVE_XSK + std::vector<std::shared_ptr<XskWorker>> d_xskInfos; + std::vector<std::shared_ptr<XskSocket>> d_xskSockets; +#endif std::atomic<uint64_t> idOffset{0}; size_t socketsOffset{0}; double latencyUsec{0.0}; double latencyUsecTCP{0.0}; unsigned int d_nextCheck{0}; uint16_t currentCheckFailures{0}; - uint8_t consecutiveSuccessfulChecks{0}; std::atomic<bool> hashesComputed{false}; std::atomic<bool> connected{false}; bool upStatus{false}; private: + void handleUDPTimeout(IDState& ids); + void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional<time_t> 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<std::vector<ComboAddress>> d_socketSourceAddresses; +#endif std::atomic_flag threadStarted; + 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 @@ -1054,12 +986,17 @@ 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&&); std::optional<InternalQueryState> getState(uint16_t id); +#ifdef HAVE_XSK + void registerXsk(std::vector<std::shared_ptr<XskSocket>>& xsks); + [[nodiscard]] ComboAddress pickSourceAddressForSending(); +#endif /* HAVE_XSK */ + dnsdist::Protocol getProtocol() const { if (isDoH()) { @@ -1085,9 +1022,6 @@ public: static int s_udpTimeout; static bool s_randomizeSockets; static bool s_randomizeIDs; -private: - void handleUDPTimeout(IDState& ids); - void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional<time_t> currentTime = std::nullopt); }; using servers_t = vector<std::shared_ptr<DownstreamState>>; @@ -1101,7 +1035,7 @@ public: virtual ~DNSRule () { } - virtual bool matches(const DNSQuestion* dq) const =0; + virtual bool matches(const DNSQuestion* dq) const = 0; virtual string toString() const = 0; mutable stat_t d_matches{0}; }; @@ -1182,6 +1116,8 @@ extern ComboAddress g_serverControl; // not changed during runtime extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals; extern std::vector<shared_ptr<DOHFrontend>> g_dohlocals; +extern std::vector<shared_ptr<DOQFrontend>> g_doqlocals; +extern std::vector<shared_ptr<DOH3Frontend>> g_doh3locals; extern std::vector<std::unique_ptr<ClientState>> g_frontends; extern bool g_truncateTC; extern bool g_fixupCase; @@ -1226,20 +1162,16 @@ struct LocalHolders LocalStateHolder<pools_t> pools; }; -void tcpAcceptorThread(std::vector<ClientState*> states); - -#ifdef HAVE_DNS_OVER_HTTPS -void dohThread(ClientState* cs); -#endif /* HAVE_DNS_OVER_HTTPS */ +void tcpAcceptorThread(const std::vector<ClientState*>& states); void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls 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<DownstreamState>& remote, unsigned int& qnameWireLength); +bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote); -bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs); +bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState); extern std::vector<std::shared_ptr<DNSCryptContext>> g_dnsCryptLocals; int handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response); @@ -1254,7 +1186,6 @@ extern bool g_addEDNSToSelfGeneratedResponses; extern std::set<std::string> g_capabilitiesToRetain; static const uint16_t s_udpIncomingBufferSize{1500}; // don't accept UDP queries larger than this value -static const size_t s_maxPacketCacheEntrySize{4096}; // don't cache responses larger than this value enum class ProcessQueryResult : uint8_t { Drop, SendAnswer, PassToBackend, Asynchronous }; ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend); @@ -1262,10 +1193,11 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders bool processResponse(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& respRuleActions, const std::vector<DNSDistResponseRuleAction>& 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<DNSDistResponseRuleAction>& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted); +bool processResponderPacket(std::shared_ptr<DownstreamState>& dss, PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& localRespRuleActions, const std::vector<DNSDistResponseRuleAction>& cacheInsertedRespRuleActions, InternalQueryState&& ids); -bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, ComboAddress& dest); +bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend = true); -ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const PacketBuffer& request, bool healthCheck = false); +ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& backend, const int socketDesc, const PacketBuffer& request, bool healthCheck = false); 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); |