diff options
Diffstat (limited to 'snmp-agent.cc')
-rw-r--r-- | snmp-agent.cc | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/snmp-agent.cc b/snmp-agent.cc new file mode 100644 index 0000000..4b2bc6b --- /dev/null +++ b/snmp-agent.cc @@ -0,0 +1,206 @@ +#include "snmp-agent.hh" +#include "misc.hh" +#include "threadname.hh" +#ifdef RECURSOR +#include "logger.hh" +#else +#include "dolog.hh" +#endif + +#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); + +int SNMPAgent::setCounter64Value(netsnmp_request_info* request, + uint64_t value) +{ + struct counter64 val64; + val64.high = value >> 32; + val64.low = value & 0xffffffff; + snmp_set_var_typed_value(request->requestvb, + ASN_COUNTER64, + &val64, + sizeof(val64)); + return SNMP_ERR_NOERROR; +} + +bool SNMPAgent::sendTrap(int fd, + netsnmp_variable_list* varList) +{ + ssize_t written = write(fd, &varList, sizeof(varList)); + + if (written != sizeof(varList)) { + snmp_free_varbind(varList); + return false; + } + return true; +} + +void SNMPAgent::handleTrapsEvent() +{ + netsnmp_variable_list* varList = nullptr; + ssize_t got = 0; + + do { + got = read(d_trapPipe[0], &varList, sizeof(varList)); + + if (got == sizeof(varList)) { + send_v2trap(varList); + snmp_free_varbind(varList); + } + } + 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); +} + +#endif /* HAVE_NET_SNMP */ + +void SNMPAgent::worker() +{ +#ifdef HAVE_NET_SNMP + FDMultiplexer* mplexer = FDMultiplexer::getMultiplexerSilent(); + if (mplexer == nullptr) { + throw std::runtime_error("No FD multiplexer found for the SNMP agent!"); + } + +#ifdef RECURSOR + string threadName = "pdns-r/SNMP"; +#else + string threadName = "dnsdist/SNMP"; +#endif + setThreadName(threadName); + + int maxfd = 0; + int block = 1; + 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) { + netsnmp_large_fd_set_init(&fdset, FD_SETSIZE); + NETSNMP_LARGE_FD_ZERO(&fdset); + + block = 1; + timeout = { 0, 0 }; + snmp_select_info2(&maxfd, &fdset, &timeout, &block); + + for (int fd = 0; fd < maxfd; fd++) { + if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) { + mplexer->addReadFD(fd, &handleSNMPQueryCB, this); + } + } + + /* 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 */ +} + +SNMPAgent::SNMPAgent(const std::string& name, const std::string& daemonSocket) +{ +#ifdef HAVE_NET_SNMP + netsnmp_enable_subagent(); + snmp_disable_log(); + if (!daemonSocket.empty()) { + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, + daemonSocket.c_str()); + } + /* no need to load any MIBS, + and it causes import errors if some modules are not present */ + setenv("MIBS", "", 1); + + init_agent(name.c_str()); + + /* we use select() so don't use SIGALARM to handle alarms. + Note that we need to handle alarms for automatic reconnection + to the daemon to work. + */ + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_ALARM_DONT_USE_SIG, + 1); + + init_snmp(name.c_str()); + + if (pipe(d_trapPipe) < 0) + unixDie("Creating pipe"); + + if (!setNonBlocking(d_trapPipe[0])) { + close(d_trapPipe[0]); + close(d_trapPipe[1]); + unixDie("Setting pipe non-blocking"); + } + + if (!setNonBlocking(d_trapPipe[1])) { + close(d_trapPipe[0]); + close(d_trapPipe[1]); + unixDie("Setting pipe non-blocking"); + } + +#endif /* HAVE_NET_SNMP */ +} |