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