]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/snmp-agent.cc
rec: ensure correct service user on debian
[thirdparty/pdns.git] / pdns / snmp-agent.cc
CommitLineData
9f4eb5cc
RG
1#include "snmp-agent.hh"
2#include "misc.hh"
519f5484 3#include "threadname.hh"
77c9bc9a 4#ifdef RECURSOR
c390b2da 5#include "logger.hh"
77c9bc9a
PL
6#else
7#include "dolog.hh"
8#endif
9f4eb5cc
RG
9
10#ifdef HAVE_NET_SNMP
11
0e663c3b
RG
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
9f4eb5cc
RG
28const oid SNMPAgent::snmpTrapOID[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
29const size_t SNMPAgent::snmpTrapOIDLen = OID_LENGTH(SNMPAgent::snmpTrapOID);
30
31int 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
44bool 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
0e663c3b 56void SNMPAgent::handleTrapsEvent()
9f4eb5cc
RG
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}
0e663c3b
RG
71
72void 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
81void 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
90void 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
9f4eb5cc
RG
99#endif /* HAVE_NET_SNMP */
100
101void SNMPAgent::worker()
102{
103#ifdef HAVE_NET_SNMP
4226cfd0 104 FDMultiplexer* mplexer = FDMultiplexer::getMultiplexerSilent();
0e663c3b
RG
105 if (mplexer == nullptr) {
106 throw std::runtime_error("No FD multiplexer found for the SNMP agent!");
107 }
108
77c9bc9a 109#ifdef RECURSOR
c390b2da 110 string threadName = "pdns-r/SNMP";
77c9bc9a
PL
111#else
112 string threadName = "dnsdist/SNMP";
113#endif
519f5484 114 setThreadName(threadName);
c390b2da 115
0e663c3b 116 int maxfd = 0;
9f4eb5cc 117 int block = 1;
0e663c3b 118 netsnmp_large_fd_set fdset;
9f4eb5cc 119 struct timeval timeout = { 0, 0 };
0e663c3b
RG
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);
9f4eb5cc
RG
125
126 while(true) {
0e663c3b
RG
127 netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
128 NETSNMP_LARGE_FD_ZERO(&fdset);
9f4eb5cc 129
0576fe19
RG
130 block = 1;
131 timeout = { 0, 0 };
0e663c3b 132 snmp_select_info2(&maxfd, &fdset, &timeout, &block);
9f4eb5cc 133
0e663c3b
RG
134 for (int fd = 0; fd < maxfd; fd++) {
135 if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
136 mplexer->addReadFD(fd, &handleSNMPQueryCB, this);
9f4eb5cc
RG
137 }
138 }
0e663c3b
RG
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) {
9f4eb5cc 145 snmp_timeout();
0576fe19 146 run_alarms();
9f4eb5cc 147 }
0e663c3b
RG
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 }
9f4eb5cc
RG
160 }
161#endif /* HAVE_NET_SNMP */
162}
163
164SNMPAgent::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());
0576fe19
RG
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
9f4eb5cc
RG
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}