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