Skip to content

Commit

Permalink
Merge pull request #6170 from zeha/dnstap
Browse files Browse the repository at this point in the history
dnsdist: Add simple dnstap logging
  • Loading branch information
ahupowerdns committed Feb 5, 2018
2 parents 4df9923 + 82a91dd commit ad3e187
Show file tree
Hide file tree
Showing 32 changed files with 1,446 additions and 57 deletions.
7 changes: 6 additions & 1 deletion build-scripts/travis.sh
Expand Up @@ -347,9 +347,13 @@ install_recursor() {

install_dnsdist() {
# test requirements / setup
run "sudo add-apt-repository -y ppa:zeha/libfstrm-ppa"
run 'curl "http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x396160EF8126A2E2" | sudo apt-key add - '
run "sudo apt-get -qq update"
run "sudo apt-get -qq --no-install-recommends install \
snmpd \
libsnmp-dev"
libsnmp-dev \
libfstrm-dev"
run "sudo sed -i \"s/agentxperms 0700 0755 dnsdist/agentxperms 0700 0755 ${USER}/g\" regression-tests.dnsdist/snmpd.conf"
run "sudo cp -f regression-tests.dnsdist/snmpd.conf /etc/snmp/snmpd.conf"
run "sudo service snmpd restart"
Expand Down Expand Up @@ -408,6 +412,7 @@ build_dnsdist(){
--enable-libsodium \
--enable-dnscrypt \
--enable-dns-over-tls \
--enable-fstrm \
--prefix=$HOME/dnsdist \
--disable-silent-rules"
run "make -k -j3"
Expand Down
30 changes: 30 additions & 0 deletions m4/pdns_check_fstrm.m4
@@ -0,0 +1,30 @@
AC_DEFUN([PDNS_CHECK_FSTRM], [
AC_MSG_CHECKING([whether we will be linking in fstrm])
AC_ARG_ENABLE([fstrm],
AS_HELP_STRING([--enable-fstrm],[use fstrm @<:@default=auto@:>@]),
[enable_fstrm=$enableval],
[enable_fstrm=auto],
)
AC_MSG_RESULT([$enable_fstrm])
AS_IF([test "x$enable_fstrm" != "xno"], [
AS_IF([test "x$enable_fstrm" = "xyes" -o "x$enable_fstrm" = "xauto"], [
PKG_CHECK_MODULES([FSTRM], [libfstrm], [
AC_DEFINE([HAVE_FSTRM], [1], [Define to 1 if you have libfstrm])
save_CFLAGS=$CFLAGS
save_LIBS=$LIBS
CFLAGS="$FSTRM_CFLAGS $CFLAGS"
LIBS="$FSTRM_LIBS $LIBS"
AC_CHECK_FUNCS([fstrm_tcp_writer_init])
CFLAGS=$save_CFLAGS
LIBS=$save_LIBS
], [ : ])
])
])
AM_CONDITIONAL([FSTRM], [test "x$FSTRM_LIBS" != "x"])
AS_IF([test "x$enable_fstrm" = "xyes"], [
AS_IF([test x"$FSTRM_LIBS" = "x"], [
AC_MSG_ERROR([fstrm requested but libfstrm was not found])
])
])
])
4 changes: 4 additions & 0 deletions pdns/dnsdist-console.cc
Expand Up @@ -308,6 +308,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
{ "delta", true, "", "shows all commands entered that changed the configuration" },
{ "DisableValidationAction", true, "", "set the CD bit in the question, let it go through" },
{ "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" },
{ "dumpStats", true, "", "print all statistics we gather" },
Expand Down Expand Up @@ -341,6 +343,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "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" },
{ "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" },
{ "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
{ "newFrameStreamTcpLogger", true, "addr", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
{ "newFrameStreamUnixLogger", true, "socket", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
{ "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true]", "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()`" },
Expand Down
108 changes: 102 additions & 6 deletions pdns/dnsdist-lua-actions.cc
Expand Up @@ -25,7 +25,9 @@
#include "dnsdist-protobuf.hh"

#include "dolog.hh"
#include "dnstap.hh"
#include "ednsoptions.hh"
#include "fstrm_logger.hh"
#include "remote_logger.hh"

class DropAction : public DNSAction
Expand Down Expand Up @@ -599,10 +601,42 @@ class DisableECSAction : public DNSAction
}
};

class DnstapLogAction : public DNSAction, public boost::noncopyable
{
public:
DnstapLogAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
{
}
DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
{
#ifdef HAVE_PROTOBUF
DnstapMessage message(d_identity, dq->remote, dq->local, dq->tcp, reinterpret_cast<const char*>(dq->dh), dq->len, dq->queryTime, nullptr);
{
if (d_alterFunc) {
std::lock_guard<std::mutex> lock(g_luamutex);
(*d_alterFunc)(*dq, &message);
}
}
std::string data;
message.serialize(data);
d_logger->queueData(data);
#endif /* HAVE_PROTOBUF */
return Action::None;
}
string toString() const override
{
return "remote log as dnstap to " + (d_logger ? d_logger->toString() : "");
}
private:
std::string d_identity;
std::shared_ptr<RemoteLoggerInterface> d_logger;
boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > d_alterFunc;
};

class RemoteLogAction : public DNSAction, public boost::noncopyable
{
public:
RemoteLogAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
RemoteLogAction(std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
{
}
DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
Expand Down Expand Up @@ -630,7 +664,7 @@ class RemoteLogAction : public DNSAction, public boost::noncopyable
return "remote log to " + (d_logger ? d_logger->toString() : "");
}
private:
std::shared_ptr<RemoteLogger> d_logger;
std::shared_ptr<RemoteLoggerInterface> d_logger;
boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
};

Expand Down Expand Up @@ -681,10 +715,44 @@ class TagAction : public DNSAction
std::string d_value;
};

class DnstapLogResponseAction : public DNSResponseAction, public boost::noncopyable
{
public:
DnstapLogResponseAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
{
}
DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
{
#ifdef HAVE_PROTOBUF
struct timespec now;
gettime(&now, true);
DnstapMessage message(d_identity, dr->remote, dr->local, dr->tcp, reinterpret_cast<const char*>(dr->dh), dr->len, dr->queryTime, &now);
{
if (d_alterFunc) {
std::lock_guard<std::mutex> lock(g_luamutex);
(*d_alterFunc)(*dr, &message);
}
}
std::string data;
message.serialize(data);
d_logger->queueData(data);
#endif /* HAVE_PROTOBUF */
return Action::None;
}
string toString() const override
{
return "log response as dnstap to " + (d_logger ? d_logger->toString() : "");
}
private:
std::string d_identity;
std::shared_ptr<RemoteLoggerInterface> d_logger;
boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > d_alterFunc;
};

class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
{
public:
RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
RemoteLogResponseAction(std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
{
}
DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
Expand Down Expand Up @@ -712,7 +780,7 @@ class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopya
return "remote log response to " + (d_logger ? d_logger->toString() : "");
}
private:
std::shared_ptr<RemoteLogger> d_logger;
std::shared_ptr<RemoteLoggerInterface> d_logger;
boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
bool d_includeCNAME;
};
Expand Down Expand Up @@ -991,22 +1059,50 @@ void setupLuaActions()
return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
});

g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
// avoids potentially-evaluated-expression warning with clang.
RemoteLoggerInterface& rl = *logger.get();
if (typeid(rl) != typeid(RemoteLogger)) {
// We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
throw std::runtime_error(std::string("RemoteLogAction only takes RemoteLogger. For other types, please look at DnstapLogAction."));
}
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
#else
throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
#endif
});

g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
// avoids potentially-evaluated-expression warning with clang.
RemoteLoggerInterface& rl = *logger.get();
if (typeid(rl) != typeid(RemoteLogger)) {
// We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
throw std::runtime_error("RemoteLogResponseAction only takes RemoteLogger. For other types, please look at DnstapLogResponseAction.");
}
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
#else
throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
#endif
});

g_lua.writeFunction("DnstapLogAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > alterFunc) {
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSAction>(new DnstapLogAction(identity, logger, alterFunc));
#else
throw std::runtime_error("Protobuf support is required to use DnstapLogAction");
#endif
});

g_lua.writeFunction("DnstapLogResponseAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > alterFunc) {
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSResponseAction>(new DnstapLogResponseAction(identity, logger, alterFunc));
#else
throw std::runtime_error("Protobuf support is required to use DnstapLogResponseAction");
#endif
});

g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
});
Expand Down
35 changes: 32 additions & 3 deletions pdns/dnsdist-lua-bindings.cc
Expand Up @@ -23,7 +23,9 @@
#include "dnsdist-lua.hh"
#include "dnsdist-protobuf.hh"

#include "dnstap.hh"
#include "dolog.hh"
#include "fstrm_logger.hh"
#include "remote_logger.hh"

void setupLuaBindings(bool client)
Expand Down Expand Up @@ -257,13 +259,40 @@ void setupLuaBindings(bool client)
message.setResponder(str);
});

g_lua.registerFunction<std::string(DnstapMessage::*)()>("toDebugString", [](const DnstapMessage& message) { return message.toDebugString(); });
g_lua.registerFunction<void(DnstapMessage::*)(const std::string&)>("setExtra", [](DnstapMessage& message, const std::string& str) {
message.setExtra(str);
});

/* RemoteLogger */
g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
if (client) {
return std::shared_ptr<RemoteLogger>();
return std::shared_ptr<RemoteLoggerInterface>();
}
return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
});
return std::shared_ptr<RemoteLoggerInterface>(new RemoteLogger(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1));
});

g_lua.writeFunction("newFrameStreamUnixLogger", [client](const std::string& address) {
if (client) {
return std::shared_ptr<RemoteLoggerInterface>();
}
#ifdef HAVE_FSTRM
return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_UNIX, address));
#else
throw std::runtime_error("fstrm support is required to build an AF_UNIX FrameStreamLogger");
#endif /* HAVE_FSTRM */
});

g_lua.writeFunction("newFrameStreamTcpLogger", [client](const std::string& address) {
if (client) {
return std::shared_ptr<RemoteLoggerInterface>();
}
#if defined(HAVE_FSTRM) && defined(HAVE_FSTRM_TCP_WRITER_INIT)
return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_INET, address));
#else
throw std::runtime_error("fstrm with TCP support is required to build an AF_INET FrameStreamLogger");
#endif /* HAVE_FSTRM */
});

#ifdef HAVE_DNSCRYPT
/* DnsCryptContext bindings */
Expand Down
8 changes: 4 additions & 4 deletions pdns/dnsdist-lua-rules.cc
Expand Up @@ -1137,15 +1137,15 @@ void setupLuaRules()

int matches=0;
ComboAddress dummy("127.0.0.1");
DTime dt;
dt.set();
StopWatch sw;
sw.start();
for(int n=0; n < times; ++n) {
const item& i = items[n % items.size()];
DNSQuestion dq(&i.qname, i.qtype, i.qclass, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false);
DNSQuestion dq(&i.qname, i.qtype, i.qclass, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false, &sw.d_start);
if(rule->matches(&dq))
matches++;
}
double udiff=dt.udiff();
double udiff=sw.udiff();
g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f usec\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str();

});
Expand Down
1 change: 1 addition & 0 deletions pdns/dnsdist-protobuf.cc
Expand Up @@ -30,6 +30,7 @@

DNSDistProtoBufMessage::DNSDistProtoBufMessage(const DNSQuestion& dq): DNSProtoBufMessage(Query, dq.uniqueId ? *dq.uniqueId : t_uuidGenerator(), dq.remote, dq.local, *dq.qname, dq.qtype, dq.qclass, dq.dh->id, dq.tcp, dq.len)
{
setQueryTime(dq.queryTime->tv_sec, dq.queryTime->tv_nsec / 1000);
};

DNSDistProtoBufMessage::DNSDistProtoBufMessage(const DNSResponse& dr, bool includeCNAME): DNSProtoBufMessage(Response, dr.uniqueId ? *dr.uniqueId : t_uuidGenerator(), dr.remote, dr.local, *dr.qname, dr.qtype, dr.qclass, dr.dh->id, dr.tcp, dr.len)
Expand Down
14 changes: 7 additions & 7 deletions pdns/dnsdist-tcp.cc
Expand Up @@ -340,13 +340,6 @@ void* tcpClientThread(int pipefd)
goto drop;
}

const uint16_t* flags = getFlagsFromDNSHeader(dh);
uint16_t origFlags = *flags;
uint16_t qtype, qclass;
unsigned int consumed = 0;
DNSName qname(query, qlen, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
DNSQuestion dq(&qname, qtype, qclass, &dest, &ci.remote, dh, queryBuffer.capacity(), qlen, true);

string poolname;
int delayMsec=0;
/* we need this one to be accurate ("real") for the protobuf message */
Expand All @@ -355,6 +348,13 @@ void* tcpClientThread(int pipefd)
gettime(&now);
gettime(&queryRealTime, true);

const uint16_t* flags = getFlagsFromDNSHeader(dh);
uint16_t origFlags = *flags;
uint16_t qtype, qclass;
unsigned int consumed = 0;
DNSName qname(query, qlen, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
DNSQuestion dq(&qname, qtype, qclass, &dest, &ci.remote, dh, queryBuffer.capacity(), qlen, true, &queryRealTime);

if (!processQuery(holders, dq, poolname, &delayMsec, now)) {
goto drop;
}
Expand Down

0 comments on commit ad3e187

Please sign in to comment.