]>
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" |
ae435b19 | 33 | #include "version.h" |
d6be5fb9 | 34 | |
56e2c5e8 DL |
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 | ||
d62a17ae | 43 | static int agentx_timeout(struct thread *t) |
56e2c5e8 | 44 | { |
d62a17ae | 45 | timeout_thr = NULL; |
56e2c5e8 | 46 | |
d62a17ae | 47 | snmp_timeout(); |
48 | run_alarms(); | |
49 | netsnmp_check_outstanding_agent_requests(); | |
50 | agentx_events_update(); | |
51 | return 0; | |
56e2c5e8 DL |
52 | } |
53 | ||
d62a17ae | 54 | static int agentx_read(struct thread *t) |
56e2c5e8 | 55 | { |
d62a17ae | 56 | fd_set fds; |
57 | struct listnode *ln = THREAD_ARG(t); | |
58 | list_delete_node(events, ln); | |
56e2c5e8 | 59 | |
d62a17ae | 60 | FD_ZERO(&fds); |
61 | FD_SET(THREAD_FD(t), &fds); | |
62 | snmp_read(&fds); | |
56e2c5e8 | 63 | |
d62a17ae | 64 | netsnmp_check_outstanding_agent_requests(); |
65 | agentx_events_update(); | |
66 | return 0; | |
56e2c5e8 DL |
67 | } |
68 | ||
d62a17ae | 69 | static void agentx_events_update(void) |
56e2c5e8 | 70 | { |
d62a17ae | 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 | } | |
56e2c5e8 | 127 | } |
d6be5fb9 VB |
128 | |
129 | /* AgentX node. */ | |
d62a17ae | 130 | static struct cmd_node agentx_node = {SMUX_NODE, |
131 | "", /* AgentX has no interface. */ | |
132 | 1}; | |
d6be5fb9 VB |
133 | |
134 | /* Logging NetSNMP messages */ | |
d62a17ae | 135 | static int agentx_log_callback(int major, int minor, void *serverarg, |
136 | void *clientarg) | |
d6be5fb9 | 137 | { |
d62a17ae | 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; | |
d6be5fb9 VB |
170 | } |
171 | ||
d62a17ae | 172 | static int config_write_agentx(struct vty *vty) |
d6be5fb9 | 173 | { |
d62a17ae | 174 | if (agentx_enabled) |
175 | vty_out(vty, "agentx\n"); | |
176 | return 1; | |
d6be5fb9 VB |
177 | } |
178 | ||
179 | DEFUN (agentx_enable, | |
180 | agentx_enable_cmd, | |
181 | "agentx", | |
007b0667 | 182 | "SNMP AgentX protocol settings\n") |
d6be5fb9 | 183 | { |
d62a17ae | 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; | |
d6be5fb9 VB |
193 | } |
194 | ||
195 | DEFUN (no_agentx, | |
196 | no_agentx_cmd, | |
197 | "no agentx", | |
198 | NO_STR | |
007b0667 | 199 | "SNMP AgentX protocol settings\n") |
d6be5fb9 | 200 | { |
d62a17ae | 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; | |
d6be5fb9 VB |
205 | } |
206 | ||
d62a17ae | 207 | void smux_init(struct thread_master *tm) |
d6be5fb9 | 208 | { |
d62a17ae | 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); | |
d6be5fb9 VB |
221 | } |
222 | ||
d62a17ae | 223 | void smux_register_mib(const char *descr, struct variable *var, size_t width, |
224 | int num, oid name[], size_t namelen) | |
d6be5fb9 | 225 | { |
d62a17ae | 226 | register_mib(descr, var, width, num, name, namelen); |
d6be5fb9 VB |
227 | } |
228 | ||
d62a17ae | 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 | u_char sptrap) | |
d6be5fb9 | 234 | { |
d62a17ae | 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(¬ification_vars, objid_snmptrap, | |
250 | objid_snmptrap_len, ASN_OBJECT_ID, | |
251 | (u_char *)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 | u_char *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(¬ification_vars, oid, | |
303 | oid_len, vp[j].type, val, | |
304 | val_len); | |
305 | break; | |
306 | } | |
b7c0d065 | 307 | } |
b7c0d065 VB |
308 | |
309 | ||
d62a17ae | 310 | send_v2trap(notification_vars); |
311 | snmp_free_varbind(notification_vars); | |
312 | agentx_events_update(); | |
313 | return 1; | |
d6be5fb9 VB |
314 | } |
315 | ||
5986b66b | 316 | #endif /* SNMP_AGENTX */ |