You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

207 lines
5.3 KiB

  1. #include "snmp-agent.hh"
  2. #include "misc.hh"
  3. #include "threadname.hh"
  4. #ifdef RECURSOR
  5. #include "logger.hh"
  6. #else
  7. #include "dolog.hh"
  8. #endif
  9. #ifdef HAVE_NET_SNMP
  10. #ifndef HAVE_SNMP_SELECT_INFO2
  11. /* that's terrible, because it means we are going to have trouble with large
  12. FD numbers at some point.. */
  13. # define netsnmp_large_fd_set fd_set
  14. # define snmp_read2 snmp_read
  15. # define snmp_select_info2 snmp_select_info
  16. # define netsnmp_large_fd_set_init(...)
  17. # define netsnmp_large_fd_set_cleanup(...)
  18. # define NETSNMP_LARGE_FD_SET FD_SET
  19. # define NETSNMP_LARGE_FD_CLR FD_CLR
  20. # define NETSNMP_LARGE_FD_ZERO FD_ZERO
  21. # define NETSNMP_LARGE_FD_ISSET FD_ISSET
  22. #else
  23. # include <net-snmp/library/large_fd_set.h>
  24. #endif
  25. const oid SNMPAgent::snmpTrapOID[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
  26. const size_t SNMPAgent::snmpTrapOIDLen = OID_LENGTH(SNMPAgent::snmpTrapOID);
  27. int SNMPAgent::setCounter64Value(netsnmp_request_info* request,
  28. uint64_t value)
  29. {
  30. struct counter64 val64;
  31. val64.high = value >> 32;
  32. val64.low = value & 0xffffffff;
  33. snmp_set_var_typed_value(request->requestvb,
  34. ASN_COUNTER64,
  35. &val64,
  36. sizeof(val64));
  37. return SNMP_ERR_NOERROR;
  38. }
  39. bool SNMPAgent::sendTrap(int fd,
  40. netsnmp_variable_list* varList)
  41. {
  42. ssize_t written = write(fd, &varList, sizeof(varList));
  43. if (written != sizeof(varList)) {
  44. snmp_free_varbind(varList);
  45. return false;
  46. }
  47. return true;
  48. }
  49. void SNMPAgent::handleTrapsEvent()
  50. {
  51. netsnmp_variable_list* varList = nullptr;
  52. ssize_t got = 0;
  53. do {
  54. got = read(d_trapPipe[0], &varList, sizeof(varList));
  55. if (got == sizeof(varList)) {
  56. send_v2trap(varList);
  57. snmp_free_varbind(varList);
  58. }
  59. }
  60. while (got > 0);
  61. }
  62. void SNMPAgent::handleSNMPQueryEvent(int fd)
  63. {
  64. netsnmp_large_fd_set fdset;
  65. netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
  66. NETSNMP_LARGE_FD_ZERO(&fdset);
  67. NETSNMP_LARGE_FD_SET(fd, &fdset);
  68. snmp_read2(&fdset);
  69. }
  70. void SNMPAgent::handleTrapsCB(int fd, FDMultiplexer::funcparam_t& var)
  71. {
  72. SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
  73. if (!agent || !*agent)
  74. throw std::runtime_error("Invalid value received in SNMP trap callback");
  75. (*agent)->handleTrapsEvent();
  76. }
  77. void SNMPAgent::handleSNMPQueryCB(int fd, FDMultiplexer::funcparam_t& var)
  78. {
  79. SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
  80. if (!agent || !*agent)
  81. throw std::runtime_error("Invalid value received in SNMP trap callback");
  82. (*agent)->handleSNMPQueryEvent(fd);
  83. }
  84. #endif /* HAVE_NET_SNMP */
  85. void SNMPAgent::worker()
  86. {
  87. #ifdef HAVE_NET_SNMP
  88. FDMultiplexer* mplexer = FDMultiplexer::getMultiplexerSilent();
  89. if (mplexer == nullptr) {
  90. throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
  91. }
  92. #ifdef RECURSOR
  93. string threadName = "pdns-r/SNMP";
  94. #else
  95. string threadName = "dnsdist/SNMP";
  96. #endif
  97. setThreadName(threadName);
  98. int maxfd = 0;
  99. int block = 1;
  100. netsnmp_large_fd_set fdset;
  101. struct timeval timeout = { 0, 0 };
  102. struct timeval now;
  103. /* we want to be notified if a trap is waiting
  104. to be sent */
  105. mplexer->addReadFD(d_trapPipe[0], &handleTrapsCB, this);
  106. while(true) {
  107. netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
  108. NETSNMP_LARGE_FD_ZERO(&fdset);
  109. block = 1;
  110. timeout = { 0, 0 };
  111. snmp_select_info2(&maxfd, &fdset, &timeout, &block);
  112. for (int fd = 0; fd < maxfd; fd++) {
  113. if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
  114. mplexer->addReadFD(fd, &handleSNMPQueryCB, this);
  115. }
  116. }
  117. /* run updates now */
  118. int res = mplexer->run(&now, (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000));
  119. /* we handle timeouts here, the rest has already been handled by callbacks */
  120. if (res == 0) {
  121. snmp_timeout();
  122. run_alarms();
  123. }
  124. for (int fd = 0; fd < maxfd; fd++) {
  125. if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
  126. try {
  127. mplexer->removeReadFD(fd);
  128. }
  129. catch(const FDMultiplexerException& e) {
  130. /* we might get an exception when removing a closed file descriptor,
  131. just ignore it */
  132. }
  133. }
  134. }
  135. }
  136. #endif /* HAVE_NET_SNMP */
  137. }
  138. SNMPAgent::SNMPAgent(const std::string& name, const std::string& masterSocket)
  139. {
  140. #ifdef HAVE_NET_SNMP
  141. netsnmp_enable_subagent();
  142. snmp_disable_log();
  143. if (!masterSocket.empty()) {
  144. netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
  145. NETSNMP_DS_AGENT_X_SOCKET,
  146. masterSocket.c_str());
  147. }
  148. /* no need to load any MIBS,
  149. and it causes import errors if some modules are not present */
  150. setenv("MIBS", "", 1);
  151. init_agent(name.c_str());
  152. /* we use select() so don't use SIGALARM to handle alarms.
  153. Note that we need to handle alarms for automatic reconnection
  154. to the master to work.
  155. */
  156. netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
  157. NETSNMP_DS_LIB_ALARM_DONT_USE_SIG,
  158. 1);
  159. init_snmp(name.c_str());
  160. if (pipe(d_trapPipe) < 0)
  161. unixDie("Creating pipe");
  162. if (!setNonBlocking(d_trapPipe[0])) {
  163. close(d_trapPipe[0]);
  164. close(d_trapPipe[1]);
  165. unixDie("Setting pipe non-blocking");
  166. }
  167. if (!setNonBlocking(d_trapPipe[1])) {
  168. close(d_trapPipe[0]);
  169. close(d_trapPipe[1]);
  170. unixDie("Setting pipe non-blocking");
  171. }
  172. #endif /* HAVE_NET_SNMP */
  173. }