AS_IF([test "x$with_net_snmp" = "xyes" -o "x$with_net_snmp" = "xauto"], [
AC_CHECK_PROG([NET_SNMP_CFLAGS], [net-snmp-config], [`net-snmp-config --cflags`])
AC_CHECK_PROG([NET_SNMP_LIBS], [net-snmp-config], [`net-snmp-config --agent-libs`])
+ AC_CHECK_DECLS([snmp_select_info2], [ : ], [ : ],
+ [AC_INCLUDES_DEFAULT
+ #include <net-snmp/net-snmp-config.h>
+ #include <net-snmp/definitions.h>
+ #include <net-snmp/types.h>
+ #include <net-snmp/utilities.h>
+ #include <net-snmp/config_api.h>
+ #include <net-snmp/session_api.h>
+ ])
])
])
AS_IF([test "x$with_net_snmp" = "xyes"], [
#include "misc.hh"
#include "syncres.hh"
-#include "namespaces.hh"
#include "namespaces.hh"
class DevPollFDMultiplexer : public FDMultiplexer
close(d_devpollfd);
}
- virtual int run(struct timeval* tv);
+ virtual int run(struct timeval* tv, int timeout=500);
virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter);
virtual void removeFD(callbackmap_t& cbmap, int fd);
}
}
-int DevPollFDMultiplexer::run(struct timeval* now)
+int DevPollFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
struct dvpoll dvp;
dvp.dp_nfds = d_readCallbacks.size() + d_writeCallbacks.size();
dvp.dp_fds = new pollfd[dvp.dp_nfds];
- dvp.dp_timeout = 500;
+ dvp.dp_timeout = timeout;
int ret=ioctl(d_devpollfd, DP_POLL, &dvp);
gettimeofday(now,0); // MANDATORY!
}
delete[] dvp.dp_fds;
d_inrun=false;
- return 0;
+ return ret;
}
#if 0
bpf-filter.main.ebpf \
bpf-filter.qname.ebpf \
bpf-filter.ebpf.src \
- DNSDIST-MIB.txt
+ DNSDIST-MIB.txt \
+ devpollmplexer.cc \
+ epollmplexer.cc \
+ kqueuemplexer.cc \
+ portsmplexer.cc
bin_PROGRAMS = dnsdist
ednscookies.cc ednscookies.hh \
ednssubnet.cc ednssubnet.hh \
gettime.cc gettime.hh \
+ htmlfiles.h \
iputils.cc iputils.hh \
lock.hh \
misc.cc misc.hh \
- htmlfiles.h \
+ mplexer.hh \
namespaces.hh \
pdnsexception.hh \
protobuf.cc protobuf.hh \
qtype.cc qtype.hh \
remote_logger.cc remote_logger.hh \
+ selectmplexer.cc \
sholder.hh \
snmp-agent.cc snmp-agent.hh \
sodcrypto.cc sodcrypto.hh \
endif
endif
+if HAVE_FREEBSD
+dnsdist_SOURCES += kqueuemplexer.cc
+endif
+
+if HAVE_LINUX
+dnsdist_SOURCES += epollmplexer.cc
+endif
+
+if HAVE_SOLARIS
+dnsdist_SOURCES += \
+ devpollmplexer.cc \
+ portsmplexer.cc
+endif
+
testrunner_SOURCES = \
base64.hh \
dns.hh \
--- /dev/null
+../devpollmplexer.cc
\ No newline at end of file
--- /dev/null
+../epollmplexer.cc
\ No newline at end of file
--- /dev/null
+../kqueuemplexer.cc
\ No newline at end of file
--- /dev/null
+../mplexer.hh
\ No newline at end of file
--- /dev/null
+../portsmplexer.cc
\ No newline at end of file
--- /dev/null
+../selectmplexer.cc
\ No newline at end of file
#include <sys/epoll.h>
#endif
-#include "namespaces.hh"
#include "namespaces.hh"
class EpollFDMultiplexer : public FDMultiplexer
close(d_epollfd);
}
- virtual int run(struct timeval* tv);
+ virtual int run(struct timeval* tv, int timeout=500);
virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter);
virtual void removeFD(callbackmap_t& cbmap, int fd);
throw FDMultiplexerException("Removing fd from epoll set: "+stringerror());
}
-int EpollFDMultiplexer::run(struct timeval* now)
+int EpollFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
}
- int ret=epoll_wait(d_epollfd, d_eevents.get(), s_maxevents, 500);
+ int ret=epoll_wait(d_epollfd, d_eevents.get(), s_maxevents, timeout);
gettimeofday(now,0); // MANDATORY
if(ret < 0 && errno!=EINTR)
}
}
d_inrun=false;
- return 0;
+ return ret;
}
#if 0
#include <iostream>
#include <unistd.h>
#include "misc.hh"
-#include "syncres.hh"
#include <sys/types.h>
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/event.h>
#endif
#include <sys/time.h>
-#include "namespaces.hh"
#include "namespaces.hh"
class KqueueFDMultiplexer : public FDMultiplexer
close(d_kqueuefd);
}
- virtual int run(struct timeval* tv);
+ virtual int run(struct timeval* tv, int timeout=500);
virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const boost::any& parameter);
virtual void removeFD(callbackmap_t& cbmap, int fd);
throw FDMultiplexerException("Removing fd from kqueue set: "+stringerror());
}
-int KqueueFDMultiplexer::run(struct timeval* now)
+int KqueueFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
}
struct timespec ts;
- ts.tv_sec=0;
- ts.tv_nsec=500000000U;
+ ts.tv_sec=timeout/1000;
+ ts.tv_nsec=(timeout % 1000) * 1000000;
int ret=kevent(d_kqueuefd, 0, 0, d_kevents.get(), s_maxevents, &ts);
gettimeofday(now,0); // MANDATORY!
}
d_inrun=false;
- return 0;
+ return ret;
}
#if 0
#include <map>
#include <stdexcept>
#include <string>
-#include "utility.hh"
class FDMultiplexerException : public std::runtime_error
{
class FDMultiplexer
{
public:
- // typedef boost::variant<PacketID, TCPConnection> funcparam_t;
typedef boost::any funcparam_t;
protected:
virtual ~FDMultiplexer()
{}
- virtual int run(struct timeval* tv) = 0;
+ /* tv will be updated to 'now' before run returns */
+ /* timeout is in ms */
+ virtual int run(struct timeval* tv, int timeout=500) = 0;
//! Add an fd to the read watch list - currently an fd can only be on one list at a time!
virtual void addReadFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter=funcparam_t())
virtual std::string getName() = 0;
-
protected:
typedef std::map<int, Callback> callbackmap_t;
callbackmap_t d_readCallbacks, d_writeCallbacks;
virtual ~SelectFDMultiplexer()
{}
- virtual int run(struct timeval* tv);
+ virtual int run(struct timeval* tv, int timeout=500);
virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter);
virtual void removeFD(callbackmap_t& cbmap, int fd);
#include <iostream>
#include <poll.h>
#include "misc.hh"
-#include "syncres.hh"
-#include "utility.hh"
-#include "namespaces.hh"
#include "namespaces.hh"
return a.fd < b.fd;
}
-int PollFDMultiplexer::run(struct timeval* now)
+int PollFDMultiplexer::run(struct timeval* now, int timeout=500)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
pollfds.push_back(pollfd);
}
- int ret=poll(&pollfds[0], pollfds.size(), 500);
- Utility::gettimeofday(now, 0); // MANDATORY!
+ int ret=poll(&pollfds[0], pollfds.size(), timeout);
+ gettimeofday(now, 0); // MANDATORY!
if(ret < 0 && errno!=EINTR)
throw FDMultiplexerException("poll returned error: "+stringerror());
d_iter=d_readCallbacks.end();
d_inrun=true;
-
+
for(unsigned int n = 0; n < pollfds.size(); ++n) {
if(pollfds[n].revents == POLLIN) {
d_iter=d_readCallbacks.find(pollfds[n].fd);
}
}
d_inrun=false;
- return 0;
+ return ret;
}
#if 0
#include <iostream>
#include "misc.hh"
-#include "syncres.hh"
-#include "namespaces.hh"
#include "namespaces.hh"
class PortsFDMultiplexer : public FDMultiplexer
close(d_portfd);
}
- virtual int run(struct timeval* tv);
+ virtual int run(struct timeval* tv, int timeout=500);
virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const boost::any& parameter);
virtual void removeFD(callbackmap_t& cbmap, int fd);
throw FDMultiplexerException("Removing fd from port set: "+stringerror());
}
-int PortsFDMultiplexer::run(struct timeval* now)
+int PortsFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
}
- struct timespec timeout;
- timeout.tv_sec=0; timeout.tv_nsec=500000000;
+ struct timespec timeoutspec;
+ timeoutspec.tv_sec = time / 1000;
+ timeoutspec.tv_nsec = (time % 1000) * 1000000;
unsigned int numevents=1;
- int ret= port_getn(d_portfd, d_pevents.get(), min(PORT_MAX_LIST, s_maxevents), &numevents, &timeout);
+ int ret= port_getn(d_portfd, d_pevents.get(), min(PORT_MAX_LIST, s_maxevents), &numevents, &timeoutspec);
/* port_getn has an unusual API - (ret == -1, errno == ETIME) can
mean partial success; you must check (*numevents) in this case
}
d_inrun=false;
- return 0;
+ return numevents;
}
#if 0
#include "sstuff.hh"
#include <iostream>
#include "misc.hh"
-#include "utility.hh"
-
-#include "namespaces.hh"
#include "namespaces.hh"
static FDMultiplexer* make()
throw FDMultiplexerException("Tried to remove unlisted fd "+std::to_string(fd)+ " from multiplexer");
}
-int SelectFDMultiplexer::run(struct timeval* now)
+int SelectFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
fdmax=max(i->first, fdmax);
}
- struct timeval tv={0,500000};
+ struct timeval tv={timeout / 1000 , (timeout % 1000) * 1000};
int ret=select(fdmax + 1, &readfds, &writefds, 0, &tv);
- Utility::gettimeofday(now, 0); // MANDATORY!
+ gettimeofday(now, 0); // MANDATORY!
if(ret < 0 && errno!=EINTR)
throw FDMultiplexerException("select returned error: "+stringerror());
d_iter=d_readCallbacks.end();
d_inrun=true;
-
+
+ int got = 0;
for(callbackmap_t::iterator i=d_readCallbacks.begin(); i != d_readCallbacks.end() && i->first <= fdmax; ) {
d_iter=i++;
if(FD_ISSET(d_iter->first, &readfds)) {
d_iter->second.d_callback(d_iter->first, d_iter->second.d_parameter);
+ got++;
continue; // so we don't refind ourselves as writable
}
}
d_iter=i++;
if(FD_ISSET(d_iter->first, &writefds)) {
d_iter->second.d_callback(d_iter->first, d_iter->second.d_parameter);
+ got++;
}
}
d_inrun=false;
- return 0;
+ return got;
}
#if 0
#ifdef HAVE_NET_SNMP
+#ifndef HAVE_SNMP_SELECT_INFO2
+/* that's terrible, because it means we are going to have trouble with large
+ FD numbers at some point.. */
+# define netsnmp_large_fd_set fd_set
+# define snmp_read2 snmp_read
+# define snmp_select_info2 snmp_select_info
+# define netsnmp_large_fd_set_init(...)
+# define netsnmp_large_fd_set_cleanup(...)
+# define NETSNMP_LARGE_FD_SET FD_SET
+# define NETSNMP_LARGE_FD_CLR FD_CLR
+# define NETSNMP_LARGE_FD_ZERO FD_ZERO
+# define NETSNMP_LARGE_FD_ISSET FD_ISSET
+#else
+# include <net-snmp/library/large_fd_set.h>
+#endif
+
const oid SNMPAgent::snmpTrapOID[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
const size_t SNMPAgent::snmpTrapOIDLen = OID_LENGTH(SNMPAgent::snmpTrapOID);
return true;
}
-void SNMPAgent::handleTraps()
+void SNMPAgent::handleTrapsEvent()
{
netsnmp_variable_list* varList = nullptr;
ssize_t got = 0;
}
while (got > 0);
}
+
+void SNMPAgent::handleSNMPQueryEvent(int fd)
+{
+ netsnmp_large_fd_set fdset;
+ netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
+ NETSNMP_LARGE_FD_ZERO(&fdset);
+ NETSNMP_LARGE_FD_SET(fd, &fdset);
+ snmp_read2(&fdset);
+}
+
+void SNMPAgent::handleTrapsCB(int fd, FDMultiplexer::funcparam_t& var)
+{
+ SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
+ if (!agent || !*agent)
+ throw std::runtime_error("Invalid value received in SNMP trap callback");
+
+ (*agent)->handleTrapsEvent();
+}
+
+void SNMPAgent::handleSNMPQueryCB(int fd, FDMultiplexer::funcparam_t& var)
+{
+ SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
+ if (!agent || !*agent)
+ throw std::runtime_error("Invalid value received in SNMP trap callback");
+
+ (*agent)->handleSNMPQueryEvent(fd);
+}
+
+static FDMultiplexer* getMultiplexer()
+{
+ FDMultiplexer* ret = nullptr;
+ for(const auto& i : FDMultiplexer::getMultiplexerMap()) {
+ try {
+ ret = i.second();
+ return ret;
+ }
+ catch(const FDMultiplexerException& fe) {
+ }
+ catch(...) {
+ }
+ }
+ return ret;
+}
+
#endif /* HAVE_NET_SNMP */
void SNMPAgent::worker()
{
#ifdef HAVE_NET_SNMP
- int numfds = 0;
+ FDMultiplexer* mplexer = getMultiplexer();
+ if (mplexer == nullptr) {
+ throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
+ }
+
+ int maxfd = 0;
int block = 1;
- fd_set fdset;
+ netsnmp_large_fd_set fdset;
struct timeval timeout = { 0, 0 };
+ struct timeval now;
+
+ /* we want to be notified if a trap is waiting
+ to be sent */
+ mplexer->addReadFD(d_trapPipe[0], &handleTrapsCB, this);
while(true) {
- numfds = FD_SETSIZE;
+ netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
+ NETSNMP_LARGE_FD_ZERO(&fdset);
- FD_ZERO(&fdset);
- FD_SET(d_trapPipe[0], &fdset);
block = 1;
timeout = { 0, 0 };
- snmp_select_info(&numfds, &fdset, &timeout, &block);
+ snmp_select_info2(&maxfd, &fdset, &timeout, &block);
- int res = select(FD_SETSIZE, &fdset, nullptr, nullptr, block ? nullptr : &timeout);
-
- if (res == 2) {
- FD_CLR(d_trapPipe[0], &fdset);
- snmp_read(&fdset);
- handleTraps();
- }
- else if (res == 1)
- {
- if (FD_ISSET(d_trapPipe[0], &fdset)) {
- handleTraps();
- } else {
- snmp_read(&fdset);
+ for (int fd = 0; fd < maxfd; fd++) {
+ if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
+ mplexer->addReadFD(fd, &handleSNMPQueryCB, this);
}
}
- else if (res == 0) {
+
+ /* run updates now */
+ int res = mplexer->run(&now, (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000));
+
+ /* we handle timeouts here, the rest has already been handled by callbacks */
+ if (res == 0) {
snmp_timeout();
run_alarms();
}
+
+ for (int fd = 0; fd < maxfd; fd++) {
+ if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
+ try {
+ mplexer->removeReadFD(fd);
+ }
+ catch(const FDMultiplexerException& e) {
+ /* we might get an exception when removing a closed file descriptor,
+ just ignore it */
+ }
+ }
+ }
}
#endif /* HAVE_NET_SNMP */
}
#undef INET6 /* SRSLY? */
#endif /* HAVE_NET_SNMP */
+#include "mplexer.hh"
+
class SNMPAgent
{
public:
static bool sendTrap(int fd,
netsnmp_variable_list* varList);
- void handleTraps();
-
int d_trapPipe[2] = { -1, -1};
#endif /* HAVE_NET_SNMP */
private:
void worker();
+ static void handleTrapsCB(int fd, FDMultiplexer::funcparam_t& var);
+ static void handleSNMPQueryCB(int fd, FDMultiplexer::funcparam_t& var);
+ void handleTrapsEvent();
+ void handleSNMPQueryEvent(int fd);
std::thread d_thread;
};