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