]> git.proxmox.com Git - mirror_frr.git/blob - lib/agentx.c
lib: Fix double set of event pointer to NULL
[mirror_frr.git] / lib / agentx.c
1 /* SNMP support
2 * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #ifdef SNMP_AGENTX
24 #include <net-snmp/net-snmp-config.h>
25 #include <net-snmp/net-snmp-includes.h>
26 #include <net-snmp/agent/net-snmp-agent-includes.h>
27 #include <net-snmp/agent/snmp_vars.h>
28
29 #include "command.h"
30 #include "smux.h"
31 #include "memory.h"
32 #include "linklist.h"
33 #include "lib/version.h"
34 #include "lib_errors.h"
35 #include "xref.h"
36
37 XREF_SETUP();
38
39 DEFINE_HOOK(agentx_enabled, (), ());
40
41 static int agentx_enabled = 0;
42
43 static struct thread_master *agentx_tm;
44 static struct thread *timeout_thr = NULL;
45 static struct list *events = NULL;
46
47 static void agentx_events_update(void);
48
49 static void agentx_timeout(struct thread *t)
50 {
51 snmp_timeout();
52 run_alarms();
53 netsnmp_check_outstanding_agent_requests();
54 agentx_events_update();
55 }
56
57 static void agentx_read(struct thread *t)
58 {
59 fd_set fds;
60 int flags, new_flags = 0;
61 int nonblock = false;
62 struct listnode *ln = THREAD_ARG(t);
63 struct thread **thr = listgetdata(ln);
64 XFREE(MTYPE_TMP, thr);
65 list_delete_node(events, ln);
66
67 /* fix for non blocking socket */
68 flags = fcntl(THREAD_FD(t), F_GETFL, 0);
69 if (-1 == flags) {
70 flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)",
71 strerror(errno), errno);
72 return;
73 }
74
75 if (flags & O_NONBLOCK)
76 nonblock = true;
77 else
78 new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK);
79
80 if (new_flags == -1)
81 flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)",
82 strerror(errno), errno);
83
84 FD_ZERO(&fds);
85 FD_SET(THREAD_FD(t), &fds);
86 snmp_read(&fds);
87
88 /* Reset the flag */
89 if (!nonblock) {
90 new_flags = fcntl(THREAD_FD(t), F_SETFL, flags);
91
92 if (new_flags == -1)
93 flog_err(
94 EC_LIB_SYSTEM_CALL,
95 "Failed to set snmp fd back to original settings: %s(%d)",
96 strerror(errno), errno);
97 }
98
99 netsnmp_check_outstanding_agent_requests();
100 agentx_events_update();
101 }
102
103 static void agentx_events_update(void)
104 {
105 int maxfd = 0;
106 int block = 1;
107 struct timeval timeout = {.tv_sec = 0, .tv_usec = 0};
108 fd_set fds;
109 struct listnode *ln;
110 struct thread **thr;
111 int fd, thr_fd;
112
113 thread_cancel(&timeout_thr);
114
115 FD_ZERO(&fds);
116 snmp_select_info(&maxfd, &fds, &timeout, &block);
117
118 if (!block) {
119 thread_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout,
120 &timeout_thr);
121 }
122
123 ln = listhead(events);
124 thr = ln ? listgetdata(ln) : NULL;
125 thr_fd = thr ? THREAD_FD(*thr) : -1;
126
127 /* "two-pointer" / two-list simultaneous iteration
128 * ln/thr/thr_fd point to the next existing event listener to hit while
129 * fd counts to catch up */
130 for (fd = 0; fd < maxfd; fd++) {
131 /* caught up */
132 if (thr_fd == fd) {
133 struct listnode *nextln = listnextnode(ln);
134 if (!FD_ISSET(fd, &fds)) {
135 thread_cancel(thr);
136 XFREE(MTYPE_TMP, thr);
137 list_delete_node(events, ln);
138 }
139 ln = nextln;
140 thr = ln ? listgetdata(ln) : NULL;
141 thr_fd = thr ? THREAD_FD(*thr) : -1;
142 }
143 /* need listener, but haven't hit one where it would be */
144 else if (FD_ISSET(fd, &fds)) {
145 struct listnode *newln;
146 thr = XCALLOC(MTYPE_TMP, sizeof(struct thread *));
147
148 newln = listnode_add_before(events, ln, thr);
149 thread_add_read(agentx_tm, agentx_read, newln, fd, thr);
150 }
151 }
152
153 /* leftover event listeners at this point have fd > maxfd, delete them
154 */
155 while (ln) {
156 struct listnode *nextln = listnextnode(ln);
157 thr = listgetdata(ln);
158 thread_cancel(thr);
159 XFREE(MTYPE_TMP, thr);
160 list_delete_node(events, ln);
161 ln = nextln;
162 }
163 }
164
165 /* AgentX node. */
166 static int config_write_agentx(struct vty *vty);
167 static struct cmd_node agentx_node = {
168 .name = "smux",
169 .node = SMUX_NODE,
170 .prompt = "",
171 .config_write = config_write_agentx,
172 };
173
174 /* Logging NetSNMP messages */
175 static int agentx_log_callback(int major, int minor, void *serverarg,
176 void *clientarg)
177 {
178 struct snmp_log_message *slm = (struct snmp_log_message *)serverarg;
179 char *msg = XSTRDUP(MTYPE_TMP, slm->msg);
180 if (msg)
181 msg[strlen(msg) - 1] = '\0';
182 switch (slm->priority) {
183 case LOG_EMERG:
184 flog_err(EC_LIB_SNMP, "snmp[emerg]: %s", msg ? msg : slm->msg);
185 break;
186 case LOG_ALERT:
187 flog_err(EC_LIB_SNMP, "snmp[alert]: %s", msg ? msg : slm->msg);
188 break;
189 case LOG_CRIT:
190 flog_err(EC_LIB_SNMP, "snmp[crit]: %s", msg ? msg : slm->msg);
191 break;
192 case LOG_ERR:
193 flog_err(EC_LIB_SNMP, "snmp[err]: %s", msg ? msg : slm->msg);
194 break;
195 case LOG_WARNING:
196 flog_warn(EC_LIB_SNMP, "snmp[warning]: %s",
197 msg ? msg : slm->msg);
198 break;
199 case LOG_NOTICE:
200 zlog_notice("snmp[notice]: %s", msg ? msg : slm->msg);
201 break;
202 case LOG_INFO:
203 zlog_info("snmp[info]: %s", msg ? msg : slm->msg);
204 break;
205 case LOG_DEBUG:
206 zlog_debug("snmp[debug]: %s", msg ? msg : slm->msg);
207 break;
208 }
209 XFREE(MTYPE_TMP, msg);
210 return SNMP_ERR_NOERROR;
211 }
212
213 static int config_write_agentx(struct vty *vty)
214 {
215 if (agentx_enabled)
216 vty_out(vty, "agentx\n");
217 return 1;
218 }
219
220 DEFUN (agentx_enable,
221 agentx_enable_cmd,
222 "agentx",
223 "SNMP AgentX protocol settings\n")
224 {
225 if (!agentx_enabled) {
226 init_snmp(FRR_SMUX_NAME);
227 events = list_new();
228 agentx_events_update();
229 agentx_enabled = 1;
230 hook_call(agentx_enabled);
231 }
232
233 return CMD_SUCCESS;
234 }
235
236 DEFUN (no_agentx,
237 no_agentx_cmd,
238 "no agentx",
239 NO_STR
240 "SNMP AgentX protocol settings\n")
241 {
242 if (!agentx_enabled)
243 return CMD_SUCCESS;
244 vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n");
245 return CMD_WARNING_CONFIG_FAILED;
246 }
247
248 int smux_enabled(void)
249 {
250 return agentx_enabled;
251 }
252
253 void smux_init(struct thread_master *tm)
254 {
255 agentx_tm = tm;
256
257 netsnmp_enable_subagent();
258 snmp_disable_log();
259 snmp_enable_calllog();
260 snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING,
261 agentx_log_callback, NULL);
262 init_agent(FRR_SMUX_NAME);
263
264 install_node(&agentx_node);
265 install_element(CONFIG_NODE, &agentx_enable_cmd);
266 install_element(CONFIG_NODE, &no_agentx_cmd);
267 }
268
269 void smux_agentx_enable(void)
270 {
271 if (!agentx_enabled) {
272 init_snmp(FRR_SMUX_NAME);
273 events = list_new();
274 agentx_events_update();
275 agentx_enabled = 1;
276 }
277 }
278
279 void smux_register_mib(const char *descr, struct variable *var, size_t width,
280 int num, oid name[], size_t namelen)
281 {
282 register_mib(descr, var, width, num, name, namelen);
283 }
284
285 void smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
286 size_t enamelen, const oid *name, size_t namelen,
287 const oid *iname, size_t inamelen,
288 const struct trap_object *trapobj, size_t trapobjlen,
289 uint8_t sptrap)
290 {
291 struct index_oid trap_index[1];
292
293 /* copy the single index into the multi-index format */
294 oid_copy(trap_index[0].indexname, iname, inamelen);
295 trap_index[0].indexlen = inamelen;
296
297 smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen,
298 trap_index, array_size(trap_index), trapobj,
299 trapobjlen, sptrap);
300 }
301
302 int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename,
303 size_t enamelen, const oid *name, size_t namelen,
304 struct index_oid *iname, size_t index_len,
305 const struct trap_object *trapobj, size_t trapobjlen,
306 uint8_t sptrap)
307 {
308 oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
309 size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid);
310 oid notification_oid[MAX_OID_LEN];
311 size_t notification_oid_len;
312 unsigned int i;
313
314 netsnmp_variable_list *notification_vars = NULL;
315 if (!agentx_enabled)
316 return 0;
317
318 /* snmpTrapOID */
319 oid_copy(notification_oid, ename, enamelen);
320 notification_oid[enamelen] = sptrap;
321 notification_oid_len = enamelen + 1;
322 snmp_varlist_add_variable(&notification_vars, objid_snmptrap,
323 objid_snmptrap_len, ASN_OBJECT_ID,
324 (uint8_t *)notification_oid,
325 notification_oid_len * sizeof(oid));
326
327 /* Provided bindings */
328 for (i = 0; i < trapobjlen; i++) {
329 unsigned int j;
330 oid oid[MAX_OID_LEN];
331 size_t oid_len, onamelen;
332 uint8_t *val;
333 size_t val_len;
334 WriteMethod *wm = NULL;
335 struct variable cvp;
336 unsigned int iindex;
337 /*
338 * this allows the behaviour of smux_trap with a singe index
339 * for all objects to be maintained whilst allowing traps which
340 * have different indices per object to be supported
341 */
342 iindex = (index_len == 1) ? 0 : i;
343
344 /* Make OID. */
345 if (trapobj[i].namelen > 0) {
346 /* Columnar object */
347 onamelen = trapobj[i].namelen;
348 oid_copy(oid, name, namelen);
349 oid_copy(oid + namelen, trapobj[i].name, onamelen);
350 oid_copy(oid + namelen + onamelen,
351 iname[iindex].indexname,
352 iname[iindex].indexlen);
353 oid_len = namelen + onamelen + iname[iindex].indexlen;
354 } else {
355 /* Scalar object */
356 onamelen = trapobj[i].namelen * (-1);
357 oid_copy(oid, name, namelen);
358 oid_copy(oid + namelen, trapobj[i].name, onamelen);
359 oid[onamelen + namelen] = 0;
360 oid_len = namelen + onamelen + 1;
361 }
362
363 /* Locate the appropriate function and type in the MIB registry.
364 */
365 for (j = 0; j < vp_len; j++) {
366 if (oid_compare(trapobj[i].name, onamelen, vp[j].name,
367 vp[j].namelen)
368 != 0)
369 continue;
370 /* We found the appropriate variable in the MIB
371 * registry. */
372 oid_copy(cvp.name, name, namelen);
373 oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen);
374 cvp.namelen = namelen + vp[j].namelen;
375 cvp.type = vp[j].type;
376 cvp.magic = vp[j].magic;
377 cvp.acl = vp[j].acl;
378 cvp.findVar = vp[j].findVar;
379
380 /* Grab the result. */
381 val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len,
382 &wm);
383 if (!val)
384 break;
385 snmp_varlist_add_variable(&notification_vars, oid,
386 oid_len, vp[j].type, val,
387 val_len);
388 break;
389 }
390 }
391
392
393 send_v2trap(notification_vars);
394 snmp_free_varbind(notification_vars);
395 agentx_events_update();
396 return 1;
397 }
398
399 void smux_events_update(void)
400 {
401 agentx_events_update();
402 }
403
404 #endif /* SNMP_AGENTX */