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