]> git.proxmox.com Git - mirror_frr.git/blob - lib/agentx.c
*: Convert thread_cancelXXX to event_cancelXXX
[mirror_frr.git] / lib / agentx.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* SNMP support
3 * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx>
4 */
5
6 #include <zebra.h>
7
8 #ifdef SNMP_AGENTX
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>
13
14 #include "command.h"
15 #include "smux.h"
16 #include "memory.h"
17 #include "linklist.h"
18 #include "lib/version.h"
19 #include "lib_errors.h"
20 #include "hook.h"
21 #include "libfrr.h"
22 #include "xref.h"
23
24 XREF_SETUP();
25
26 DEFINE_HOOK(agentx_enabled, (), ());
27
28 static bool agentx_enabled = false;
29
30 static struct thread_master *agentx_tm;
31 static struct event *timeout_thr = NULL;
32 static struct list *events = NULL;
33
34 static void agentx_events_update(void);
35
36 static void agentx_timeout(struct event *t)
37 {
38 snmp_timeout();
39 run_alarms();
40 netsnmp_check_outstanding_agent_requests();
41 agentx_events_update();
42 }
43
44 static void agentx_read(struct event *t)
45 {
46 fd_set fds;
47 int flags, new_flags = 0;
48 int nonblock = false;
49 struct listnode *ln = THREAD_ARG(t);
50 struct event **thr = listgetdata(ln);
51 XFREE(MTYPE_TMP, thr);
52 list_delete_node(events, ln);
53
54 /* fix for non blocking socket */
55 flags = fcntl(THREAD_FD(t), F_GETFL, 0);
56 if (-1 == flags) {
57 flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)",
58 strerror(errno), errno);
59 return;
60 }
61
62 if (flags & O_NONBLOCK)
63 nonblock = true;
64 else
65 new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK);
66
67 if (new_flags == -1)
68 flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)",
69 strerror(errno), errno);
70
71 FD_ZERO(&fds);
72 FD_SET(THREAD_FD(t), &fds);
73 snmp_read(&fds);
74
75 /* Reset the flag */
76 if (!nonblock) {
77 new_flags = fcntl(THREAD_FD(t), F_SETFL, flags);
78
79 if (new_flags == -1)
80 flog_err(
81 EC_LIB_SYSTEM_CALL,
82 "Failed to set snmp fd back to original settings: %s(%d)",
83 strerror(errno), errno);
84 }
85
86 netsnmp_check_outstanding_agent_requests();
87 agentx_events_update();
88 }
89
90 static void agentx_events_update(void)
91 {
92 int maxfd = 0;
93 int block = 1;
94 struct timeval timeout = {.tv_sec = 0, .tv_usec = 0};
95 fd_set fds;
96 struct listnode *ln;
97 struct event **thr;
98 int fd, thr_fd;
99
100 event_cancel(&timeout_thr);
101
102 FD_ZERO(&fds);
103 snmp_select_info(&maxfd, &fds, &timeout, &block);
104
105 if (!block) {
106 event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout,
107 &timeout_thr);
108 }
109
110 ln = listhead(events);
111 thr = ln ? listgetdata(ln) : NULL;
112 thr_fd = thr ? THREAD_FD(*thr) : -1;
113
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++) {
118 /* caught up */
119 if (thr_fd == fd) {
120 struct listnode *nextln = listnextnode(ln);
121 if (!FD_ISSET(fd, &fds)) {
122 event_cancel(thr);
123 XFREE(MTYPE_TMP, thr);
124 list_delete_node(events, ln);
125 }
126 ln = nextln;
127 thr = ln ? listgetdata(ln) : NULL;
128 thr_fd = thr ? THREAD_FD(*thr) : -1;
129 }
130 /* need listener, but haven't hit one where it would be */
131 else if (FD_ISSET(fd, &fds)) {
132 struct listnode *newln;
133
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);
137 }
138 }
139
140 /* leftover event listeners at this point have fd > maxfd, delete them
141 */
142 while (ln) {
143 struct listnode *nextln = listnextnode(ln);
144 thr = listgetdata(ln);
145 event_cancel(thr);
146 XFREE(MTYPE_TMP, thr);
147 list_delete_node(events, ln);
148 ln = nextln;
149 }
150 }
151
152 /* AgentX node. */
153 static int config_write_agentx(struct vty *vty);
154 static struct cmd_node agentx_node = {
155 .name = "smux",
156 .node = SMUX_NODE,
157 .prompt = "",
158 .config_write = config_write_agentx,
159 };
160
161 /* Logging NetSNMP messages */
162 static int agentx_log_callback(int major, int minor, void *serverarg,
163 void *clientarg)
164 {
165 struct snmp_log_message *slm = (struct snmp_log_message *)serverarg;
166 char *msg = XSTRDUP(MTYPE_TMP, slm->msg);
167 if (msg)
168 msg[strlen(msg) - 1] = '\0';
169 switch (slm->priority) {
170 case LOG_EMERG:
171 flog_err(EC_LIB_SNMP, "snmp[emerg]: %s", msg ? msg : slm->msg);
172 break;
173 case LOG_ALERT:
174 flog_err(EC_LIB_SNMP, "snmp[alert]: %s", msg ? msg : slm->msg);
175 break;
176 case LOG_CRIT:
177 flog_err(EC_LIB_SNMP, "snmp[crit]: %s", msg ? msg : slm->msg);
178 break;
179 case LOG_ERR:
180 flog_err(EC_LIB_SNMP, "snmp[err]: %s", msg ? msg : slm->msg);
181 break;
182 case LOG_WARNING:
183 flog_warn(EC_LIB_SNMP, "snmp[warning]: %s",
184 msg ? msg : slm->msg);
185 break;
186 case LOG_NOTICE:
187 zlog_notice("snmp[notice]: %s", msg ? msg : slm->msg);
188 break;
189 case LOG_INFO:
190 zlog_info("snmp[info]: %s", msg ? msg : slm->msg);
191 break;
192 case LOG_DEBUG:
193 zlog_debug("snmp[debug]: %s", msg ? msg : slm->msg);
194 break;
195 }
196 XFREE(MTYPE_TMP, msg);
197 return SNMP_ERR_NOERROR;
198 }
199
200 static int config_write_agentx(struct vty *vty)
201 {
202 if (agentx_enabled)
203 vty_out(vty, "agentx\n");
204 return 1;
205 }
206
207 DEFUN (agentx_enable,
208 agentx_enable_cmd,
209 "agentx",
210 "SNMP AgentX protocol settings\n")
211 {
212 if (!agentx_enabled) {
213 init_snmp(FRR_SMUX_NAME);
214 events = list_new();
215 agentx_events_update();
216 agentx_enabled = true;
217 hook_call(agentx_enabled);
218 }
219
220 return CMD_SUCCESS;
221 }
222
223 DEFUN (no_agentx,
224 no_agentx_cmd,
225 "no agentx",
226 NO_STR
227 "SNMP AgentX protocol settings\n")
228 {
229 if (!agentx_enabled)
230 return CMD_SUCCESS;
231 vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n");
232 return CMD_WARNING_CONFIG_FAILED;
233 }
234
235 static int smux_disable(void)
236 {
237 agentx_enabled = false;
238
239 return 0;
240 }
241
242 bool smux_enabled(void)
243 {
244 return agentx_enabled;
245 }
246
247 void smux_init(struct thread_master *tm)
248 {
249 agentx_tm = tm;
250
251 netsnmp_enable_subagent();
252 snmp_disable_log();
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);
257
258 install_node(&agentx_node);
259 install_element(CONFIG_NODE, &agentx_enable_cmd);
260 install_element(CONFIG_NODE, &no_agentx_cmd);
261
262 hook_register(frr_early_fini, smux_disable);
263 }
264
265 void smux_agentx_enable(void)
266 {
267 if (!agentx_enabled) {
268 init_snmp(FRR_SMUX_NAME);
269 events = list_new();
270 agentx_events_update();
271 agentx_enabled = true;
272 }
273 }
274
275 void smux_register_mib(const char *descr, struct variable *var, size_t width,
276 int num, oid name[], size_t namelen)
277 {
278 register_mib(descr, var, width, num, name, namelen);
279 }
280
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,
285 uint8_t sptrap)
286 {
287 struct index_oid trap_index[1];
288
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;
292
293 smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen,
294 trap_index, array_size(trap_index), trapobj,
295 trapobjlen, sptrap);
296 }
297
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,
302 uint8_t sptrap)
303 {
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;
308 unsigned int i;
309
310 netsnmp_variable_list *notification_vars = NULL;
311 if (!agentx_enabled)
312 return 0;
313
314 /* snmpTrapOID */
315 oid_copy(notification_oid, ename, enamelen);
316 notification_oid[enamelen] = sptrap;
317 notification_oid_len = enamelen + 1;
318 snmp_varlist_add_variable(&notification_vars, objid_snmptrap,
319 objid_snmptrap_len, ASN_OBJECT_ID,
320 (uint8_t *)notification_oid,
321 notification_oid_len * sizeof(oid));
322
323 /* Provided bindings */
324 for (i = 0; i < trapobjlen; i++) {
325 unsigned int j;
326 oid oid[MAX_OID_LEN];
327 size_t oid_len, onamelen;
328 uint8_t *val;
329 size_t val_len;
330 WriteMethod *wm = NULL;
331 struct variable cvp;
332 unsigned int iindex;
333 /*
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
337 */
338 iindex = (index_len == 1) ? 0 : i;
339
340 /* Make OID. */
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;
350 } else {
351 /* Scalar object */
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;
357 }
358
359 /* Locate the appropriate function and type in the MIB registry.
360 */
361 for (j = 0; j < vp_len; j++) {
362 if (oid_compare(trapobj[i].name, onamelen, vp[j].name,
363 vp[j].namelen)
364 != 0)
365 continue;
366 /* We found the appropriate variable in the MIB
367 * registry. */
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;
373 cvp.acl = vp[j].acl;
374 cvp.findVar = vp[j].findVar;
375
376 /* Grab the result. */
377 val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len,
378 &wm);
379 if (!val)
380 break;
381 snmp_varlist_add_variable(&notification_vars, oid,
382 oid_len, vp[j].type, val,
383 val_len);
384 break;
385 }
386 }
387
388
389 send_v2trap(notification_vars);
390 snmp_free_varbind(notification_vars);
391 agentx_events_update();
392 return 1;
393 }
394
395 void smux_events_update(void)
396 {
397 agentx_events_update();
398 }
399
400 #endif /* SNMP_AGENTX */