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