/src/net-snmp/agent/agent_trap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * agent_trap.c |
3 | | */ |
4 | | /* Portions of this file are subject to the following copyright(s). See |
5 | | * the Net-SNMP's COPYING file for more details and other copyrights |
6 | | * that may apply: |
7 | | */ |
8 | | /* |
9 | | * Portions of this file are copyrighted by: |
10 | | * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
11 | | * Use is subject to license terms specified in the COPYING file |
12 | | * distributed with the Net-SNMP package. |
13 | | * |
14 | | * Portions of this file are copyrighted by: |
15 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
16 | | * Use is subject to license terms specified in the COPYING file |
17 | | * distributed with the Net-SNMP package. |
18 | | */ |
19 | | /** @defgroup agent_trap Trap generation routines for mib modules to use |
20 | | * @ingroup agent |
21 | | * |
22 | | * @{ |
23 | | */ |
24 | | |
25 | | #include <net-snmp/net-snmp-config.h> |
26 | | #include <net-snmp/net-snmp-features.h> |
27 | | |
28 | | #ifdef HAVE_UNISTD_H |
29 | | #include <unistd.h> |
30 | | #endif |
31 | | #ifdef HAVE_NETDB_H |
32 | | #include <netdb.h> |
33 | | #endif |
34 | | #ifdef HAVE_STDLIB_H |
35 | | #include <stdlib.h> |
36 | | #endif |
37 | | #ifdef HAVE_STRING_H |
38 | | #include <string.h> |
39 | | #else |
40 | | #include <strings.h> |
41 | | #endif |
42 | | #ifdef TIME_WITH_SYS_TIME |
43 | | # include <sys/time.h> |
44 | | # include <time.h> |
45 | | #else |
46 | | # ifdef HAVE_SYS_TIME_H |
47 | | # include <sys/time.h> |
48 | | # else |
49 | | # include <time.h> |
50 | | # endif |
51 | | #endif |
52 | | #ifdef HAVE_SYS_SOCKET_H |
53 | | #include <sys/socket.h> |
54 | | #endif |
55 | | #ifdef HAVE_NETINET_IN_H |
56 | | #include <netinet/in.h> |
57 | | #endif |
58 | | #include <net-snmp/utilities.h> |
59 | | |
60 | | #include <net-snmp/net-snmp-includes.h> |
61 | | #include <net-snmp/agent/net-snmp-agent-includes.h> |
62 | | #include <net-snmp/agent/agent_trap.h> |
63 | | #include <net-snmp/agent/snmp_agent.h> |
64 | | #include <net-snmp/agent/agent_callbacks.h> |
65 | | #include "agent_global_vars.h" |
66 | | |
67 | | #include <net-snmp/agent/agent_module_config.h> |
68 | | #include <net-snmp/agent/mib_module_config.h> |
69 | | |
70 | | #ifdef USING_AGENTX_PROTOCOL_MODULE |
71 | | #include "agentx/protocol.h" |
72 | | #endif |
73 | | |
74 | | #ifdef USING_NOTIFICATION_SNMPNOTIFYTABLE_DATA_MODULE |
75 | | #include "mibgroup/notification/snmpNotifyTable_data.h" |
76 | | #endif |
77 | | |
78 | | netsnmp_feature_child_of(agent_trap_all, libnetsnmpagent); |
79 | | |
80 | | netsnmp_feature_child_of(trap_vars_with_context, agent_trap_all); |
81 | | netsnmp_feature_child_of(remove_trap_session, agent_trap_all); |
82 | | |
83 | | netsnmp_feature_child_of(send_v3trap,netsnmp_unused); |
84 | | netsnmp_feature_child_of(send_trap_pdu,netsnmp_unused); |
85 | | |
86 | | struct trap_sink { |
87 | | netsnmp_session *sesp; |
88 | | struct trap_sink *next; |
89 | | int pdutype; |
90 | | int version; |
91 | | }; |
92 | | |
93 | | struct trap_sink *sinks = NULL; |
94 | | |
95 | | const oid objid_enterprisetrap[] = { NETSNMP_NOTIFICATION_MIB }; |
96 | | const oid trap_version_id[] = { NETSNMP_SYSTEM_MIB }; |
97 | | const int enterprisetrap_len = OID_LENGTH(objid_enterprisetrap); |
98 | | const int trap_version_id_len = OID_LENGTH(trap_version_id); |
99 | | |
100 | | #define SNMPV2_TRAPS_PREFIX SNMP_OID_SNMPMODULES,1,1,5 |
101 | | const oid trap_prefix[] = { SNMPV2_TRAPS_PREFIX }; |
102 | | const oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 }; /* SNMPv2-MIB */ |
103 | | |
104 | | #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4 |
105 | | const oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 }; |
106 | | const oid snmptrapenterprise_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 }; |
107 | | const oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 }; |
108 | | const size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid); |
109 | | const size_t snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid); |
110 | | const size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid); |
111 | | |
112 | | #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1 |
113 | | const oid agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 }; |
114 | | const size_t agentaddr_oid_len = OID_LENGTH(agentaddr_oid); |
115 | | const oid community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 }; |
116 | | const size_t community_oid_len = OID_LENGTH(community_oid); |
117 | | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
118 | | char *snmp_trapcommunity = NULL; |
119 | | #endif |
120 | | |
121 | | |
122 | 0 | #define SNMP_AUTHENTICATED_TRAPS_ENABLED 1 |
123 | 0 | #define SNMP_AUTHENTICATED_TRAPS_DISABLED 2 |
124 | | |
125 | | long snmp_enableauthentraps = SNMP_AUTHENTICATED_TRAPS_DISABLED; |
126 | | int snmp_enableauthentrapsset = 0; |
127 | | |
128 | | /* |
129 | | * Prototypes |
130 | | */ |
131 | | /* |
132 | | * static void free_trap_session (struct trap_sink *sp); |
133 | | * static void send_v1_trap (netsnmp_session *, int, int); |
134 | | * static void send_v2_trap (netsnmp_session *, int, int, int); |
135 | | */ |
136 | | |
137 | | |
138 | | /******************* |
139 | | * |
140 | | * Trap session handling |
141 | | * |
142 | | *******************/ |
143 | | |
144 | | void |
145 | | init_traps(void) |
146 | 3.06k | { |
147 | 3.06k | } |
148 | | |
149 | | static void |
150 | | free_trap_session(struct trap_sink *sp) |
151 | 0 | { |
152 | 0 | DEBUGMSGTL(("trap", "freeing callback trap session (%p, %p)\n", sp, sp->sesp)); |
153 | 0 | snmp_close(sp->sesp); |
154 | 0 | free(sp); |
155 | 0 | } |
156 | | |
157 | | #ifndef NETSNMP_NO_TRAP_STATS |
158 | | static void |
159 | | _dump_trap_stats(netsnmp_session *sess) |
160 | 0 | { |
161 | 0 | if (NULL == sess || NULL == sess->trap_stats) |
162 | 0 | return; |
163 | | |
164 | 0 | DEBUGIF("stats:notif") { |
165 | 0 | DEBUGMSGT_NC(("stats:notif", "%s inform stats\n", sess->paramName)); |
166 | 0 | DEBUGMSGT_NC(("stats:notif", " %ld sends, last @ %ld\n", |
167 | 0 | sess->trap_stats->sent_count, |
168 | 0 | sess->trap_stats->sent_last_sent)); |
169 | 0 | DEBUGMSGT_NC(("stats:notif", " %ld acks, last @ %ld\n", |
170 | 0 | sess->trap_stats->ack_count, |
171 | 0 | sess->trap_stats->ack_last_rcvd)); |
172 | 0 | DEBUGMSGT_NC(("stats:notif", " %ld failed sends, last @ %ld\n", |
173 | 0 | sess->trap_stats->sent_fail_count, |
174 | 0 | sess->trap_stats->sent_last_fail)); |
175 | 0 | DEBUGMSGT_NC(("stats:notif", " %ld timeouts, last @ %ld\n", |
176 | 0 | sess->trap_stats->timeouts, |
177 | 0 | sess->trap_stats->sent_last_timeout)); |
178 | 0 | DEBUGMSGT_NC(("stats:notif", " %ld v3 errs, last @ %ld\n", |
179 | 0 | sess->trap_stats->sec_err_count, |
180 | 0 | sess->trap_stats->sec_err_last)); |
181 | 0 | } |
182 | 0 | } |
183 | | #endif /* NETSNMP_NO_TRAP_STATS */ |
184 | | |
185 | | int |
186 | | netsnmp_add_closable_notification_session(netsnmp_session *ss, int close_sess, int pdutype, |
187 | | int confirm, int version, const char *name, |
188 | | const char *tag, const char* profile) |
189 | 0 | { |
190 | 0 | if (NETSNMP_RUNTIME_PROTOCOL_SKIP(version)) { |
191 | 0 | DEBUGMSGTL(("trap", "skipping trap sink (version 0x%02x disabled)\n", |
192 | 0 | version)); |
193 | 0 | return 0; |
194 | 0 | } |
195 | 0 | if (snmp_callback_available(SNMP_CALLBACK_APPLICATION, |
196 | 0 | SNMPD_CALLBACK_REGISTER_NOTIFICATIONS) == |
197 | 0 | SNMPERR_SUCCESS) { |
198 | | /* |
199 | | * something else wants to handle notification registrations |
200 | | */ |
201 | 0 | struct agent_add_trap_args args; |
202 | 0 | DEBUGMSGTL(("trap", "adding callback trap sink (%p)\n", ss)); |
203 | 0 | args.ss = ss; |
204 | 0 | args.close_sess = close_sess; |
205 | 0 | args.confirm = confirm; |
206 | 0 | args.nameData = name; |
207 | 0 | args.nameLen = (NULL == name) ? 0 : strlen(name); |
208 | 0 | args.tagData = tag; |
209 | 0 | args.tagLen = (NULL == tag) ? 0 : strlen(tag); |
210 | 0 | args.profileData = profile; |
211 | 0 | args.profileLen = (NULL == profile) ? 0: strlen(profile); |
212 | 0 | snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
213 | 0 | SNMPD_CALLBACK_REGISTER_NOTIFICATIONS, |
214 | 0 | (void *) &args); |
215 | 0 | if (args.rc != SNMPERR_SUCCESS) |
216 | 0 | return 0; |
217 | 0 | } else { |
218 | | /* |
219 | | * no other support exists, handle it ourselves. |
220 | | */ |
221 | 0 | struct trap_sink *new_sink; |
222 | |
|
223 | 0 | DEBUGMSGTL(("trap", "adding internal trap sink\n")); |
224 | 0 | new_sink = (struct trap_sink *) malloc(sizeof(*new_sink)); |
225 | 0 | if (new_sink == NULL) |
226 | 0 | return 0; |
227 | | |
228 | 0 | new_sink->sesp = ss; |
229 | 0 | new_sink->pdutype = pdutype; |
230 | 0 | new_sink->version = version; |
231 | 0 | new_sink->next = sinks; |
232 | 0 | sinks = new_sink; |
233 | 0 | } |
234 | | |
235 | 0 | return 1; |
236 | 0 | } |
237 | | |
238 | | /* This is a wrapper to netsnmp_add_closable_notification_session |
239 | | for backward compatibility |
240 | | */ |
241 | | int |
242 | | netsnmp_add_notification_session(netsnmp_session *ss, int pdutype, |
243 | | int confirm, int version, const char *name, |
244 | | const char *tag, const char* profile) |
245 | 0 | { |
246 | 0 | return netsnmp_add_closable_notification_session(ss, 1, pdutype, confirm, |
247 | 0 | version, name, |
248 | 0 | tag, profile); |
249 | 0 | } |
250 | | |
251 | | /* |
252 | | * xxx needs update to support embedded NUL. |
253 | | * xxx should probably also be using and unregister callback, similar to |
254 | | * how registration is done. |
255 | | */ |
256 | | void |
257 | | netsnmp_unregister_notification(const char *name, u_char len) |
258 | 0 | { |
259 | 0 | if (snmp_callback_available(SNMP_CALLBACK_APPLICATION, |
260 | 0 | SNMPD_CALLBACK_UNREGISTER_NOTIFICATIONS) == |
261 | 0 | SNMPERR_SUCCESS) { |
262 | | /* |
263 | | * something else wants to handle notification registrations |
264 | | */ |
265 | 0 | struct agent_add_trap_args args; |
266 | 0 | DEBUGMSGTL(("trap", "removing callback trap sink\n")); |
267 | 0 | args.nameData = name; |
268 | 0 | args.nameLen = len; |
269 | 0 | snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
270 | 0 | SNMPD_CALLBACK_UNREGISTER_NOTIFICATIONS, |
271 | 0 | (void *) &args); |
272 | 0 | } else |
273 | 0 | NETSNMP_LOGONCE((LOG_WARNING, |
274 | 0 | "netsnmp_unregister_notification not supported\n")); |
275 | 0 | } |
276 | | |
277 | | int |
278 | | handle_disconnect_packet(int operation, netsnmp_session *session, int reqid, |
279 | | netsnmp_pdu *pdu, void *magic) |
280 | 0 | { |
281 | 0 | if (NETSNMP_CALLBACK_OP_DISCONNECT == operation) { |
282 | 0 | netsnmp_transport *t = snmp_sess_transport(snmp_sess_pointer(session)); |
283 | 0 | char *addr_string = NULL; |
284 | |
|
285 | 0 | if (t && (addr_string = netsnmp_transport_peer_string(t, t->remote, t->remote_length))) { |
286 | 0 | snmp_log(LOG_WARNING, |
287 | 0 | "send_trap: session closed by snmptrap (%s)\n", addr_string); |
288 | 0 | free(addr_string); |
289 | 0 | } else { |
290 | 0 | snmp_log(LOG_WARNING, |
291 | 0 | "send_trap: session %8p closed by snmptrap\n", session); |
292 | 0 | } |
293 | 0 | snmp_log(LOG_WARNING, |
294 | 0 | "send_trap: re-opening of TCP connection is not supported.\n"); |
295 | |
|
296 | 0 | netsnmp_unregister_notification(session->paramName, strlen(session->paramName)); |
297 | 0 | } |
298 | 0 | return 1; |
299 | 0 | } |
300 | | |
301 | | int |
302 | | add_trap_session(netsnmp_session * ss, int pdutype, int confirm, |
303 | | int version) |
304 | 0 | { |
305 | 0 | return netsnmp_add_closable_notification_session(ss, 0, pdutype, confirm, version, |
306 | 0 | NULL, NULL, NULL); |
307 | 0 | } |
308 | | |
309 | | #ifndef NETSNMP_FEATURE_REMOVE_REMOVE_TRAP_SESSION |
310 | | int |
311 | | remove_trap_session(netsnmp_session * ss) |
312 | 0 | { |
313 | 0 | struct trap_sink *sp = sinks, *prev = NULL; |
314 | |
|
315 | 0 | DEBUGMSGTL(("trap", "removing trap sessions\n")); |
316 | 0 | while (sp) { |
317 | 0 | if (sp->sesp == ss) { |
318 | 0 | if (prev) { |
319 | 0 | prev->next = sp->next; |
320 | 0 | } else { |
321 | 0 | sinks = sp->next; |
322 | 0 | } |
323 | | /* |
324 | | * I don't believe you *really* want to close the session here; |
325 | | * it may still be in use for other purposes. In particular this |
326 | | * is awkward for AgentX, since we want to call this function |
327 | | * from the session's callback. Let's just free the trapsink |
328 | | * data structure. [jbpn] |
329 | | */ |
330 | | /* |
331 | | * free_trap_session(sp); |
332 | | */ |
333 | 0 | DEBUGMSGTL(("trap", "removing trap session (%p, %p)\n", sp, sp->sesp)); |
334 | 0 | free(sp); |
335 | 0 | return 1; |
336 | 0 | } |
337 | 0 | prev = sp; |
338 | 0 | sp = sp->next; |
339 | 0 | } |
340 | 0 | return 0; |
341 | 0 | } |
342 | | #endif /* NETSNMP_FEATURE_REMOVE_REMOVE_TRAP_SESSION */ |
343 | | |
344 | | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
345 | | netsnmp_session * |
346 | | netsnmp_create_v1v2_notification_session(const char *sink, const char* sinkport, |
347 | | const char *com, const char *src, |
348 | | int version, int pdutype, |
349 | | const char *name, const char *tag, |
350 | | const char* profile) |
351 | 0 | { |
352 | 0 | netsnmp_transport *t; |
353 | 0 | netsnmp_session session, *sesp; |
354 | 0 | netsnmp_tdomain_spec tspec; |
355 | 0 | char tmp[SPRINT_MAX_LEN]; |
356 | 0 | int rc; |
357 | 0 | const char *client_addr = NULL; |
358 | |
|
359 | 0 | if (NETSNMP_RUNTIME_PROTOCOL_SKIP(version)) { |
360 | 0 | config_perror("SNMP version disabled"); |
361 | 0 | DEBUGMSGTL(("trap", "skipping trap sink (version 0x%02x disabled)\n", |
362 | 0 | version)); |
363 | 0 | return NULL; |
364 | 0 | } |
365 | | |
366 | 0 | snmp_sess_init(&session); |
367 | 0 | session.callback = handle_disconnect_packet; |
368 | 0 | session.version = version; |
369 | 0 | if (com) { |
370 | 0 | session.community = (u_char *)strdup(com); |
371 | 0 | session.community_len = strlen(com); |
372 | 0 | } |
373 | | |
374 | | /* |
375 | | * for informs, set retries to default |
376 | | */ |
377 | 0 | if (SNMP_MSG_INFORM == pdutype) { |
378 | 0 | session.timeout = SNMP_DEFAULT_TIMEOUT; |
379 | 0 | session.retries = SNMP_DEFAULT_RETRIES; |
380 | 0 | } |
381 | |
|
382 | 0 | memset(&tspec, 0, sizeof(netsnmp_tdomain_spec)); |
383 | | |
384 | | /* |
385 | | * use specified source or client addr, if available. If no, and |
386 | | * if the sink is localhost, bind to localhost, to reduce open ports. |
387 | | */ |
388 | 0 | if (NULL != src) |
389 | 0 | tspec.source = src; |
390 | 0 | else { |
391 | 0 | client_addr = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
392 | 0 | NETSNMP_DS_LIB_CLIENT_ADDR); |
393 | 0 | if ((NULL == client_addr) && |
394 | 0 | ((0 == strcmp("localhost",sink)) || |
395 | 0 | (0 == strcmp("127.0.0.1",sink)))) |
396 | 0 | client_addr = "localhost"; |
397 | 0 | tspec.source = client_addr; |
398 | 0 | } |
399 | 0 | session.localname = tspec.source ? strdup(tspec.source) : NULL; |
400 | |
|
401 | 0 | tspec.application = "snmptrap"; |
402 | 0 | if (NULL == sinkport) |
403 | 0 | tspec.target = sink; |
404 | 0 | else { |
405 | 0 | snprintf(tmp, sizeof(tmp)-1,"%s:%s", sink, sinkport); |
406 | 0 | tspec.target = tmp; |
407 | 0 | } |
408 | 0 | tspec.default_domain = NULL; |
409 | 0 | tspec.default_target = sinkport; |
410 | 0 | t = netsnmp_tdomain_transport_tspec(&tspec); |
411 | 0 | if ((NULL == t) || |
412 | 0 | ((sesp = snmp_add(&session, t, NULL, NULL)) == NULL)) { |
413 | | /** diagnose snmp_open errors with the input netsnmp_session pointer */ |
414 | 0 | snmp_sess_perror("snmpd: netsnmp_create_notification_session", |
415 | 0 | &session); |
416 | 0 | netsnmp_cleanup_session(&session); |
417 | | /* transport freed by snmp_add */ |
418 | 0 | return NULL; |
419 | 0 | } |
420 | | |
421 | 0 | netsnmp_cleanup_session(&session); |
422 | |
|
423 | 0 | rc = netsnmp_add_closable_notification_session(sesp, |
424 | 0 | !(t->flags & NETSNMP_TRANSPORT_FLAG_STREAM), |
425 | 0 | pdutype, |
426 | 0 | (pdutype == SNMP_MSG_INFORM), |
427 | 0 | version, name, tag, profile); |
428 | 0 | if (0 == rc) |
429 | 0 | return NULL; |
430 | | |
431 | 0 | return sesp; |
432 | 0 | } |
433 | | |
434 | | int |
435 | | create_trap_session_with_src(const char *sink, const char* sinkport, |
436 | | const char *com, const char *src, int version, |
437 | | int pdutype) |
438 | 0 | { |
439 | 0 | void *ss = netsnmp_create_v1v2_notification_session(sink, sinkport, com, |
440 | 0 | src, version, pdutype, |
441 | 0 | NULL, NULL, NULL); |
442 | 0 | return (ss != NULL); |
443 | 0 | } |
444 | | |
445 | | int |
446 | | create_trap_session2(const char *sink, const char* sinkport, |
447 | | char *com, int version, int pdutype) |
448 | 0 | { |
449 | 0 | return create_trap_session_with_src(sink, sinkport, com, NULL, version, |
450 | 0 | pdutype); |
451 | 0 | } |
452 | | |
453 | | int |
454 | | create_trap_session(char *sink, u_short sinkport, |
455 | | char *com, int version, int pdutype) |
456 | 0 | { |
457 | 0 | void *ss; |
458 | 0 | char buf[sizeof(sinkport) * 3 + 2]; |
459 | 0 | if (sinkport != 0) { |
460 | 0 | sprintf(buf, ":%hu", sinkport); |
461 | 0 | snmp_log(LOG_NOTICE, |
462 | 0 | "Using a separate port number is deprecated, please correct " |
463 | 0 | "the sink specification instead"); |
464 | 0 | } |
465 | 0 | ss = netsnmp_create_v1v2_notification_session(sink, sinkport ? buf : NULL, |
466 | 0 | com, NULL, version, pdutype, |
467 | 0 | NULL, NULL, NULL); |
468 | 0 | return (ss != NULL); |
469 | 0 | } |
470 | | |
471 | | #endif /* support for community based SNMP */ |
472 | | |
473 | | void |
474 | | snmpd_free_trapsinks(void) |
475 | 24.5k | { |
476 | 24.5k | struct trap_sink *sp = sinks; |
477 | 24.5k | DEBUGMSGTL(("trap", "freeing trap sessions\n")); |
478 | 24.5k | while (sp) { |
479 | 0 | sinks = sinks->next; |
480 | 0 | free_trap_session(sp); |
481 | 0 | sp = sinks; |
482 | 0 | } |
483 | 24.5k | } |
484 | | |
485 | | /******************* |
486 | | * |
487 | | * Trap handling |
488 | | * |
489 | | *******************/ |
490 | | |
491 | | |
492 | | netsnmp_pdu* |
493 | | convert_v2pdu_to_v1( netsnmp_pdu* template_v2pdu ) |
494 | 0 | { |
495 | 0 | netsnmp_pdu *template_v1pdu; |
496 | 0 | netsnmp_variable_list *first_vb, *vblist; |
497 | 0 | netsnmp_variable_list *var; |
498 | | |
499 | | /* |
500 | | * Make a copy of the v2 Trap PDU |
501 | | * before starting to convert this |
502 | | * into a v1 Trap PDU. |
503 | | */ |
504 | 0 | template_v1pdu = snmp_clone_pdu( template_v2pdu); |
505 | 0 | if (!template_v1pdu) { |
506 | 0 | snmp_log(LOG_WARNING, |
507 | 0 | "send_trap: failed to copy v1 template PDU\n"); |
508 | 0 | return NULL; |
509 | 0 | } |
510 | 0 | template_v1pdu->command = SNMP_MSG_TRAP; |
511 | 0 | first_vb = template_v1pdu->variables; |
512 | 0 | vblist = template_v1pdu->variables; |
513 | | |
514 | | /* |
515 | | * The first varbind should be the system uptime. |
516 | | */ |
517 | 0 | if (!vblist || |
518 | 0 | snmp_oid_compare(vblist->name, vblist->name_length, |
519 | 0 | sysuptime_oid, sysuptime_oid_len)) { |
520 | 0 | snmp_log(LOG_WARNING, |
521 | 0 | "send_trap: no v2 sysUptime varbind to set from\n"); |
522 | 0 | snmp_free_pdu(template_v1pdu); |
523 | 0 | return NULL; |
524 | 0 | } |
525 | 0 | template_v1pdu->time = *vblist->val.integer; |
526 | 0 | vblist = vblist->next_variable; |
527 | | |
528 | | /* |
529 | | * The second varbind should be the snmpTrapOID. |
530 | | */ |
531 | 0 | if (!vblist || |
532 | 0 | snmp_oid_compare(vblist->name, vblist->name_length, |
533 | 0 | snmptrap_oid, snmptrap_oid_len)) { |
534 | 0 | snmp_log(LOG_WARNING, |
535 | 0 | "send_trap: no v2 trapOID varbind to set from\n"); |
536 | 0 | snmp_free_pdu(template_v1pdu); |
537 | 0 | return NULL; |
538 | 0 | } |
539 | | |
540 | | /* |
541 | | * Check the v2 varbind list for any varbinds |
542 | | * that are not valid in an SNMPv1 trap. |
543 | | * This basically means Counter64 values. |
544 | | * |
545 | | * RFC 2089 said to omit such varbinds from the list. |
546 | | * RFC 2576/3584 say to drop the trap completely. |
547 | | */ |
548 | 0 | for (var = vblist->next_variable; var; var = var->next_variable) { |
549 | 0 | if ( var->type == ASN_COUNTER64 ) { |
550 | 0 | snmp_log(LOG_WARNING, |
551 | 0 | "send_trap: v1 traps can't carry Counter64 varbinds\n"); |
552 | 0 | snmp_free_pdu(template_v1pdu); |
553 | 0 | return NULL; |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | | /* |
558 | | * Set the generic & specific trap types, |
559 | | * and the enterprise field from the v2 varbind list. |
560 | | * If there's an agentIPAddress varbind, set the agent_addr too |
561 | | */ |
562 | 0 | if (!snmp_oid_compare(vblist->val.objid, OID_LENGTH(trap_prefix), |
563 | 0 | trap_prefix, OID_LENGTH(trap_prefix))) { |
564 | | /* |
565 | | * For 'standard' traps, extract the generic trap type |
566 | | * from the snmpTrapOID value, and take the enterprise |
567 | | * value from the 'snmpEnterprise' varbind. |
568 | | */ |
569 | 0 | template_v1pdu->trap_type = |
570 | 0 | vblist->val.objid[OID_LENGTH(trap_prefix)] - 1; |
571 | 0 | template_v1pdu->specific_type = 0; |
572 | |
|
573 | 0 | var = find_varbind_in_list( vblist, |
574 | 0 | snmptrapenterprise_oid, |
575 | 0 | snmptrapenterprise_oid_len); |
576 | 0 | if (var) { |
577 | 0 | template_v1pdu->enterprise_length = var->val_len/sizeof(oid); |
578 | 0 | template_v1pdu->enterprise = |
579 | 0 | snmp_duplicate_objid(var->val.objid, |
580 | 0 | template_v1pdu->enterprise_length); |
581 | 0 | } else { |
582 | 0 | template_v1pdu->enterprise = NULL; |
583 | 0 | template_v1pdu->enterprise_length = 0; /* XXX ??? */ |
584 | 0 | } |
585 | 0 | } else { |
586 | | /* |
587 | | * For enterprise-specific traps, split the snmpTrapOID value |
588 | | * into enterprise and specific trap |
589 | | */ |
590 | 0 | size_t len = vblist->val_len / sizeof(oid); |
591 | 0 | if ( len <= 2 ) { |
592 | 0 | snmp_log(LOG_WARNING, |
593 | 0 | "send_trap: v2 trapOID too short (%d)\n", (int)len); |
594 | 0 | snmp_free_pdu(template_v1pdu); |
595 | 0 | return NULL; |
596 | 0 | } |
597 | 0 | template_v1pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC; |
598 | 0 | template_v1pdu->specific_type = vblist->val.objid[len - 1]; |
599 | 0 | len--; |
600 | 0 | if (vblist->val.objid[len-1] == 0) |
601 | 0 | len--; |
602 | 0 | SNMP_FREE(template_v1pdu->enterprise); |
603 | 0 | template_v1pdu->enterprise = |
604 | 0 | snmp_duplicate_objid(vblist->val.objid, len); |
605 | 0 | template_v1pdu->enterprise_length = len; |
606 | 0 | } |
607 | 0 | var = find_varbind_in_list( vblist, agentaddr_oid, |
608 | 0 | agentaddr_oid_len); |
609 | 0 | if (var) { |
610 | 0 | memcpy(template_v1pdu->agent_addr, |
611 | 0 | var->val.string, 4); |
612 | 0 | } |
613 | | |
614 | | /* |
615 | | * The remainder of the v2 varbind list is kept |
616 | | * as the v2 varbind list. Update the PDU and |
617 | | * free the two redundant varbinds. |
618 | | */ |
619 | 0 | template_v1pdu->variables = vblist->next_variable; |
620 | 0 | vblist->next_variable = NULL; |
621 | 0 | snmp_free_varbind( first_vb ); |
622 | | |
623 | 0 | return template_v1pdu; |
624 | 0 | } |
625 | | |
626 | | /* |
627 | | * Set t_oid from the PDU enterprise & specific trap fields. |
628 | | */ |
629 | | int |
630 | | netsnmp_build_trap_oid(netsnmp_pdu *pdu, oid *t_oid, size_t *t_oid_len) |
631 | 0 | { |
632 | 0 | if (NULL == pdu || NULL == t_oid || NULL == t_oid_len) |
633 | 0 | return SNMPERR_GENERR; |
634 | 0 | if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) { |
635 | 0 | if (*t_oid_len < (pdu->enterprise_length + 2)) |
636 | 0 | return SNMPERR_LONG_OID; |
637 | 0 | memcpy(t_oid, pdu->enterprise, pdu->enterprise_length*sizeof(oid)); |
638 | 0 | *t_oid_len = pdu->enterprise_length; |
639 | 0 | t_oid[(*t_oid_len)++] = 0; |
640 | 0 | t_oid[(*t_oid_len)++] = pdu->specific_type; |
641 | 0 | } else { |
642 | | /** use cold_start_oid as template */ |
643 | 0 | if (*t_oid_len < OID_LENGTH(cold_start_oid)) |
644 | 0 | return SNMPERR_LONG_OID; |
645 | 0 | memcpy(t_oid, cold_start_oid, sizeof(cold_start_oid)); |
646 | 0 | t_oid[9] = pdu->trap_type + 1; /* set actual trap type */ |
647 | 0 | *t_oid_len = OID_LENGTH(cold_start_oid); |
648 | 0 | } |
649 | 0 | return SNMPERR_SUCCESS; |
650 | 0 | } |
651 | | |
652 | | netsnmp_pdu* |
653 | | convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu ) |
654 | 0 | { |
655 | 0 | netsnmp_pdu *template_v2pdu; |
656 | 0 | netsnmp_variable_list *var; |
657 | 0 | oid enterprise[MAX_OID_LEN]; |
658 | 0 | size_t enterprise_len; |
659 | | |
660 | | /* |
661 | | * Make a copy of the v1 Trap PDU |
662 | | * before starting to convert this |
663 | | * into a v2 Trap PDU. |
664 | | */ |
665 | 0 | template_v2pdu = snmp_clone_pdu( template_v1pdu); |
666 | 0 | if (!template_v2pdu) { |
667 | 0 | snmp_log(LOG_WARNING, |
668 | 0 | "send_trap: failed to copy v2 template PDU\n"); |
669 | 0 | return NULL; |
670 | 0 | } |
671 | 0 | template_v2pdu->command = SNMP_MSG_TRAP2; |
672 | | |
673 | | /* |
674 | | * Insert an snmpTrapOID varbind before the original v1 varbind list |
675 | | * either using one of the standard defined trap OIDs, |
676 | | * or constructing this from the PDU enterprise & specific trap fields |
677 | | */ |
678 | 0 | var = NULL; |
679 | 0 | enterprise_len = OID_LENGTH(enterprise); |
680 | 0 | if ((netsnmp_build_trap_oid(template_v1pdu, enterprise, &enterprise_len) |
681 | 0 | != SNMPERR_SUCCESS) || |
682 | 0 | !snmp_varlist_add_variable( &var, |
683 | 0 | snmptrap_oid, snmptrap_oid_len, |
684 | 0 | ASN_OBJECT_ID, |
685 | 0 | (u_char*)enterprise, enterprise_len*sizeof(oid))) { |
686 | 0 | snmp_log(LOG_WARNING, |
687 | 0 | "send_trap: failed to insert copied snmpTrapOID varbind\n"); |
688 | 0 | snmp_free_pdu(template_v2pdu); |
689 | 0 | return NULL; |
690 | 0 | } |
691 | 0 | var->next_variable = template_v2pdu->variables; |
692 | 0 | template_v2pdu->variables = var; |
693 | | |
694 | | /* |
695 | | * Insert a sysUptime varbind at the head of the v2 varbind list |
696 | | */ |
697 | 0 | var = NULL; |
698 | 0 | if (!snmp_varlist_add_variable( &var, |
699 | 0 | sysuptime_oid, sysuptime_oid_len, |
700 | 0 | ASN_TIMETICKS, |
701 | 0 | (u_char*)&(template_v1pdu->time), |
702 | 0 | sizeof(template_v1pdu->time))) { |
703 | 0 | snmp_log(LOG_WARNING, |
704 | 0 | "send_trap: failed to insert copied sysUptime varbind\n"); |
705 | 0 | snmp_free_pdu(template_v2pdu); |
706 | 0 | return NULL; |
707 | 0 | } |
708 | 0 | var->next_variable = template_v2pdu->variables; |
709 | 0 | template_v2pdu->variables = var; |
710 | | |
711 | | /* |
712 | | * Append the other three conversion varbinds, |
713 | | * (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise) |
714 | | * if they're not already present. |
715 | | * But don't bomb out completely if there are problems. |
716 | | */ |
717 | 0 | var = find_varbind_in_list( template_v2pdu->variables, |
718 | 0 | agentaddr_oid, agentaddr_oid_len); |
719 | 0 | if (!var && (template_v1pdu->agent_addr[0] |
720 | 0 | || template_v1pdu->agent_addr[1] |
721 | 0 | || template_v1pdu->agent_addr[2] |
722 | 0 | || template_v1pdu->agent_addr[3])) { |
723 | 0 | if (!snmp_varlist_add_variable( &(template_v2pdu->variables), |
724 | 0 | agentaddr_oid, agentaddr_oid_len, |
725 | 0 | ASN_IPADDRESS, |
726 | 0 | (u_char*)&(template_v1pdu->agent_addr), |
727 | 0 | sizeof(template_v1pdu->agent_addr))) |
728 | 0 | snmp_log(LOG_WARNING, |
729 | 0 | "send_trap: failed to append snmpTrapAddr varbind\n"); |
730 | 0 | } |
731 | 0 | var = find_varbind_in_list( template_v2pdu->variables, |
732 | 0 | community_oid, community_oid_len); |
733 | 0 | if (!var && template_v1pdu->community) { |
734 | 0 | if (!snmp_varlist_add_variable( &(template_v2pdu->variables), |
735 | 0 | community_oid, community_oid_len, |
736 | 0 | ASN_OCTET_STR, |
737 | 0 | template_v1pdu->community, |
738 | 0 | template_v1pdu->community_len)) |
739 | 0 | snmp_log(LOG_WARNING, |
740 | 0 | "send_trap: failed to append snmpTrapCommunity varbind\n"); |
741 | 0 | } |
742 | 0 | var = find_varbind_in_list( template_v2pdu->variables, |
743 | 0 | snmptrapenterprise_oid, |
744 | 0 | snmptrapenterprise_oid_len); |
745 | 0 | if (!var) { |
746 | 0 | if (!snmp_varlist_add_variable( &(template_v2pdu->variables), |
747 | 0 | snmptrapenterprise_oid, snmptrapenterprise_oid_len, |
748 | 0 | ASN_OBJECT_ID, |
749 | 0 | (u_char*)template_v1pdu->enterprise, |
750 | 0 | template_v1pdu->enterprise_length*sizeof(oid))) |
751 | 0 | snmp_log(LOG_WARNING, |
752 | 0 | "send_trap: failed to append snmpEnterprise varbind\n"); |
753 | 0 | } |
754 | 0 | return template_v2pdu; |
755 | 0 | } |
756 | | |
757 | | /** |
758 | | * This function allows you to make a distinction between generic |
759 | | * traps from different classes of equipment. For example, you may want |
760 | | * to handle a SNMP_TRAP_LINKDOWN trap for a particular device in a |
761 | | * different manner to a generic system SNMP_TRAP_LINKDOWN trap. |
762 | | * |
763 | | * |
764 | | * @param trap is the generic trap type. The trap types are: |
765 | | * - SNMP_TRAP_COLDSTART: |
766 | | * cold start |
767 | | * - SNMP_TRAP_WARMSTART: |
768 | | * warm start |
769 | | * - SNMP_TRAP_LINKDOWN: |
770 | | * link down |
771 | | * - SNMP_TRAP_LINKUP: |
772 | | * link up |
773 | | * - SNMP_TRAP_AUTHFAIL: |
774 | | * authentication failure |
775 | | * - SNMP_TRAP_EGPNEIGHBORLOSS: |
776 | | * egp neighbor loss |
777 | | * - SNMP_TRAP_ENTERPRISESPECIFIC: |
778 | | * enterprise specific |
779 | | * |
780 | | * @param specific is the specific trap value. |
781 | | * |
782 | | * @param enterprise is an enterprise oid in which you want to send specific |
783 | | * traps from. |
784 | | * |
785 | | * @param enterprise_length is the length of the enterprise oid, use macro, |
786 | | * OID_LENGTH, to compute length. |
787 | | * |
788 | | * @param vars is used to supply list of variable bindings to form an SNMPv2 |
789 | | * trap. |
790 | | * |
791 | | * @param context currently unused |
792 | | * |
793 | | * @param flags currently unused |
794 | | * |
795 | | * @return void |
796 | | * |
797 | | * @see send_easy_trap |
798 | | * @see send_v2trap |
799 | | */ |
800 | | int |
801 | | netsnmp_send_traps(int trap, int specific, |
802 | | const oid * enterprise, int enterprise_length, |
803 | | netsnmp_variable_list * vars, |
804 | | const char * context, int flags) |
805 | 0 | { |
806 | 0 | netsnmp_pdu *template_v1pdu; |
807 | 0 | netsnmp_pdu *template_v2pdu; |
808 | 0 | netsnmp_variable_list *vblist = NULL; |
809 | 0 | netsnmp_variable_list *trap_vb; |
810 | 0 | netsnmp_variable_list *var; |
811 | 0 | in_addr_t *pdu_in_addr_t; |
812 | 0 | u_long uptime; |
813 | 0 | struct trap_sink *sink; |
814 | 0 | const char *v1trapaddress; |
815 | 0 | int res = 0; |
816 | |
|
817 | 0 | DEBUGMSGTL(( "trap", "send_trap %d %d ", trap, specific)); |
818 | 0 | DEBUGMSGOID(("trap", enterprise, enterprise_length)); |
819 | 0 | DEBUGMSG(( "trap", "\n")); |
820 | |
|
821 | 0 | if (vars) { |
822 | 0 | vblist = snmp_clone_varbind( vars ); |
823 | 0 | if (!vblist) { |
824 | 0 | snmp_log(LOG_WARNING, |
825 | 0 | "send_trap: failed to clone varbind list\n"); |
826 | 0 | return -1; |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | 0 | if ( trap == -1 ) { |
831 | | /* |
832 | | * Construct the SNMPv2-style notification PDU |
833 | | */ |
834 | 0 | if (!vblist) { |
835 | 0 | snmp_log(LOG_WARNING, |
836 | 0 | "send_trap: called with NULL v2 information\n"); |
837 | 0 | return -1; |
838 | 0 | } |
839 | 0 | template_v2pdu = snmp_pdu_create(SNMP_MSG_TRAP2); |
840 | 0 | if (!template_v2pdu) { |
841 | 0 | snmp_log(LOG_WARNING, |
842 | 0 | "send_trap: failed to construct v2 template PDU\n"); |
843 | 0 | snmp_free_varbind(vblist); |
844 | 0 | return -1; |
845 | 0 | } |
846 | | |
847 | | /* |
848 | | * Check the varbind list we've been given. |
849 | | * If it starts with a 'sysUptime.0' varbind, then use that. |
850 | | * Otherwise, prepend a suitable 'sysUptime.0' varbind. |
851 | | */ |
852 | 0 | if (!snmp_oid_compare( vblist->name, vblist->name_length, |
853 | 0 | sysuptime_oid, sysuptime_oid_len )) { |
854 | 0 | template_v2pdu->variables = vblist; |
855 | 0 | trap_vb = vblist->next_variable; |
856 | 0 | } else { |
857 | 0 | uptime = netsnmp_get_agent_uptime(); |
858 | 0 | var = NULL; |
859 | 0 | snmp_varlist_add_variable( &var, |
860 | 0 | sysuptime_oid, sysuptime_oid_len, |
861 | 0 | ASN_TIMETICKS, (u_char*)&uptime, sizeof(uptime)); |
862 | 0 | if (!var) { |
863 | 0 | snmp_log(LOG_WARNING, |
864 | 0 | "send_trap: failed to insert sysUptime varbind\n"); |
865 | 0 | snmp_free_pdu(template_v2pdu); |
866 | 0 | snmp_free_varbind(vblist); |
867 | 0 | return -1; |
868 | 0 | } |
869 | 0 | template_v2pdu->variables = var; |
870 | 0 | var->next_variable = vblist; |
871 | 0 | trap_vb = vblist; |
872 | 0 | } |
873 | | |
874 | | /* |
875 | | * 'trap_vb' should point to the snmpTrapOID.0 varbind, |
876 | | * identifying the requested trap. If not then bomb out. |
877 | | * If it's a 'standard' trap, then we need to append an |
878 | | * snmpEnterprise varbind (if there isn't already one). |
879 | | */ |
880 | 0 | if (!trap_vb || |
881 | 0 | snmp_oid_compare(trap_vb->name, trap_vb->name_length, |
882 | 0 | snmptrap_oid, snmptrap_oid_len)) { |
883 | 0 | snmp_log(LOG_WARNING, |
884 | 0 | "send_trap: no v2 trapOID varbind provided\n"); |
885 | 0 | snmp_free_pdu(template_v2pdu); |
886 | 0 | return -1; |
887 | 0 | } |
888 | 0 | if (!snmp_oid_compare(vblist->val.objid, OID_LENGTH(trap_prefix), |
889 | 0 | trap_prefix, OID_LENGTH(trap_prefix))) { |
890 | 0 | var = find_varbind_in_list( template_v2pdu->variables, |
891 | 0 | snmptrapenterprise_oid, |
892 | 0 | snmptrapenterprise_oid_len); |
893 | 0 | if (!var && |
894 | 0 | !snmp_varlist_add_variable( &(template_v2pdu->variables), |
895 | 0 | snmptrapenterprise_oid, snmptrapenterprise_oid_len, |
896 | 0 | ASN_OBJECT_ID, |
897 | 0 | enterprise, enterprise_length*sizeof(oid))) { |
898 | 0 | snmp_log(LOG_WARNING, |
899 | 0 | "send_trap: failed to add snmpEnterprise to v2 trap\n"); |
900 | 0 | snmp_free_pdu(template_v2pdu); |
901 | 0 | return -1; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | | |
906 | | /* |
907 | | * If everything's OK, convert the v2 template into an SNMPv1 trap PDU. |
908 | | */ |
909 | 0 | template_v1pdu = convert_v2pdu_to_v1( template_v2pdu ); |
910 | 0 | if (!template_v1pdu) { |
911 | 0 | snmp_log(LOG_WARNING, |
912 | 0 | "send_trap: failed to convert v2->v1 template PDU\n"); |
913 | 0 | } |
914 | |
|
915 | 0 | } else { |
916 | | /* |
917 | | * Construct the SNMPv1 trap PDU.... |
918 | | */ |
919 | 0 | template_v1pdu = snmp_pdu_create(SNMP_MSG_TRAP); |
920 | 0 | if (!template_v1pdu) { |
921 | 0 | snmp_log(LOG_WARNING, |
922 | 0 | "send_trap: failed to construct v1 template PDU\n"); |
923 | 0 | snmp_free_varbind(vblist); |
924 | 0 | return -1; |
925 | 0 | } |
926 | 0 | template_v1pdu->trap_type = trap; |
927 | 0 | template_v1pdu->specific_type = specific; |
928 | 0 | template_v1pdu->time = netsnmp_get_agent_uptime(); |
929 | |
|
930 | 0 | if (snmp_clone_mem((void **) &template_v1pdu->enterprise, |
931 | 0 | enterprise, enterprise_length * sizeof(oid))) { |
932 | 0 | snmp_log(LOG_WARNING, |
933 | 0 | "send_trap: failed to set v1 enterprise OID\n"); |
934 | 0 | snmp_free_varbind(vblist); |
935 | 0 | snmp_free_pdu(template_v1pdu); |
936 | 0 | return -1; |
937 | 0 | } |
938 | 0 | template_v1pdu->enterprise_length = enterprise_length; |
939 | |
|
940 | 0 | template_v1pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY; |
941 | 0 | template_v1pdu->variables = vblist; |
942 | | |
943 | | /* |
944 | | * ... and convert it into an SNMPv2-style notification PDU. |
945 | | */ |
946 | |
|
947 | 0 | template_v2pdu = convert_v1pdu_to_v2( template_v1pdu ); |
948 | 0 | if (!template_v2pdu) { |
949 | 0 | snmp_log(LOG_WARNING, |
950 | 0 | "send_trap: failed to convert v1->v2 template PDU\n"); |
951 | 0 | } |
952 | 0 | } |
953 | | |
954 | | /* |
955 | | * Check whether we're ignoring authFail traps |
956 | | */ |
957 | 0 | if (template_v1pdu) { |
958 | 0 | if (template_v1pdu->trap_type == SNMP_TRAP_AUTHFAIL && |
959 | 0 | snmp_enableauthentraps == SNMP_AUTHENTICATED_TRAPS_DISABLED) { |
960 | 0 | snmp_free_pdu(template_v1pdu); |
961 | 0 | snmp_free_pdu(template_v2pdu); |
962 | 0 | return 0; |
963 | 0 | } |
964 | | |
965 | | /* |
966 | | * Ensure that the v1 trap PDU includes the local IP address |
967 | | */ |
968 | 0 | pdu_in_addr_t = (in_addr_t *) template_v1pdu->agent_addr; |
969 | 0 | v1trapaddress = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, |
970 | 0 | NETSNMP_DS_AGENT_TRAP_ADDR); |
971 | 0 | if (v1trapaddress != NULL) { |
972 | | /* "v1trapaddress" was specified in config, try to resolve it */ |
973 | 0 | res = netsnmp_gethostbyname_v4(v1trapaddress, pdu_in_addr_t); |
974 | 0 | } |
975 | 0 | if (v1trapaddress == NULL || res < 0) { |
976 | | /* "v1trapaddress" was not specified in config or the resolution failed, |
977 | | * try any local address */ |
978 | 0 | *pdu_in_addr_t = get_myaddr(); |
979 | 0 | } |
980 | |
|
981 | 0 | } |
982 | | |
983 | 0 | if (template_v2pdu) { |
984 | | /* A context name was provided, so copy it and its length to the v2 pdu |
985 | | * template. */ |
986 | 0 | if (context != NULL) |
987 | 0 | { |
988 | 0 | template_v2pdu->contextName = strdup(context); |
989 | 0 | template_v2pdu->contextNameLen = strlen(context); |
990 | 0 | } |
991 | 0 | } |
992 | | |
993 | | /* |
994 | | * Now loop through the list of trap sinks |
995 | | * and call the trap callback routines, |
996 | | * providing an appropriately formatted PDU in each case |
997 | | */ |
998 | 0 | for (sink = sinks; sink; sink = sink->next) { |
999 | 0 | #ifndef NETSNMP_DISABLE_SNMPV1 |
1000 | 0 | if (sink->version == SNMP_VERSION_1) { |
1001 | 0 | if (template_v1pdu && |
1002 | 0 | !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
1003 | 0 | NETSNMP_DS_LIB_DISABLE_V1)) { |
1004 | 0 | send_trap_to_sess(sink->sesp, template_v1pdu); |
1005 | 0 | } |
1006 | 0 | } else |
1007 | 0 | #endif |
1008 | 0 | if (template_v2pdu) { |
1009 | 0 | template_v2pdu->command = sink->pdutype; |
1010 | 0 | send_trap_to_sess(sink->sesp, template_v2pdu); |
1011 | 0 | } |
1012 | 0 | } |
1013 | 0 | #ifndef NETSNMP_DISABLE_SNMPV1 |
1014 | 0 | if (template_v1pdu) |
1015 | 0 | snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
1016 | 0 | SNMPD_CALLBACK_SEND_TRAP1, template_v1pdu); |
1017 | 0 | #endif |
1018 | 0 | if (template_v2pdu) |
1019 | 0 | snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
1020 | 0 | SNMPD_CALLBACK_SEND_TRAP2, template_v2pdu); |
1021 | 0 | snmp_free_pdu(template_v1pdu); |
1022 | 0 | snmp_free_pdu(template_v2pdu); |
1023 | 0 | return 0; |
1024 | 0 | } |
1025 | | |
1026 | | |
1027 | | void |
1028 | | send_enterprise_trap_vars(int trap, |
1029 | | int specific, |
1030 | | const oid * enterprise, int enterprise_length, |
1031 | | netsnmp_variable_list * vars) |
1032 | 0 | { |
1033 | 0 | netsnmp_send_traps(trap, specific, |
1034 | 0 | enterprise, enterprise_length, |
1035 | 0 | vars, NULL, 0); |
1036 | 0 | return; |
1037 | 0 | } |
1038 | | |
1039 | | /** |
1040 | | * Handles stats for basic traps (really just send failed |
1041 | | */ |
1042 | | int |
1043 | | handle_trap_callback(int op, netsnmp_session * session, int reqid, |
1044 | | netsnmp_pdu *pdu, void *magic) |
1045 | 0 | { |
1046 | 0 | if (NULL == session) |
1047 | 0 | return 0; |
1048 | | |
1049 | 0 | DEBUGMSGTL(("trap", "handle_trap_callback for session %s\n", |
1050 | 0 | session->paramName ? session->paramName : "UNKNOWN")); |
1051 | 0 | switch (op) { |
1052 | | |
1053 | 0 | case NETSNMP_CALLBACK_OP_SEND_FAILED: |
1054 | 0 | DEBUGMSGTL(("trap", "failed to send an inform for reqid=%d\n", reqid)); |
1055 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1056 | 0 | if (session->trap_stats) { |
1057 | 0 | session->trap_stats->sent_last_fail = netsnmp_get_agent_uptime(); |
1058 | 0 | ++session->trap_stats->sent_fail_count; |
1059 | 0 | } |
1060 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1061 | 0 | break; |
1062 | | |
1063 | 0 | case NETSNMP_CALLBACK_OP_SEC_ERROR: |
1064 | 0 | DEBUGMSGTL(("trap", "sec error sending a trap for reqid=%d\n", |
1065 | 0 | reqid)); |
1066 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1067 | 0 | if (session->trap_stats) { |
1068 | 0 | session->trap_stats->sec_err_last = netsnmp_get_agent_uptime(); |
1069 | 0 | ++session->trap_stats->sec_err_count; |
1070 | 0 | } |
1071 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1072 | 0 | break; |
1073 | | |
1074 | 0 | case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: |
1075 | 0 | case NETSNMP_CALLBACK_OP_TIMED_OUT: |
1076 | 0 | case NETSNMP_CALLBACK_OP_RESEND: |
1077 | 0 | default: |
1078 | 0 | DEBUGMSGTL(("trap", |
1079 | 0 | "received op=%d for reqid=%d when trying to send a trap\n", |
1080 | 0 | op, reqid)); |
1081 | 0 | } |
1082 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1083 | 0 | if (session->trap_stats) |
1084 | 0 | _dump_trap_stats(session); |
1085 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1086 | |
|
1087 | 0 | return 1; |
1088 | 0 | } |
1089 | | |
1090 | | |
1091 | | /** |
1092 | | * Captures responses or the lack there of from INFORMs that were sent |
1093 | | * 1) a response is received from an INFORM |
1094 | | * 2) one isn't received and the retries/timeouts have failed |
1095 | | */ |
1096 | | int |
1097 | | handle_inform_response(int op, netsnmp_session * session, |
1098 | | int reqid, netsnmp_pdu *pdu, |
1099 | | void *magic) |
1100 | 0 | { |
1101 | 0 | if (NULL == session) |
1102 | 0 | return 0; |
1103 | | |
1104 | 0 | DEBUGMSGTL(("trap", "handle_inform_response for session %s\n", |
1105 | 0 | session->paramName ? session->paramName : "UNKNOWN")); |
1106 | 0 | switch (op) { |
1107 | | |
1108 | 0 | case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: |
1109 | 0 | snmp_increment_statistic(STAT_SNMPINPKTS); |
1110 | 0 | if (pdu->command != SNMP_MSG_REPORT) { |
1111 | 0 | DEBUGMSGTL(("trap", "received the inform response for reqid=%d\n", |
1112 | 0 | reqid)); |
1113 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1114 | 0 | if (session->trap_stats) { |
1115 | 0 | ++session->trap_stats->ack_count; |
1116 | 0 | session->trap_stats->ack_last_rcvd = netsnmp_get_agent_uptime(); |
1117 | 0 | } |
1118 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1119 | 0 | break; |
1120 | 0 | } else { |
1121 | 0 | int type = session->s_snmp_errno ? session->s_snmp_errno : |
1122 | 0 | snmpv3_get_report_type(pdu); |
1123 | 0 | DEBUGMSGTL(("trap", "received report %d for inform reqid=%d\n", |
1124 | 0 | type, reqid)); |
1125 | | /* |
1126 | | * xxx-rks: what stats, if any, to bump for other report types? |
1127 | | * - ignore NOT_IN_TIME, as agent will sync and retry. |
1128 | | */ |
1129 | 0 | if (SNMPERR_AUTHENTICATION_FAILURE != type) |
1130 | 0 | break; |
1131 | 0 | } |
1132 | | /** AUTH failures fall through to sec error */ |
1133 | 0 | NETSNMP_FALLTHROUGH; |
1134 | | |
1135 | 0 | case NETSNMP_CALLBACK_OP_SEC_ERROR: |
1136 | 0 | DEBUGMSGTL(("trap", "sec error sending an inform for reqid=%d\n", |
1137 | 0 | reqid)); |
1138 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1139 | 0 | if (session->trap_stats) { |
1140 | 0 | session->trap_stats->sec_err_last = netsnmp_get_agent_uptime(); |
1141 | 0 | ++session->trap_stats->sec_err_count; |
1142 | 0 | } |
1143 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1144 | 0 | break; |
1145 | | |
1146 | 0 | case NETSNMP_CALLBACK_OP_TIMED_OUT: |
1147 | 0 | DEBUGMSGTL(("trap", |
1148 | 0 | "received a timeout sending an inform for reqid=%d\n", |
1149 | 0 | reqid)); |
1150 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1151 | 0 | if (session->trap_stats) { |
1152 | 0 | ++session->trap_stats->timeouts; |
1153 | 0 | session->trap_stats->sent_last_timeout = |
1154 | 0 | netsnmp_get_agent_uptime(); |
1155 | 0 | } |
1156 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1157 | 0 | break; |
1158 | | |
1159 | 0 | case NETSNMP_CALLBACK_OP_RESEND: |
1160 | 0 | DEBUGMSGTL(("trap", "resending an inform for reqid=%d\n", reqid)); |
1161 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1162 | 0 | if (session->trap_stats) |
1163 | 0 | session->trap_stats->sent_last_sent = netsnmp_get_agent_uptime(); |
1164 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1165 | 0 | break; |
1166 | | |
1167 | 0 | case NETSNMP_CALLBACK_OP_SEND_FAILED: |
1168 | 0 | DEBUGMSGTL(("trap", "failed to send an inform for reqid=%d\n", reqid)); |
1169 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1170 | 0 | if (session->trap_stats) { |
1171 | 0 | session->trap_stats->sent_last_fail = netsnmp_get_agent_uptime(); |
1172 | 0 | ++session->trap_stats->sent_fail_count; |
1173 | 0 | } |
1174 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1175 | 0 | break; |
1176 | | |
1177 | 0 | default: |
1178 | 0 | DEBUGMSGTL(("trap", "received op=%d for reqid=%d when trying to send an inform\n", op, reqid)); |
1179 | 0 | } |
1180 | | |
1181 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1182 | 0 | if (session->trap_stats) |
1183 | 0 | _dump_trap_stats(session); |
1184 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1185 | |
|
1186 | 0 | return 1; |
1187 | 0 | } |
1188 | | |
1189 | | |
1190 | | /* |
1191 | | * send_trap_to_sess: sends a trap to a session but assumes that the |
1192 | | * pdu is constructed correctly for the session type. |
1193 | | */ |
1194 | | void |
1195 | | send_trap_to_sess(netsnmp_session * sess, netsnmp_pdu *template_pdu) |
1196 | 0 | { |
1197 | 0 | netsnmp_pdu *pdu; |
1198 | 0 | int result; |
1199 | |
|
1200 | 0 | if (!sess || !template_pdu) |
1201 | 0 | return; |
1202 | | |
1203 | 0 | if (NETSNMP_RUNTIME_PROTOCOL_SKIP(sess->version)) { |
1204 | 0 | DEBUGMSGTL(("trap", "not sending trap type=%d, version %02lx disabled\n", |
1205 | 0 | template_pdu->command, sess->version)); |
1206 | 0 | return; |
1207 | 0 | } |
1208 | 0 | DEBUGIF("trap") { |
1209 | 0 | struct session_list *sessp = snmp_sess_pointer(sess); |
1210 | 0 | netsnmp_transport *t = sessp->transport; |
1211 | 0 | const void *dst = template_pdu->transport_data; |
1212 | 0 | const int dst_len = template_pdu->transport_data_length; |
1213 | 0 | char *peer = NULL; |
1214 | |
|
1215 | 0 | if (t && t->f_fmtaddr) |
1216 | 0 | peer = t->f_fmtaddr(t, dst, dst_len); |
1217 | 0 | DEBUGMSGTL(("trap", "sending trap type=%d, version=%ld to %s\n", |
1218 | 0 | template_pdu->command, sess->version, peer ? peer : "(?)")); |
1219 | 0 | free(peer); |
1220 | 0 | } |
1221 | |
|
1222 | 0 | #ifndef NETSNMP_DISABLE_SNMPV1 |
1223 | 0 | if (sess->version == SNMP_VERSION_1 && |
1224 | 0 | (template_pdu->command != SNMP_MSG_TRAP)) |
1225 | 0 | return; /* Skip v1 sinks for v2 only traps */ |
1226 | 0 | if (sess->version != SNMP_VERSION_1 && |
1227 | 0 | (template_pdu->command == SNMP_MSG_TRAP)) |
1228 | 0 | return; /* Skip v2+ sinks for v1 only traps */ |
1229 | 0 | #endif |
1230 | 0 | template_pdu->version = sess->version; |
1231 | 0 | pdu = snmp_clone_pdu(template_pdu); |
1232 | 0 | if(!pdu) { |
1233 | 0 | snmp_log(LOG_WARNING, "send_trap: failed to clone PDU\n"); |
1234 | 0 | return; |
1235 | 0 | } |
1236 | | |
1237 | 0 | pdu->sessid = sess->sessid; /* AgentX only ? */ |
1238 | | /* |
1239 | | * RFC 3414 sayeth: |
1240 | | * |
1241 | | * - If an SNMP engine uses a msgID for correlating Response messages to |
1242 | | * outstanding Request messages, then it MUST use different msgIDs in |
1243 | | * all such Request messages that it sends out during a Time Window |
1244 | | * (150 seconds) period. |
1245 | | * |
1246 | | * A Command Generator or Notification Originator Application MUST use |
1247 | | * different request-ids in all Request PDUs that it sends out during |
1248 | | * a TimeWindow (150 seconds) period. |
1249 | | */ |
1250 | 0 | pdu->reqid = snmp_get_next_reqid(); |
1251 | 0 | pdu->msgid = snmp_get_next_msgid(); |
1252 | |
|
1253 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1254 | | /** allocate space for trap stats */ |
1255 | 0 | if (NULL == sess->trap_stats) { |
1256 | 0 | sess->trap_stats = SNMP_MALLOC_TYPEDEF(netsnmp_trap_stats); |
1257 | 0 | if (NULL == sess->trap_stats) |
1258 | 0 | snmp_log(LOG_ERR, "malloc for %s trap stats failed\n", |
1259 | 0 | sess->paramName ? sess->paramName : "UNKNOWN"); |
1260 | 0 | } |
1261 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1262 | |
|
1263 | 0 | if ( template_pdu->command == SNMP_MSG_INFORM |
1264 | 0 | #ifdef USING_AGENTX_PROTOCOL_MODULE |
1265 | 0 | || template_pdu->command == AGENTX_MSG_NOTIFY |
1266 | 0 | #endif |
1267 | 0 | ) { |
1268 | 0 | result = |
1269 | 0 | snmp_async_send(sess, pdu, &handle_inform_response, NULL); |
1270 | 0 | } else { |
1271 | 0 | if ((sess->version == SNMP_VERSION_3) && |
1272 | 0 | (pdu->command == SNMP_MSG_TRAP2) && |
1273 | 0 | (sess->securityEngineIDLen == 0)) { |
1274 | 0 | u_char tmp[SPRINT_MAX_LEN]; |
1275 | |
|
1276 | 0 | int len = snmpv3_get_engineID(tmp, sizeof(tmp)); |
1277 | 0 | pdu->securityEngineID = netsnmp_memdup(tmp, len); |
1278 | 0 | pdu->securityEngineIDLen = len; |
1279 | 0 | } |
1280 | |
|
1281 | 0 | result = snmp_async_send(sess, pdu, &handle_trap_callback, NULL); |
1282 | 0 | } |
1283 | |
|
1284 | 0 | if (result == 0) { |
1285 | 0 | snmp_sess_perror("snmpd: send_trap", sess); |
1286 | 0 | snmp_free_pdu(pdu); |
1287 | | /** trap stats for failure handled in callback */ |
1288 | 0 | } else { |
1289 | 0 | snmp_increment_statistic(STAT_SNMPOUTTRAPS); |
1290 | 0 | snmp_increment_statistic(STAT_SNMPOUTPKTS); |
1291 | 0 | #ifndef NETSNMP_NO_TRAP_STATS |
1292 | 0 | if (sess->trap_stats) { |
1293 | 0 | sess->trap_stats->sent_last_sent = netsnmp_get_agent_uptime(); |
1294 | 0 | ++sess->trap_stats->sent_count; |
1295 | 0 | _dump_trap_stats(sess); |
1296 | 0 | } |
1297 | 0 | #endif /* NETSNMP_NO_TRAP_STATS */ |
1298 | 0 | } |
1299 | 0 | } |
1300 | | |
1301 | | void |
1302 | | send_trap_vars(int trap, int specific, netsnmp_variable_list * vars) |
1303 | 0 | { |
1304 | 0 | if (trap == SNMP_TRAP_ENTERPRISESPECIFIC) |
1305 | 0 | send_enterprise_trap_vars(trap, specific, objid_enterprisetrap, |
1306 | 0 | OID_LENGTH(objid_enterprisetrap), vars); |
1307 | 0 | else |
1308 | 0 | send_enterprise_trap_vars(trap, specific, trap_version_id, |
1309 | 0 | OID_LENGTH(trap_version_id), vars); |
1310 | 0 | } |
1311 | | |
1312 | | #ifndef NETSNMP_FEATURE_REMOVE_TRAP_VARS_WITH_CONTEXT |
1313 | | /* Send a trap under a context */ |
1314 | | void send_trap_vars_with_context(int trap, int specific, |
1315 | | netsnmp_variable_list *vars, const char *context) |
1316 | 0 | { |
1317 | 0 | if (trap == SNMP_TRAP_ENTERPRISESPECIFIC) |
1318 | 0 | netsnmp_send_traps(trap, specific, objid_enterprisetrap, |
1319 | 0 | OID_LENGTH(objid_enterprisetrap), vars, |
1320 | 0 | context, 0); |
1321 | 0 | else |
1322 | 0 | netsnmp_send_traps(trap, specific, trap_version_id, |
1323 | 0 | OID_LENGTH(trap_version_id), vars, |
1324 | 0 | context, 0); |
1325 | | |
1326 | 0 | } |
1327 | | #endif /* NETSNMP_FEATURE_REMOVE_TRAP_VARS_WITH_CONTEXT */ |
1328 | | |
1329 | | /** |
1330 | | * Sends an SNMPv1 trap (or the SNMPv2 equivalent) to the list of |
1331 | | * configured trap destinations (or "sinks"), using the provided |
1332 | | * values for the generic trap type and specific trap value. |
1333 | | * |
1334 | | * This function eventually calls send_enterprise_trap_vars. If the |
1335 | | * trap type is not set to SNMP_TRAP_ENTERPRISESPECIFIC the enterprise |
1336 | | * and enterprise_length parameter is set to the pre defined NETSNMP_SYSTEM_MIB |
1337 | | * oid and length respectively. If the trap type is set to |
1338 | | * SNMP_TRAP_ENTERPRISESPECIFIC the enterprise and enterprise_length |
1339 | | * parameters are set to the pre-defined NETSNMP_NOTIFICATION_MIB oid and length |
1340 | | * respectively. |
1341 | | * |
1342 | | * @param trap is the generic trap type. |
1343 | | * |
1344 | | * @param specific is the specific trap value. |
1345 | | * |
1346 | | * @return void |
1347 | | * |
1348 | | * @see send_enterprise_trap_vars |
1349 | | * @see send_v2trap |
1350 | | */ |
1351 | | |
1352 | | void |
1353 | | send_easy_trap(int trap, int specific) |
1354 | 0 | { |
1355 | 0 | send_trap_vars(trap, specific, NULL); |
1356 | 0 | } |
1357 | | |
1358 | | /** |
1359 | | * Uses the supplied list of variable bindings to form an SNMPv2 trap, |
1360 | | * which is sent to SNMPv2-capable sinks on the configured list. |
1361 | | * An equivalent INFORM is sent to the configured list of inform sinks. |
1362 | | * Sinks that can only handle SNMPv1 traps are skipped. |
1363 | | * |
1364 | | * This function eventually calls send_enterprise_trap_vars. If the |
1365 | | * trap type is not set to SNMP_TRAP_ENTERPRISESPECIFIC the enterprise |
1366 | | * and enterprise_length parameter is set to the pre defined NETSNMP_SYSTEM_MIB |
1367 | | * oid and length respectively. If the trap type is set to |
1368 | | * SNMP_TRAP_ENTERPRISESPECIFIC the enterprise and enterprise_length |
1369 | | * parameters are set to the pre-defined NETSNMP_NOTIFICATION_MIB oid and length |
1370 | | * respectively. |
1371 | | * |
1372 | | * @param vars is used to supply list of variable bindings to form an SNMPv2 |
1373 | | * trap. |
1374 | | * |
1375 | | * @return void |
1376 | | * |
1377 | | * @see send_easy_trap |
1378 | | * @see send_enterprise_trap_vars |
1379 | | */ |
1380 | | |
1381 | | void |
1382 | | send_v2trap(netsnmp_variable_list * vars) |
1383 | 0 | { |
1384 | 0 | send_trap_vars(-1, -1, vars); |
1385 | 0 | } |
1386 | | |
1387 | | /** |
1388 | | * Similar to send_v2trap(), with the added ability to specify a context. If |
1389 | | * the last parameter is NULL, then this call is equivalent to send_v2trap(). |
1390 | | * |
1391 | | * @param vars is used to supply the list of variable bindings for the trap. |
1392 | | * |
1393 | | * @param context is used to specify the context of the trap. |
1394 | | * |
1395 | | * @return void |
1396 | | * |
1397 | | * @see send_v2trap |
1398 | | */ |
1399 | | #ifndef NETSNMP_FEATURE_REMOVE_SEND_V3TRAP |
1400 | | void send_v3trap(netsnmp_variable_list *vars, const char *context) |
1401 | 0 | { |
1402 | 0 | netsnmp_send_traps(-1, -1, |
1403 | 0 | trap_version_id, OID_LENGTH(trap_version_id), |
1404 | 0 | vars, context, 0); |
1405 | 0 | } |
1406 | | #endif /* NETSNMP_FEATURE_REMOVE_SEND_V3TRAP */ |
1407 | | |
1408 | | #ifndef NETSNMP_FEATURE_REMOVE_SEND_TRAP_PDU |
1409 | | void |
1410 | | send_trap_pdu(netsnmp_pdu *pdu) |
1411 | 0 | { |
1412 | 0 | send_trap_vars(-1, -1, pdu->variables); |
1413 | 0 | } |
1414 | | #endif /* NETSNMP_FEATURE_REMOVE_SEND_TRAP_PDU */ |
1415 | | |
1416 | | |
1417 | | |
1418 | | /******************* |
1419 | | * |
1420 | | * Config file handling |
1421 | | * |
1422 | | *******************/ |
1423 | | |
1424 | | void |
1425 | | snmpd_parse_config_authtrap(const char *token, char *cptr) |
1426 | 0 | { |
1427 | 0 | int i; |
1428 | |
|
1429 | 0 | i = atoi(cptr); |
1430 | 0 | if (i == 0) { |
1431 | 0 | if (strcmp(cptr, "enable") == 0) { |
1432 | 0 | i = SNMP_AUTHENTICATED_TRAPS_ENABLED; |
1433 | 0 | } else if (strcmp(cptr, "disable") == 0) { |
1434 | 0 | i = SNMP_AUTHENTICATED_TRAPS_DISABLED; |
1435 | 0 | } |
1436 | 0 | } |
1437 | 0 | if (i < 1 || i > 2) { |
1438 | 0 | config_perror("authtrapenable must be 1 or 2"); |
1439 | 0 | } else { |
1440 | 0 | if (strcmp(token, "pauthtrapenable") == 0) { |
1441 | 0 | if (snmp_enableauthentrapsset < 0) { |
1442 | | /* |
1443 | | * This is bogus (and shouldn't happen anyway) -- the value |
1444 | | * of snmpEnableAuthenTraps.0 is already configured |
1445 | | * read-only. |
1446 | | */ |
1447 | 0 | snmp_log(LOG_WARNING, |
1448 | 0 | "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n"); |
1449 | 0 | return; |
1450 | 0 | } else { |
1451 | 0 | snmp_enableauthentrapsset++; |
1452 | 0 | } |
1453 | 0 | } else { |
1454 | 0 | if (snmp_enableauthentrapsset > 0) { |
1455 | | /* |
1456 | | * This is bogus (and shouldn't happen anyway) -- we already |
1457 | | * read a persistent value of snmpEnableAuthenTraps.0, which |
1458 | | * we should ignore in favour of this one. |
1459 | | */ |
1460 | 0 | snmp_log(LOG_WARNING, |
1461 | 0 | "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n"); |
1462 | | /* |
1463 | | * Fall through and copy in this value. |
1464 | | */ |
1465 | 0 | } |
1466 | 0 | snmp_enableauthentrapsset = -1; |
1467 | 0 | } |
1468 | 0 | snmp_enableauthentraps = i; |
1469 | 0 | } |
1470 | 0 | } |
1471 | | |
1472 | | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
1473 | | static void |
1474 | | _parse_config_sink(const char *token, char *cptr, int version, int type) |
1475 | 0 | { |
1476 | 0 | char *sp, *cp, *pp = NULL, *src = NULL; |
1477 | 0 | char *st, *name = NULL, *tag = NULL, *profile = NULL; |
1478 | 0 | int done = 0; |
1479 | |
|
1480 | 0 | if (!snmp_trapcommunity) |
1481 | 0 | snmp_trapcommunity = strdup("public"); |
1482 | 0 | sp = strtok_r(cptr, " \t\n", &st); |
1483 | 0 | if (!sp) |
1484 | 0 | return; |
1485 | | /* |
1486 | | * check for optional arguments |
1487 | | */ |
1488 | 0 | do { |
1489 | 0 | if (*sp != '-') { |
1490 | 0 | done = 1; |
1491 | 0 | continue; |
1492 | 0 | } |
1493 | 0 | if (strcmp(sp, "-name") == 0) |
1494 | 0 | name = strtok_r(NULL, " \t\n", &st); |
1495 | 0 | else if (strcmp(sp, "-tag") == 0) |
1496 | 0 | tag = strtok_r(NULL, " \t\n", &st); |
1497 | 0 | else if (strcmp(sp, "-profile") == 0) |
1498 | 0 | profile = strtok_r(NULL, " \t\n", &st); |
1499 | 0 | else if (strcmp(sp, "-s") == 0) |
1500 | 0 | src = strtok_r(NULL, " \t\n", &st); |
1501 | 0 | else |
1502 | 0 | netsnmp_config_warn("ignoring unknown argument: %s", sp); |
1503 | 0 | sp = strtok_r(NULL, " \t\n", &st); |
1504 | 0 | } while (!done); |
1505 | 0 | cp = strtok_r(NULL, " \t\n", &st); |
1506 | 0 | if (cp) |
1507 | 0 | pp = strtok_r(NULL, " \t\n", &st); |
1508 | 0 | if (pp) |
1509 | 0 | config_pwarn("The separate port argument for sinks is deprecated"); |
1510 | 0 | if (netsnmp_create_v1v2_notification_session(sp, pp, |
1511 | 0 | cp ? cp : snmp_trapcommunity, |
1512 | 0 | src, version, type, name, tag, |
1513 | 0 | profile) == NULL) { |
1514 | 0 | netsnmp_config_error("cannot create sink: %s", cptr); |
1515 | 0 | } |
1516 | 0 | } |
1517 | | #endif |
1518 | | |
1519 | | #ifndef NETSNMP_DISABLE_SNMPV1 |
1520 | | void |
1521 | | snmpd_parse_config_trapsink(const char *token, char *cptr) |
1522 | 0 | { |
1523 | 0 | _parse_config_sink(token, cptr, SNMP_VERSION_1, SNMP_MSG_TRAP); |
1524 | 0 | } |
1525 | | #endif |
1526 | | |
1527 | | #ifndef NETSNMP_DISABLE_SNMPV2C |
1528 | | void |
1529 | | snmpd_parse_config_trap2sink(const char *word, char *cptr) |
1530 | 0 | { |
1531 | 0 | _parse_config_sink(word, cptr, SNMP_VERSION_2c, SNMP_MSG_TRAP2); |
1532 | 0 | } |
1533 | | |
1534 | | void |
1535 | | snmpd_parse_config_informsink(const char *word, char *cptr) |
1536 | 0 | { |
1537 | 0 | _parse_config_sink(word, cptr, SNMP_VERSION_2c, SNMP_MSG_INFORM); |
1538 | 0 | } |
1539 | | #endif |
1540 | | |
1541 | | /* |
1542 | | * this must be standardized somewhere, right? |
1543 | | */ |
1544 | 0 | #define MAX_ARGS 128 |
1545 | | |
1546 | | static int traptype; |
1547 | | |
1548 | | static void |
1549 | | trapOptProc(int argc, char *const *argv, int opt) |
1550 | 0 | { |
1551 | 0 | switch (opt) { |
1552 | 0 | case 'C': |
1553 | 0 | while (*optarg) { |
1554 | 0 | switch (*optarg++) { |
1555 | 0 | case 'i': |
1556 | 0 | traptype = SNMP_MSG_INFORM; |
1557 | 0 | break; |
1558 | 0 | default: |
1559 | 0 | config_perror("unknown argument passed to -C"); |
1560 | 0 | break; |
1561 | 0 | } |
1562 | 0 | } |
1563 | 0 | break; |
1564 | 0 | } |
1565 | 0 | } |
1566 | | |
1567 | | netsnmp_session * |
1568 | | netsnmp_create_v3user_notification_session(const char *dest, const char *user, |
1569 | | int level, const char *context, |
1570 | | int pdutype, const u_char *engineId, |
1571 | | size_t engineId_len, const char *src, |
1572 | | const char *notif_name, |
1573 | | const char *notif_tag, |
1574 | | const char* notif_profile) |
1575 | 0 | { |
1576 | 0 | netsnmp_session session, *ss = NULL; |
1577 | 0 | struct usmUser *usmUser; |
1578 | 0 | netsnmp_tdomain_spec tspec; |
1579 | 0 | netsnmp_transport *transport; |
1580 | 0 | u_char tmp_engineId[SPRINT_MAX_LEN]; |
1581 | 0 | int rc; |
1582 | |
|
1583 | 0 | if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
1584 | 0 | NETSNMP_DS_LIB_DISABLE_V3)) { |
1585 | 0 | netsnmp_config_error("SNMPv3 disabled, cannot create notification session"); |
1586 | 0 | return NULL; |
1587 | 0 | } |
1588 | 0 | if (NULL == dest || NULL == user) |
1589 | 0 | return NULL; |
1590 | | |
1591 | | /** authlevel */ |
1592 | 0 | if ((SNMP_SEC_LEVEL_AUTHPRIV != level) && |
1593 | 0 | (SNMP_SEC_LEVEL_AUTHNOPRIV != level) && |
1594 | 0 | (SNMP_SEC_LEVEL_NOAUTH != level)) { |
1595 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "bad level %d\n", level)); |
1596 | 0 | return NULL; |
1597 | 0 | } |
1598 | | |
1599 | | /** need engineId to look up users */ |
1600 | 0 | if (NULL == engineId) { |
1601 | 0 | engineId_len = snmpv3_get_engineID( tmp_engineId, sizeof(tmp_engineId)); |
1602 | 0 | engineId = tmp_engineId; |
1603 | 0 | } |
1604 | |
|
1605 | 0 | usmUser = usm_get_user(engineId, engineId_len, user); |
1606 | 0 | if (NULL == usmUser) { |
1607 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "usmUser %s not found\n", user)); |
1608 | 0 | return NULL; |
1609 | 0 | } |
1610 | | |
1611 | 0 | snmp_sess_init(&session); |
1612 | |
|
1613 | 0 | session.callback = handle_disconnect_packet; |
1614 | |
|
1615 | 0 | session.version = SNMP_VERSION_3; |
1616 | |
|
1617 | 0 | session.peername = strdup(dest); |
1618 | |
|
1619 | 0 | session.securityName = strdup(user); |
1620 | 0 | session.securityNameLen = strlen(user); |
1621 | |
|
1622 | 0 | if (context) { |
1623 | 0 | session.contextName = strdup(context); |
1624 | 0 | session.contextNameLen = strlen(context); |
1625 | 0 | } |
1626 | |
|
1627 | 0 | session.securityLevel = level; |
1628 | | |
1629 | | /** auth prot */ |
1630 | 0 | if (NULL != usmUser->authProtocol) { |
1631 | 0 | session.securityAuthProto = |
1632 | 0 | snmp_duplicate_objid(usmUser->authProtocol, |
1633 | 0 | usmUser->authProtocolLen); |
1634 | 0 | session.securityAuthProtoLen = usmUser->authProtocolLen; |
1635 | 0 | if (NULL == session.securityAuthProto) |
1636 | 0 | goto bail; |
1637 | 0 | } |
1638 | | |
1639 | | /** authkey */ |
1640 | 0 | if (((SNMP_SEC_LEVEL_AUTHPRIV == level) || |
1641 | 0 | (SNMP_SEC_LEVEL_AUTHNOPRIV == level)) && |
1642 | 0 | (usmUser->flags & USMUSER_FLAG_KEEP_MASTER_KEY)) { |
1643 | 0 | netsnmp_assert(usmUser->authKeyKuLen > 0); |
1644 | 0 | memcpy(session.securityAuthKey, usmUser->authKeyKu, |
1645 | 0 | usmUser->authKeyKuLen); |
1646 | 0 | session.securityAuthKeyLen = usmUser->authKeyKuLen; |
1647 | 0 | } |
1648 | | |
1649 | | /** priv prot */ |
1650 | 0 | if (NULL != usmUser->privProtocol) { |
1651 | 0 | session.securityPrivProto = |
1652 | 0 | snmp_duplicate_objid(usmUser->privProtocol, |
1653 | 0 | usmUser->privProtocolLen); |
1654 | 0 | session.securityPrivProtoLen = usmUser->privProtocolLen; |
1655 | 0 | if (NULL == session.securityPrivProto) |
1656 | 0 | goto bail; |
1657 | 0 | } |
1658 | | |
1659 | | /** privkey */ |
1660 | 0 | if ((SNMP_SEC_LEVEL_AUTHPRIV == level) && |
1661 | 0 | (usmUser->flags & USMUSER_FLAG_KEEP_MASTER_KEY)) { |
1662 | 0 | netsnmp_assert(usmUser->privKeyKuLen > 0); |
1663 | 0 | memcpy(session.securityPrivKey, usmUser->privKeyKu, |
1664 | 0 | usmUser->privKeyKuLen); |
1665 | 0 | session.securityPrivKeyLen = usmUser->privKeyKuLen; |
1666 | 0 | } |
1667 | | |
1668 | | /** engineId */ |
1669 | 0 | session.contextEngineID = netsnmp_memdup(usmUser->engineID, |
1670 | 0 | usmUser->engineIDLen); |
1671 | 0 | session.contextEngineIDLen = usmUser->engineIDLen; |
1672 | | |
1673 | | /** open the tranport */ |
1674 | |
|
1675 | 0 | memset(&tspec, 0, sizeof(netsnmp_tdomain_spec)); |
1676 | 0 | tspec.application = "snmptrap"; |
1677 | 0 | tspec.target = session.peername; |
1678 | 0 | tspec.default_domain = NULL; |
1679 | 0 | tspec.default_target = NULL; |
1680 | 0 | tspec.source = src; |
1681 | 0 | transport = netsnmp_tdomain_transport_tspec(&tspec); |
1682 | 0 | if (transport == NULL) { |
1683 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "could not create transport\n")); |
1684 | 0 | goto bail; |
1685 | 0 | } |
1686 | | |
1687 | 0 | if ((rc = netsnmp_sess_config_and_open_transport(&session, transport)) |
1688 | 0 | != SNMPERR_SUCCESS) { |
1689 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "config/open failed\n")); |
1690 | 0 | goto bail; |
1691 | 0 | } |
1692 | | |
1693 | 0 | ss = snmp_add(&session, transport, NULL, NULL); |
1694 | 0 | if (!ss) { |
1695 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "snmp_add failed\n")); |
1696 | 0 | goto bail; |
1697 | 0 | } |
1698 | | |
1699 | 0 | if (netsnmp_add_closable_notification_session(ss, |
1700 | 0 | !(transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM), |
1701 | 0 | pdutype, |
1702 | 0 | (pdutype == SNMP_MSG_INFORM), |
1703 | 0 | ss->version, notif_name, notif_tag, |
1704 | 0 | notif_profile) != 1) { |
1705 | 0 | DEBUGMSGTL(("trap:v3user_notif_sess", "add notification failed\n")); |
1706 | 0 | snmp_close(ss); |
1707 | 0 | return NULL; |
1708 | 0 | } |
1709 | | |
1710 | 0 | bail: |
1711 | | /** free any allocated mem in session */ |
1712 | 0 | netsnmp_cleanup_session(&session); |
1713 | |
|
1714 | 0 | return ss; |
1715 | 0 | } |
1716 | | |
1717 | | void |
1718 | | snmpd_parse_config_trapsess(const char *word, char *cptr) |
1719 | 0 | { |
1720 | 0 | char *argv[MAX_ARGS], *cp = cptr; |
1721 | 0 | char *profile = NULL, *name = NULL, *tag = NULL; |
1722 | 0 | int argn, rc; |
1723 | 0 | netsnmp_session session, *ss; |
1724 | 0 | netsnmp_transport *transport; |
1725 | 0 | size_t len; |
1726 | 0 | char tmp[SPRINT_MAX_LEN]; |
1727 | 0 | char *clientaddr_save = NULL; |
1728 | | |
1729 | | /* |
1730 | | * inform or trap? default to trap |
1731 | | */ |
1732 | 0 | traptype = SNMP_MSG_TRAP2; |
1733 | |
|
1734 | 0 | do { |
1735 | 0 | if (strncmp(cp, "-profile", 8) == 0) { |
1736 | 0 | cp = skip_token(cp); |
1737 | 0 | cp = copy_nword(cp, tmp, SPRINT_MAX_LEN); |
1738 | 0 | free(profile); |
1739 | 0 | profile = strdup(tmp); |
1740 | 0 | } else if (strncmp(cp, "-name", 5) == 0) { |
1741 | 0 | cp = skip_token(cp); |
1742 | 0 | cp = copy_nword(cp, tmp, SPRINT_MAX_LEN); |
1743 | 0 | free(name); |
1744 | 0 | name = strdup(tmp); |
1745 | 0 | } else if (strncmp(cp, "-tag", 4) == 0) { |
1746 | 0 | cp = skip_token(cp); |
1747 | 0 | cp = copy_nword(cp, tmp, SPRINT_MAX_LEN); |
1748 | 0 | free(tag); |
1749 | 0 | tag = strdup(tmp); |
1750 | 0 | } else |
1751 | 0 | break; |
1752 | 0 | } while(cp); |
1753 | | |
1754 | | /* |
1755 | | * create the argv[] like array |
1756 | | */ |
1757 | 0 | argv[0] = strdup("snmpd-trapsess"); /* bogus entry for getopt() */ |
1758 | 0 | for (argn = 1; cp && argn < MAX_ARGS; argn++) { |
1759 | 0 | cp = copy_nword(cp, tmp, SPRINT_MAX_LEN); |
1760 | 0 | argv[argn] = strdup(tmp); |
1761 | 0 | } |
1762 | | |
1763 | | /** parse args (also initializes session) */ |
1764 | 0 | netsnmp_parse_args(argn, argv, &session, "C:", trapOptProc, |
1765 | 0 | NETSNMP_PARSE_ARGS_NOLOGGING | |
1766 | 0 | NETSNMP_PARSE_ARGS_NOZERO); |
1767 | |
|
1768 | 0 | if (NETSNMP_RUNTIME_PROTOCOL_SKIP(session.version)) { |
1769 | 0 | config_perror("snmpd: protocol version disabled at runtime"); |
1770 | 0 | for (; argn > 0; argn--) |
1771 | 0 | free(argv[argn - 1]); |
1772 | 0 | goto cleanup; |
1773 | 0 | } |
1774 | | |
1775 | 0 | if (NULL != session.localname) { |
1776 | 0 | clientaddr_save = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
1777 | 0 | NETSNMP_DS_LIB_CLIENT_ADDR); |
1778 | 0 | if (clientaddr_save) |
1779 | 0 | clientaddr_save = strdup(clientaddr_save); |
1780 | 0 | netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
1781 | 0 | NETSNMP_DS_LIB_CLIENT_ADDR, |
1782 | 0 | session.localname); |
1783 | 0 | } |
1784 | |
|
1785 | 0 | transport = netsnmp_transport_open_client("snmptrap", session.peername); |
1786 | |
|
1787 | 0 | if (NULL != session.localname) |
1788 | 0 | netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
1789 | 0 | NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save); |
1790 | |
|
1791 | 0 | if (transport == NULL) { |
1792 | 0 | config_perror("snmpd: failed to parse this line."); |
1793 | 0 | for (; argn > 0; argn--) |
1794 | 0 | free(argv[argn - 1]); |
1795 | 0 | goto cleanup; |
1796 | 0 | } |
1797 | 0 | if ((rc = netsnmp_sess_config_and_open_transport(&session, transport)) |
1798 | 0 | != SNMPERR_SUCCESS) { |
1799 | 0 | session.s_snmp_errno = rc; |
1800 | 0 | session.s_errno = 0; |
1801 | 0 | for (; argn > 0; argn--) |
1802 | 0 | free(argv[argn - 1]); |
1803 | 0 | goto cleanup; |
1804 | 0 | } |
1805 | 0 | session.callback = handle_disconnect_packet; |
1806 | 0 | ss = snmp_add(&session, transport, NULL, NULL); |
1807 | 0 | for (; argn > 0; argn--) |
1808 | 0 | free(argv[argn - 1]); |
1809 | |
|
1810 | 0 | if (!ss) { |
1811 | 0 | config_perror |
1812 | 0 | ("snmpd: failed to parse this line or the remote trap receiver is down. Possible cause:"); |
1813 | 0 | snmp_sess_perror("snmpd: snmpd_parse_config_trapsess()", &session); |
1814 | 0 | goto cleanup; |
1815 | 0 | } |
1816 | | |
1817 | | /* |
1818 | | * If this is an SNMPv3 TRAP session, then the agent is |
1819 | | * the authoritative engine, so set the engineID accordingly |
1820 | | */ |
1821 | 0 | if (ss->version == SNMP_VERSION_3 && |
1822 | 0 | traptype != SNMP_MSG_INFORM && |
1823 | 0 | ss->securityEngineIDLen == 0) { |
1824 | 0 | u_char tmp[SPRINT_MAX_LEN]; |
1825 | |
|
1826 | 0 | len = snmpv3_get_engineID( tmp, sizeof(tmp)); |
1827 | 0 | ss->securityEngineID = netsnmp_memdup(tmp, len); |
1828 | 0 | ss->securityEngineIDLen = len; |
1829 | 0 | } |
1830 | |
|
1831 | 0 | #ifndef NETSNMP_DISABLE_SNMPV1 |
1832 | 0 | if ((ss->version == SNMP_VERSION_1) && |
1833 | 0 | !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
1834 | 0 | NETSNMP_DS_LIB_DISABLE_V1)) |
1835 | 0 | traptype = SNMP_MSG_TRAP; |
1836 | 0 | #endif |
1837 | 0 | netsnmp_add_closable_notification_session(ss, |
1838 | 0 | !(transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM), |
1839 | 0 | traptype, |
1840 | 0 | (traptype == SNMP_MSG_INFORM), |
1841 | 0 | ss->version, name, tag, profile); |
1842 | |
|
1843 | 0 | cleanup: |
1844 | 0 | netsnmp_cleanup_session(&session); |
1845 | 0 | SNMP_FREE(clientaddr_save); |
1846 | 0 | SNMP_FREE(profile); |
1847 | 0 | SNMP_FREE(name); |
1848 | 0 | SNMP_FREE(tag); |
1849 | 0 | } |
1850 | | |
1851 | | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
1852 | | void |
1853 | | snmpd_parse_config_trapcommunity(const char *word, char *cptr) |
1854 | 0 | { |
1855 | 0 | if (snmp_trapcommunity != NULL) { |
1856 | 0 | free(snmp_trapcommunity); |
1857 | 0 | } |
1858 | 0 | snmp_trapcommunity = (char *) malloc(strlen(cptr) + 1); |
1859 | 0 | if (snmp_trapcommunity != NULL) { |
1860 | 0 | copy_nword(cptr, snmp_trapcommunity, strlen(cptr) + 1); |
1861 | 0 | } |
1862 | 0 | } |
1863 | | |
1864 | | void |
1865 | | snmpd_free_trapcommunity(void) |
1866 | 6.12k | { |
1867 | 6.12k | if (snmp_trapcommunity) { |
1868 | 0 | free(snmp_trapcommunity); |
1869 | 0 | snmp_trapcommunity = NULL; |
1870 | 0 | } |
1871 | 6.12k | } |
1872 | | #endif |
1873 | | /** @} */ |