1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx>
9 #include <net-snmp/net-snmp-config.h>
10 #include <net-snmp/net-snmp-includes.h>
11 #include <net-snmp/agent/net-snmp-agent-includes.h>
12 #include <net-snmp/agent/snmp_vars.h>
18 #include "lib/version.h"
19 #include "lib_errors.h"
26 DEFINE_HOOK(agentx_enabled
, (), ());
28 static bool agentx_enabled
= false;
30 static struct event_loop
*agentx_tm
;
31 static struct event
*timeout_thr
= NULL
;
32 static struct list
*events
= NULL
;
34 static void agentx_events_update(void);
36 static void agentx_timeout(struct event
*t
)
40 netsnmp_check_outstanding_agent_requests();
41 agentx_events_update();
44 static void agentx_read(struct event
*t
)
47 int flags
, new_flags
= 0;
49 struct listnode
*ln
= EVENT_ARG(t
);
50 struct event
**thr
= listgetdata(ln
);
51 XFREE(MTYPE_TMP
, thr
);
52 list_delete_node(events
, ln
);
54 /* fix for non blocking socket */
55 flags
= fcntl(EVENT_FD(t
), F_GETFL
, 0);
57 flog_err(EC_LIB_SYSTEM_CALL
, "Failed to get FD settings fcntl: %s(%d)",
58 strerror(errno
), errno
);
62 if (flags
& O_NONBLOCK
)
65 new_flags
= fcntl(EVENT_FD(t
), F_SETFL
, flags
| O_NONBLOCK
);
68 flog_err(EC_LIB_SYSTEM_CALL
, "Failed to set snmp fd non blocking: %s(%d)",
69 strerror(errno
), errno
);
72 FD_SET(EVENT_FD(t
), &fds
);
77 new_flags
= fcntl(EVENT_FD(t
), F_SETFL
, flags
);
82 "Failed to set snmp fd back to original settings: %s(%d)",
83 strerror(errno
), errno
);
86 netsnmp_check_outstanding_agent_requests();
87 agentx_events_update();
90 static void agentx_events_update(void)
94 struct timeval timeout
= {.tv_sec
= 0, .tv_usec
= 0};
100 event_cancel(&timeout_thr
);
103 snmp_select_info(&maxfd
, &fds
, &timeout
, &block
);
106 event_add_timer_tv(agentx_tm
, agentx_timeout
, NULL
, &timeout
,
110 ln
= listhead(events
);
111 thr
= ln
? listgetdata(ln
) : NULL
;
112 thr_fd
= thr
? EVENT_FD(*thr
) : -1;
114 /* "two-pointer" / two-list simultaneous iteration
115 * ln/thr/thr_fd point to the next existing event listener to hit while
116 * fd counts to catch up */
117 for (fd
= 0; fd
< maxfd
; fd
++) {
120 struct listnode
*nextln
= listnextnode(ln
);
121 if (!FD_ISSET(fd
, &fds
)) {
123 XFREE(MTYPE_TMP
, thr
);
124 list_delete_node(events
, ln
);
127 thr
= ln
? listgetdata(ln
) : NULL
;
128 thr_fd
= thr
? EVENT_FD(*thr
) : -1;
130 /* need listener, but haven't hit one where it would be */
131 else if (FD_ISSET(fd
, &fds
)) {
132 struct listnode
*newln
;
134 thr
= XCALLOC(MTYPE_TMP
, sizeof(struct event
*));
135 newln
= listnode_add_before(events
, ln
, thr
);
136 event_add_read(agentx_tm
, agentx_read
, newln
, fd
, thr
);
140 /* leftover event listeners at this point have fd > maxfd, delete them
143 struct listnode
*nextln
= listnextnode(ln
);
144 thr
= listgetdata(ln
);
146 XFREE(MTYPE_TMP
, thr
);
147 list_delete_node(events
, ln
);
153 static int config_write_agentx(struct vty
*vty
);
154 static struct cmd_node agentx_node
= {
158 .config_write
= config_write_agentx
,
161 /* Logging NetSNMP messages */
162 static int agentx_log_callback(int major
, int minor
, void *serverarg
,
165 struct snmp_log_message
*slm
= (struct snmp_log_message
*)serverarg
;
166 char *msg
= XSTRDUP(MTYPE_TMP
, slm
->msg
);
168 msg
[strlen(msg
) - 1] = '\0';
169 switch (slm
->priority
) {
171 flog_err(EC_LIB_SNMP
, "snmp[emerg]: %s", msg
? msg
: slm
->msg
);
174 flog_err(EC_LIB_SNMP
, "snmp[alert]: %s", msg
? msg
: slm
->msg
);
177 flog_err(EC_LIB_SNMP
, "snmp[crit]: %s", msg
? msg
: slm
->msg
);
180 flog_err(EC_LIB_SNMP
, "snmp[err]: %s", msg
? msg
: slm
->msg
);
183 flog_warn(EC_LIB_SNMP
, "snmp[warning]: %s",
184 msg
? msg
: slm
->msg
);
187 zlog_notice("snmp[notice]: %s", msg
? msg
: slm
->msg
);
190 zlog_info("snmp[info]: %s", msg
? msg
: slm
->msg
);
193 zlog_debug("snmp[debug]: %s", msg
? msg
: slm
->msg
);
196 XFREE(MTYPE_TMP
, msg
);
197 return SNMP_ERR_NOERROR
;
200 static int config_write_agentx(struct vty
*vty
)
203 vty_out(vty
, "agentx\n");
207 DEFUN (agentx_enable
,
210 "SNMP AgentX protocol settings\n")
212 if (!agentx_enabled
) {
213 init_snmp(FRR_SMUX_NAME
);
215 agentx_events_update();
216 agentx_enabled
= true;
217 hook_call(agentx_enabled
);
227 "SNMP AgentX protocol settings\n")
231 vty_out(vty
, "SNMP AgentX support cannot be disabled once enabled\n");
232 return CMD_WARNING_CONFIG_FAILED
;
235 static int smux_disable(void)
237 agentx_enabled
= false;
242 bool smux_enabled(void)
244 return agentx_enabled
;
247 void smux_init(struct event_loop
*tm
)
251 netsnmp_enable_subagent();
253 snmp_enable_calllog();
254 snmp_register_callback(SNMP_CALLBACK_LIBRARY
, SNMP_CALLBACK_LOGGING
,
255 agentx_log_callback
, NULL
);
256 init_agent(FRR_SMUX_NAME
);
258 install_node(&agentx_node
);
259 install_element(CONFIG_NODE
, &agentx_enable_cmd
);
260 install_element(CONFIG_NODE
, &no_agentx_cmd
);
262 hook_register(frr_early_fini
, smux_disable
);
265 void smux_agentx_enable(void)
267 if (!agentx_enabled
) {
268 init_snmp(FRR_SMUX_NAME
);
270 agentx_events_update();
271 agentx_enabled
= true;
275 void smux_register_mib(const char *descr
, struct variable
*var
, size_t width
,
276 int num
, oid name
[], size_t namelen
)
278 register_mib(descr
, var
, width
, num
, name
, namelen
);
281 void smux_trap(struct variable
*vp
, size_t vp_len
, const oid
*ename
,
282 size_t enamelen
, const oid
*name
, size_t namelen
,
283 const oid
*iname
, size_t inamelen
,
284 const struct trap_object
*trapobj
, size_t trapobjlen
,
287 struct index_oid trap_index
[1];
289 /* copy the single index into the multi-index format */
290 oid_copy(trap_index
[0].indexname
, iname
, inamelen
);
291 trap_index
[0].indexlen
= inamelen
;
293 smux_trap_multi_index(vp
, vp_len
, ename
, enamelen
, name
, namelen
,
294 trap_index
, array_size(trap_index
), trapobj
,
298 int smux_trap_multi_index(struct variable
*vp
, size_t vp_len
, const oid
*ename
,
299 size_t enamelen
, const oid
*name
, size_t namelen
,
300 struct index_oid
*iname
, size_t index_len
,
301 const struct trap_object
*trapobj
, size_t trapobjlen
,
304 oid objid_snmptrap
[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
305 size_t objid_snmptrap_len
= sizeof(objid_snmptrap
) / sizeof(oid
);
306 oid notification_oid
[MAX_OID_LEN
];
307 size_t notification_oid_len
;
310 netsnmp_variable_list
*notification_vars
= NULL
;
315 oid_copy(notification_oid
, ename
, enamelen
);
316 notification_oid
[enamelen
] = sptrap
;
317 notification_oid_len
= enamelen
+ 1;
318 snmp_varlist_add_variable(¬ification_vars
, objid_snmptrap
,
319 objid_snmptrap_len
, ASN_OBJECT_ID
,
320 (uint8_t *)notification_oid
,
321 notification_oid_len
* sizeof(oid
));
323 /* Provided bindings */
324 for (i
= 0; i
< trapobjlen
; i
++) {
326 oid oid
[MAX_OID_LEN
];
327 size_t oid_len
, onamelen
;
330 WriteMethod
*wm
= NULL
;
334 * this allows the behaviour of smux_trap with a singe index
335 * for all objects to be maintained whilst allowing traps which
336 * have different indices per object to be supported
338 iindex
= (index_len
== 1) ? 0 : i
;
341 if (trapobj
[i
].namelen
> 0) {
342 /* Columnar object */
343 onamelen
= trapobj
[i
].namelen
;
344 oid_copy(oid
, name
, namelen
);
345 oid_copy(oid
+ namelen
, trapobj
[i
].name
, onamelen
);
346 oid_copy(oid
+ namelen
+ onamelen
,
347 iname
[iindex
].indexname
,
348 iname
[iindex
].indexlen
);
349 oid_len
= namelen
+ onamelen
+ iname
[iindex
].indexlen
;
352 onamelen
= trapobj
[i
].namelen
* (-1);
353 oid_copy(oid
, name
, namelen
);
354 oid_copy(oid
+ namelen
, trapobj
[i
].name
, onamelen
);
355 oid
[onamelen
+ namelen
] = 0;
356 oid_len
= namelen
+ onamelen
+ 1;
359 /* Locate the appropriate function and type in the MIB registry.
361 for (j
= 0; j
< vp_len
; j
++) {
362 if (oid_compare(trapobj
[i
].name
, onamelen
, vp
[j
].name
,
366 /* We found the appropriate variable in the MIB
368 oid_copy(cvp
.name
, name
, namelen
);
369 oid_copy(cvp
.name
+ namelen
, vp
[j
].name
, vp
[j
].namelen
);
370 cvp
.namelen
= namelen
+ vp
[j
].namelen
;
371 cvp
.type
= vp
[j
].type
;
372 cvp
.magic
= vp
[j
].magic
;
374 cvp
.findVar
= vp
[j
].findVar
;
376 /* Grab the result. */
377 val
= cvp
.findVar(&cvp
, oid
, &oid_len
, 1, &val_len
,
381 snmp_varlist_add_variable(¬ification_vars
, oid
,
382 oid_len
, vp
[j
].type
, val
,
389 send_v2trap(notification_vars
);
390 snmp_free_varbind(notification_vars
);
391 agentx_events_update();
395 void smux_events_update(void)
397 agentx_events_update();
400 #endif /* SNMP_AGENTX */