root/src/modules/snmp.c

Revision 5736a6fb2c84d9bc9b1f63d377c23abce42ea20d, 56.7 kB (checked in by Phil Maddox <philip.maddox@circonus.com>, 2 months ago)

Accept "AES128" As A Parameter For SNMPv3 Privacy Protocol

  • 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")) || (!strcasecmp(cval, "noAuthNoPriv")))
944       sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
945     else if ((!strcasecmp(cval, "anp")) || (!strcasecmp(cval, "authNoPriv")))
946       sess.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
947     else if ((!strcasecmp(cval, "ap")) || (!strcasecmp(cval, "authPriv")))
948       sess.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
949   }
950   if(CONF_GET("auth_protocol", &cval)) {
951     if (!strcasecmp(cval,"MD5")) {
952       sess.securityAuthProto = usmHMACMD5AuthProtocol;
953       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
954     }
955     else if (!strcasecmp(cval,"SHA")) {
956       sess.securityAuthProto = usmHMACSHA1AuthProtocol;
957       sess.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
958     }
959   }
960   if(CONF_GET("auth_passphrase", &cval)) {
961     if(sess.securityAuthProto == NULL) {
962       sess.securityAuthProto = usmHMACMD5AuthProtocol;
963       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
964     }
965     sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
966     if(generate_Ku(sess.securityAuthProto, sess.securityAuthProtoLen,
967                    (u_char *)cval, strlen(cval),
968                    sess.securityAuthKey, &sess.securityAuthKeyLen) != SNMPERR_SUCCESS) {
969       /* What do we do? */
970       mtevL(nlerr, "auth_passphrase failed to gen master key\n");
971     }
972   }
973   if(CONF_GET("privacy_protocol", &cval)) {
974     if (!strcasecmp(cval,"DES")) {
975       sess.securityPrivProto = usmDESPrivProtocol;
976       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
977     }
978     else if ((!strcasecmp(cval,"AES"))||(!strcasecmp(cval,"AES128"))) {
979       sess.securityPrivProto = usmAESPrivProtocol;
980       sess.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
981     }
982   }
983   if(CONF_GET("privacy_passphrase", &cval)) {
984     if(sess.securityAuthProto == NULL) {
985       sess.securityAuthProto = usmHMACMD5AuthProtocol;
986       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
987     }
988     if(sess.securityPrivProto == NULL) {
989       sess.securityPrivProto = usmDESPrivProtocol;
990       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
991     }
992     sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
993     if(generate_Ku(sess.securityAuthProto, sess.securityAuthProtoLen,
994                    (u_char *)cval, strlen(cval),
995                    sess.securityPrivKey, &sess.securityPrivKeyLen) != SNMPERR_SUCCESS) {
996       /* What do we do? */
997       mtevL(nlerr, "priv_passphrase failed to gen master key\n");
998     }
999   }
1000   sess.callback = noit_snmp_asynch_response;
1001   sess.callback_magic = ts;
1002   sess.flags |= SNMP_FLAGS_DONT_PROBE;
1003   ts->slp = snmp_sess_open_C1(&sess, &transport);
1004   ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1005   ts->fd = transport->sock;
1006   gettimeofday(&ts->last_open, NULL);
1007 }
1008
1009 static int noit_snmp_fill_oidinfo(noit_check_t *check) {
1010   int i, klen;
1011   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1012   const char *name, *value;
1013   struct check_info *info = check->closure;
1014   mtev_hash_table check_attrs_hash = MTEV_HASH_EMPTY;
1015
1016   /* Toss the old set and bail if we have zero */
1017   if(info->oids) {
1018     for(i=0; i<info->noids;i++) {
1019       if(info->oids[i].confname) free(info->oids[i].confname);
1020       if(info->oids[i].oidname) free(info->oids[i].oidname);
1021     }
1022     free(info->oids);
1023   }
1024   info->noids = 0;
1025   info->nresults = 0;
1026   info->noids_seen = 0;
1027   info->oids = NULL;
1028
1029   /* Figure our how many. */
1030   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1031     if(!strncasecmp(name, "oid_", 4)) {
1032       info->noids++;
1033     }
1034   }
1035
1036   if(info->noids == 0) return 0;
1037
1038   /* Create a hash of important check attributes */
1039   noit_check_make_attrs(check, &check_attrs_hash);
1040
1041   /* Fill out the new set of required oids */
1042   info->oids = calloc(info->noids, sizeof(*info->oids));
1043   memset(&iter, 0, sizeof(iter));
1044   i = 0;
1045   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1046     if(!strncasecmp(name, "oid_", 4)) {
1047       const char *type_override;
1048       char oidbuff[2048], typestr[256];
1049       name += 4;
1050       info->oids[i].confname = strdup(name);
1051       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
1052                              &check_attrs_hash, check->config);
1053       info->oids[i].oidname = strdup(oidbuff);
1054       info->oids[i].oidlen = MAX_OID_LEN;
1055       if(oidbuff[0] == '.') {
1056         if(!read_objid(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1057           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1058           info->noids--;
1059           continue;
1060         }
1061       }
1062       else {
1063         if(!get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1064           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1065           info->noids--;
1066           continue;
1067         }
1068       }
1069       snprintf(typestr, sizeof(typestr), "type_%s", name);
1070       if(mtev_hash_retr_str(check->config, typestr, strlen(typestr),
1071                             &type_override)) {
1072         int type_enum_fake = *type_override;
1073
1074         if(!strcasecmp(type_override, "guess"))
1075           type_enum_fake = METRIC_GUESS;
1076         else if(!strcasecmp(type_override, "int32"))
1077           type_enum_fake = METRIC_INT32;
1078         else if(!strcasecmp(type_override, "uint32"))
1079           type_enum_fake = METRIC_UINT32;
1080         else if(!strcasecmp(type_override, "int64"))
1081           type_enum_fake = METRIC_INT64;
1082         else if(!strcasecmp(type_override, "uint64"))
1083           type_enum_fake = METRIC_UINT64;
1084         else if(!strcasecmp(type_override, "double"))
1085           type_enum_fake = METRIC_DOUBLE;
1086         else if(!strcasecmp(type_override, "string"))
1087           type_enum_fake = METRIC_STRING;
1088
1089         switch(type_enum_fake) {
1090           case METRIC_GUESS:
1091           case METRIC_INT32: case METRIC_UINT32:
1092           case METRIC_INT64: case METRIC_UINT64:
1093           case METRIC_DOUBLE: case METRIC_STRING:
1094             info->oids[i].type_override = *type_override;
1095             info->oids[i].type_should_override = mtev_true;
1096           default: break;
1097         }
1098       }
1099       i++;
1100     }
1101   }
1102   assert(info->noids == i);
1103   mtev_hash_destroy(&check_attrs_hash, NULL, NULL);
1104   return info->noids;
1105 }
1106 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check, int idx) {
1107   int i;
1108   struct check_info *info = check->closure;
1109
1110   if(idx == -1) {
1111     for(i=0; i<info->noids; i++)
1112       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
1113     return info->noids;
1114   }
1115
1116   assert(idx >= 0 && idx <info->noids);
1117   snmp_add_null_var(req, info->oids[idx].oid, info->oids[idx].oidlen);
1118   return 1;
1119 }
1120
1121 static void
1122 ensure_usm_user(const char *username, u_char *engineID, size_t engineIDLen) {
1123   struct usmUser *user;
1124   user = usm_get_user(NULL, 0, (char *)username);
1125   if (user == NULL) {
1126     user = (struct usmUser *) calloc(1, sizeof(struct usmUser));
1127     user->name = strdup(username);
1128     user->secName = strdup(username);
1129     user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid);
1130     user->authProtocol =
1131         snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen);
1132     user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid);
1133     user->privProtocol =
1134         snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen);
1135     if(engineIDLen) {
1136       user->engineID = malloc(engineIDLen);
1137       memcpy(user->engineID, engineID, engineIDLen);
1138       user->engineIDLen = engineIDLen;
1139     }
1140     usm_add_user(user);
1141     mtevL(nldeb, "usm adding user: %s\n", username);
1142   }
1143 }
1144
1145 /* Shenanigans to work around snmp v3 probes blocking */
1146 static int
1147 snmpv3_build_probe_pdu(netsnmp_pdu **pdu) {
1148     struct usmUser *user;
1149
1150     /*
1151      * create the pdu
1152      */
1153     if (!pdu)
1154         return -1;
1155     *pdu = snmp_pdu_create(SNMP_MSG_GET);
1156     if (!(*pdu))
1157         return -1;
1158     (*pdu)->version = SNMP_VERSION_3;
1159     (*pdu)->securityName = strdup("");
1160     (*pdu)->securityNameLen = strlen((*pdu)->securityName);
1161     (*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
1162     (*pdu)->securityModel = SNMP_SEC_MODEL_USM;
1163
1164     /*
1165      * create the empty user
1166      */
1167     ensure_usm_user((*pdu)->securityName, NULL, 0);
1168     return 0;
1169 }
1170
1171 static int
1172 noit_snmpv3_probe_timeout(eventer_t e, int mask, void *closure,
1173                           struct timeval *now) {
1174   struct v3_probe_magic *magic = closure;
1175   struct check_info *info = magic->check->closure;
1176   struct target_session *ts = magic->ts;
1177   if(ts && ts->slp) {
1178     ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1179   }
1180   if(ts) ts->refcnt--;
1181   magic->timeoutevent = NULL;
1182   return 0;
1183 }
1184
1185 static void
1186 copy_auth_to_pdu(struct snmp_session *sp, struct snmp_pdu *pdu) {
1187 #define pdu_copystring(name) do { \
1188   if(pdu->name) free(pdu->name); \
1189   pdu->name = NULL; \
1190   if(sp->name) pdu->name = strdup(sp->name); \
1191   pdu->name##Len = strlen(pdu->name); \
1192 } while(0)
1193 #define pdu_copyoid(oid) do { \
1194   if(pdu->oid) free(pdu->oid); \
1195   pdu->oid = NULL; \
1196   pdu->oid = snmp_duplicate_objid(sp->oid, sp->oid##Len); \
1197   pdu->oid##Len = sp->oid##Len; \
1198 } while(0)
1199
1200   pdu_copystring(securityName);
1201   pdu->securityModel = sp->securityModel;
1202   pdu->securityLevel = sp->securityLevel;
1203 }
1204
1205 static int
1206 probe_engine_step1_cb(int operation,
1207                       struct snmp_session *sp,
1208                       int reqid,
1209                       struct snmp_pdu *pdu,
1210                       void *arg) {
1211   struct v3_probe_magic *magic = arg;
1212   struct check_info *info = magic->check->closure;
1213   struct target_session *ts = info->ts;
1214   int ret = 1;
1215
1216   if(magic->timeoutevent) {
1217     eventer_remove(magic->timeoutevent);
1218     eventer_free(magic->timeoutevent);
1219     magic->timeoutevent = NULL;
1220   }
1221   /* Did we receive the appropriate Report message? */
1222   if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE &&
1223       pdu && pdu->command == SNMP_MSG_REPORT) {
1224     int reqid, i;
1225     if(pdu->securityEngineIDLen) {
1226       if(sp->securityEngineID) free(sp->securityEngineID);
1227       sp->securityEngineID = malloc(sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1228       memcpy(sp->securityEngineID, pdu->securityEngineID, sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1229       sp->securityEngineIDLen = pdu->securityEngineIDLen;
1230     }
1231     if(pdu->contextEngineIDLen) {
1232       if(sp->contextEngineID) free(sp->contextEngineID);
1233       sp->contextEngineID = malloc(sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1234       memcpy(sp->contextEngineID, pdu->contextEngineID, sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1235       sp->contextEngineIDLen = pdu->contextEngineIDLen;
1236     }
1237     i = usm_create_user_from_session(sp);
1238     mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1239     copy_auth_to_pdu(sp, magic->pdu);
1240     reqid = snmp_sess_send(ts->slp, magic->pdu);
1241     if(reqid == 0) {
1242       int liberr, snmperr;
1243       char *errmsg;
1244       snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1245       mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1246       snmp_free_pdu(magic->pdu);
1247       magic->pdu = NULL;
1248       goto probe_failed;
1249     }
1250     for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1251     mtevL(nldeb, "Probe followup sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1252     add_check(info);
1253     ts->refcnt--;
1254     goto out;
1255   }
1256
1257  probe_failed:
1258   if(magic->pdu) snmp_free_pdu(magic->pdu);
1259   sp->flags &= ~SNMP_FLAGS_DONT_PROBE;
1260   ret = magic->cb(NETSNMP_CALLBACK_OP_SEND_FAILED,
1261                   sp, reqid, pdu, info);
1262
1263  out:
1264   return ret;
1265 }
1266
1267 static int noit_snmp_send(noit_module_t *self, noit_check_t *check,
1268                           noit_check_t *cause) {
1269   struct timeval when, to;
1270   struct snmp_pdu *req = NULL;
1271   struct target_session *ts;
1272   struct check_info *info = check->closure;
1273   int port = 161, i;
1274   mtev_boolean separate_queries = mtev_false;
1275   const char *portstr, *versstr, *sepstr;
1276   const char *err = "unknown err";
1277   char target_port[64];
1278
1279   info->version = SNMP_VERSION_2c;
1280   info->self = self;
1281   info->check = check;
1282   info->timedout = 0;
1283
1284   BAIL_ON_RUNNING_CHECK(check);
1285   check->flags |= NP_RUNNING;
1286
1287   gettimeofday(&check->last_fire_time, NULL);
1288   if(mtev_hash_retr_str(check->config, "separate_queries",
1289                         strlen("separate_queries"), &sepstr)) {
1290     if(!strcasecmp(sepstr, "on") || !strcasecmp(sepstr, "true"))
1291       separate_queries = mtev_true;
1292   }
1293   if(mtev_hash_retr_str(check->config, "port", strlen("port"),
1294                         &portstr)) {
1295     port = atoi(portstr);
1296   }
1297   if(mtev_hash_retr_str(check->config, "version", strlen("version"),
1298                         &versstr)) {
1299     /* We don't care about 2c or others... as they all default to 2c */
1300     if(!strcmp(versstr, "1")) info->version = SNMP_VERSION_1;
1301     if(!strcmp(versstr, "3")) info->version = SNMP_VERSION_3;
1302   }
1303   snprintf(target_port, sizeof(target_port), "%s:%d", check->target_ip, port);
1304   ts = _get_target_session(self, target_port, info->version);
1305   gettimeofday(&check->last_fire_time, NULL);
1306   if(!ts->refcnt) {
1307     eventer_t newe;
1308     struct timeval timeout;
1309     netsnmp_session *rsess;
1310     noit_snmp_sess_open(ts, check);
1311     newe = eventer_alloc();
1312     newe->fd = ts->fd;
1313     newe->callback = noit_snmp_handler;
1314     newe->closure = ts;
1315     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1316     eventer_add(newe);
1317   }
1318   if(!ts->slp) goto bail;
1319   ts->refcnt++; /* Increment here, decrement when this check completes */
1320
1321   noit_snmp_fill_oidinfo(check);
1322   /* Do we need probing? */
1323   if (info->version == SNMP_VERSION_3 &&
1324       ts->sess_handle->securityEngineIDLen == 0 &&
1325       (0 == (ts->sess_handle->flags & SNMP_FLAGS_DONT_PROBE))) {
1326     /* Allocate some "magic" structure to remember PDU, callback and argument*/
1327     struct v3_probe_magic *magic = calloc(1, sizeof(struct v3_probe_magic));
1328     netsnmp_pdu *probe = NULL;
1329
1330     mtevL(nldeb, "Probing for v3\n");
1331     if (!magic) goto bail;
1332     magic->cb = noit_snmp_asynch_response;
1333     magic->check = check;
1334     magic->ts = ts;
1335  
1336     if (snmpv3_build_probe_pdu(&probe) != 0) {
1337       free(magic);
1338       goto bail;
1339     }
1340
1341     /* Send it. */
1342     ts->sess_handle->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
1343     ts->refcnt++;
1344
1345     magic->pdu = snmp_pdu_create(SNMP_MSG_GET);
1346     noit_snmp_fill_req(magic->pdu, check, -1);
1347     magic->pdu->version = info->version;
1348
1349     if (!snmp_sess_async_send(ts->slp, probe, probe_engine_step1_cb, magic)) {
1350       ts->refcnt--;
1351       snmp_free_pdu(probe);
1352       free(magic);
1353       ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1354       goto bail;
1355     }
1356
1357     magic->timeoutevent = eventer_alloc();
1358     magic->timeoutevent->callback = noit_snmpv3_probe_timeout;
1359     magic->timeoutevent->closure = magic;
1360     magic->timeoutevent->mask = EVENTER_TIMER;
1361  
1362     gettimeofday(&when, NULL);
1363     to.tv_sec = 5;
1364     to.tv_usec = 0;
1365     add_timeval(when, to, &magic->timeoutevent->whence);
1366     eventer_add(magic->timeoutevent);
1367   }
1368   else {
1369     /* Separate queries is not supported on v3... it makes no sense */
1370     if(separate_queries && info->version != SNMP_VERSION_3) {
1371       int reqid, i;
1372       mtevL(nldeb, "Regular old get...\n");
1373       for(i=0;i<info->noids;i++) {
1374         req = snmp_pdu_create(SNMP_MSG_GET);
1375         if(!req) continue;
1376         noit_snmp_fill_req(req, check, i);
1377         req->version = info->version;
1378         reqid = snmp_sess_send(ts->slp, req);
1379         if(reqid == 0) {
1380           int liberr, snmperr;
1381           char *errmsg;
1382           snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1383           mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1384           snmp_free_pdu(req);
1385           continue;
1386         }
1387         info->oids[i].reqid = reqid;
1388         mtevL(nldeb, "Sent snmp get[%d/%d] -> reqid:%d\n", i, info->noids, reqid);
1389       }
1390     }
1391     else {
1392       int reqid, i;
1393       mtevL(nldeb, "Regular old get...\n");
1394       req = snmp_pdu_create(SNMP_MSG_GET);
1395       if(!req) goto bail;
1396       noit_snmp_fill_req(req, check, -1);
1397       if(info->version == SNMP_VERSION_3 && ts->sess_handle->securityName) {
1398         i = usm_create_user_from_session(ts->sess_handle);
1399         mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1400       }
1401       req->version = info->version;
1402       reqid = snmp_sess_send(ts->slp, req);
1403       if(reqid == 0) {
1404         int liberr, snmperr;
1405         char *errmsg;
1406         snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1407         mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1408         err = errmsg;
1409         if(reqid == 0) goto bail;
1410       }
1411       for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1412       mtevL(nldeb, "Sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1413     }
1414   }
1415   info->ts = ts;
1416   info->timeoutevent = eventer_alloc();
1417   info->timeoutevent->callback = noit_snmp_check_timeout;
1418   info->timeoutevent->closure = info;
1419   info->timeoutevent->mask = EVENTER_TIMER;
1420
1421   gettimeofday(&when, NULL);
1422   to.tv_sec = check->timeout / 1000;
1423   to.tv_usec = (check->timeout % 1000) * 1000;
1424   add_timeval(when, to, &info->timeoutevent->whence);
1425   eventer_add(info->timeoutevent);
1426   add_check(info);
1427   return 0;
1428
1429  bail:
1430   ts->refcnt--;
1431   noit_snmp_log_results(self, check, err);
1432   noit_snmp_session_cleanse(ts, 1);
1433   if(req) snmp_free_pdu(req);
1434   check->flags &= ~NP_RUNNING;
1435   return 0;
1436 }
1437
1438 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
1439                                     int once, noit_check_t *cause) {
1440   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
1441   INITIATE_CHECK(noit_snmp_send, self, check, cause);
1442   return 0;
1443 }
1444
1445 static int noit_snmptrap_initiate_check(noit_module_t *self,
1446                                         noit_check_t *check,
1447                                         int once, noit_check_t *cause) {
1448   /* We don't do anything for snmptrap checks.  Not intuitive... but they
1449    * never "run."  We accept input out-of-band via snmp traps.
1450    */
1451   check->flags |= NP_PASSIVE_COLLECTION;
1452   return 0;
1453 }
1454
1455 static int noit_snmp_config(noit_module_t *self, mtev_hash_table *options) {
1456   snmp_mod_config_t *conf;
1457   conf = noit_module_get_userdata(self);
1458   if(conf) {
1459     if(conf->options) {
1460       mtev_hash_destroy(conf->options, free, free);
1461       free(conf->options);
1462     }
1463   }
1464   else
1465     conf = calloc(1, sizeof(*conf));
1466   conf->options = options;
1467   noit_module_set_userdata(self, conf);
1468   return 1;
1469 }
1470 static int noit_snmp_onload(mtev_image_t *self) {
1471   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1472   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1473   if(!nlerr) nlerr = noit_stderr;
1474   if(!nldeb) nldeb = noit_debug;
1475   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
1476   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1477   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1478   return 0;
1479 }
1480
1481 static int noit_snmptrap_onload(mtev_image_t *self) {
1482   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1483   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1484   if(!nlerr) nlerr = noit_stderr;
1485   if(!nldeb) nldeb = noit_debug;
1486   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1487   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1488   return 0;
1489 }
1490
1491 static void
1492 nc_printf_snmpts_brief(mtev_console_closure_t ncct,
1493                        struct target_session *ts) {
1494   char fd[32];
1495   struct timeval now, diff;
1496   const char *snmpvers = "v(unknown)";
1497   gettimeofday(&now, NULL);
1498   sub_timeval(now, ts->last_open, &diff);
1499   if(ts->fd < 0)
1500     snprintf(fd, sizeof(fd), "%s", "(closed)");
1501   else
1502     snprintf(fd, sizeof(fd), "%d", ts->fd);
1503   switch(ts->version) {
1504     case SNMP_VERSION_1: snmpvers = "v1"; break;
1505     case SNMP_VERSION_2c: snmpvers = "v2c"; break;
1506     case SNMP_VERSION_3: snmpvers = "v3"; break;
1507   }
1508   nc_printf(ncct, "[%s %s]\n\topened: %0.3fs ago\n\tFD: %s\n\trefcnt: %d\n",
1509             ts->target, snmpvers, diff.tv_sec + (float)diff.tv_usec/1000000,
1510             fd, ts->refcnt);
1511 }
1512
1513 static int
1514 noit_console_show_snmp(mtev_console_closure_t ncct,
1515                        int argc, char **argv,
1516                        mtev_console_state_t *dstate,
1517                        void *closure) {
1518   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1519   uuid_t key_id;
1520   int klen;
1521   void *vts;
1522   snmp_mod_config_t *conf = closure;
1523
1524   while(mtev_hash_next(&conf->target_sessions, &iter,
1525                        (const char **)key_id, &klen,
1526                        &vts)) {
1527     struct target_session *ts = vts;
1528     nc_printf_snmpts_brief(ncct, ts);
1529   }
1530   return 0;
1531 }
1532
1533 static void
1534 register_console_snmp_commands(snmp_mod_config_t *conf) {
1535   mtev_console_state_t *tl;
1536   cmd_info_t *showcmd;
1537
1538   tl = mtev_console_state_initial();
1539   showcmd = mtev_console_state_get_cmd(tl, "show");
1540   assert(showcmd && showcmd->dstate);
1541   mtev_console_state_add_cmd(showcmd->dstate,
1542     NCSCMD("snmp", noit_console_show_snmp, NULL, NULL, conf));
1543 }
1544
1545 static __thread char linebuf[1024] = "\0";
1546 static int
1547 _private_snmp_log(int majorID, int minorID, void *serverarg, void *clientarg) {
1548   struct snmp_log_message *slm;
1549   snmp_mod_config_t *conf;
1550   size_t len;
1551   conf = clientarg;
1552   slm = serverarg;
1553   len = strlcat(linebuf, slm->msg, sizeof(linebuf));
1554   if(len > sizeof(linebuf)-1) {
1555     linebuf[sizeof(linebuf)-2] = '\n';
1556     linebuf[sizeof(linebuf)-1] = '\0';
1557   }
1558   else if(linebuf[len-1] == '\n') {
1559   }
1560   else {
1561     return 1;
1562   }
1563   mtevL(((slm->priority < LOG_NOTICE) ? nlerr : nldeb), "[pri:%d] %s",
1564         slm->priority, linebuf);
1565   linebuf[0] = '\0';
1566   return 1;
1567 }
1568
1569 static int noit_snmp_init(noit_module_t *self) {
1570   const char *opt;
1571   snmp_mod_config_t *conf;
1572
1573   conf = noit_module_get_userdata(self);
1574   if(mtev_hash_retr_str(conf->options, "debugging", strlen("debugging"), &opt)) {
1575     snmp_set_do_debugging(atoi(opt));
1576   }
1577
1578   if(!__snmp_initialize_once) {
1579     register_mib_handlers();
1580     read_premib_configs();
1581     read_configs();
1582     init_snmp("noitd");
1583     snmp_disable_stderrlog();
1584     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING,
1585                            _private_snmp_log, conf);
1586     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_EMERG);
1587     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ALERT);
1588     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_CRIT);
1589     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ERR);
1590     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_WARNING);
1591     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_NOTICE);
1592     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_INFO);
1593     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG);
1594     __snmp_initialize_once = 1;
1595   }
1596   if(strcmp(self->hdr.name, "snmp") == 0) {
1597     register_console_snmp_commands(conf);
1598   }
1599
1600   if(strcmp(self->hdr.name, "snmptrap") == 0) {
1601     eventer_t newe;
1602     int i, block = 0, fds = 0;
1603     fd_set fdset;
1604     struct timeval timeout = { 0, 0 };
1605     struct target_session *ts;
1606     netsnmp_transport *transport;
1607     netsnmp_session sess, *session = &sess;
1608
1609     if(!mtev_hash_retrieve(conf->options,
1610                            "snmptrapd_port", strlen("snmptrapd_port"),
1611                            (void **)&opt))
1612       opt = "162";
1613
1614     transport = netsnmp_transport_open_server("snmptrap", opt);
1615     if(!transport) {
1616       mtevL(nlerr, "cannot open netsnmp transport for trap daemon\n");
1617       return -1;
1618     }
1619     ts = _get_target_session(self, "snmptrapd", SNMP_DEFAULT_VERSION);
1620     snmp_sess_init(session);
1621     session->peername = SNMP_DEFAULT_PEERNAME;
1622     session->version = SNMP_DEFAULT_VERSION;
1623     session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
1624     session->retries = SNMP_DEFAULT_RETRIES;
1625     session->timeout = SNMP_DEFAULT_TIMEOUT;
1626     session->callback = noit_snmp_trapd_response;
1627     session->callback_magic = (void *) ts;
1628     session->authenticator = NULL;
1629     session->isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
1630     ts->slp = snmp_sess_add(session, transport, NULL, NULL);
1631
1632     FD_ZERO(&fdset);
1633     snmp_sess_select_info(ts->slp, &fds, &fdset, &timeout, &block);
1634     assert(fds > 0);
1635     for(i=0; i<fds; i++) {
1636       if(FD_ISSET(i, &fdset)) {
1637         ts->refcnt++;
1638         ts->fd = i;
1639         newe = eventer_alloc();
1640         newe->fd = ts->fd;
1641         newe->callback = noit_snmp_handler;
1642         newe->closure = ts;
1643         newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1644         eventer_add(newe);
1645       }
1646     }
1647   }
1648   return 0;
1649 }
1650
1651 #include "snmp.xmlh"
1652 noit_module_t snmp = {
1653   {
1654     .magic = NOIT_MODULE_MAGIC,
1655     .version = NOIT_MODULE_ABI_VERSION,
1656     .name = "snmp",
1657     .description = "SNMP collection",
1658     .xml_description = snmp_xml_description,
1659     .onload = noit_snmp_onload
1660   },
1661   noit_snmp_config,
1662   noit_snmp_init,
1663   noit_snmp_initiate_check,
1664   NULL, /* noit_snmp_cleanup */
1665   .thread_unsafe = 1
1666 };
1667
1668 #include "snmptrap.xmlh"
1669 noit_module_t snmptrap = {
1670   {
1671     .magic = NOIT_MODULE_MAGIC,
1672     .version = NOIT_MODULE_ABI_VERSION,
1673     .name = "snmptrap",
1674     .description = "SNMP trap collection",
1675     .xml_description = snmptrap_xml_description,
1676     .onload = noit_snmptrap_onload
1677   },
1678   noit_snmp_config,
1679   noit_snmp_init,
1680   noit_snmptrap_initiate_check,
1681   NULL, /* noit_snmp_cleanup */
1682   .thread_unsafe = 1
1683 };
Note: See TracBrowser for help on using the browser.