]> git.proxmox.com Git - mirror_frr.git/blame - lib/agentx.c
Merge pull request #10621 from donaldsharp/cov_fun
[mirror_frr.git] / lib / agentx.c
CommitLineData
d6be5fb9
VB
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 *
896014f4
DL
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
d6be5fb9
VB
19 */
20
21#include <zebra.h>
22
5986b66b 23#ifdef SNMP_AGENTX
d6be5fb9
VB
24#include <net-snmp/net-snmp-config.h>
25#include <net-snmp/net-snmp-includes.h>
56e2c5e8
DL
26#include <net-snmp/agent/net-snmp-agent-includes.h>
27#include <net-snmp/agent/snmp_vars.h>
d6be5fb9
VB
28
29#include "command.h"
30#include "smux.h"
d11f748b 31#include "memory.h"
56e2c5e8 32#include "linklist.h"
09781197 33#include "lib/version.h"
220d7368 34#include "lib_errors.h"
b2fa8c0f
DL
35#include "xref.h"
36
80413c20 37XREF_SETUP();
d6be5fb9 38
8451921b 39DEFINE_HOOK(agentx_enabled, (), ());
ec48460e 40
56e2c5e8
DL
41static int agentx_enabled = 0;
42
43static struct thread_master *agentx_tm;
44static struct thread *timeout_thr = NULL;
45static struct list *events = NULL;
46
47static void agentx_events_update(void);
48
d62a17ae 49static int agentx_timeout(struct thread *t)
56e2c5e8 50{
d62a17ae 51 timeout_thr = NULL;
56e2c5e8 52
d62a17ae 53 snmp_timeout();
54 run_alarms();
55 netsnmp_check_outstanding_agent_requests();
56 agentx_events_update();
57 return 0;
56e2c5e8
DL
58}
59
d62a17ae 60static int agentx_read(struct thread *t)
56e2c5e8 61{
d62a17ae 62 fd_set fds;
19d95d40 63 int flags, new_flags = 0;
3b717261 64 int nonblock = false;
d62a17ae 65 struct listnode *ln = THREAD_ARG(t);
7640e3c6
IR
66 struct thread **thr = listgetdata(ln);
67 XFREE(MTYPE_TMP, thr);
d62a17ae 68 list_delete_node(events, ln);
56e2c5e8 69
3b717261 70 /* fix for non blocking socket */
71 flags = fcntl(THREAD_FD(t), F_GETFL, 0);
19d95d40
DS
72 if (-1 == flags) {
73 flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)",
74 strerror(errno), errno);
3b717261 75 return -1;
19d95d40 76 }
3b717261 77
78 if (flags & O_NONBLOCK)
79 nonblock = true;
80 else
19d95d40
DS
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);
3b717261 86
d62a17ae 87 FD_ZERO(&fds);
88 FD_SET(THREAD_FD(t), &fds);
89 snmp_read(&fds);
56e2c5e8 90
3b717261 91 /* Reset the flag */
19d95d40
DS
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 }
3b717261 101
d62a17ae 102 netsnmp_check_outstanding_agent_requests();
103 agentx_events_update();
104 return 0;
56e2c5e8
DL
105}
106
d62a17ae 107static void agentx_events_update(void)
56e2c5e8 108{
d62a17ae 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;
7640e3c6 114 struct thread **thr;
d62a17ae 115 int fd, thr_fd;
116
50478845 117 thread_cancel(&timeout_thr);
d62a17ae 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;
7640e3c6 130 thr_fd = thr ? THREAD_FD(*thr) : -1;
d62a17ae 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)) {
7640e3c6
IR
140 thread_cancel(thr);
141 XFREE(MTYPE_TMP, thr);
d62a17ae 142 list_delete_node(events, ln);
143 }
144 ln = nextln;
145 thr = ln ? listgetdata(ln) : NULL;
7640e3c6 146 thr_fd = thr ? THREAD_FD(*thr) : -1;
d62a17ae 147 }
148 /* need listener, but haven't hit one where it would be */
149 else if (FD_ISSET(fd, &fds)) {
150 struct listnode *newln;
7640e3c6
IR
151 thr = XCALLOC(MTYPE_TMP, sizeof(struct thread *));
152 thread_add_read(agentx_tm, agentx_read, NULL, fd, thr);
d62a17ae 153 newln = listnode_add_before(events, ln, thr);
7640e3c6 154 (*thr)->arg = newln;
d62a17ae 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);
b3d6bc6e 162 thr = listgetdata(ln);
7640e3c6
IR
163 thread_cancel(thr);
164 XFREE(MTYPE_TMP, thr);
d62a17ae 165 list_delete_node(events, ln);
166 ln = nextln;
167 }
56e2c5e8 168}
d6be5fb9
VB
169
170/* AgentX node. */
612c2c15 171static int config_write_agentx(struct vty *vty);
62b346ee 172static struct cmd_node agentx_node = {
f4b8291f 173 .name = "smux",
62b346ee
DL
174 .node = SMUX_NODE,
175 .prompt = "",
612c2c15 176 .config_write = config_write_agentx,
62b346ee 177};
d6be5fb9
VB
178
179/* Logging NetSNMP messages */
d62a17ae 180static int agentx_log_callback(int major, int minor, void *serverarg,
181 void *clientarg)
d6be5fb9 182{
d62a17ae 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:
1c50c1c0 189 flog_err(EC_LIB_SNMP, "snmp[emerg]: %s", msg ? msg : slm->msg);
d62a17ae 190 break;
191 case LOG_ALERT:
1c50c1c0 192 flog_err(EC_LIB_SNMP, "snmp[alert]: %s", msg ? msg : slm->msg);
d62a17ae 193 break;
194 case LOG_CRIT:
1c50c1c0 195 flog_err(EC_LIB_SNMP, "snmp[crit]: %s", msg ? msg : slm->msg);
d62a17ae 196 break;
197 case LOG_ERR:
1c50c1c0 198 flog_err(EC_LIB_SNMP, "snmp[err]: %s", msg ? msg : slm->msg);
d62a17ae 199 break;
200 case LOG_WARNING:
450971aa 201 flog_warn(EC_LIB_SNMP, "snmp[warning]: %s",
ade6974d 202 msg ? msg : slm->msg);
d62a17ae 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;
d6be5fb9
VB
216}
217
d62a17ae 218static int config_write_agentx(struct vty *vty)
d6be5fb9 219{
d62a17ae 220 if (agentx_enabled)
221 vty_out(vty, "agentx\n");
222 return 1;
d6be5fb9
VB
223}
224
225DEFUN (agentx_enable,
226 agentx_enable_cmd,
227 "agentx",
007b0667 228 "SNMP AgentX protocol settings\n")
d6be5fb9 229{
d62a17ae 230 if (!agentx_enabled) {
231 init_snmp(FRR_SMUX_NAME);
232 events = list_new();
233 agentx_events_update();
234 agentx_enabled = 1;
ec48460e 235 hook_call(agentx_enabled);
d62a17ae 236 }
5fedee18 237
d62a17ae 238 return CMD_SUCCESS;
d6be5fb9
VB
239}
240
241DEFUN (no_agentx,
242 no_agentx_cmd,
243 "no agentx",
244 NO_STR
007b0667 245 "SNMP AgentX protocol settings\n")
d6be5fb9 246{
d62a17ae 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;
d6be5fb9
VB
251}
252
1ee746d9 253int smux_enabled(void)
254{
255 return agentx_enabled;
256}
257
d62a17ae 258void smux_init(struct thread_master *tm)
d6be5fb9 259{
d62a17ae 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
612c2c15 269 install_node(&agentx_node);
d62a17ae 270 install_element(CONFIG_NODE, &agentx_enable_cmd);
271 install_element(CONFIG_NODE, &no_agentx_cmd);
d6be5fb9
VB
272}
273
ec48460e
KS
274void 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
d62a17ae 284void smux_register_mib(const char *descr, struct variable *var, size_t width,
285 int num, oid name[], size_t namelen)
d6be5fb9 286{
d62a17ae 287 register_mib(descr, var, width, num, name, namelen);
d6be5fb9
VB
288}
289
97e21b4b
DS
290void 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)
4eb8c74f
PR
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
97e21b4b
DS
302 smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen,
303 trap_index, array_size(trap_index), trapobj,
304 trapobjlen, sptrap);
4eb8c74f
PR
305}
306
307int 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)
d6be5fb9 312{
d62a17ae 313 oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
0d6f7fd6 314 size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid);
d62a17ae 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,
d7c0a89a 329 (uint8_t *)notification_oid,
d62a17ae 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;
d7c0a89a 337 uint8_t *val;
d62a17ae 338 size_t val_len;
339 WriteMethod *wm = NULL;
340 struct variable cvp;
4eb8c74f
PR
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;
d62a17ae 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);
4eb8c74f
PR
355 oid_copy(oid + namelen + onamelen,
356 iname[iindex].indexname,
357 iname[iindex].indexlen);
358 oid_len = namelen + onamelen + iname[iindex].indexlen;
d62a17ae 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;
4eb8c74f 384
d62a17ae 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 }
b7c0d065 395 }
b7c0d065
VB
396
397
d62a17ae 398 send_v2trap(notification_vars);
399 snmp_free_varbind(notification_vars);
400 agentx_events_update();
401 return 1;
d6be5fb9
VB
402}
403
1ee746d9 404void smux_events_update(void)
405{
406 agentx_events_update();
407}
408
5986b66b 409#endif /* SNMP_AGENTX */