]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/snmp-agent.cc
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / snmp-agent.cc
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
10 #ifdef HAVE_NET_SNMP
11
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
24 #else
25 # include <net-snmp/library/large_fd_set.h>
26 #endif
27
28 const std::array<oid, 11> SNMPAgent::snmpTrapOID = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
29
30 int SNMPAgent::setCounter64Value(netsnmp_request_info* request,
31 uint64_t value)
32 {
33 struct counter64 val64;
34 val64.high = value >> 32;
35 val64.low = value & 0xffffffff;
36 snmp_set_var_typed_value(request->requestvb,
37 ASN_COUNTER64,
38 &val64,
39 sizeof(val64));
40 return SNMP_ERR_NOERROR;
41 }
42
43 bool SNMPAgent::sendTrap(pdns::channel::Sender<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>& sender,
44 netsnmp_variable_list* varList)
45 {
46 try {
47 auto obj = std::unique_ptr<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>(varList, snmp_free_varbind);
48 return sender.send(std::move(obj));
49 }
50 catch (...) {
51 return false;
52 }
53 }
54
55 void SNMPAgent::handleTrapsEvent()
56 {
57 try {
58 while (true) {
59 auto obj = d_receiver.receive(snmp_free_varbind);
60 if (!obj) {
61 break;
62 }
63 send_v2trap(obj->get());
64 }
65 }
66 catch (const std::exception& e) {
67 }
68 }
69
70 void SNMPAgent::handleSNMPQueryEvent(int fd)
71 {
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);
76 snmp_read2(&fdset);
77 }
78
79 void SNMPAgent::handleTrapsCB(int /* fd */, FDMultiplexer::funcparam_t& var)
80 {
81 SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
82 if (!agent || !*agent)
83 throw std::runtime_error("Invalid value received in SNMP trap callback");
84
85 (*agent)->handleTrapsEvent();
86 }
87
88 void SNMPAgent::handleSNMPQueryCB(int fd, FDMultiplexer::funcparam_t& var)
89 {
90 SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
91 if (!agent || !*agent)
92 throw std::runtime_error("Invalid value received in SNMP trap callback");
93
94 (*agent)->handleSNMPQueryEvent(fd);
95 }
96
97 #endif /* HAVE_NET_SNMP */
98
99 void SNMPAgent::worker()
100 {
101 #ifdef HAVE_NET_SNMP
102 FDMultiplexer* mplexer = FDMultiplexer::getMultiplexerSilent();
103 if (mplexer == nullptr) {
104 throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
105 }
106
107 #ifdef RECURSOR
108 string threadName = "rec/snmp";
109 #else
110 string threadName = "dnsdist/SNMP";
111 #endif
112 setThreadName(threadName);
113
114 int maxfd = 0;
115 int block = 1;
116 netsnmp_large_fd_set fdset;
117 struct timeval timeout = { 0, 0 };
118 struct timeval now;
119
120 /* we want to be notified if a trap is waiting
121 to be sent */
122 mplexer->addReadFD(d_receiver.getDescriptor(), &handleTrapsCB, this);
123
124 while(true) {
125 netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
126 NETSNMP_LARGE_FD_ZERO(&fdset);
127
128 block = 1;
129 timeout = { 0, 0 };
130 snmp_select_info2(&maxfd, &fdset, &timeout, &block);
131
132 for (int fd = 0; fd < maxfd; fd++) {
133 if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
134 mplexer->addReadFD(fd, &handleSNMPQueryCB, this);
135 }
136 }
137
138 /* run updates now */
139 int res = mplexer->run(&now, (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000));
140
141 /* we handle timeouts here, the rest has already been handled by callbacks */
142 if (res == 0) {
143 snmp_timeout();
144 run_alarms();
145 }
146
147 for (int fd = 0; fd < maxfd; fd++) {
148 if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
149 try {
150 mplexer->removeReadFD(fd);
151 }
152 catch(const FDMultiplexerException& e) {
153 /* we might get an exception when removing a closed file descriptor,
154 just ignore it */
155 }
156 }
157 }
158 }
159 #endif /* HAVE_NET_SNMP */
160 }
161
162 SNMPAgent::SNMPAgent([[maybe_unused]] const std::string& name, [[maybe_unused]] const std::string& daemonSocket)
163 {
164 #ifdef HAVE_NET_SNMP
165 netsnmp_enable_subagent();
166 snmp_disable_log();
167 if (!daemonSocket.empty()) {
168 netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
169 NETSNMP_DS_AGENT_X_SOCKET,
170 daemonSocket.c_str());
171 }
172 /* no need to load any MIBS,
173 and it causes import errors if some modules are not present */
174 setenv("MIBS", "", 1);
175
176 init_agent(name.c_str());
177
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.
181 */
182 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
183 NETSNMP_DS_LIB_ALARM_DONT_USE_SIG,
184 1);
185
186 init_snmp(name.c_str());
187
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 */
192 }