summaryrefslogtreecommitdiffstats
path: root/snmp-agent.cc
diff options
context:
space:
mode:
Diffstat (limited to 'snmp-agent.cc')
-rw-r--r--snmp-agent.cc206
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 */
+}