root/src/modules/snmp.c

Revision 9c44e5a54c4ada69e5e61476fc5775b6e4d96c14, 56.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

Make stats on checks use epoch memory reclamation.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2010-2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <mtev_defines.h>
35
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <math.h>
42 #include <ctype.h>
43 #include <arpa/inet.h>
44
45 #include <net-snmp/net-snmp-config.h>
46 #include <net-snmp/net-snmp-includes.h>
47
48 #include <mtev_hash.h>
49
50 #include "noit_module.h"
51 #include "noit_check.h"
52 #include "noit_check_tools.h"
53 #include "noit_mtev_bridge.h"
54
55 static mtev_log_stream_t nlerr = NULL;
56 static mtev_log_stream_t nldeb = NULL;
57 static int __snmp_initialize_once = 0;
58 static void ensure_usm_user(const char *username, u_char *engineID, size_t engineIDLen);
59
60 #define SNMPV2_TRAPS_PREFIX     SNMP_OID_SNMPMODULES,1,1,5
61 oid trap_prefix[]    = { SNMPV2_TRAPS_PREFIX };
62 oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 };  /* SNMPv2-MIB */
63 oid warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 };  /* SNMPv2-MIB */
64 oid link_down_oid[]  = { SNMPV2_TRAPS_PREFIX, 3 };  /* IF-MIB */
65 oid link_up_oid[]    = { SNMPV2_TRAPS_PREFIX, 4 };  /* IF-MIB */
66 oid auth_fail_oid[]  = { SNMPV2_TRAPS_PREFIX, 5 };  /* SNMPv2-MIB */
67 oid egp_xxx_oid[]    = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
68
69 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
70 oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
71 size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
72 oid snmptrapenterprise_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
73 size_t snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
74 oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
75 size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
76
77 #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1
78 oid agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 };
79 size_t agentaddr_oid_len = OID_LENGTH(agentaddr_oid);
80 oid community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 };
81 size_t community_oid_len = OID_LENGTH(community_oid);
82
83 #define RECONNOITER_PREFIX     SNMP_OID_ENTERPRISES,32863,1
84 oid reconnoiter_oid[] = { RECONNOITER_PREFIX };
85 size_t reconnoiter_oid_len = OID_LENGTH(reconnoiter_oid);
86 oid reconnoiter_check_prefix_oid[] = { RECONNOITER_PREFIX,1,1 };
87 size_t reconnoiter_check_prefix_oid_len =
88   OID_LENGTH(reconnoiter_check_prefix_oid);
89 size_t reconnoiter_check_oid_len = OID_LENGTH(reconnoiter_check_prefix_oid) + 8;
90 oid reconnoiter_metric_prefix_oid[] = { RECONNOITER_PREFIX,1,2 };
91 size_t reconnoiter_metric_prefix_oid_len =
92   OID_LENGTH(reconnoiter_metric_prefix_oid);
93
94 oid reconnoiter_check_status_oid[] = { RECONNOITER_PREFIX,1,3};
95 size_t reconnoiter_check_status_oid_len =
96   OID_LENGTH(reconnoiter_check_status_oid);
97 oid reconnoiter_check_state_oid[] = { RECONNOITER_PREFIX,1,3,1};
98 size_t reconnoiter_check_state_oid_len =
99   OID_LENGTH(reconnoiter_check_state_oid);
100 oid reconnoiter_check_state_unknown_oid[] = { RECONNOITER_PREFIX,1,3,1,0};
101 oid reconnoiter_check_state_good_oid[] = { RECONNOITER_PREFIX,1,3,1,1};
102 oid reconnoiter_check_state_bad_oid[] = { RECONNOITER_PREFIX,1,3,1,2};
103 size_t reconnoiter_check_state_val_len =
104   OID_LENGTH(reconnoiter_check_state_unknown_oid);
105 /* Boolean */
106 oid reconnoiter_check_available_oid[] = { RECONNOITER_PREFIX,1,3,2};
107 size_t reconnoiter_check_available_oid_len =
108   OID_LENGTH(reconnoiter_check_available_oid);
109 oid reconnoiter_check_available_unknown_oid[] = { RECONNOITER_PREFIX,1,3,2,0};
110 oid reconnoiter_check_available_yes_oid[] = { RECONNOITER_PREFIX,1,3,2,1};
111 oid reconnoiter_check_available_no_oid[] = { RECONNOITER_PREFIX,1,3,2,2};
112 size_t reconnoiter_check_available_val_len =
113   OID_LENGTH(reconnoiter_check_available_unknown_oid);
114 /* timeticks? gauge/unsigned? */
115 oid reconnoiter_check_duration_oid[] = { RECONNOITER_PREFIX,1,3,3};
116 size_t reconnoiter_check_duration_oid_len =
117   OID_LENGTH(reconnoiter_check_duration_oid);
118 /* string */
119 oid reconnoiter_check_status_msg_oid[] = { RECONNOITER_PREFIX,1,3,4};
120 size_t reconnoiter_check_status_msg_oid_len =
121   OID_LENGTH(reconnoiter_check_status_msg_oid);
122
123 typedef struct _mod_config {
124   mtev_hash_table *options;
125   mtev_hash_table target_sessions;
126 } snmp_mod_config_t;
127
128 struct target_session {
129   struct synch_state state;
130   struct session_list *slp;
131   noit_module_t *self;
132   char *key;
133   char *target;
134   eventer_t timeoutevent;
135   int version;
136   int fd;
137   int in_table;
138   int refcnt;
139   struct timeval last_open;
140 };
141
142 #define sess_handle slp->session
143
144 struct snmp_check_closure {
145   noit_module_t *self;
146   noit_check_t *check;
147 };
148
149 struct v3_probe_magic {
150   eventer_t timeoutevent;
151   netsnmp_callback cb;
152   noit_check_t *check;
153   struct target_session *ts; /* for timeout */
154   struct snmp_pdu *pdu;
155 };
156
157 struct check_info {
158   int timedout;
159   struct {
160      int reqid;
161      char *confname;
162      char *oidname;
163      oid oid[MAX_OID_LEN];
164      size_t oidlen;
165      metric_type_t type_override;
166      mtev_boolean type_should_override;
167      int seen;
168   } *oids;
169   int noids;
170   int noids_seen;
171   int nresults;
172   eventer_t timeoutevent;
173   noit_module_t *self;
174   noit_check_t *check;
175   struct target_session *ts;
176   int version;
177 };
178
179 /* We hold struct check_info's in there key's by their reqid.
180  *   If they timeout, we remove them.
181  *
182  *   When SNMP queries complete, we look them up, if we find them
183  *   then we know we can remove the timeout and  complete the check.
184  *   If we don't find them, the timeout fired and removed the check.
185  */
186 mtev_hash_table active_checks = MTEV_HASH_EMPTY;
187 static void add_check(struct check_info *c) {
188   int i;
189   for(i=0; i<c->noids; i++)
190     mtev_hash_store(&active_checks, (char *)&c->oids[i].reqid, sizeof(c->oids[i].reqid), c);
191 }
192 static struct check_info *get_check(int reqid) {
193   void *vc;
194   if(mtev_hash_retrieve(&active_checks, (char *)&reqid, sizeof(reqid), &vc))
195     return (struct check_info *)vc;
196   return NULL;
197 }
198 static void remove_check_req(struct check_info *c, int reqid) {
199   (void)c;
200   mtev_hash_delete(&active_checks, (char *)&reqid, sizeof(reqid),
201                    NULL, NULL);
202 }
203 static void remove_check(struct check_info *c) {
204   int i, lastreq = -1;
205   for(i=0; i<c->noids; i++) {
206     if(c->oids[i].reqid != lastreq) {
207       mtev_hash_delete(&active_checks, (char *)&c->oids[i].reqid, sizeof(c->oids[i].reqid),
208                        NULL, NULL);
209       lastreq = c->oids[i].reqid;
210     }
211   }
212 }
213
214 struct target_session *
215 _get_target_session(noit_module_t *self, char *target, int version) {
216   char key[128];
217   void *vts;
218   struct target_session *ts;
219   snmp_mod_config_t *conf;
220   conf = noit_module_get_userdata(self);
221   snprintf(key, sizeof(key), "%s:v%d", target, version);
222   if(!mtev_hash_retrieve(&conf->target_sessions,
223                          key, strlen(key), &vts)) {
224     ts = calloc(1, sizeof(*ts));
225     ts->self = self;
226     ts->version = version;
227     ts->fd = -1;
228     ts->refcnt = 0;
229     ts->target = strdup(target);
230     ts->key = strdup(key);
231     ts->in_table = 1;
232     mtev_hash_store(&conf->target_sessions,
233                     ts->key, strlen(ts->key), ts);
234     vts = ts;
235   }
236   return (struct target_session *)vts;
237 }
238
239 static int noit_snmp_accumulate_results(noit_check_t *check, struct snmp_pdu *pdu) {
240   struct check_info *info = check->closure;
241   struct variable_list *vars;
242
243   if(pdu)
244     for(vars = pdu->variables; vars; vars = vars->next_variable)
245       info->nresults++;
246
247   /* manipulate the information ourselves */
248   for(vars = pdu->variables; vars; vars = vars->next_variable) {
249     char *sp;
250     int nresults = 0;
251     int oid_idx;
252     double float_conv;
253     u_int64_t u64;
254     int64_t i64;
255     char *endptr;
256     char varbuff[256];
257
258     snprint_variable(varbuff, sizeof(varbuff),
259                      vars->name, vars->name_length, vars);
260
261     /* find the oid to which this is the response */
262     oid_idx = nresults; /* our check->stats.inprogress idx is the most likely */
263     if(info->oids[oid_idx].oidlen != vars->name_length ||
264        memcmp(info->oids[oid_idx].oid, vars->name,
265               vars->name_length * sizeof(oid))) {
266       /* Not the most obvious guess */
267       for(oid_idx = info->noids - 1; oid_idx >= 0; oid_idx--) {
268         if(info->oids[oid_idx].oidlen == vars->name_length &&
269            !memcmp(info->oids[oid_idx].oid, vars->name,
270                   vars->name_length * sizeof(oid))) break;
271       }
272     }
273     if(oid_idx < 0) {
274       mtevL(nlerr, "Unexpected oid results to %s`%s`%s: %s\n",
275             check->target, check->module, check->name, varbuff);
276       nresults++;
277       info->nresults++;
278       continue;
279     }
280     if(info->oids[oid_idx].seen == 0) {
281       info->oids[oid_idx].seen = 1;
282       info->noids_seen++;
283     }
284
285 #define SETM(a,b) noit_stats_set_metric(check, info->oids[oid_idx].confname, a, b)
286     if(info->oids[oid_idx].type_should_override) {
287       sp = strchr(varbuff, ' ');
288       if(sp) sp++;
289       noit_stats_set_metric_coerce(check, info->oids[oid_idx].confname,
290                                    info->oids[oid_idx].type_override,
291                                    sp);
292     }
293     else {
294       switch(vars->type) {
295         case ASN_OCTET_STR:
296           sp = malloc(1 + vars->val_len);
297           memcpy(sp, vars->val.string, vars->val_len);
298           sp[vars->val_len] = '\0';
299           SETM(METRIC_STRING, sp);
300           free(sp);
301           break;
302         case ASN_INTEGER:
303         case ASN_GAUGE:
304           SETM(METRIC_INT32, vars->val.integer);
305           break;
306         case ASN_TIMETICKS:
307         case ASN_COUNTER:
308           SETM(METRIC_UINT32, vars->val.integer);
309           break;
310 #ifdef ASN_OPAQUE_I64
311         case ASN_OPAQUE_I64:
312 #endif
313         case ASN_INTEGER64:
314           printI64(varbuff, vars->val.counter64);
315           i64 = strtoll(varbuff, &endptr, 10);
316           SETM(METRIC_INT64, (varbuff == endptr) ? NULL : &i64);
317           break;
318 #ifdef ASN_OPAQUE_U64
319         case ASN_OPAQUE_U64:
320 #endif
321 #ifdef ASN_OPAQUE_COUNTER64
322         case ASN_OPAQUE_COUNTER64:
323 #endif
324         case ASN_COUNTER64:
325           printU64(varbuff, vars->val.counter64);
326           u64 = strtoull(varbuff, &endptr, 10);
327           SETM(METRIC_UINT64, (varbuff == endptr) ? NULL : &u64);
328           break;
329 #ifdef ASN_OPAQUE_FLOAT
330         case ASN_OPAQUE_FLOAT:
331 #endif
332         case ASN_FLOAT:
333           if(vars->val.floatVal) float_conv = *(vars->val.floatVal);
334           SETM(METRIC_DOUBLE, vars->val.floatVal ? &float_conv : NULL);
335           break;
336 #ifdef ASN_OPAQUE_DOUBLE
337         case ASN_OPAQUE_DOUBLE:
338 #endif
339         case ASN_DOUBLE:
340           SETM(METRIC_DOUBLE, vars->val.doubleVal);
341           break;
342         case ASN_NULL:
343           mtevL(nldeb, "snmp[null]: %s\n", varbuff);
344         case SNMP_NOSUCHOBJECT:
345         case SNMP_NOSUCHINSTANCE:
346           SETM(METRIC_STRING, NULL);
347           break;
348         default:
349           /* Advance passed the first space and use that unless there
350            * is no space or we have no more string left.
351            */
352           sp = strchr(varbuff, ' ');
353           if(sp) sp++;
354           SETM(METRIC_STRING, (sp && *sp) ? sp : NULL);
355           mtevL(nlerr, "snmp: unknown type[%d] %s\n", vars->type, varbuff);
356       }
357     }
358     nresults++;
359     info->nresults++;
360   }
361   return (info->noids_seen == info->noids) ? 1 : 0;
362 }
363
364 /* Handling of results */
365 static void noit_snmp_log_results(noit_module_t *self, noit_check_t *check, const char *err) {
366   struct check_info *info = check->closure;
367   struct timeval duration, now;
368   char buff[128];
369
370   gettimeofday(&now, NULL);
371   sub_timeval(now, check->last_fire_time, &duration);
372   noit_stats_set_whence(check, &now);
373   noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);
374   noit_stats_set_available(check, (info->nresults > 0) ? NP_AVAILABLE : NP_UNAVAILABLE);
375   noit_stats_set_state(check, (info->noids_seen == info->noids) ? NP_GOOD : NP_BAD);
376   if(err) snprintf(buff, sizeof(buff), "%s", err);
377   else snprintf(buff, sizeof(buff), "%d/%d gets", info->noids_seen, info->noids);
378   noit_stats_set_status(check, buff);
379
380   noit_check_set_stats(check);
381   return;
382 }
383
384 static int noit_snmp_session_cleanse(struct target_session *ts,
385                                      int needs_free) {
386   if(ts->refcnt == 0 && ts->slp) {
387     eventer_t e = eventer_remove_fd(ts->fd);
388     if(needs_free) eventer_free(e);
389     ts->fd = -1;
390     if(ts->timeoutevent) {
391       eventer_remove(ts->timeoutevent);
392       ts->timeoutevent = NULL;
393     }
394     snmp_sess_close(ts->slp);
395     ts->slp = NULL;
396     if(!ts->in_table) {
397       free(ts);
398     }
399     return 1;
400   }
401   return 0;
402 }
403
404 static int noit_snmp_session_timeout(eventer_t e, int mask, void *closure,
405                                      struct timeval *now) {
406   struct target_session *ts = closure;
407   if(ts->slp) snmp_sess_timeout(ts->slp);
408   noit_snmp_session_cleanse(ts, 1);
409   if(ts->timeoutevent == e)
410     ts->timeoutevent = NULL; /* this will be freed on return */
411   return 0;
412 }
413
414 static int noit_snmp_check_timeout(eventer_t e, int mask, void *closure,
415                                    struct timeval *now) {
416   struct check_info *info = closure;
417   info->timeoutevent = NULL;
418   info->timedout = 1;
419   if(info->ts) {
420     info->ts->refcnt--;
421     noit_snmp_session_cleanse(info->ts, 1);
422     info->ts = NULL;
423   }
424   remove_check(info);
425   /* Log our findings */
426   noit_snmp_log_results(info->self, info->check, NULL);
427   info->check->flags &= ~NP_RUNNING;
428   return 0;
429 }
430
431 static void _set_ts_timeout(struct target_session *ts, struct timeval *t) {
432   struct timeval now;
433   eventer_t e = NULL;
434   if(ts->timeoutevent) {
435     e = eventer_remove(ts->timeoutevent);
436     ts->timeoutevent = NULL;
437   }
438   if(!t) return;
439
440   gettimeofday(&now, NULL);
441   if(!e) e = eventer_alloc();
442   e->callback = noit_snmp_session_timeout;
443   e->closure = ts;
444   e->mask = EVENTER_TIMER;
445   add_timeval(now, *t, &e->whence);
446   ts->timeoutevent = e;
447   eventer_add(e);
448 }
449
450 static int noit_snmp_handler(eventer_t e, int mask, void *closure,
451                              struct timeval *now) {
452   int block = 0, rv, liberr, snmperr;
453   struct timeval timeout = { 0, 0 };
454   struct target_session *ts = closure;
455   char *errmsg;
456
457   rv = snmp_sess_read_C1(ts->slp, e->fd);
458   if(noit_snmp_session_cleanse(ts, 0))
459     return 0;
460   snmp_sess_select_info2_flags(ts->slp, NULL, NULL,
461                                &timeout, &block, NETSNMP_SELECT_NOFLAGS);
462   _set_ts_timeout(ts, block ? &timeout : NULL);
463
464   return EVENTER_READ | EVENTER_EXCEPTION;
465 }
466
467 /* This 'convert_v1pdu_to_v2' was cribbed directly from netsnmp */
468 static netsnmp_pdu *
469 convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu ) {
470   netsnmp_pdu *template_v2pdu;
471   netsnmp_variable_list *var;
472   oid enterprise[MAX_OID_LEN];
473   size_t enterprise_len;
474
475   /*
476    * Make a copy of the v1 Trap PDU
477    *   before starting to convert this
478    *   into a v2 Trap PDU.
479    */
480   template_v2pdu = snmp_clone_pdu( template_v1pdu);
481   if(!template_v2pdu) {
482     snmp_log(LOG_WARNING,
483              "send_trap: failed to copy v2 template PDU\n");
484     return NULL;
485   }
486   template_v2pdu->command = SNMP_MSG_TRAP2;
487
488   /*
489    * Insert an snmpTrapOID varbind before the original v1 varbind list
490    *   either using one of the standard defined trap OIDs,
491    *   or constructing this from the PDU enterprise & specific trap fields
492    */
493   if(template_v1pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
494     if(template_v1pdu->enterprise_length + 2 > MAX_OID_LEN) {
495       mtevL(nlerr, "send_trap: enterprise_length too large\n");
496       snmp_free_pdu(template_v2pdu);
497       return NULL;
498     }
499     memcpy(enterprise, template_v1pdu->enterprise,
500            template_v1pdu->enterprise_length*sizeof(oid));
501     enterprise_len = template_v1pdu->enterprise_length;
502     enterprise[enterprise_len++] = 0;
503     enterprise[enterprise_len++] = template_v1pdu->specific_type;
504   } else {
505     memcpy(enterprise, cold_start_oid, sizeof(cold_start_oid));
506     enterprise[9]  = template_v1pdu->trap_type+1;
507     enterprise_len = sizeof(cold_start_oid)/sizeof(oid);
508   }
509
510   var = NULL;
511   if(!snmp_varlist_add_variable(&var,
512                                 snmptrap_oid, snmptrap_oid_len,
513                                 ASN_OBJECT_ID,
514                                 (u_char*)enterprise,
515                                 enterprise_len*sizeof(oid))) {
516     mtevL(nlerr, "send_trap: failed to insert copied snmpTrapOID varbind\n");
517     snmp_free_pdu(template_v2pdu);
518     return NULL;
519   }
520   var->next_variable        = template_v2pdu->variables;
521   template_v2pdu->variables = var;
522
523   /*
524    * Insert a sysUptime varbind at the head of the v2 varbind list
525    */
526   var = NULL;
527   if(!snmp_varlist_add_variable(&var,
528                                 sysuptime_oid, sysuptime_oid_len,
529                                 ASN_TIMETICKS,
530                                 (u_char*)&(template_v1pdu->time),
531                                 sizeof(template_v1pdu->time))) {
532     mtevL(nlerr, "send_trap: failed to insert copied sysUptime varbind\n");
533     snmp_free_pdu(template_v2pdu);
534     return NULL;
535   }
536   var->next_variable = template_v2pdu->variables;
537   template_v2pdu->variables = var;
538
539   /*
540    * Append the other three conversion varbinds,
541    *  (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise)
542    *  if they're not already present.
543    *  But don't bomb out completely if there are problems.
544    */
545   var = find_varbind_in_list(template_v2pdu->variables,
546                              agentaddr_oid, agentaddr_oid_len);
547   if(!var && (template_v1pdu->agent_addr[0]
548               || template_v1pdu->agent_addr[1]
549               || template_v1pdu->agent_addr[2]
550               || template_v1pdu->agent_addr[3])) {
551     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
552                                   agentaddr_oid, agentaddr_oid_len,
553                                   ASN_IPADDRESS,
554                                   (u_char*)&(template_v1pdu->agent_addr),
555                                   sizeof(template_v1pdu->agent_addr)))
556       mtevL(nlerr, "send_trap: failed to append snmpTrapAddr varbind\n");
557   }
558   var = find_varbind_in_list(template_v2pdu->variables,
559                              community_oid, community_oid_len);
560   if(!var && template_v1pdu->community) {
561     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
562                                   community_oid, community_oid_len,
563                                   ASN_OCTET_STR,
564                                   template_v1pdu->community,
565                                   template_v1pdu->community_len))
566       mtevL(nlerr, "send_trap: failed to append snmpTrapCommunity varbind\n");
567   }
568   var = find_varbind_in_list(template_v2pdu->variables,
569                              snmptrapenterprise_oid,
570                              snmptrapenterprise_oid_len);
571   if(!var &&
572      template_v1pdu->trap_type != SNMP_TRAP_ENTERPRISESPECIFIC) {
573     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
574                                   snmptrapenterprise_oid,
575                                   snmptrapenterprise_oid_len,
576                                   ASN_OBJECT_ID,
577                                   (u_char*)template_v1pdu->enterprise,
578                                   template_v1pdu->enterprise_length*sizeof(oid)))
579       mtevL(nlerr, "send_trap: failed to append snmpEnterprise varbind\n");
580   }
581   return template_v2pdu;
582 }
583
584 static int noit_snmp_oid_to_checkid(oid *o, int l, uuid_t checkid, char *out) {
585   int i;
586   char _uuid_str[UUID_STR_LEN+1], *cp, *uuid_str;
587
588   uuid_str = out ? out : _uuid_str;
589   if(l != reconnoiter_check_oid_len) {
590     mtevL(nlerr, "unsupported (length) trap recieved\n");
591     return -1;
592   }
593   if(netsnmp_oid_equals(o,
594                         reconnoiter_check_prefix_oid_len,
595                         reconnoiter_check_prefix_oid,
596                         reconnoiter_check_prefix_oid_len) != 0) {
597     mtevL(nlerr, "unsupported (wrong namespace) trap recieved\n");
598     return -1;
599   }
600   /* encode this as a uuid */
601   cp = uuid_str;
602   for(i=0;
603       i < reconnoiter_check_oid_len - reconnoiter_check_prefix_oid_len;
604       i++) {
605     oid v = o[i + reconnoiter_check_prefix_oid_len];
606     if(v > 0xffff) {
607       mtevL(nlerr, "trap target oid [%ld] out of range\n", (long int)v);
608       return -1;
609     }
610     snprintf(cp, 5, "%04x", (unsigned short)(v & 0xffff));
611     cp += 4;
612     /* hyphens after index 1,2,3,4 */
613     if(i > 0 && i < 5) *cp++ = '-';
614   }
615   if(uuid_parse(uuid_str, checkid) != 0) {
616     mtevL(nlerr, "unexpected error decoding trap uuid '%s'\n", uuid_str);
617     return -1;
618   }
619   return 0;
620 }
621
622 #define isoid(a,b,c,d) (netsnmp_oid_equals(a,b,c,d) == 0)
623 #define isoidprefix(a,b,c,d) (netsnmp_oid_equals(a,MIN(b,d),c,d) == 0)
624 #define setstatus(st,soid,sv) \
625   if(isoid(o,l,soid,reconnoiter_check_state_val_len)) noit_stats_set_##st(check, sv)
626
627 static int
628 noit_snmp_trapvars_to_stats(noit_check_t *check, netsnmp_variable_list *var) {
629   if(isoidprefix(var->name, var->name_length, reconnoiter_check_status_oid,
630                  reconnoiter_check_status_oid_len)) {
631     if(var->type == ASN_OBJECT_ID) {
632       if(isoid(var->name, var->name_length,
633                reconnoiter_check_state_oid, reconnoiter_check_state_oid_len)) {
634         oid *o = var->val.objid;
635         size_t l = var->val_len / sizeof(*o);
636         setstatus(state, reconnoiter_check_state_unknown_oid, NP_UNKNOWN);
637         else setstatus(state, reconnoiter_check_state_good_oid, NP_GOOD);
638         else setstatus(state, reconnoiter_check_state_bad_oid, NP_BAD);
639         else return -1;
640       }
641       else if(isoid(var->name, var->name_length,
642                     reconnoiter_check_available_oid,
643                     reconnoiter_check_available_oid_len)) {
644         oid *o = var->val.objid;
645         size_t l = var->val_len / sizeof(*o);
646         setstatus(available, reconnoiter_check_available_unknown_oid, NP_UNKNOWN);
647         else setstatus(available, reconnoiter_check_available_yes_oid, NP_AVAILABLE);
648         else setstatus(available, reconnoiter_check_available_no_oid, NP_UNAVAILABLE);
649         else return -1;
650       }
651       else {
652         /* We don't unerstand any other OBJECT_ID types */
653         return -1;
654       }
655     }
656     else if(var->type == ASN_UNSIGNED) {
657       /* This is only for the duration (in ms) */
658       if(isoid(var->name, var->name_length,
659                reconnoiter_check_duration_oid,
660                reconnoiter_check_duration_oid_len)) {
661         noit_stats_set_duration(check, *(var->val.integer));
662       }
663       else
664         return -1;
665     }
666     else if(var->type == ASN_OCTET_STR) {
667       /* This is only for the status message */
668       if(isoid(var->name, var->name_length,
669                reconnoiter_check_status_msg_oid,
670                reconnoiter_check_status_msg_oid_len)) {
671         char buff[256];
672         strlcpy(buff, (char *)var->val.string, MIN(sizeof(buff), var->val_len+1));
673         noit_stats_set_status(check, buff);
674       }
675       else
676         return -1;
677     }
678     else {
679       /* I don't understand any other type of status message */
680       return -1;
681     }
682   }
683   else if(isoidprefix(var->name, var->name_length,
684                       reconnoiter_metric_prefix_oid,
685                       reconnoiter_metric_prefix_oid_len)) {
686     /* decode the metric and store the value */
687     int i, len;
688     u_int64_t u64;
689     double doubleVal;
690     char metric_name[128], buff[128], *cp;
691     if(var->name_length <= reconnoiter_metric_prefix_oid_len) return -1;
692     len = var->name[reconnoiter_metric_prefix_oid_len];
693     if(var->name_length != (reconnoiter_metric_prefix_oid_len + 1 + len) ||
694        len > sizeof(metric_name) - 1) {
695       mtevL(nlerr, "snmp trap, malformed metric name\n");
696       return -1;
697     }
698     for(i=0;i<len;i++) {
699       ((unsigned char *)metric_name)[i] =
700         (unsigned char)var->name[reconnoiter_metric_prefix_oid_len + 1 + i];
701       if(!isprint(metric_name[i])) {
702         mtevL(nlerr, "metric_name contains unprintable characters\n");
703         return -1;
704       }
705     }
706     metric_name[i] = '\0';
707     switch(var->type) {
708       case ASN_INTEGER:
709       case ASN_UINTEGER:
710       case ASN_TIMETICKS:
711       case ASN_INTEGER64:
712         noit_stats_set_metric(check, metric_name,
713                               METRIC_INT64, var->val.integer);
714         break;
715       case ASN_COUNTER64:
716         u64 = ((u_int64_t)var->val.counter64->high) << 32;
717         u64 |= var->val.counter64->low;
718         noit_stats_set_metric(check, metric_name,
719                               METRIC_UINT64, &u64);
720         break;
721       case ASN_OPAQUE_FLOAT:
722         doubleVal = (double)*var->val.floatVal;
723         noit_stats_set_metric(check, metric_name,
724                               METRIC_DOUBLE, &doubleVal);
725         break;
726       case ASN_OPAQUE_DOUBLE:
727         noit_stats_set_metric(check, metric_name,
728                               METRIC_DOUBLE, var->val.doubleVal);
729         break;
730       case ASN_OCTET_STR:
731         snprint_value(buff, sizeof(buff), var->name, var->name_length, var);
732         /* Advance passed the first space and use that unless there
733          * is no space or we have no more string left.
734          */
735         cp = strchr(buff, ' ');
736         if(cp) {
737           char *ecp;
738           cp++;
739           if(*cp == '"') {
740             ecp = cp + strlen(cp) - 1;
741             if(*ecp == '"') {
742               cp++; *ecp = '\0';
743             }
744           }
745         }
746         noit_stats_set_metric(check, metric_name,
747                               METRIC_STRING, (cp && *cp) ? cp : NULL);
748         break;
749       default:
750         mtevL(nlerr, "snmp trap unsupport data type %d\n", var->type);
751     }
752     mtevL(nldeb, "metric_name -> '%s'\n", metric_name);
753   }
754   else {
755     /* No idea what this is */
756     return -1;
757   }
758   return 0;
759 }
760 static int noit_snmp_trapd_response(int operation, struct snmp_session *sp,
761                                     int reqid, struct snmp_pdu *pdu,
762                                     void *magic) {
763   /* the noit pieces */
764   struct timeval now;
765   noit_check_t *check;
766   struct target_session *ts = magic;
767   snmp_mod_config_t *conf;
768   const char *community = NULL;
769   int success = 0;
770
771   /* parsing destination */
772   char uuid_str[UUID_STR_LEN + 1];
773   uuid_t checkid;
774
775   /* snmp oid parsing helper vars */
776   netsnmp_pdu *newpdu = pdu;
777   netsnmp_variable_list *var;
778
779   conf = noit_module_get_userdata(ts->self);
780
781   if(pdu->version == SNMP_VERSION_1)
782     newpdu = convert_v1pdu_to_v2(pdu);
783   if(!newpdu || newpdu->version != SNMP_VERSION_2c) goto cleanup;
784
785   for(var = newpdu->variables; var != NULL; var = var->next_variable) {
786     if(netsnmp_oid_equals(var->name, var->name_length,
787                           snmptrap_oid, snmptrap_oid_len) == 0)
788       break;
789   }
790
791   if (!var || var->type != ASN_OBJECT_ID) {
792     mtevL(nlerr, "unsupport trap (not a trap?) received\n");
793     goto cleanup;
794   }
795
796   /* var is the oid on which we are trapping.
797    * It should be in the reconnoiter check prefix.
798    */
799   if(noit_snmp_oid_to_checkid(var->val.objid, var->val_len/sizeof(oid),
800                               checkid, uuid_str)) {
801     goto cleanup;
802   }
803   mtevL(nldeb, "recieved trap for %s\n", uuid_str);
804   check = noit_poller_lookup(checkid);
805   if(!check) {
806     mtevL(nlerr, "trap received for non-existent check '%s'\n", uuid_str);
807     goto cleanup;
808   }
809   if(!mtev_hash_retr_str(check->config, "community", strlen("community"),
810                          &community) &&
811      !mtev_hash_retr_str(conf->options, "community", strlen("community"),
812                          &community)) {
813     mtevL(nlerr, "No community defined for check, dropping trap\n");
814     goto cleanup;
815   }
816
817   if(strlen(community) != newpdu->community_len ||
818      memcmp(community, newpdu->community, newpdu->community_len)) {
819     mtevL(nlerr, "trap attempt with wrong community string\n");
820     goto cleanup;
821   }
822
823   /* We have a check. The trap is authorized. Now, extract everything. */
824   gettimeofday(&now, NULL);
825   noit_stats_set_whence(check, &now);
826   noit_stats_set_available(check, NP_AVAILABLE);
827
828   /* Rate limit */
829   if(((now.tv_sec * 1000 +
830        now.tv_usec / 1000) -
831       (check->last_fire_time.tv_sec * 1000 +
832        check->last_fire_time.tv_usec / 1000)) < check->period) goto cleanup;
833
834   /* update the last fire time... */
835   gettimeofday(&check->last_fire_time, NULL);
836
837   for(; var != NULL; var = var->next_variable)
838     if(noit_snmp_trapvars_to_stats(check, var) == 0) success++;
839   if(success) {
840     char buff[24];
841     snprintf(buff, sizeof(buff), "%d datum", success);
842     noit_stats_set_state(check, NP_GOOD);
843     noit_stats_set_status(check, buff);
844   }
845   else {
846     noit_stats_set_state(check, NP_BAD);
847     noit_stats_set_status(check, "no data");
848   }
849   noit_check_set_stats(check);
850
851  cleanup:
852   if(newpdu != pdu)
853     snmp_free_pdu(newpdu);
854   return 0;
855 }
856 static int noit_snmp_asynch_response(int operation, struct snmp_session *sp,
857                                      int reqid, struct snmp_pdu *pdu,
858                                      void *magic) {
859   struct check_info *info;
860   /* We don't deal with refcnt hitting zero here.  We could only be hit from
861    * the snmp read/timeout stuff.  Handle it there.
862    */
863
864   info = get_check(reqid);
865   if(!info) return 1;
866   remove_check_req(info, reqid);
867
868
869   if(noit_snmp_accumulate_results(info->check, pdu)) {
870     mtevL(nldeb, "snmp %s pdu completed check requirements\n", info->check->name);
871     if(info->timeoutevent) {
872       eventer_remove(info->timeoutevent);
873       eventer_free(info->timeoutevent);
874       info->timeoutevent = NULL;
875     }
876     if(info->ts) {
877       info->ts->refcnt--;
878       info->ts = NULL;
879     }
880     noit_snmp_log_results(info->self, info->check, NULL);
881     info->check->flags &= ~NP_RUNNING;
882   }
883   return 1;
884 }
885
886 static void noit_snmp_sess_open(struct target_session *ts,
887                                 noit_check_t *check) {
888   size_t bsize, offset;
889   u_char _buf[512];
890   u_char *buf = _buf;
891   const char *cval;
892   struct snmp_session sess;
893   netsnmp_transport *transport;
894   struct check_info *info = check->closure;
895   memset(&sess, 0, sizeof(sess));
896   snmp_sess_init(&sess);
897   sess.version = info->version;
898   sess.peername = ts->target;
899   u_char contextEngineID_buf[256];
900   u_char securityEngineID_buf[256];
901
902 /*
903 final String walk_base             = config.remove("walk");
904 */
905
906 #define CONF_GET(name, tgt) \
907   mtev_hash_retr_str(check->config, name, strlen(name), tgt)
908 #define SESS_SET_STRING(value, len, key, default) do {    \
909   const char *_cval = NULL;                               \
910   if(!CONF_GET(key, &_cval)) {                            \
911     _cval = default;                                      \
912   }                                                       \
913   value = ( __typeof__ (value) )(_cval);                  \
914   len = strlen(_cval);                                    \
915 } while(0)
916
917   SESS_SET_STRING(sess.community, sess.community_len, "community", "public");
918   /* TCP/UDP -> session_flags |= SNMP_FLAGS_STREAM_SOCKET */
919   /* securityEngineID? */
920   /* contextEngineID? */
921   SESS_SET_STRING(sess.contextName, sess.contextNameLen, "context_name", "");
922   if(CONF_GET("context_engine", &cval)) {
923     bsize = sizeof(buf);
924     offset = 0;
925     if (netsnmp_hex_to_binary(&buf,&bsize,&offset,0,cval,".")) {
926             sess.contextEngineID = contextEngineID_buf;
927             memcpy(sess.contextEngineID, buf, bsize);
928             sess.contextEngineIDLen = bsize;
929     }
930         }
931   SESS_SET_STRING(sess.securityName, sess.securityNameLen, "security_name", "");
932   if(sess.securityName) sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
933   if(CONF_GET("security_engine", &cval)) {
934     bsize = sizeof(buf);
935     offset = 0;
936     if (netsnmp_hex_to_binary(&buf,&bsize,&offset,0,cval,".")) {
937             sess.securityEngineID = securityEngineID_buf;
938             memcpy(sess.securityEngineID, buf, bsize);
939             sess.securityEngineIDLen = bsize;
940     }
941         }
942   if(CONF_GET("security_level", &cval)) {
943     if(!strcasecmp(cval, "nanp")) sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
944     else if(!strcasecmp(cval, "anp")) sess.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
945     else if(!strcasecmp(cval, "ap")) sess.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
946   }
947   if(CONF_GET("auth_protocol", &cval)) {
948     if (!strcasecmp(cval,"MD5")) {
949       sess.securityAuthProto = usmHMACMD5AuthProtocol;
950       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
951     }
952     else if (!strcasecmp(cval,"SHA")) {
953       sess.securityAuthProto = usmHMACSHA1AuthProtocol;
954       sess.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
955     }
956   }
957   if(CONF_GET("auth_passphrase", &cval)) {
958     if(sess.securityAuthProto == NULL) {
959       sess.securityAuthProto = usmHMACMD5AuthProtocol;
960       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
961     }
962     sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
963     if(generate_Ku(sess.securityAuthProto, sess.securityAuthProtoLen,
964                    (u_char *)cval, strlen(cval),
965                    sess.securityAuthKey, &sess.securityAuthKeyLen) != SNMPERR_SUCCESS) {
966       /* What do we do? */
967       mtevL(nlerr, "auth_passphrase failed to gen master key\n");
968     }
969   }
970   if(CONF_GET("privacy_protocol", &cval)) {
971     if (!strcasecmp(cval,"DES")) {
972       sess.securityPrivProto = usmDESPrivProtocol;
973       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
974     }
975     else if (!strcasecmp(cval,"AES")) {
976       sess.securityPrivProto = usmAESPrivProtocol;
977       sess.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
978     }
979   }
980   if(CONF_GET("privacy_passphrase", &cval)) {
981     if(sess.securityPrivProto == NULL) {
982       sess.securityPrivProto = usmDESPrivProtocol;
983       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
984     }
985     sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
986     if(generate_Ku(sess.securityPrivProto, sess.securityPrivProtoLen,
987                    (u_char *)cval, strlen(cval),
988                    sess.securityPrivKey, &sess.securityPrivKeyLen) != SNMPERR_SUCCESS) {
989       /* What do we do? */
990     }
991   }
992   sess.callback = noit_snmp_asynch_response;
993   sess.callback_magic = ts;
994   sess.flags |= SNMP_FLAGS_DONT_PROBE;
995   ts->slp = snmp_sess_open_C1(&sess, &transport);
996   ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
997   ts->fd = transport->sock;
998   gettimeofday(&ts->last_open, NULL);
999 }
1000
1001 static int noit_snmp_fill_oidinfo(noit_check_t *check) {
1002   int i, klen;
1003   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1004   const char *name, *value;
1005   struct check_info *info = check->closure;
1006   mtev_hash_table check_attrs_hash = MTEV_HASH_EMPTY;
1007
1008   /* Toss the old set and bail if we have zero */
1009   if(info->oids) {
1010     for(i=0; i<info->noids;i++) {
1011       if(info->oids[i].confname) free(info->oids[i].confname);
1012       if(info->oids[i].oidname) free(info->oids[i].oidname);
1013     }
1014     free(info->oids);
1015   }
1016   info->noids = 0;
1017   info->nresults = 0;
1018   info->noids_seen = 0;
1019   info->oids = NULL;
1020
1021   /* Figure our how many. */
1022   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1023     if(!strncasecmp(name, "oid_", 4)) {
1024       info->noids++;
1025     }
1026   }
1027
1028   if(info->noids == 0) return 0;
1029
1030   /* Create a hash of important check attributes */
1031   noit_check_make_attrs(check, &check_attrs_hash);
1032
1033   /* Fill out the new set of required oids */
1034   info->oids = calloc(info->noids, sizeof(*info->oids));
1035   memset(&iter, 0, sizeof(iter));
1036   i = 0;
1037   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1038     if(!strncasecmp(name, "oid_", 4)) {
1039       const char *type_override;
1040       char oidbuff[2048], typestr[256];
1041       name += 4;
1042       info->oids[i].confname = strdup(name);
1043       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
1044                              &check_attrs_hash, check->config);
1045       info->oids[i].oidname = strdup(oidbuff);
1046       info->oids[i].oidlen = MAX_OID_LEN;
1047       if(oidbuff[0] == '.') {
1048         if(!read_objid(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1049           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1050           info->noids--;
1051           continue;
1052         }
1053       }
1054       else {
1055         if(!get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1056           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1057           info->noids--;
1058           continue;
1059         }
1060       }
1061       snprintf(typestr, sizeof(typestr), "type_%s", name);
1062       if(mtev_hash_retr_str(check->config, typestr, strlen(typestr),
1063                             &type_override)) {
1064         int type_enum_fake = *type_override;
1065
1066         if(!strcasecmp(type_override, "guess"))
1067           type_enum_fake = METRIC_GUESS;
1068         else if(!strcasecmp(type_override, "int32"))
1069           type_enum_fake = METRIC_INT32;
1070         else if(!strcasecmp(type_override, "uint32"))
1071           type_enum_fake = METRIC_UINT32;
1072         else if(!strcasecmp(type_override, "int64"))
1073           type_enum_fake = METRIC_INT64;
1074         else if(!strcasecmp(type_override, "uint64"))
1075           type_enum_fake = METRIC_UINT64;
1076         else if(!strcasecmp(type_override, "double"))
1077           type_enum_fake = METRIC_DOUBLE;
1078         else if(!strcasecmp(type_override, "string"))
1079           type_enum_fake = METRIC_STRING;
1080
1081         switch(type_enum_fake) {
1082           case METRIC_GUESS:
1083           case METRIC_INT32: case METRIC_UINT32:
1084           case METRIC_INT64: case METRIC_UINT64:
1085           case METRIC_DOUBLE: case METRIC_STRING:
1086             info->oids[i].type_override = *type_override;
1087             info->oids[i].type_should_override = mtev_true;
1088           default: break;
1089         }
1090       }
1091       i++;
1092     }
1093   }
1094   assert(info->noids == i);
1095   mtev_hash_destroy(&check_attrs_hash, NULL, NULL);
1096   return info->noids;
1097 }
1098 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check, int idx) {
1099   int i;
1100   struct check_info *info = check->closure;
1101
1102   if(idx == -1) {
1103     for(i=0; i<info->noids; i++)
1104       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
1105     return info->noids;
1106   }
1107
1108   assert(idx >= 0 && idx <info->noids);
1109   snmp_add_null_var(req, info->oids[idx].oid, info->oids[idx].oidlen);
1110   return 1;
1111 }
1112
1113 static void
1114 ensure_usm_user(const char *username, u_char *engineID, size_t engineIDLen) {
1115   struct usmUser *user;
1116   user = usm_get_user(NULL, 0, (char *)username);
1117   if (user == NULL) {
1118     user = (struct usmUser *) calloc(1, sizeof(struct usmUser));
1119     user->name = strdup(username);
1120     user->secName = strdup(username);
1121     user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid);
1122     user->authProtocol =
1123         snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen);
1124     user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid);
1125     user->privProtocol =
1126         snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen);
1127     if(engineIDLen) {
1128       user->engineID = malloc(engineIDLen);
1129       memcpy(user->engineID, engineID, engineIDLen);
1130       user->engineIDLen = engineIDLen;
1131     }
1132     usm_add_user(user);
1133     mtevL(nldeb, "usm adding user: %s\n", username);
1134   }
1135 }
1136
1137 /* Shenanigans to work around snmp v3 probes blocking */
1138 static int
1139 snmpv3_build_probe_pdu(netsnmp_pdu **pdu) {
1140     struct usmUser *user;
1141
1142     /*
1143      * create the pdu
1144      */
1145     if (!pdu)
1146         return -1;
1147     *pdu = snmp_pdu_create(SNMP_MSG_GET);
1148     if (!(*pdu))
1149         return -1;
1150     (*pdu)->version = SNMP_VERSION_3;
1151     (*pdu)->securityName = strdup("");
1152     (*pdu)->securityNameLen = strlen((*pdu)->securityName);
1153     (*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
1154     (*pdu)->securityModel = SNMP_SEC_MODEL_USM;
1155
1156     /*
1157      * create the empty user
1158      */
1159     ensure_usm_user((*pdu)->securityName, NULL, 0);
1160     return 0;
1161 }
1162
1163 static int
1164 noit_snmpv3_probe_timeout(eventer_t e, int mask, void *closure,
1165                           struct timeval *now) {
1166   struct v3_probe_magic *magic = closure;
1167   struct check_info *info = magic->check->closure;
1168   struct target_session *ts = magic->ts;
1169   if(ts && ts->slp) {
1170     ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1171   }
1172   if(ts) ts->refcnt--;
1173   magic->timeoutevent = NULL;
1174   return 0;
1175 }
1176
1177 static void
1178 copy_auth_to_pdu(struct snmp_session *sp, struct snmp_pdu *pdu) {
1179 #define pdu_copystring(name) do { \
1180   if(pdu->name) free(pdu->name); \
1181   pdu->name = NULL; \
1182   if(sp->name) pdu->name = strdup(sp->name); \
1183   pdu->name##Len = strlen(pdu->name); \
1184 } while(0)
1185 #define pdu_copyoid(oid) do { \
1186   if(pdu->oid) free(pdu->oid); \
1187   pdu->oid = NULL; \
1188   pdu->oid = snmp_duplicate_objid(sp->oid, sp->oid##Len); \
1189   pdu->oid##Len = sp->oid##Len; \
1190 } while(0)
1191
1192   pdu_copystring(securityName);
1193   pdu->securityModel = sp->securityModel;
1194   pdu->securityLevel = sp->securityLevel;
1195 }
1196
1197 static int
1198 probe_engine_step1_cb(int operation,
1199                       struct snmp_session *sp,
1200                       int reqid,
1201                       struct snmp_pdu *pdu,
1202                       void *arg) {
1203   struct v3_probe_magic *magic = arg;
1204   struct check_info *info = magic->check->closure;
1205   struct target_session *ts = info->ts;
1206   int ret = 1;
1207
1208   if(magic->timeoutevent) {
1209     eventer_remove(magic->timeoutevent);
1210     eventer_free(magic->timeoutevent);
1211     magic->timeoutevent = NULL;
1212   }
1213   /* Did we receive the appropriate Report message? */
1214   if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE &&
1215       pdu && pdu->command == SNMP_MSG_REPORT) {
1216     int reqid, i;
1217     if(pdu->securityEngineIDLen) {
1218       if(sp->securityEngineID) free(sp->securityEngineID);
1219       sp->securityEngineID = malloc(sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1220       memcpy(sp->securityEngineID, pdu->securityEngineID, sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1221       sp->securityEngineIDLen = pdu->securityEngineIDLen;
1222     }
1223     if(pdu->contextEngineIDLen) {
1224       if(sp->contextEngineID) free(sp->contextEngineID);
1225       sp->contextEngineID = malloc(sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1226       memcpy(sp->contextEngineID, pdu->contextEngineID, sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1227       sp->contextEngineIDLen = pdu->contextEngineIDLen;
1228     }
1229     i = usm_create_user_from_session(sp);
1230     mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1231     copy_auth_to_pdu(sp, magic->pdu);
1232     reqid = snmp_sess_send(ts->slp, magic->pdu);
1233     if(reqid == 0) {
1234       int liberr, snmperr;
1235       char *errmsg;
1236       snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1237       mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1238       snmp_free_pdu(magic->pdu);
1239       magic->pdu = NULL;
1240       goto probe_failed;
1241     }
1242     for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1243     mtevL(nldeb, "Probe followup sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1244     add_check(info);
1245     ts->refcnt--;
1246     goto out;
1247   }
1248
1249  probe_failed:
1250   if(magic->pdu) snmp_free_pdu(magic->pdu);
1251   sp->flags &= ~SNMP_FLAGS_DONT_PROBE;
1252   ret = magic->cb(NETSNMP_CALLBACK_OP_SEND_FAILED,
1253                   sp, reqid, pdu, info);
1254
1255  out:
1256   return ret;
1257 }
1258
1259 static int noit_snmp_send(noit_module_t *self, noit_check_t *check,
1260                           noit_check_t *cause) {
1261   struct timeval when, to;
1262   struct snmp_pdu *req = NULL;
1263   struct target_session *ts;
1264   struct check_info *info = check->closure;
1265   int port = 161, i;
1266   mtev_boolean separate_queries = mtev_false;
1267   const char *portstr, *versstr, *sepstr;
1268   const char *err = "unknown err";
1269   char target_port[64];
1270
1271   info->version = SNMP_VERSION_2c;
1272   info->self = self;
1273   info->check = check;
1274   info->timedout = 0;
1275
1276   BAIL_ON_RUNNING_CHECK(check);
1277   check->flags |= NP_RUNNING;
1278
1279   gettimeofday(&check->last_fire_time, NULL);
1280   if(mtev_hash_retr_str(check->config, "separate_queries",
1281                         strlen("separate_queries"), &sepstr)) {
1282     if(!strcasecmp(sepstr, "on") || !strcasecmp(sepstr, "true"))
1283       separate_queries = mtev_true;
1284   }
1285   if(mtev_hash_retr_str(check->config, "port", strlen("port"),
1286                         &portstr)) {
1287     port = atoi(portstr);
1288   }
1289   if(mtev_hash_retr_str(check->config, "version", strlen("version"),
1290                         &versstr)) {
1291     /* We don't care about 2c or others... as they all default to 2c */
1292     if(!strcmp(versstr, "1")) info->version = SNMP_VERSION_1;
1293     if(!strcmp(versstr, "3")) info->version = SNMP_VERSION_3;
1294   }
1295   snprintf(target_port, sizeof(target_port), "%s:%d", check->target_ip, port);
1296   ts = _get_target_session(self, target_port, info->version);
1297   gettimeofday(&check->last_fire_time, NULL);
1298   if(!ts->refcnt) {
1299     eventer_t newe;
1300     struct timeval timeout;
1301     netsnmp_session *rsess;
1302     noit_snmp_sess_open(ts, check);
1303     newe = eventer_alloc();
1304     newe->fd = ts->fd;
1305     newe->callback = noit_snmp_handler;
1306     newe->closure = ts;
1307     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1308     eventer_add(newe);
1309   }
1310   if(!ts->slp) goto bail;
1311   ts->refcnt++; /* Increment here, decrement when this check completes */
1312
1313   noit_snmp_fill_oidinfo(check);
1314   /* Do we need probing? */
1315   if (info->version == SNMP_VERSION_3 &&
1316       ts->sess_handle->securityEngineIDLen == 0 &&
1317       (0 == (ts->sess_handle->flags & SNMP_FLAGS_DONT_PROBE))) {
1318     /* Allocate some "magic" structure to remember PDU, callback and argument*/
1319     struct v3_probe_magic *magic = calloc(1, sizeof(struct v3_probe_magic));
1320     netsnmp_pdu *probe = NULL;
1321
1322     mtevL(nldeb, "Probing for v3\n");
1323     if (!magic) goto bail;
1324     magic->cb = noit_snmp_asynch_response;
1325     magic->check = check;
1326     magic->ts = ts;
1327  
1328     if (snmpv3_build_probe_pdu(&probe) != 0) {
1329       free(magic);
1330       goto bail;
1331     }
1332
1333     /* Send it. */
1334     ts->sess_handle->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
1335     ts->refcnt++;
1336
1337     magic->pdu = snmp_pdu_create(SNMP_MSG_GET);
1338     noit_snmp_fill_req(magic->pdu, check, -1);
1339     magic->pdu->version = info->version;
1340
1341     if (!snmp_sess_async_send(ts->slp, probe, probe_engine_step1_cb, magic)) {
1342       ts->refcnt--;
1343       snmp_free_pdu(probe);
1344       free(magic);
1345       ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1346       goto bail;
1347     }
1348
1349     magic->timeoutevent = eventer_alloc();
1350     magic->timeoutevent->callback = noit_snmpv3_probe_timeout;
1351     magic->timeoutevent->closure = magic;
1352     magic->timeoutevent->mask = EVENTER_TIMER;
1353  
1354     gettimeofday(&when, NULL);
1355     to.tv_sec = 5;
1356     to.tv_usec = 0;
1357     add_timeval(when, to, &magic->timeoutevent->whence);
1358     eventer_add(magic->timeoutevent);
1359   }
1360   else {
1361     /* Separate queries is not supported on v3... it makes no sense */
1362     if(separate_queries && info->version != SNMP_VERSION_3) {
1363       int reqid, i;
1364       mtevL(nldeb, "Regular old get...\n");
1365       for(i=0;i<info->noids;i++) {
1366         req = snmp_pdu_create(SNMP_MSG_GET);
1367         if(!req) continue;
1368         noit_snmp_fill_req(req, check, i);
1369         req->version = info->version;
1370         reqid = snmp_sess_send(ts->slp, req);
1371         if(reqid == 0) {
1372           int liberr, snmperr;
1373           char *errmsg;
1374           snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1375           mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1376           snmp_free_pdu(req);
1377           continue;
1378         }
1379         info->oids[i].reqid = reqid;
1380         mtevL(nldeb, "Sent snmp get[%d/%d] -> reqid:%d\n", i, info->noids, reqid);
1381       }
1382     }
1383     else {
1384       int reqid, i;
1385       mtevL(nldeb, "Regular old get...\n");
1386       req = snmp_pdu_create(SNMP_MSG_GET);
1387       if(!req) goto bail;
1388       noit_snmp_fill_req(req, check, -1);
1389       if(info->version == SNMP_VERSION_3 && ts->sess_handle->securityName) {
1390         i = usm_create_user_from_session(ts->sess_handle);
1391         mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1392       }
1393       req->version = info->version;
1394       reqid = snmp_sess_send(ts->slp, req);
1395       if(reqid == 0) {
1396         int liberr, snmperr;
1397         char *errmsg;
1398         snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1399         mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1400         err = errmsg;
1401         if(reqid == 0) goto bail;
1402       }
1403       for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1404       mtevL(nldeb, "Sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1405     }
1406   }
1407   info->ts = ts;
1408   info->timeoutevent = eventer_alloc();
1409   info->timeoutevent->callback = noit_snmp_check_timeout;
1410   info->timeoutevent->closure = info;
1411   info->timeoutevent->mask = EVENTER_TIMER;
1412
1413   gettimeofday(&when, NULL);
1414   to.tv_sec = check->timeout / 1000;
1415   to.tv_usec = (check->timeout % 1000) * 1000;
1416   add_timeval(when, to, &info->timeoutevent->whence);
1417   eventer_add(info->timeoutevent);
1418   add_check(info);
1419   return 0;
1420
1421  bail:
1422   ts->refcnt--;
1423   noit_snmp_log_results(self, check, err);
1424   noit_snmp_session_cleanse(ts, 1);
1425   if(req) snmp_free_pdu(req);
1426   check->flags &= ~NP_RUNNING;
1427   return 0;
1428 }
1429
1430 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
1431                                     int once, noit_check_t *cause) {
1432   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
1433   INITIATE_CHECK(noit_snmp_send, self, check, cause);
1434   return 0;
1435 }
1436
1437 static int noit_snmptrap_initiate_check(noit_module_t *self,
1438                                         noit_check_t *check,
1439                                         int once, noit_check_t *cause) {
1440   /* We don't do anything for snmptrap checks.  Not intuitive... but they
1441    * never "run."  We accept input out-of-band via snmp traps.
1442    */
1443   check->flags |= NP_PASSIVE_COLLECTION;
1444   return 0;
1445 }
1446
1447 static int noit_snmp_config(noit_module_t *self, mtev_hash_table *options) {
1448   snmp_mod_config_t *conf;
1449   conf = noit_module_get_userdata(self);
1450   if(conf) {
1451     if(conf->options) {
1452       mtev_hash_destroy(conf->options, free, free);
1453       free(conf->options);
1454     }
1455   }
1456   else
1457     conf = calloc(1, sizeof(*conf));
1458   conf->options = options;
1459   noit_module_set_userdata(self, conf);
1460   return 1;
1461 }
1462 static int noit_snmp_onload(mtev_image_t *self) {
1463   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1464   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1465   if(!nlerr) nlerr = noit_stderr;
1466   if(!nldeb) nldeb = noit_debug;
1467   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
1468   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1469   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1470   return 0;
1471 }
1472
1473 static int noit_snmptrap_onload(mtev_image_t *self) {
1474   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1475   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1476   if(!nlerr) nlerr = noit_stderr;
1477   if(!nldeb) nldeb = noit_debug;
1478   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1479   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1480   return 0;
1481 }
1482
1483 static void
1484 nc_printf_snmpts_brief(mtev_console_closure_t ncct,
1485                        struct target_session *ts) {
1486   char fd[32];
1487   struct timeval now, diff;
1488   const char *snmpvers = "v(unknown)";
1489   gettimeofday(&now, NULL);
1490   sub_timeval(now, ts->last_open, &diff);
1491   if(ts->fd < 0)
1492     snprintf(fd, sizeof(fd), "%s", "(closed)");
1493   else
1494     snprintf(fd, sizeof(fd), "%d", ts->fd);
1495   switch(ts->version) {
1496     case SNMP_VERSION_1: snmpvers = "v1"; break;
1497     case SNMP_VERSION_2c: snmpvers = "v2c"; break;
1498     case SNMP_VERSION_3: snmpvers = "v3"; break;
1499   }
1500   nc_printf(ncct, "[%s %s]\n\topened: %0.3fs ago\n\tFD: %s\n\trefcnt: %d\n",
1501             ts->target, snmpvers, diff.tv_sec + (float)diff.tv_usec/1000000,
1502             fd, ts->refcnt);
1503 }
1504
1505 static int
1506 noit_console_show_snmp(mtev_console_closure_t ncct,
1507                        int argc, char **argv,
1508                        mtev_console_state_t *dstate,
1509                        void *closure) {
1510   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1511   uuid_t key_id;
1512   int klen;
1513   void *vts;
1514   snmp_mod_config_t *conf = closure;
1515
1516   while(mtev_hash_next(&conf->target_sessions, &iter,
1517                        (const char **)key_id, &klen,
1518                        &vts)) {
1519     struct target_session *ts = vts;
1520     nc_printf_snmpts_brief(ncct, ts);
1521   }
1522   return 0;
1523 }
1524
1525 static void
1526 register_console_snmp_commands(snmp_mod_config_t *conf) {
1527   mtev_console_state_t *tl;
1528   cmd_info_t *showcmd;
1529
1530   tl = mtev_console_state_initial();
1531   showcmd = mtev_console_state_get_cmd(tl, "show");
1532   assert(showcmd && showcmd->dstate);
1533   mtev_console_state_add_cmd(showcmd->dstate,
1534     NCSCMD("snmp", noit_console_show_snmp, NULL, NULL, conf));
1535 }
1536
1537 static __thread char linebuf[1024] = "\0";
1538 static int
1539 _private_snmp_log(int majorID, int minorID, void *serverarg, void *clientarg) {
1540   struct snmp_log_message *slm;
1541   snmp_mod_config_t *conf;
1542   size_t len;
1543   conf = clientarg;
1544   slm = serverarg;
1545   len = strlcat(linebuf, slm->msg, sizeof(linebuf));
1546   if(len > sizeof(linebuf)-1) {
1547     linebuf[sizeof(linebuf)-2] = '\n';
1548     linebuf[sizeof(linebuf)-1] = '\0';
1549   }
1550   else if(linebuf[len-1] == '\n') {
1551   }
1552   else {
1553     return 1;
1554   }
1555   mtevL(((slm->priority < LOG_NOTICE) ? nlerr : nldeb), "[pri:%d] %s",
1556         slm->priority, linebuf);
1557   linebuf[0] = '\0';
1558   return 1;
1559 }
1560
1561 static int noit_snmp_init(noit_module_t *self) {
1562   const char *opt;
1563   snmp_mod_config_t *conf;
1564
1565   conf = noit_module_get_userdata(self);
1566   if(mtev_hash_retr_str(conf->options, "debugging", strlen("debugging"), &opt)) {
1567     snmp_set_do_debugging(atoi(opt));
1568   }
1569
1570   if(!__snmp_initialize_once) {
1571     register_mib_handlers();
1572     read_premib_configs();
1573     read_configs();
1574     init_snmp("noitd");
1575     snmp_disable_stderrlog();
1576     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING,
1577                            _private_snmp_log, conf);
1578     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_EMERG);
1579     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ALERT);
1580     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_CRIT);
1581     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ERR);
1582     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_WARNING);
1583     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_NOTICE);
1584     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_INFO);
1585     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG);
1586     __snmp_initialize_once = 1;
1587   }
1588   if(strcmp(self->hdr.name, "snmp") == 0) {
1589     register_console_snmp_commands(conf);
1590   }
1591
1592   if(strcmp(self->hdr.name, "snmptrap") == 0) {
1593     eventer_t newe;
1594     int i, block = 0, fds = 0;
1595     fd_set fdset;
1596     struct timeval timeout = { 0, 0 };
1597     struct target_session *ts;
1598     netsnmp_transport *transport;
1599     netsnmp_session sess, *session = &sess;
1600
1601     if(!mtev_hash_retrieve(conf->options,
1602                            "snmptrapd_port", strlen("snmptrapd_port"),
1603                            (void **)&opt))
1604       opt = "162";
1605
1606     transport = netsnmp_transport_open_server("snmptrap", opt);
1607     if(!transport) {
1608       mtevL(nlerr, "cannot open netsnmp transport for trap daemon\n");
1609       return -1;
1610     }
1611     ts = _get_target_session(self, "snmptrapd", SNMP_DEFAULT_VERSION);
1612     snmp_sess_init(session);
1613     session->peername = SNMP_DEFAULT_PEERNAME;
1614     session->version = SNMP_DEFAULT_VERSION;
1615     session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
1616     session->retries = SNMP_DEFAULT_RETRIES;
1617     session->timeout = SNMP_DEFAULT_TIMEOUT;
1618     session->callback = noit_snmp_trapd_response;
1619     session->callback_magic = (void *) ts;
1620     session->authenticator = NULL;
1621     session->isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
1622     ts->slp = snmp_sess_add(session, transport, NULL, NULL);
1623
1624     FD_ZERO(&fdset);
1625     snmp_sess_select_info(ts->slp, &fds, &fdset, &timeout, &block);
1626     assert(fds > 0);
1627     for(i=0; i<fds; i++) {
1628       if(FD_ISSET(i, &fdset)) {
1629         ts->refcnt++;
1630         ts->fd = i;
1631         newe = eventer_alloc();
1632         newe->fd = ts->fd;
1633         newe->callback = noit_snmp_handler;
1634         newe->closure = ts;
1635         newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1636         eventer_add(newe);
1637       }
1638     }
1639   }
1640   return 0;
1641 }
1642
1643 #include "snmp.xmlh"
1644 noit_module_t snmp = {
1645   {
1646     .magic = NOIT_MODULE_MAGIC,
1647     .version = NOIT_MODULE_ABI_VERSION,
1648     .name = "snmp",
1649     .description = "SNMP collection",
1650     .xml_description = snmp_xml_description,
1651     .onload = noit_snmp_onload
1652   },
1653   noit_snmp_config,
1654   noit_snmp_init,
1655   noit_snmp_initiate_check,
1656   NULL, /* noit_snmp_cleanup */
1657   .thread_unsafe = 1
1658 };
1659
1660 #include "snmptrap.xmlh"
1661 noit_module_t snmptrap = {
1662   {
1663     .magic = NOIT_MODULE_MAGIC,
1664     .version = NOIT_MODULE_ABI_VERSION,
1665     .name = "snmptrap",
1666     .description = "SNMP trap collection",
1667     .xml_description = snmptrap_xml_description,
1668     .onload = noit_snmptrap_onload
1669   },
1670   noit_snmp_config,
1671   noit_snmp_init,
1672   noit_snmptrap_initiate_check,
1673   NULL, /* noit_snmp_cleanup */
1674   .thread_unsafe = 1
1675 };
Note: See TracBrowser for help on using the browser.