1 #include "snmp-agent.hh"
3 #include "threadname.hh"
12 #ifndef HAVE_SNMP_SELECT_INFO2
13 /* that's terrible, because it means we are going to have trouble with large
14 FD numbers at some point.. */
15 # define netsnmp_large_fd_set fd_set
16 # define snmp_read2 snmp_read
17 # define snmp_select_info2 snmp_select_info
18 # define netsnmp_large_fd_set_init(...)
19 # define netsnmp_large_fd_set_cleanup(...)
20 # define NETSNMP_LARGE_FD_SET FD_SET
21 # define NETSNMP_LARGE_FD_CLR FD_CLR
22 # define NETSNMP_LARGE_FD_ZERO FD_ZERO
23 # define NETSNMP_LARGE_FD_ISSET FD_ISSET
25 # include <net-snmp/library/large_fd_set.h>
28 const std::array
<oid
, 11> SNMPAgent::snmpTrapOID
= { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
30 int SNMPAgent::setCounter64Value(netsnmp_request_info
* request
,
33 struct counter64 val64
;
34 val64
.high
= value
>> 32;
35 val64
.low
= value
& 0xffffffff;
36 snmp_set_var_typed_value(request
->requestvb
,
40 return SNMP_ERR_NOERROR
;
43 bool SNMPAgent::sendTrap(pdns::channel::Sender
<netsnmp_variable_list
, void(*)(netsnmp_variable_list
*)>& sender
,
44 netsnmp_variable_list
* varList
)
47 auto obj
= std::unique_ptr
<netsnmp_variable_list
, void(*)(netsnmp_variable_list
*)>(varList
, snmp_free_varbind
);
48 return sender
.send(std::move(obj
));
55 void SNMPAgent::handleTrapsEvent()
59 auto obj
= d_receiver
.receive(snmp_free_varbind
);
63 send_v2trap(obj
->get());
66 catch (const std::exception
& e
) {
70 void SNMPAgent::handleSNMPQueryEvent(int fd
)
72 netsnmp_large_fd_set fdset
;
73 netsnmp_large_fd_set_init(&fdset
, FD_SETSIZE
);
74 NETSNMP_LARGE_FD_ZERO(&fdset
);
75 NETSNMP_LARGE_FD_SET(fd
, &fdset
);
79 void SNMPAgent::handleTrapsCB(int /* fd */, FDMultiplexer::funcparam_t
& var
)
81 SNMPAgent
** agent
= boost::any_cast
<SNMPAgent
*>(&var
);
82 if (!agent
|| !*agent
)
83 throw std::runtime_error("Invalid value received in SNMP trap callback");
85 (*agent
)->handleTrapsEvent();
88 void SNMPAgent::handleSNMPQueryCB(int fd
, FDMultiplexer::funcparam_t
& var
)
90 SNMPAgent
** agent
= boost::any_cast
<SNMPAgent
*>(&var
);
91 if (!agent
|| !*agent
)
92 throw std::runtime_error("Invalid value received in SNMP trap callback");
94 (*agent
)->handleSNMPQueryEvent(fd
);
97 #endif /* HAVE_NET_SNMP */
99 void SNMPAgent::worker()
102 FDMultiplexer
* mplexer
= FDMultiplexer::getMultiplexerSilent();
103 if (mplexer
== nullptr) {
104 throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
108 string threadName
= "rec/snmp";
110 string threadName
= "dnsdist/SNMP";
112 setThreadName(threadName
);
116 netsnmp_large_fd_set fdset
;
117 struct timeval timeout
= { 0, 0 };
120 /* we want to be notified if a trap is waiting
122 mplexer
->addReadFD(d_receiver
.getDescriptor(), &handleTrapsCB
, this);
125 netsnmp_large_fd_set_init(&fdset
, FD_SETSIZE
);
126 NETSNMP_LARGE_FD_ZERO(&fdset
);
130 snmp_select_info2(&maxfd
, &fdset
, &timeout
, &block
);
132 for (int fd
= 0; fd
< maxfd
; fd
++) {
133 if (NETSNMP_LARGE_FD_ISSET(fd
, &fdset
)) {
134 mplexer
->addReadFD(fd
, &handleSNMPQueryCB
, this);
138 /* run updates now */
139 int res
= mplexer
->run(&now
, (timeout
.tv_sec
* 1000) + (timeout
.tv_usec
/ 1000));
141 /* we handle timeouts here, the rest has already been handled by callbacks */
147 for (int fd
= 0; fd
< maxfd
; fd
++) {
148 if (NETSNMP_LARGE_FD_ISSET(fd
, &fdset
)) {
150 mplexer
->removeReadFD(fd
);
152 catch(const FDMultiplexerException
& e
) {
153 /* we might get an exception when removing a closed file descriptor,
159 #endif /* HAVE_NET_SNMP */
162 SNMPAgent::SNMPAgent([[maybe_unused
]] const std::string
& name
, [[maybe_unused
]] const std::string
& daemonSocket
)
165 netsnmp_enable_subagent();
167 if (!daemonSocket
.empty()) {
168 netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID
,
169 NETSNMP_DS_AGENT_X_SOCKET
,
170 daemonSocket
.c_str());
172 /* no need to load any MIBS,
173 and it causes import errors if some modules are not present */
174 setenv("MIBS", "", 1);
176 init_agent(name
.c_str());
178 /* we use select() so don't use SIGALARM to handle alarms.
179 Note that we need to handle alarms for automatic reconnection
180 to the daemon to work.
182 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID
,
183 NETSNMP_DS_LIB_ALARM_DONT_USE_SIG
,
186 init_snmp(name
.c_str());
188 auto [sender
, receiver
] = pdns::channel::createObjectQueue
<netsnmp_variable_list
, void(*)(netsnmp_variable_list
*)>();
189 d_sender
= std::move(sender
);
190 d_receiver
= std::move(receiver
);
191 #endif /* HAVE_NET_SNMP */