blob: 5d7d057da094370a23f46dfa5169165c20b7b25b [file] [log] [blame]
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001/* 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
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#if defined HAVE_SNMP && defined SNMP_AGENTX
25#include <net-snmp/net-snmp-config.h>
26#include <net-snmp/net-snmp-includes.h>
David Lampartercfb48262016-06-13 17:29:14 +020027#include <net-snmp/agent/net-snmp-agent-includes.h>
28#include <net-snmp/agent/snmp_vars.h>
Vincent Bernatd6be5fb2012-05-24 09:44:43 +020029
30#include "command.h"
31#include "smux.h"
32
David Lampartercfb48262016-06-13 17:29:14 +020033static int agentx_enabled = 0;
34
35static struct thread_master *agentx_tm;
36static struct thread *timeout_thr = NULL;
37static struct list *events = NULL;
38
39static void agentx_events_update(void);
40
41static int
42agentx_timeout(struct thread *t)
43{
44 timeout_thr = NULL;
45
46 snmp_timeout ();
47 run_alarms ();
48 netsnmp_check_outstanding_agent_requests ();
49 agentx_events_update ();
50 return 0;
51}
52
53static int
54agentx_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
69static void
70agentx_events_update(void)
71{
72 int maxfd = 0;
73 int block = 1;
74 struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
75 fd_set fds;
76 struct listnode *ln;
77 struct thread *thr;
78 int fd, thr_fd;
79
80 THREAD_OFF (timeout_thr);
81
82 FD_ZERO (&fds);
83 snmp_select_info (&maxfd, &fds, &timeout, &block);
84
85 if (!block)
86 timeout_thr = thread_add_timer_tv (agentx_tm, agentx_timeout, NULL, &timeout);
87
88 ln = listhead (events);
89 thr = ln ? listgetdata (ln) : NULL;
90 thr_fd = thr ? THREAD_FD (thr) : -1;
91
92 /* "two-pointer" / two-list simultaneous iteration
93 * ln/thr/thr_fd point to the next existing event listener to hit while
94 * fd counts to catch up */
95 for (fd = 0; fd < maxfd; fd++)
96 {
97 /* caught up */
98 if (thr_fd == fd)
99 {
100 struct listnode *nextln = listnextnode (ln);
101 if (!FD_ISSET (fd, &fds))
102 {
103 thread_cancel (thr);
104 list_delete_node (events, ln);
105 }
106 ln = nextln;
107 thr = ln ? listgetdata (ln) : NULL;
108 thr_fd = thr ? THREAD_FD (thr) : -1;
109 }
110 /* need listener, but haven't hit one where it would be */
111 else if (FD_ISSET (fd, &fds))
112 {
113 struct listnode *newln;
114 thr = thread_add_read (agentx_tm, agentx_read, NULL, fd);
115 newln = listnode_add_before (events, ln, thr);
116 thr->arg = newln;
117 }
118 }
119
120 /* leftover event listeners at this point have fd > maxfd, delete them */
121 while (ln)
122 {
123 struct listnode *nextln = listnextnode (ln);
124 thread_cancel (listgetdata (ln));
125 list_delete_node (events, ln);
126 ln = nextln;
127 }
128}
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200129
130/* AgentX node. */
131static struct cmd_node agentx_node =
132{
133 SMUX_NODE,
134 "" /* AgentX has no interface. */
135};
136
137/* Logging NetSNMP messages */
138static int
139agentx_log_callback(int major, int minor,
140 void *serverarg, void *clientarg)
141{
142 struct snmp_log_message *slm = (struct snmp_log_message *)serverarg;
143 char *msg = strdup (slm->msg);
144 if (msg) msg[strlen(msg)-1] = '\0';
145 switch (slm->priority)
146 {
147 case LOG_EMERG: zlog_err ("snmp[emerg]: %s", msg?msg:slm->msg); break;
148 case LOG_ALERT: zlog_err ("snmp[alert]: %s", msg?msg:slm->msg); break;
149 case LOG_CRIT: zlog_err ("snmp[crit]: %s", msg?msg:slm->msg); break;
150 case LOG_ERR: zlog_err ("snmp[err]: %s", msg?msg:slm->msg); break;
151 case LOG_WARNING: zlog_warn ("snmp[warning]: %s", msg?msg:slm->msg); break;
152 case LOG_NOTICE: zlog_notice("snmp[notice]: %s", msg?msg:slm->msg); break;
153 case LOG_INFO: zlog_info ("snmp[info]: %s", msg?msg:slm->msg); break;
154 case LOG_DEBUG: zlog_debug ("snmp[debug]: %s", msg?msg:slm->msg); break;
155 }
156 free(msg);
157 return SNMP_ERR_NOERROR;
158}
159
160static int
161config_write_agentx (struct vty *vty)
162{
163 if (agentx_enabled)
164 vty_out (vty, "agentx%s", VTY_NEWLINE);
165 return 0;
166}
167
168DEFUN (agentx_enable,
169 agentx_enable_cmd,
170 "agentx",
171 "SNMP AgentX protocol settings\n"
172 "SNMP AgentX settings\n")
173{
174 if (!agentx_enabled)
175 {
176 init_snmp("quagga");
David Lampartercfb48262016-06-13 17:29:14 +0200177 events = list_new();
178 agentx_events_update ();
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200179 agentx_enabled = 1;
180 return CMD_SUCCESS;
181 }
182 vty_out (vty, "SNMP AgentX already enabled%s", VTY_NEWLINE);
183 return CMD_WARNING;
184}
185
186DEFUN (no_agentx,
187 no_agentx_cmd,
188 "no agentx",
189 NO_STR
190 "SNMP AgentX protocol settings\n"
191 "SNMP AgentX settings\n")
192{
193 if (!agentx_enabled) return CMD_SUCCESS;
194 vty_out (vty, "SNMP AgentX support cannot be disabled once enabled%s", VTY_NEWLINE);
195 return CMD_WARNING;
196}
197
198void
199smux_init (struct thread_master *tm)
200{
David Lampartercfb48262016-06-13 17:29:14 +0200201 agentx_tm = tm;
202
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200203 netsnmp_enable_subagent ();
204 snmp_disable_log ();
205 snmp_enable_calllog ();
206 snmp_register_callback (SNMP_CALLBACK_LIBRARY,
207 SNMP_CALLBACK_LOGGING,
208 agentx_log_callback,
209 NULL);
210 init_agent ("quagga");
211
212 install_node (&agentx_node, config_write_agentx);
213 install_element (CONFIG_NODE, &agentx_enable_cmd);
214 install_element (CONFIG_NODE, &no_agentx_cmd);
215}
216
217void
218smux_register_mib (const char *descr, struct variable *var,
219 size_t width, int num,
220 oid name[], size_t namelen)
221{
222 register_mib (descr, var, width, num, name, namelen);
223}
224
225int
Vincent Bernatb7c0d062012-05-25 11:17:01 +0200226smux_trap (struct variable *vp, size_t vp_len,
227 const oid *ename, size_t enamelen,
228 const oid *name, size_t namelen,
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200229 const oid *iname, size_t inamelen,
230 const struct trap_object *trapobj, size_t trapobjlen,
Vincent Bernat4b89e452012-05-24 21:22:01 +0200231 u_char sptrap)
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200232{
Vincent Bernatb7c0d062012-05-25 11:17:01 +0200233 oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
234 size_t objid_snmptrap_len = sizeof objid_snmptrap / sizeof (oid);
235 oid notification_oid[MAX_OID_LEN];
236 size_t notification_oid_len;
237 unsigned int i;
238
239 netsnmp_variable_list *notification_vars = NULL;
240 if (!agentx_enabled) return 0;
241
242 /* snmpTrapOID */
243 oid_copy (notification_oid, ename, enamelen);
244 notification_oid[enamelen] = sptrap;
245 notification_oid_len = enamelen + 1;
246 snmp_varlist_add_variable (&notification_vars,
247 objid_snmptrap, objid_snmptrap_len,
248 ASN_OBJECT_ID,
249 (u_char *) notification_oid,
250 notification_oid_len * sizeof(oid));
251
252 /* Provided bindings */
253 for (i = 0; i < trapobjlen; i++)
254 {
255 unsigned int j;
256 oid oid[MAX_OID_LEN];
257 size_t oid_len, onamelen;
258 u_char *val;
259 size_t val_len;
260 WriteMethod *wm = NULL;
261 struct variable cvp;
262
263 /* Make OID. */
264 if (trapobj[i].namelen > 0)
265 {
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 }
273 else
274 {
275 /* Scalar object */
276 onamelen = trapobj[i].namelen * (-1);
277 oid_copy (oid, name, namelen);
278 oid_copy (oid + namelen, trapobj[i].name, onamelen);
279 oid[onamelen + namelen] = 0;
280 oid_len = namelen + onamelen + 1;
281 }
282
283 /* Locate the appropriate function and type in the MIB registry. */
284 for (j = 0; j < vp_len; j++)
285 {
286 if (oid_compare (trapobj[i].name, onamelen, vp[j].name, vp[j].namelen) != 0)
287 continue;
288 /* We found the appropriate variable in the MIB registry. */
289 oid_copy(cvp.name, name, namelen);
290 oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen);
291 cvp.namelen = namelen + vp[j].namelen;
292 cvp.type = vp[j].type;
293 cvp.magic = vp[j].magic;
294 cvp.acl = vp[j].acl;
295 cvp.findVar = vp[j].findVar;
296 /* Grab the result. */
297 val = cvp.findVar (&cvp, oid, &oid_len, 1, &val_len, &wm);
298 if (!val) break;
299 snmp_varlist_add_variable (&notification_vars,
300 oid, oid_len,
301 vp[j].type,
302 val,
303 val_len);
304 break;
305 }
306 }
307
308
309 send_v2trap (notification_vars);
310 snmp_free_varbind (notification_vars);
David Lampartercfb48262016-06-13 17:29:14 +0200311 agentx_events_update ();
Vincent Bernatd6be5fb2012-05-24 09:44:43 +0200312 return 1;
313}
314
315#endif /* HAVE_SNMP */