]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/snmp-agent.cc
Merge pull request #9073 from pieterlexis/runtime-dirs-virtual-hosting
[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 oid SNMPAgent::snmpTrapOID[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
29 const size_t SNMPAgent::snmpTrapOIDLen = OID_LENGTH(SNMPAgent::snmpTrapOID);
30
31 int SNMPAgent::setCounter64Value(netsnmp_request_info* request,
32 uint64_t value)
33 {
34 struct counter64 val64;
35 val64.high = value >> 32;
36 val64.low = value & 0xffffffff;
37 snmp_set_var_typed_value(request->requestvb,
38 ASN_COUNTER64,
39 &val64,
40 sizeof(val64));
41 return SNMP_ERR_NOERROR;
42 }
43
44 bool SNMPAgent::sendTrap(int fd,
45 netsnmp_variable_list* varList)
46 {
47 ssize_t written = write(fd, &varList, sizeof(varList));
48
49 if (written != sizeof(varList)) {
50 snmp_free_varbind(varList);
51 return false;
52 }
53 return true;
54 }
55
56 void SNMPAgent::handleTrapsEvent()
57 {
58 netsnmp_variable_list* varList = nullptr;
59 ssize_t got = 0;
60
61 do {
62 got = read(d_trapPipe[0], &varList, sizeof(varList));
63
64 if (got == sizeof(varList)) {
65 send_v2trap(varList);
66 snmp_free_varbind(varList);
67 }
68 }
69 while (got > 0);
70 }
71
72 void SNMPAgent::handleSNMPQueryEvent(int fd)
73 {
74 netsnmp_large_fd_set fdset;
75 netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
76 NETSNMP_LARGE_FD_ZERO(&fdset);
77 NETSNMP_LARGE_FD_SET(fd, &fdset);
78 snmp_read2(&fdset);
79 }
80
81 void SNMPAgent::handleTrapsCB(int fd, FDMultiplexer::funcparam_t& var)
82 {
83 SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
84 if (!agent || !*agent)
85 throw std::runtime_error("Invalid value received in SNMP trap callback");
86
87 (*agent)->handleTrapsEvent();
88 }
89
90 void SNMPAgent::handleSNMPQueryCB(int fd, FDMultiplexer::funcparam_t& var)
91 {
92 SNMPAgent** agent = boost::any_cast<SNMPAgent*>(&var);
93 if (!agent || !*agent)
94 throw std::runtime_error("Invalid value received in SNMP trap callback");
95
96 (*agent)->handleSNMPQueryEvent(fd);
97 }
98
99 #endif /* HAVE_NET_SNMP */
100
101 void SNMPAgent::worker()
102 {
103 #ifdef HAVE_NET_SNMP
104 FDMultiplexer* mplexer = FDMultiplexer::getMultiplexerSilent();
105 if (mplexer == nullptr) {
106 throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
107 }
108
109 #ifdef RECURSOR
110 string threadName = "pdns-r/SNMP";
111 #else
112 string threadName = "dnsdist/SNMP";
113 #endif
114 setThreadName(threadName);
115
116 int maxfd = 0;
117 int block = 1;
118 netsnmp_large_fd_set fdset;
119 struct timeval timeout = { 0, 0 };
120 struct timeval now;
121
122 /* we want to be notified if a trap is waiting
123 to be sent */
124 mplexer->addReadFD(d_trapPipe[0], &handleTrapsCB, this);
125
126 while(true) {
127 netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
128 NETSNMP_LARGE_FD_ZERO(&fdset);
129
130 block = 1;
131 timeout = { 0, 0 };
132 snmp_select_info2(&maxfd, &fdset, &timeout, &block);
133
134 for (int fd = 0; fd < maxfd; fd++) {
135 if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
136 mplexer->addReadFD(fd, &handleSNMPQueryCB, this);
137 }
138 }
139
140 /* run updates now */
141 int res = mplexer->run(&now, (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000));
142
143 /* we handle timeouts here, the rest has already been handled by callbacks */
144 if (res == 0) {
145 snmp_timeout();
146 run_alarms();
147 }
148
149 for (int fd = 0; fd < maxfd; fd++) {
150 if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
151 try {
152 mplexer->removeReadFD(fd);
153 }
154 catch(const FDMultiplexerException& e) {
155 /* we might get an exception when removing a closed file descriptor,
156 just ignore it */
157 }
158 }
159 }
160 }
161 #endif /* HAVE_NET_SNMP */
162 }
163
164 SNMPAgent::SNMPAgent(const std::string& name, const std::string& masterSocket)
165 {
166 #ifdef HAVE_NET_SNMP
167 netsnmp_enable_subagent();
168 snmp_disable_log();
169 if (!masterSocket.empty()) {
170 netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
171 NETSNMP_DS_AGENT_X_SOCKET,
172 masterSocket.c_str());
173 }
174 /* no need to load any MIBS,
175 and it causes import errors if some modules are not present */
176 setenv("MIBS", "", 1);
177
178 init_agent(name.c_str());
179
180 /* we use select() so don't use SIGALARM to handle alarms.
181 Note that we need to handle alarms for automatic reconnection
182 to the master to work.
183 */
184 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
185 NETSNMP_DS_LIB_ALARM_DONT_USE_SIG,
186 1);
187
188 init_snmp(name.c_str());
189
190 if (pipe(d_trapPipe) < 0)
191 unixDie("Creating pipe");
192
193 if (!setNonBlocking(d_trapPipe[0])) {
194 close(d_trapPipe[0]);
195 close(d_trapPipe[1]);
196 unixDie("Setting pipe non-blocking");
197 }
198
199 if (!setNonBlocking(d_trapPipe[1])) {
200 close(d_trapPipe[0]);
201 close(d_trapPipe[1]);
202 unixDie("Setting pipe non-blocking");
203 }
204
205 #endif /* HAVE_NET_SNMP */
206 }