]>
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 | * | |
896014f4 DL |
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 | |
d6be5fb9 VB |
19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | ||
5986b66b | 23 | #ifdef SNMP_AGENTX |
d6be5fb9 VB |
24 | #include <net-snmp/net-snmp-config.h> |
25 | #include <net-snmp/net-snmp-includes.h> | |
56e2c5e8 DL |
26 | #include <net-snmp/agent/net-snmp-agent-includes.h> |
27 | #include <net-snmp/agent/snmp_vars.h> | |
d6be5fb9 VB |
28 | |
29 | #include "command.h" | |
30 | #include "smux.h" | |
d11f748b | 31 | #include "memory.h" |
56e2c5e8 | 32 | #include "linklist.h" |
09781197 | 33 | #include "lib/version.h" |
220d7368 | 34 | #include "lib_errors.h" |
b2fa8c0f DL |
35 | #include "xref.h" |
36 | ||
80413c20 | 37 | XREF_SETUP(); |
d6be5fb9 | 38 | |
8451921b | 39 | DEFINE_HOOK(agentx_enabled, (), ()); |
ec48460e | 40 | |
56e2c5e8 DL |
41 | static int agentx_enabled = 0; |
42 | ||
43 | static struct thread_master *agentx_tm; | |
44 | static struct thread *timeout_thr = NULL; | |
45 | static struct list *events = NULL; | |
46 | ||
47 | static void agentx_events_update(void); | |
48 | ||
d62a17ae | 49 | static int agentx_timeout(struct thread *t) |
56e2c5e8 | 50 | { |
d62a17ae | 51 | timeout_thr = NULL; |
56e2c5e8 | 52 | |
d62a17ae | 53 | snmp_timeout(); |
54 | run_alarms(); | |
55 | netsnmp_check_outstanding_agent_requests(); | |
56 | agentx_events_update(); | |
57 | return 0; | |
56e2c5e8 DL |
58 | } |
59 | ||
d62a17ae | 60 | static int agentx_read(struct thread *t) |
56e2c5e8 | 61 | { |
d62a17ae | 62 | fd_set fds; |
19d95d40 | 63 | int flags, new_flags = 0; |
3b717261 | 64 | int nonblock = false; |
d62a17ae | 65 | struct listnode *ln = THREAD_ARG(t); |
7640e3c6 IR |
66 | struct thread **thr = listgetdata(ln); |
67 | XFREE(MTYPE_TMP, thr); | |
d62a17ae | 68 | list_delete_node(events, ln); |
56e2c5e8 | 69 | |
3b717261 | 70 | /* fix for non blocking socket */ |
71 | flags = fcntl(THREAD_FD(t), F_GETFL, 0); | |
19d95d40 DS |
72 | if (-1 == flags) { |
73 | flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)", | |
74 | strerror(errno), errno); | |
3b717261 | 75 | return -1; |
19d95d40 | 76 | } |
3b717261 | 77 | |
78 | if (flags & O_NONBLOCK) | |
79 | nonblock = true; | |
80 | else | |
19d95d40 DS |
81 | new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK); |
82 | ||
83 | if (new_flags == -1) | |
84 | flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)", | |
85 | strerror(errno), errno); | |
3b717261 | 86 | |
d62a17ae | 87 | FD_ZERO(&fds); |
88 | FD_SET(THREAD_FD(t), &fds); | |
89 | snmp_read(&fds); | |
56e2c5e8 | 90 | |
3b717261 | 91 | /* Reset the flag */ |
19d95d40 DS |
92 | if (!nonblock) { |
93 | new_flags = fcntl(THREAD_FD(t), F_SETFL, flags); | |
94 | ||
95 | if (new_flags == -1) | |
96 | flog_err( | |
97 | EC_LIB_SYSTEM_CALL, | |
98 | "Failed to set snmp fd back to original settings: %s(%d)", | |
99 | strerror(errno), errno); | |
100 | } | |
3b717261 | 101 | |
d62a17ae | 102 | netsnmp_check_outstanding_agent_requests(); |
103 | agentx_events_update(); | |
104 | return 0; | |
56e2c5e8 DL |
105 | } |
106 | ||
d62a17ae | 107 | static void agentx_events_update(void) |
56e2c5e8 | 108 | { |
d62a17ae | 109 | int maxfd = 0; |
110 | int block = 1; | |
111 | struct timeval timeout = {.tv_sec = 0, .tv_usec = 0}; | |
112 | fd_set fds; | |
113 | struct listnode *ln; | |
7640e3c6 | 114 | struct thread **thr; |
d62a17ae | 115 | int fd, thr_fd; |
116 | ||
50478845 | 117 | thread_cancel(&timeout_thr); |
d62a17ae | 118 | |
119 | FD_ZERO(&fds); | |
120 | snmp_select_info(&maxfd, &fds, &timeout, &block); | |
121 | ||
122 | if (!block) { | |
123 | timeout_thr = NULL; | |
124 | thread_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout, | |
125 | &timeout_thr); | |
126 | } | |
127 | ||
128 | ln = listhead(events); | |
129 | thr = ln ? listgetdata(ln) : NULL; | |
7640e3c6 | 130 | thr_fd = thr ? THREAD_FD(*thr) : -1; |
d62a17ae | 131 | |
132 | /* "two-pointer" / two-list simultaneous iteration | |
133 | * ln/thr/thr_fd point to the next existing event listener to hit while | |
134 | * fd counts to catch up */ | |
135 | for (fd = 0; fd < maxfd; fd++) { | |
136 | /* caught up */ | |
137 | if (thr_fd == fd) { | |
138 | struct listnode *nextln = listnextnode(ln); | |
139 | if (!FD_ISSET(fd, &fds)) { | |
7640e3c6 IR |
140 | thread_cancel(thr); |
141 | XFREE(MTYPE_TMP, thr); | |
d62a17ae | 142 | list_delete_node(events, ln); |
143 | } | |
144 | ln = nextln; | |
145 | thr = ln ? listgetdata(ln) : NULL; | |
7640e3c6 | 146 | thr_fd = thr ? THREAD_FD(*thr) : -1; |
d62a17ae | 147 | } |
148 | /* need listener, but haven't hit one where it would be */ | |
149 | else if (FD_ISSET(fd, &fds)) { | |
150 | struct listnode *newln; | |
7640e3c6 IR |
151 | thr = XCALLOC(MTYPE_TMP, sizeof(struct thread *)); |
152 | thread_add_read(agentx_tm, agentx_read, NULL, fd, thr); | |
d62a17ae | 153 | newln = listnode_add_before(events, ln, thr); |
7640e3c6 | 154 | (*thr)->arg = newln; |
d62a17ae | 155 | } |
156 | } | |
157 | ||
158 | /* leftover event listeners at this point have fd > maxfd, delete them | |
159 | */ | |
160 | while (ln) { | |
161 | struct listnode *nextln = listnextnode(ln); | |
b3d6bc6e | 162 | thr = listgetdata(ln); |
7640e3c6 IR |
163 | thread_cancel(thr); |
164 | XFREE(MTYPE_TMP, thr); | |
d62a17ae | 165 | list_delete_node(events, ln); |
166 | ln = nextln; | |
167 | } | |
56e2c5e8 | 168 | } |
d6be5fb9 VB |
169 | |
170 | /* AgentX node. */ | |
612c2c15 | 171 | static int config_write_agentx(struct vty *vty); |
62b346ee | 172 | static struct cmd_node agentx_node = { |
f4b8291f | 173 | .name = "smux", |
62b346ee DL |
174 | .node = SMUX_NODE, |
175 | .prompt = "", | |
612c2c15 | 176 | .config_write = config_write_agentx, |
62b346ee | 177 | }; |
d6be5fb9 VB |
178 | |
179 | /* Logging NetSNMP messages */ | |
d62a17ae | 180 | static int agentx_log_callback(int major, int minor, void *serverarg, |
181 | void *clientarg) | |
d6be5fb9 | 182 | { |
d62a17ae | 183 | struct snmp_log_message *slm = (struct snmp_log_message *)serverarg; |
184 | char *msg = XSTRDUP(MTYPE_TMP, slm->msg); | |
185 | if (msg) | |
186 | msg[strlen(msg) - 1] = '\0'; | |
187 | switch (slm->priority) { | |
188 | case LOG_EMERG: | |
1c50c1c0 | 189 | flog_err(EC_LIB_SNMP, "snmp[emerg]: %s", msg ? msg : slm->msg); |
d62a17ae | 190 | break; |
191 | case LOG_ALERT: | |
1c50c1c0 | 192 | flog_err(EC_LIB_SNMP, "snmp[alert]: %s", msg ? msg : slm->msg); |
d62a17ae | 193 | break; |
194 | case LOG_CRIT: | |
1c50c1c0 | 195 | flog_err(EC_LIB_SNMP, "snmp[crit]: %s", msg ? msg : slm->msg); |
d62a17ae | 196 | break; |
197 | case LOG_ERR: | |
1c50c1c0 | 198 | flog_err(EC_LIB_SNMP, "snmp[err]: %s", msg ? msg : slm->msg); |
d62a17ae | 199 | break; |
200 | case LOG_WARNING: | |
450971aa | 201 | flog_warn(EC_LIB_SNMP, "snmp[warning]: %s", |
ade6974d | 202 | msg ? msg : slm->msg); |
d62a17ae | 203 | break; |
204 | case LOG_NOTICE: | |
205 | zlog_notice("snmp[notice]: %s", msg ? msg : slm->msg); | |
206 | break; | |
207 | case LOG_INFO: | |
208 | zlog_info("snmp[info]: %s", msg ? msg : slm->msg); | |
209 | break; | |
210 | case LOG_DEBUG: | |
211 | zlog_debug("snmp[debug]: %s", msg ? msg : slm->msg); | |
212 | break; | |
213 | } | |
214 | XFREE(MTYPE_TMP, msg); | |
215 | return SNMP_ERR_NOERROR; | |
d6be5fb9 VB |
216 | } |
217 | ||
d62a17ae | 218 | static int config_write_agentx(struct vty *vty) |
d6be5fb9 | 219 | { |
d62a17ae | 220 | if (agentx_enabled) |
221 | vty_out(vty, "agentx\n"); | |
222 | return 1; | |
d6be5fb9 VB |
223 | } |
224 | ||
225 | DEFUN (agentx_enable, | |
226 | agentx_enable_cmd, | |
227 | "agentx", | |
007b0667 | 228 | "SNMP AgentX protocol settings\n") |
d6be5fb9 | 229 | { |
d62a17ae | 230 | if (!agentx_enabled) { |
231 | init_snmp(FRR_SMUX_NAME); | |
232 | events = list_new(); | |
233 | agentx_events_update(); | |
234 | agentx_enabled = 1; | |
ec48460e | 235 | hook_call(agentx_enabled); |
d62a17ae | 236 | } |
5fedee18 | 237 | |
d62a17ae | 238 | return CMD_SUCCESS; |
d6be5fb9 VB |
239 | } |
240 | ||
241 | DEFUN (no_agentx, | |
242 | no_agentx_cmd, | |
243 | "no agentx", | |
244 | NO_STR | |
007b0667 | 245 | "SNMP AgentX protocol settings\n") |
d6be5fb9 | 246 | { |
d62a17ae | 247 | if (!agentx_enabled) |
248 | return CMD_SUCCESS; | |
249 | vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n"); | |
250 | return CMD_WARNING_CONFIG_FAILED; | |
d6be5fb9 VB |
251 | } |
252 | ||
1ee746d9 | 253 | int smux_enabled(void) |
254 | { | |
255 | return agentx_enabled; | |
256 | } | |
257 | ||
d62a17ae | 258 | void smux_init(struct thread_master *tm) |
d6be5fb9 | 259 | { |
d62a17ae | 260 | agentx_tm = tm; |
261 | ||
262 | netsnmp_enable_subagent(); | |
263 | snmp_disable_log(); | |
264 | snmp_enable_calllog(); | |
265 | snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, | |
266 | agentx_log_callback, NULL); | |
267 | init_agent(FRR_SMUX_NAME); | |
268 | ||
612c2c15 | 269 | install_node(&agentx_node); |
d62a17ae | 270 | install_element(CONFIG_NODE, &agentx_enable_cmd); |
271 | install_element(CONFIG_NODE, &no_agentx_cmd); | |
d6be5fb9 VB |
272 | } |
273 | ||
ec48460e KS |
274 | void smux_agentx_enable(void) |
275 | { | |
276 | if (!agentx_enabled) { | |
277 | init_snmp(FRR_SMUX_NAME); | |
278 | events = list_new(); | |
279 | agentx_events_update(); | |
280 | agentx_enabled = 1; | |
281 | } | |
282 | } | |
283 | ||
d62a17ae | 284 | void smux_register_mib(const char *descr, struct variable *var, size_t width, |
285 | int num, oid name[], size_t namelen) | |
d6be5fb9 | 286 | { |
d62a17ae | 287 | register_mib(descr, var, width, num, name, namelen); |
d6be5fb9 VB |
288 | } |
289 | ||
97e21b4b DS |
290 | void smux_trap(struct variable *vp, size_t vp_len, const oid *ename, |
291 | size_t enamelen, const oid *name, size_t namelen, | |
292 | const oid *iname, size_t inamelen, | |
293 | const struct trap_object *trapobj, size_t trapobjlen, | |
294 | uint8_t sptrap) | |
4eb8c74f PR |
295 | { |
296 | struct index_oid trap_index[1]; | |
297 | ||
298 | /* copy the single index into the multi-index format */ | |
299 | oid_copy(trap_index[0].indexname, iname, inamelen); | |
300 | trap_index[0].indexlen = inamelen; | |
301 | ||
97e21b4b DS |
302 | smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen, |
303 | trap_index, array_size(trap_index), trapobj, | |
304 | trapobjlen, sptrap); | |
4eb8c74f PR |
305 | } |
306 | ||
307 | int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename, | |
308 | size_t enamelen, const oid *name, size_t namelen, | |
309 | struct index_oid *iname, size_t index_len, | |
310 | const struct trap_object *trapobj, size_t trapobjlen, | |
311 | uint8_t sptrap) | |
d6be5fb9 | 312 | { |
d62a17ae | 313 | oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; |
0d6f7fd6 | 314 | size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid); |
d62a17ae | 315 | oid notification_oid[MAX_OID_LEN]; |
316 | size_t notification_oid_len; | |
317 | unsigned int i; | |
318 | ||
319 | netsnmp_variable_list *notification_vars = NULL; | |
320 | if (!agentx_enabled) | |
321 | return 0; | |
322 | ||
323 | /* snmpTrapOID */ | |
324 | oid_copy(notification_oid, ename, enamelen); | |
325 | notification_oid[enamelen] = sptrap; | |
326 | notification_oid_len = enamelen + 1; | |
327 | snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, | |
328 | objid_snmptrap_len, ASN_OBJECT_ID, | |
d7c0a89a | 329 | (uint8_t *)notification_oid, |
d62a17ae | 330 | notification_oid_len * sizeof(oid)); |
331 | ||
332 | /* Provided bindings */ | |
333 | for (i = 0; i < trapobjlen; i++) { | |
334 | unsigned int j; | |
335 | oid oid[MAX_OID_LEN]; | |
336 | size_t oid_len, onamelen; | |
d7c0a89a | 337 | uint8_t *val; |
d62a17ae | 338 | size_t val_len; |
339 | WriteMethod *wm = NULL; | |
340 | struct variable cvp; | |
4eb8c74f PR |
341 | unsigned int iindex; |
342 | /* | |
343 | * this allows the behaviour of smux_trap with a singe index | |
344 | * for all objects to be maintained whilst allowing traps which | |
345 | * have different indices per object to be supported | |
346 | */ | |
347 | iindex = (index_len == 1) ? 0 : i; | |
d62a17ae | 348 | |
349 | /* Make OID. */ | |
350 | if (trapobj[i].namelen > 0) { | |
351 | /* Columnar object */ | |
352 | onamelen = trapobj[i].namelen; | |
353 | oid_copy(oid, name, namelen); | |
354 | oid_copy(oid + namelen, trapobj[i].name, onamelen); | |
4eb8c74f PR |
355 | oid_copy(oid + namelen + onamelen, |
356 | iname[iindex].indexname, | |
357 | iname[iindex].indexlen); | |
358 | oid_len = namelen + onamelen + iname[iindex].indexlen; | |
d62a17ae | 359 | } else { |
360 | /* Scalar object */ | |
361 | onamelen = trapobj[i].namelen * (-1); | |
362 | oid_copy(oid, name, namelen); | |
363 | oid_copy(oid + namelen, trapobj[i].name, onamelen); | |
364 | oid[onamelen + namelen] = 0; | |
365 | oid_len = namelen + onamelen + 1; | |
366 | } | |
367 | ||
368 | /* Locate the appropriate function and type in the MIB registry. | |
369 | */ | |
370 | for (j = 0; j < vp_len; j++) { | |
371 | if (oid_compare(trapobj[i].name, onamelen, vp[j].name, | |
372 | vp[j].namelen) | |
373 | != 0) | |
374 | continue; | |
375 | /* We found the appropriate variable in the MIB | |
376 | * registry. */ | |
377 | oid_copy(cvp.name, name, namelen); | |
378 | oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen); | |
379 | cvp.namelen = namelen + vp[j].namelen; | |
380 | cvp.type = vp[j].type; | |
381 | cvp.magic = vp[j].magic; | |
382 | cvp.acl = vp[j].acl; | |
383 | cvp.findVar = vp[j].findVar; | |
4eb8c74f | 384 | |
d62a17ae | 385 | /* Grab the result. */ |
386 | val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len, | |
387 | &wm); | |
388 | if (!val) | |
389 | break; | |
390 | snmp_varlist_add_variable(¬ification_vars, oid, | |
391 | oid_len, vp[j].type, val, | |
392 | val_len); | |
393 | break; | |
394 | } | |
b7c0d065 | 395 | } |
b7c0d065 VB |
396 | |
397 | ||
d62a17ae | 398 | send_v2trap(notification_vars); |
399 | snmp_free_varbind(notification_vars); | |
400 | agentx_events_update(); | |
401 | return 1; | |
d6be5fb9 VB |
402 | } |
403 | ||
1ee746d9 | 404 | void smux_events_update(void) |
405 | { | |
406 | agentx_events_update(); | |
407 | } | |
408 | ||
5986b66b | 409 | #endif /* SNMP_AGENTX */ |