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