root/src/modules/snmp.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 35.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 10 years ago)

fixes #126

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <math.h>
40 #include <ctype.h>
41
42 #include <net-snmp/net-snmp-config.h>
43 #include <net-snmp/net-snmp-includes.h>
44
45 #include "noit_module.h"
46 #include "noit_check.h"
47 #include "noit_check_tools.h"
48 #include "utils/noit_log.h"
49 #include "utils/noit_hash.h"
50
51 static noit_log_stream_t nlerr = NULL;
52 static noit_log_stream_t nldeb = NULL;
53 static int __snmp_initialize_once = 0;
54
55 #define SNMPV2_TRAPS_PREFIX     SNMP_OID_SNMPMODULES,1,1,5
56 oid trap_prefix[]    = { SNMPV2_TRAPS_PREFIX };
57 oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 };  /* SNMPv2-MIB */
58 oid warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 };  /* SNMPv2-MIB */
59 oid link_down_oid[]  = { SNMPV2_TRAPS_PREFIX, 3 };  /* IF-MIB */
60 oid link_up_oid[]    = { SNMPV2_TRAPS_PREFIX, 4 };  /* IF-MIB */
61 oid auth_fail_oid[]  = { SNMPV2_TRAPS_PREFIX, 5 };  /* SNMPv2-MIB */
62 oid egp_xxx_oid[]    = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
63
64 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
65 oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
66 size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
67 oid snmptrapenterprise_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
68 size_t snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
69 oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
70 size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
71
72 #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1
73 oid agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 };
74 size_t agentaddr_oid_len = OID_LENGTH(agentaddr_oid);
75 oid community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 };
76 size_t community_oid_len = OID_LENGTH(community_oid);
77
78 #define RECONNOITER_PREFIX     SNMP_OID_ENTERPRISES,32863,1
79 oid reconnoiter_oid[] = { RECONNOITER_PREFIX };
80 size_t reconnoiter_oid_len = OID_LENGTH(reconnoiter_oid);
81 oid reconnoiter_check_prefix_oid[] = { RECONNOITER_PREFIX,1,1 };
82 size_t reconnoiter_check_prefix_oid_len =
83   OID_LENGTH(reconnoiter_check_prefix_oid);
84 size_t reconnoiter_check_oid_len = OID_LENGTH(reconnoiter_check_prefix_oid) + 8;
85 oid reconnoiter_metric_prefix_oid[] = { RECONNOITER_PREFIX,1,2 };
86 size_t reconnoiter_metric_prefix_oid_len =
87   OID_LENGTH(reconnoiter_metric_prefix_oid);
88
89 oid reconnoiter_check_status_oid[] = { RECONNOITER_PREFIX,1,3};
90 size_t reconnoiter_check_status_oid_len =
91   OID_LENGTH(reconnoiter_check_status_oid);
92 oid reconnoiter_check_state_oid[] = { RECONNOITER_PREFIX,1,3,1};
93 size_t reconnoiter_check_state_oid_len =
94   OID_LENGTH(reconnoiter_check_state_oid);
95 oid reconnoiter_check_state_unknown_oid[] = { RECONNOITER_PREFIX,1,3,1,0};
96 oid reconnoiter_check_state_good_oid[] = { RECONNOITER_PREFIX,1,3,1,1};
97 oid reconnoiter_check_state_bad_oid[] = { RECONNOITER_PREFIX,1,3,1,2};
98 size_t reconnoiter_check_state_val_len =
99   OID_LENGTH(reconnoiter_check_state_unknown_oid);
100 /* Boolean */
101 oid reconnoiter_check_available_oid[] = { RECONNOITER_PREFIX,1,3,2};
102 size_t reconnoiter_check_available_oid_len =
103   OID_LENGTH(reconnoiter_check_available_oid);
104 oid reconnoiter_check_available_unknown_oid[] = { RECONNOITER_PREFIX,1,3,2,0};
105 oid reconnoiter_check_available_yes_oid[] = { RECONNOITER_PREFIX,1,3,2,1};
106 oid reconnoiter_check_available_no_oid[] = { RECONNOITER_PREFIX,1,3,2,2};
107 size_t reconnoiter_check_available_val_len =
108   OID_LENGTH(reconnoiter_check_available_unknown_oid);
109 /* timeticks? gauge/unsigned? */
110 oid reconnoiter_check_duration_oid[] = { RECONNOITER_PREFIX,1,3,3};
111 size_t reconnoiter_check_duration_oid_len =
112   OID_LENGTH(reconnoiter_check_duration_oid);
113 /* string */
114 oid reconnoiter_check_status_msg_oid[] = { RECONNOITER_PREFIX,1,3,4};
115 size_t reconnoiter_check_status_msg_oid_len =
116   OID_LENGTH(reconnoiter_check_status_msg_oid);
117
118 typedef struct _mod_config {
119   noit_hash_table *options;
120   noit_hash_table target_sessions;
121 } snmp_mod_config_t;
122
123 struct target_session {
124   void *sess_handle;
125   noit_module_t *self;
126   eventer_t timeoutevent;
127   int fd;
128   int in_table;
129   int refcnt;
130 };
131
132 struct snmp_check_closure {
133   noit_module_t *self;
134   noit_check_t *check;
135 };
136
137 struct check_info {
138   int reqid;
139   int timedout;
140   struct {
141      char *confname;
142      char *oidname;
143      oid oid[MAX_OID_LEN];
144      size_t oidlen;
145   } *oids;
146   int noids;
147   eventer_t timeoutevent;
148   noit_module_t *self;
149   noit_check_t *check;
150 };
151
152 /* We hold struct check_info's in there key's by their reqid.
153  *   If they timeout, we remove them.
154  *
155  *   When SNMP queries complete, we look them up, if we find them
156  *   then we know we can remove the timeout and  complete the check.
157  *   If we don't find them, the timeout fired and removed the check.
158  */
159 noit_hash_table active_checks = NOIT_HASH_EMPTY;
160 static void add_check(struct check_info *c) {
161   noit_hash_store(&active_checks, (char *)&c->reqid, sizeof(c->reqid), c);
162 }
163 static struct check_info *get_check(int reqid) {
164   void *vc;
165   if(noit_hash_retrieve(&active_checks, (char *)&reqid, sizeof(reqid), &vc))
166     return (struct check_info *)vc;
167   return NULL;
168 }
169 static void remove_check(struct check_info *c) {
170   noit_hash_delete(&active_checks, (char *)&c->reqid, sizeof(c->reqid),
171                    NULL, NULL);
172 }
173
174 struct target_session *
175 _get_target_session(noit_module_t *self, char *target) {
176   void *vts;
177   struct target_session *ts;
178   snmp_mod_config_t *conf;
179   conf = noit_module_get_userdata(self);
180   if(!noit_hash_retrieve(&conf->target_sessions,
181                          target, strlen(target), &vts)) {
182     ts = calloc(1, sizeof(*ts));
183     ts->self = self;
184     ts->fd = -1;
185     ts->refcnt = 0;
186     ts->in_table = 1;
187     noit_hash_store(&conf->target_sessions,
188                     strdup(target), strlen(target), ts);
189     vts = ts;
190   }
191   return (struct target_session *)vts;
192 }
193
194 /* Handling of results */
195 static void noit_snmp_log_results(noit_module_t *self, noit_check_t *check,
196                                   struct snmp_pdu *pdu) {
197   struct check_info *info = check->closure;
198   struct variable_list *vars;
199   struct timeval duration;
200   char buff[128];
201   stats_t current;
202   int nresults = 0;
203
204   noit_check_stats_clear(&current);
205
206   if(pdu)
207     for(vars = pdu->variables; vars; vars = vars->next_variable)
208       nresults++;
209
210   gettimeofday(&current.whence, NULL);
211   sub_timeval(current.whence, check->last_fire_time, &duration);
212   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
213   current.available = pdu ? NP_AVAILABLE : NP_UNAVAILABLE;
214   current.state = (nresults == info->noids) ? NP_GOOD : NP_BAD;
215   snprintf(buff, sizeof(buff), "%d/%d gets", nresults, info->noids);
216   current.status = buff;
217
218   /* We have no results over which to iterate. */
219   if(!pdu) {
220     noit_check_set_stats(self, check, &current);
221     return;
222   }
223
224   /* manipulate the information ourselves */
225   nresults = 0;
226   for(vars = pdu->variables; vars; vars = vars->next_variable) {
227     char *sp;
228     int oid_idx;
229     double float_conv;
230     u_int64_t u64;
231     int64_t i64;
232     char *endptr;
233     char varbuff[256];
234
235     /* find the oid to which this is the response */
236     oid_idx = nresults; /* our current idx is the most likely */
237     if(info->oids[oid_idx].oidlen != vars->name_length ||
238        memcmp(info->oids[oid_idx].oid, vars->name,
239               vars->name_length * sizeof(oid))) {
240       /* Not the most obvious guess */
241       for(oid_idx = info->noids - 1; oid_idx >= 0; oid_idx--) {
242         if(info->oids[oid_idx].oidlen == vars->name_length &&
243            memcmp(info->oids[oid_idx].oid, vars->name,
244                   vars->name_length * sizeof(oid))) break;
245       }
246     }
247     if(oid_idx < 0) {
248       snprint_variable(varbuff, sizeof(varbuff),
249                        vars->name, vars->name_length, vars);
250       noitL(nlerr, "Unexpected oid results to %s`%s`%s: %s\n",
251             check->target, check->module, check->name, varbuff);
252       nresults++;
253       continue;
254     }
255    
256 #define SETM(a,b) noit_stats_set_metric(&current, \
257                                         info->oids[oid_idx].confname, a, b)
258     switch(vars->type) {
259       case ASN_OCTET_STR:
260         sp = malloc(1 + vars->val_len);
261         memcpy(sp, vars->val.string, vars->val_len);
262         sp[vars->val_len] = '\0';
263         SETM(METRIC_STRING, sp);
264         free(sp);
265         break;
266       case ASN_INTEGER:
267       case ASN_GAUGE:
268         SETM(METRIC_INT32, vars->val.integer);
269         break;
270       case ASN_TIMETICKS:
271       case ASN_COUNTER:
272         SETM(METRIC_UINT32, vars->val.integer);
273         break;
274       case ASN_INTEGER64:
275         printI64(varbuff, vars->val.counter64);
276         i64 = strtoll(varbuff, &endptr, 10);
277         SETM(METRIC_INT64, (varbuff == endptr) ? NULL : &i64);
278         break;
279       case ASN_COUNTER64:
280         printU64(varbuff, vars->val.counter64);
281         u64 = strtoull(varbuff, &endptr, 10);
282         SETM(METRIC_UINT64, (varbuff == endptr) ? NULL : &u64);
283         break;
284       case ASN_FLOAT:
285         if(vars->val.floatVal) float_conv = *(vars->val.floatVal);
286         SETM(METRIC_DOUBLE, vars->val.floatVal ? &float_conv : NULL);
287         break;
288       case ASN_DOUBLE:
289         SETM(METRIC_DOUBLE, vars->val.doubleVal);
290         break;
291       case SNMP_NOSUCHOBJECT:
292       case SNMP_NOSUCHINSTANCE:
293         SETM(METRIC_STRING, NULL);
294         break;
295       default:
296         snprint_variable(varbuff, sizeof(varbuff), vars->name, vars->name_length, vars);
297         /* Advance passed the first space and use that unless there
298          * is no space or we have no more string left.
299          */
300         sp = strchr(varbuff, ' ');
301         if(sp) sp++;
302         SETM(METRIC_STRING, (sp && *sp) ? sp : NULL);
303     }
304     nresults++;
305   }
306   noit_check_set_stats(self, check, &current);
307 }
308
309 static int noit_snmp_session_cleanse(struct target_session *ts) {
310   if(ts->refcnt == 0 && ts->sess_handle) {
311     eventer_remove_fd(ts->fd);
312     if(ts->timeoutevent) {
313       eventer_remove(ts->timeoutevent);
314       ts->timeoutevent = NULL;
315     }
316     snmp_sess_close(ts->sess_handle);
317     ts->sess_handle = NULL;
318     if(!ts->in_table) {
319       free(ts);
320     }
321     return 1;
322   }
323   return 0;
324 }
325
326 static int noit_snmp_session_timeout(eventer_t e, int mask, void *closure,
327                                      struct timeval *now) {
328   struct target_session *ts = closure;
329   snmp_sess_timeout(ts->sess_handle);
330   noit_snmp_session_cleanse(ts);
331   if(ts->timeoutevent == e)
332     ts->timeoutevent = NULL; /* this will be freed on return */
333   return 0;
334 }
335
336 static int noit_snmp_check_timeout(eventer_t e, int mask, void *closure,
337                                    struct timeval *now) {
338   struct check_info *info = closure;
339   info->timedout = 1;
340   remove_check(info);
341   /* Log our findings */
342   noit_snmp_log_results(info->self, info->check, NULL);
343   info->check->flags &= ~NP_RUNNING;
344   return 0;
345 }
346
347 static void _set_ts_timeout(struct target_session *ts, struct timeval *t) {
348   struct timeval now;
349   eventer_t e = NULL;
350   if(ts->timeoutevent) {
351     e = eventer_remove(ts->timeoutevent);
352     ts->timeoutevent = NULL;
353   }
354   if(!t) return;
355
356   gettimeofday(&now, NULL);
357   if(!e) e = eventer_alloc();
358   e->callback = noit_snmp_session_timeout;
359   e->closure = ts;
360   e->mask = EVENTER_TIMER;
361   add_timeval(now, *t, &e->whence);
362   ts->timeoutevent = e;
363   eventer_add(e);
364 }
365
366 static int noit_snmp_handler(eventer_t e, int mask, void *closure,
367                              struct timeval *now) {
368   fd_set fdset;
369   int fds, block = 0;
370   struct timeval timeout = { 0, 0 };
371   struct target_session *ts = closure;
372   FD_ZERO(&fdset);
373   FD_SET(e->fd, &fdset);
374   fds = e->fd + 1;
375   snmp_sess_read(ts->sess_handle, &fdset);
376   if(noit_snmp_session_cleanse(ts))
377     return 0;
378   snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
379   _set_ts_timeout(ts, block ? &timeout : NULL);
380   return EVENTER_READ | EVENTER_EXCEPTION;
381 }
382
383 /* This 'convert_v1pdu_to_v2' was cribbed directly from netsnmp */
384 static netsnmp_pdu *
385 convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu ) {
386   netsnmp_pdu *template_v2pdu;
387   netsnmp_variable_list *first_vb;
388   netsnmp_variable_list *var;
389   oid enterprise[MAX_OID_LEN];
390   size_t enterprise_len;
391
392   /*
393    * Make a copy of the v1 Trap PDU
394    *   before starting to convert this
395    *   into a v2 Trap PDU.
396    */
397   template_v2pdu = snmp_clone_pdu( template_v1pdu);
398   if(!template_v2pdu) {
399     snmp_log(LOG_WARNING,
400              "send_trap: failed to copy v2 template PDU\n");
401     return NULL;
402   }
403   template_v2pdu->command = SNMP_MSG_TRAP2;
404   first_vb = template_v2pdu->variables;
405
406   /*
407    * Insert an snmpTrapOID varbind before the original v1 varbind list
408    *   either using one of the standard defined trap OIDs,
409    *   or constructing this from the PDU enterprise & specific trap fields
410    */
411   if(template_v1pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
412     memcpy(enterprise, template_v1pdu->enterprise,
413            template_v1pdu->enterprise_length*sizeof(oid));
414     enterprise_len = template_v1pdu->enterprise_length;
415     enterprise[enterprise_len++] = 0;
416     enterprise[enterprise_len++] = template_v1pdu->specific_type;
417   } else {
418     memcpy(enterprise, cold_start_oid, sizeof(cold_start_oid));
419     enterprise[9]  = template_v1pdu->trap_type+1;
420     enterprise_len = sizeof(cold_start_oid)/sizeof(oid);
421   }
422
423   var = NULL;
424   if(!snmp_varlist_add_variable(&var,
425                                 snmptrap_oid, snmptrap_oid_len,
426                                 ASN_OBJECT_ID,
427                                 (u_char*)enterprise,
428                                 enterprise_len*sizeof(oid))) {
429     noitL(nlerr, "send_trap: failed to insert copied snmpTrapOID varbind\n");
430     snmp_free_pdu(template_v2pdu);
431     return NULL;
432   }
433   var->next_variable        = template_v2pdu->variables;
434   template_v2pdu->variables = var;
435
436   /*
437    * Insert a sysUptime varbind at the head of the v2 varbind list
438    */
439   var = NULL;
440   if(!snmp_varlist_add_variable(&var,
441                                 sysuptime_oid, sysuptime_oid_len,
442                                 ASN_TIMETICKS,
443                                 (u_char*)&(template_v1pdu->time),
444                                 sizeof(template_v1pdu->time))) {
445     noitL(nlerr, "send_trap: failed to insert copied sysUptime varbind\n");
446     snmp_free_pdu(template_v2pdu);
447     return NULL;
448   }
449   var->next_variable = template_v2pdu->variables;
450   template_v2pdu->variables = var;
451
452   /*
453    * Append the other three conversion varbinds,
454    *  (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise)
455    *  if they're not already present.
456    *  But don't bomb out completely if there are problems.
457    */
458   var = find_varbind_in_list(template_v2pdu->variables,
459                              agentaddr_oid, agentaddr_oid_len);
460   if(!var && (template_v1pdu->agent_addr[0]
461               || template_v1pdu->agent_addr[1]
462               || template_v1pdu->agent_addr[2]
463               || template_v1pdu->agent_addr[3])) {
464     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
465                                   agentaddr_oid, agentaddr_oid_len,
466                                   ASN_IPADDRESS,
467                                   (u_char*)&(template_v1pdu->agent_addr),
468                                   sizeof(template_v1pdu->agent_addr)))
469       noitL(nlerr, "send_trap: failed to append snmpTrapAddr varbind\n");
470   }
471   var = find_varbind_in_list(template_v2pdu->variables,
472                              community_oid, community_oid_len);
473   if(!var && template_v1pdu->community) {
474     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
475                                   community_oid, community_oid_len,
476                                   ASN_OCTET_STR,
477                                   template_v1pdu->community,
478                                   template_v1pdu->community_len))
479       noitL(nlerr, "send_trap: failed to append snmpTrapCommunity varbind\n");
480   }
481   var = find_varbind_in_list(template_v2pdu->variables,
482                              snmptrapenterprise_oid,
483                              snmptrapenterprise_oid_len);
484   if(!var &&
485      template_v1pdu->trap_type != SNMP_TRAP_ENTERPRISESPECIFIC) {
486     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
487                                   snmptrapenterprise_oid,
488                                   snmptrapenterprise_oid_len,
489                                   ASN_OBJECT_ID,
490                                   (u_char*)template_v1pdu->enterprise,
491                                   template_v1pdu->enterprise_length*sizeof(oid)))
492       noitL(nlerr, "send_trap: failed to append snmpEnterprise varbind\n");
493   }
494   return template_v2pdu;
495 }
496
497 static int noit_snmp_oid_to_checkid(oid *o, int l, uuid_t checkid, char *out) {
498   int i;
499   char _uuid_str[UUID_STR_LEN+1], *cp, *uuid_str;
500
501   uuid_str = out ? out : _uuid_str;
502   if(l != reconnoiter_check_oid_len) {
503     noitL(nlerr, "unsupported (length) trap recieved\n");
504     return -1;
505   }
506   if(netsnmp_oid_equals(o,
507                         reconnoiter_check_prefix_oid_len,
508                         reconnoiter_check_prefix_oid,
509                         reconnoiter_check_prefix_oid_len) != 0) {
510     noitL(nlerr, "unsupported (wrong namespace) trap recieved\n");
511     return -1;
512   }
513   /* encode this as a uuid */
514   cp = uuid_str;
515   for(i=0;
516       i < reconnoiter_check_oid_len - reconnoiter_check_prefix_oid_len;
517       i++) {
518     oid v = o[i + reconnoiter_check_prefix_oid_len];
519     if(v < 0 || v > 0xffff) {
520       noitL(nlerr, "trap target oid [%ld] out of range\n", v);
521       return -1;
522     }
523     snprintf(cp, 5, "%04x", (unsigned short)(v & 0xffff));
524     cp += 4;
525     /* hyphens after index 1,2,3,4 */
526     if(i > 0 && i < 5) *cp++ = '-';
527   }
528   if(uuid_parse(uuid_str, checkid) != 0) {
529     noitL(nlerr, "unexpected error decoding trap uuid '%s'\n", uuid_str);
530     return -1;
531   }
532   return 0;
533 }
534
535 #define isoid(a,b,c,d) (netsnmp_oid_equals(a,b,c,d) == 0)
536 #define isoidprefix(a,b,c,d) (netsnmp_oid_equals(a,MIN(b,d),c,d) == 0)
537 #define setstatus(st,soid,sv) \
538   if(isoid(o,l,soid,reconnoiter_check_state_val_len)) current->st = sv
539
540 static int
541 noit_snmp_trapvars_to_stats(stats_t *current, netsnmp_variable_list *var) {
542   if(isoidprefix(var->name, var->name_length, reconnoiter_check_status_oid,
543                  reconnoiter_check_status_oid_len)) {
544     if(var->type == ASN_OBJECT_ID) {
545       if(isoid(var->name, var->name_length,
546                reconnoiter_check_state_oid, reconnoiter_check_state_oid_len)) {
547         oid *o = var->val.objid;
548         size_t l = var->val_len / sizeof(*o);
549         setstatus(state, reconnoiter_check_state_unknown_oid, NP_UNKNOWN);
550         else setstatus(state, reconnoiter_check_state_good_oid, NP_GOOD);
551         else setstatus(state, reconnoiter_check_state_bad_oid, NP_BAD);
552         else return -1;
553       }
554       else if(isoid(var->name, var->name_length,
555                     reconnoiter_check_available_oid,
556                     reconnoiter_check_available_oid_len)) {
557         oid *o = var->val.objid;
558         size_t l = var->val_len / sizeof(*o);
559         setstatus(available, reconnoiter_check_available_unknown_oid, NP_UNKNOWN);
560         else setstatus(available, reconnoiter_check_available_yes_oid, NP_AVAILABLE);
561         else setstatus(available, reconnoiter_check_available_no_oid, NP_UNAVAILABLE);
562         else return -1;
563       }
564       else {
565         /* We don't unerstand any other OBJECT_ID types */
566         return -1;
567       }
568     }
569     else if(var->type == ASN_UNSIGNED) {
570       /* This is only for the duration (in ms) */
571       if(isoid(var->name, var->name_length,
572                reconnoiter_check_duration_oid,
573                reconnoiter_check_duration_oid_len)) {
574         current->duration = *(var->val.integer);
575       }
576       else
577         return -1;
578     }
579     else if(var->type == ASN_OCTET_STR) {
580       /* This is only for the status message */
581       if(isoid(var->name, var->name_length,
582                reconnoiter_check_status_msg_oid,
583                reconnoiter_check_status_msg_oid_len)) {
584         current->status = malloc(var->val_len + 1);
585         memcpy(current->status, var->val.string, var->val_len);
586         current->status[var->val_len] = '\0';
587       }
588       else
589         return -1;
590     }
591     else {
592       /* I don't understand any other type of status message */
593       return -1;
594     }
595   }
596   else if(isoidprefix(var->name, var->name_length,
597                       reconnoiter_metric_prefix_oid,
598                       reconnoiter_metric_prefix_oid_len)) {
599     /* decode the metric and store the value */
600     int i, len;
601     u_int64_t u64;
602     double doubleVal;
603     char metric_name[128], buff[128], *cp;
604     if(var->name_length <= reconnoiter_metric_prefix_oid_len) return -1;
605     len = var->name[reconnoiter_metric_prefix_oid_len];
606     if(var->name_length != (reconnoiter_metric_prefix_oid_len + 1 + len) ||
607        len > sizeof(metric_name) - 1) {
608       noitL(nlerr, "snmp trap, malformed metric name\n");
609       return -1;
610     }
611     for(i=0;i<len;i++) {
612       ((unsigned char *)metric_name)[i] =
613         (unsigned char)var->name[reconnoiter_metric_prefix_oid_len + 1 + i];
614       if(!isprint(metric_name[i])) {
615         noitL(nlerr, "metric_name contains unprintable characters\n");
616         return -1;
617       }
618     }
619     metric_name[i] = '\0';
620     switch(var->type) {
621       case ASN_INTEGER:
622       case ASN_UINTEGER:
623       case ASN_TIMETICKS:
624       case ASN_INTEGER64:
625         noit_stats_set_metric(current, metric_name,
626                               METRIC_INT64, var->val.integer);
627         break;
628       case ASN_COUNTER64:
629         u64 = ((u_int64_t)var->val.counter64->high) << 32;
630         u64 |= var->val.counter64->low;
631         noit_stats_set_metric(current, metric_name,
632                               METRIC_UINT64, &u64);
633         break;
634       case ASN_OPAQUE_FLOAT:
635         doubleVal = (double)*var->val.floatVal;
636         noit_stats_set_metric(current, metric_name,
637                               METRIC_DOUBLE, &doubleVal);
638         break;
639       case ASN_OPAQUE_DOUBLE:
640         noit_stats_set_metric(current, metric_name,
641                               METRIC_DOUBLE, var->val.doubleVal);
642         break;
643       case ASN_OCTET_STR:
644         snprint_value(buff, sizeof(buff), var->name, var->name_length, var);
645         /* Advance passed the first space and use that unless there
646          * is no space or we have no more string left.
647          */
648         cp = strchr(buff, ' ');
649         if(cp) {
650           char *ecp;
651           cp++;
652           if(*cp == '"') {
653             ecp = cp + strlen(cp) - 1;
654             if(*ecp == '"') {
655               cp++; *ecp = '\0';
656             }
657           }
658         }
659         noit_stats_set_metric(current, metric_name,
660                               METRIC_STRING, (cp && *cp) ? cp : NULL);
661         break;
662       default:
663         noitL(nlerr, "snmp trap unsupport data type %d\n", var->type);
664     }
665     noitL(nldeb, "metric_name -> '%s'\n", metric_name);
666   }
667   else {
668     /* No idea what this is */
669     return -1;
670   }
671   return 0;
672 }
673 static int noit_snmp_trapd_response(int operation, struct snmp_session *sp,
674                                     int reqid, struct snmp_pdu *pdu,
675                                     void *magic) {
676   /* the noit pieces */
677   noit_check_t *check;
678   struct target_session *ts = magic;
679   snmp_mod_config_t *conf;
680   const char *community = NULL;
681   stats_t current;
682
683   /* parsing destination */
684   char uuid_str[UUID_STR_LEN + 1];
685   uuid_t checkid;
686
687   /* snmp oid parsing helper vars */
688   netsnmp_pdu *newpdu = pdu;
689   netsnmp_variable_list *var;
690
691   conf = noit_module_get_userdata(ts->self);
692
693   if(pdu->version == SNMP_VERSION_1)
694     newpdu = convert_v1pdu_to_v2(pdu);
695   if(!newpdu || newpdu->version != SNMP_VERSION_2c) goto cleanup;
696
697   for(var = newpdu->variables; var != NULL; var = var->next_variable) {
698     if(netsnmp_oid_equals(var->name, var->name_length,
699                           snmptrap_oid, snmptrap_oid_len) == 0)
700       break;
701   }
702
703   if (!var || var->type != ASN_OBJECT_ID) {
704     noitL(nlerr, "unsupport trap (not a trap?) received\n");
705     goto cleanup;
706   }
707
708   /* var is the oid on which we are trapping.
709    * It should be in the reconnoiter check prefix.
710    */
711   if(noit_snmp_oid_to_checkid(var->val.objid, var->val_len/sizeof(oid),
712                               checkid, uuid_str)) {
713     goto cleanup;
714   }
715   noitL(nldeb, "recieved trap for %s\n", uuid_str);
716   check = noit_poller_lookup(checkid);
717   if(!check) {
718     noitL(nlerr, "trap received for non-existent check '%s'\n", uuid_str);
719     goto cleanup;
720   }
721   if(!noit_hash_retr_str(check->config, "community", strlen("community"),
722                          &community) &&
723      !noit_hash_retr_str(conf->options, "community", strlen("community"),
724                          &community)) {
725     noitL(nlerr, "No community defined for check, dropping trap\n");
726     goto cleanup;
727   }
728
729   if(strlen(community) != newpdu->community_len ||
730      memcmp(community, newpdu->community, newpdu->community_len)) {
731     noitL(nlerr, "trap attempt with wrong community string\n");
732     goto cleanup;
733   }
734
735   /* We have a check. The trap is authorized. Now, extract everything. */
736   memset(&current, 0, sizeof(current));
737   gettimeofday(&current.whence, NULL);
738
739   for(; var != NULL; var = var->next_variable)
740     noit_snmp_trapvars_to_stats(&current, var);
741   noit_check_set_stats(ts->self, check, &current);
742
743  cleanup:
744   if(newpdu != pdu)
745     snmp_free_pdu(newpdu);
746   return 0;
747 }
748 static int noit_snmp_asynch_response(int operation, struct snmp_session *sp,
749                                      int reqid, struct snmp_pdu *pdu,
750                                      void *magic) {
751   struct check_info *info;
752   struct target_session *ts = magic;
753
754   /* We don't deal with refcnt hitting zero here.  We could only be hit from
755    * the snmp read/timeout stuff.  Handle it there.
756    */
757   ts->refcnt--;
758
759   info = get_check(reqid);
760   if(!info) return 1;
761   remove_check(info);
762   if(info->timeoutevent) {
763     eventer_remove(info->timeoutevent);
764     eventer_free(info->timeoutevent);
765     info->timeoutevent = NULL;
766   }
767
768   /* Log our findings */
769   noit_snmp_log_results(info->self, info->check, pdu);
770   info->check->flags &= ~NP_RUNNING;
771   return 1;
772 }
773
774 static void noit_snmp_sess_open(struct target_session *ts,
775                                 noit_check_t *check) {
776   const char *community;
777   struct snmp_session sess;
778   snmp_sess_init(&sess);
779   sess.version = SNMP_VERSION_2c;
780   sess.peername = check->target;
781   if(!noit_hash_retr_str(check->config, "community", strlen("community"),
782                          &community)) {
783     community = "public";
784   }
785   sess.community = (unsigned char *)community;
786   sess.community_len = strlen(community);
787   sess.callback = noit_snmp_asynch_response;
788   sess.callback_magic = ts;
789   ts->sess_handle = snmp_sess_open(&sess);
790 }
791
792 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check) {
793   int i, klen;
794   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
795   const char *name, *value;
796   struct check_info *info = check->closure;
797   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
798
799   /* Toss the old set and bail if we have zero */
800   if(info->oids) {
801     for(i=0; i<info->noids;i++) {
802       if(info->oids[i].confname) free(info->oids[i].confname);
803       if(info->oids[i].oidname) free(info->oids[i].oidname);
804     }
805     free(info->oids);
806   }
807   info->noids = 0;
808   info->oids = NULL;
809
810   /* Figure our how many. */
811   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value)) {
812     if(!strncasecmp(name, "oid_", 4)) {
813       info->noids++;
814     }
815   }
816
817   if(info->noids == 0) return 0;
818
819   /* Create a hash of important check attributes */
820   noit_check_make_attrs(check, &check_attrs_hash);
821
822   /* Fill out the new set of required oids */
823   info->oids = calloc(info->noids, sizeof(*info->oids));
824   memset(&iter, 0, sizeof(iter));
825   i = 0;
826   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value)) {
827     if(!strncasecmp(name, "oid_", 4)) {
828       char oidbuff[128];
829       name += 4;
830       info->oids[i].confname = strdup(name);
831       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
832                              &check_attrs_hash, check->config);
833       info->oids[i].oidname = strdup(oidbuff);
834       info->oids[i].oidlen = MAX_OID_LEN;
835       get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen);
836       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
837       i++;
838     }
839   }
840   assert(info->noids == i);
841   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
842   return info->noids;
843 }
844 static int noit_snmp_send(noit_module_t *self, noit_check_t *check) {
845   struct snmp_pdu *req;
846   struct target_session *ts;
847   struct check_info *info = check->closure;
848
849   info->self = self;
850   info->check = check;
851   info->timedout = 0;
852
853   check->flags |= NP_RUNNING;
854   ts = _get_target_session(self, check->target);
855   gettimeofday(&check->last_fire_time, NULL);
856   if(!ts->refcnt) {
857     eventer_t newe;
858     int fds, block;
859     struct timeval timeout;
860     fd_set fdset;
861     noit_snmp_sess_open(ts, check);
862     block = 0;
863     fds = 0;
864     FD_ZERO(&fdset);
865     snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
866     assert(fds > 0);
867     ts->fd = fds-1;
868     newe = eventer_alloc();
869     newe->fd = ts->fd;
870     newe->callback = noit_snmp_handler;
871     newe->closure = ts;
872     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
873     eventer_add(newe);
874   }
875   if(!ts->sess_handle) {
876     /* Error */
877     /* No need to do anything, this will be handled in the else below */
878   }
879   ts->refcnt++; /* Increment here, decrement when this check completes */
880
881   req = snmp_pdu_create(SNMP_MSG_GET);
882   if(req) noit_snmp_fill_req(req, check);
883   /* Setup out snmp requests */
884   if(ts->sess_handle && req &&
885      (info->reqid = snmp_sess_send(ts->sess_handle, req)) != 0) {
886     struct timeval when, to;
887     info->timeoutevent = eventer_alloc();
888     info->timeoutevent->callback = noit_snmp_check_timeout;
889     info->timeoutevent->closure = info;
890     info->timeoutevent->mask = EVENTER_TIMER;
891
892     gettimeofday(&when, NULL);
893     to.tv_sec = check->timeout / 1000;
894     to.tv_usec = (check->timeout % 1000) * 1000;
895     add_timeval(when, to, &info->timeoutevent->whence);
896     eventer_add(info->timeoutevent);
897     add_check(info);
898   }
899   else {
900     ts->refcnt--;
901     noit_snmp_session_cleanse(ts);
902     /* Error */
903     if(req) snmp_free_pdu(req);
904     /* Log our findings */
905     noit_snmp_log_results(self, check, NULL);
906     check->flags &= ~NP_RUNNING;
907   }
908   return 0;
909 }
910
911 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
912                                     int once, noit_check_t *cause) {
913   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
914   INITIATE_CHECK(noit_snmp_send, self, check);
915   return 0;
916 }
917
918 static int noit_snmptrap_initiate_check(noit_module_t *self,
919                                         noit_check_t *check,
920                                         int once, noit_check_t *cause) {
921   /* We don't do anything for snmptrap checks.  Not intuitive... but they
922    * never "run."  We accept input out-of-band via snmp traps.
923    */
924   return 0;
925 }
926
927 static int noit_snmp_config(noit_module_t *self, noit_hash_table *options) {
928   snmp_mod_config_t *conf;
929   conf = noit_module_get_userdata(self);
930   if(conf) {
931     if(conf->options) {
932       noit_hash_destroy(conf->options, free, free);
933       free(conf->options);
934     }
935   }
936   else
937     conf = calloc(1, sizeof(*conf));
938   conf->options = options;
939   noit_module_set_userdata(self, conf);
940   return 1;
941 }
942 static int noit_snmp_onload(noit_image_t *self) {
943   if(!nlerr) nlerr = noit_log_stream_find("error/snmp");
944   if(!nldeb) nldeb = noit_log_stream_find("debug/snmp");
945   if(!nlerr) nlerr = noit_stderr;
946   if(!nldeb) nldeb = noit_debug;
947   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
948   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
949   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
950   return 0;
951 }
952
953 static int noit_snmptrap_onload(noit_image_t *self) {
954   if(!nlerr) nlerr = noit_log_stream_find("error/snmp");
955   if(!nldeb) nldeb = noit_log_stream_find("debug/snmp");
956   if(!nlerr) nlerr = noit_stderr;
957   if(!nldeb) nldeb = noit_debug;
958   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
959   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
960   return 0;
961 }
962
963 static int noit_snmp_init(noit_module_t *self) {
964   const char *opt;
965   snmp_mod_config_t *conf;
966
967   conf = noit_module_get_userdata(self);
968
969   if(!__snmp_initialize_once) {
970     register_mib_handlers();
971     read_premib_configs();
972     read_configs();
973     init_snmp("noitd");
974     __snmp_initialize_once = 1;
975   }
976
977   if(strcmp(self->hdr.name, "snmptrap") == 0) {
978     eventer_t newe;
979     int i, block = 0, fds = 0;
980     fd_set fdset;
981     struct timeval timeout = { 0, 0 };
982     struct target_session *ts;
983     netsnmp_transport *transport;
984     netsnmp_session sess, *session = &sess;
985
986     if(!noit_hash_retrieve(conf->options,
987                            "snmptrapd_port", strlen("snmptrapd_port"),
988                            (void **)&opt))
989       opt = "162";
990
991     transport = netsnmp_transport_open_server("snmptrap", opt);
992     if(!transport) {
993       noitL(nlerr, "cannot open netsnmp transport for trap daemon\n");
994       return -1;
995     }
996     ts = _get_target_session(self, "snmptrapd");
997     snmp_sess_init(session);
998     session->peername = SNMP_DEFAULT_PEERNAME;
999     session->version = SNMP_DEFAULT_VERSION;
1000     session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
1001     session->retries = SNMP_DEFAULT_RETRIES;
1002     session->timeout = SNMP_DEFAULT_TIMEOUT;
1003     session->callback = noit_snmp_trapd_response;
1004     session->callback_magic = (void *) ts;
1005     session->authenticator = NULL;
1006     session->isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
1007     ts->sess_handle = snmp_sess_add(session, transport, NULL, NULL);
1008
1009     FD_ZERO(&fdset);
1010     snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
1011     assert(fds > 0);
1012     for(i=0; i<fds; i++) {
1013       if(FD_ISSET(i, &fdset)) {
1014         ts->refcnt++;
1015         ts->fd = i;
1016         newe = eventer_alloc();
1017         newe->fd = ts->fd;
1018         newe->callback = noit_snmp_handler;
1019         newe->closure = ts;
1020         newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1021         eventer_add(newe);
1022       }
1023     }
1024   }
1025   return 0;
1026 }
1027
1028 #include "snmp.xmlh"
1029 noit_module_t snmp = {
1030   {
1031     NOIT_MODULE_MAGIC,
1032     NOIT_MODULE_ABI_VERSION,
1033     "snmp",
1034     "SNMP collection",
1035     snmp_xml_description,
1036     noit_snmp_onload
1037   },
1038   noit_snmp_config,
1039   noit_snmp_init,
1040   noit_snmp_initiate_check,
1041   NULL /* noit_snmp_cleanup */
1042 };
1043
1044 #include "snmptrap.xmlh"
1045 noit_module_t snmptrap = {
1046   {
1047     NOIT_MODULE_MAGIC,
1048     NOIT_MODULE_ABI_VERSION,
1049     "snmptrap",
1050     "SNMP trap collection",
1051     snmptrap_xml_description,
1052     noit_snmptrap_onload
1053   },
1054   noit_snmp_config,
1055   noit_snmp_init,
1056   noit_snmptrap_initiate_check,
1057   NULL /* noit_snmp_cleanup */
1058 };
Note: See TracBrowser for help on using the browser.