root/src/modules/snmp.c

Revision 95eb6440020ee8162ea9576ef6379843541f21fe, 33.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

let's try this as a fix. refs #109

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