Import Upstream version 1.2.2
[quagga-debian.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
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>
27 #include <net-snmp/agent/net-snmp-agent-includes.h>
28 #include <net-snmp/agent/snmp_vars.h>
29
30 #include "command.h"
31 #include "smux.h"
32
33 static int agentx_enabled = 0;
34
35 static struct thread_master *agentx_tm;
36 static struct thread *timeout_thr = NULL;
37 static struct list *events = NULL;
38
39 static void agentx_events_update(void);
40
41 static int
42 agentx_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
53 static int
54 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
70 agentx_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 }
129
130 /* AgentX node. */
131 static struct cmd_node agentx_node =
132 {
133   SMUX_NODE,
134   ""                            /* AgentX has no interface. */
135 };
136
137 /* Logging NetSNMP messages */
138 static int
139 agentx_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
160 static int
161 config_write_agentx (struct vty *vty)
162 {
163   if (agentx_enabled)
164       vty_out (vty, "agentx%s", VTY_NEWLINE);
165   return 0;
166 }
167
168 DEFUN (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");
177       events = list_new();
178       agentx_events_update ();
179       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
186 DEFUN (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
198 void
199 smux_init (struct thread_master *tm)
200 {
201   agentx_tm = tm;
202
203   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
217 void
218 smux_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
225 int
226 smux_trap (struct variable *vp, size_t vp_len,
227            const oid *ename, size_t enamelen,
228            const oid *name, size_t namelen,
229            const oid *iname, size_t inamelen,
230            const struct trap_object *trapobj, size_t trapobjlen,
231            u_char sptrap)
232 {
233   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);
311   agentx_events_update ();
312   return 1;
313 }
314
315 #endif /* HAVE_SNMP */