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