/* * * COPYRIGHT: * * This software is Copyright (c) 2011,2012 NETWAYS GmbH, William Preston * * * (Except where explicitly superseded by other copyright notices) * * * LICENSE: * * This work is made available to you under the terms of Version 2 of * the GNU General Public License. A copy of that license should have * been provided with this software, but in any event can be snarfed * from http://www.fsf.org. * * This work is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 or visit their web page on the internet at * http://www.fsf.org. * * * CONTRIBUTION SUBMISSION POLICY: * * (The following paragraph is not intended to limit the rights granted * to you to modify and distribute this software under the terms of * the GNU General Public License and is only of importance to you if * you choose to contribute your changes and enhancements to the * community by submitting them to NETWAYS GmbH.) * * By intentionally submitting any modifications, corrections or * derivatives to this work, or any other work intended for use with * this Software, to NETWAYS GmbH, you confirm that * you are the copyright holder for those contributions and you grant * NETWAYS GmbH a nonexclusive, worldwide, irrevocable, * royalty-free, perpetual, license to use, copy, create derivative * works based on those contributions, and sublicense and distribute * those contributions and any derivatives thereof. * * * */ /* asprintf and getopt_long */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include /* getenv */ #include /* getopt_long */ #include #include "snmp_bulkget.h" #include "utils.h" /* we assume that the index number is the same as the last digit of the OID * which may not always hold true... * but seems to do so in practice * * input error checking * index parameter support * auto-detect hardware and switch mode * make non-posix code optional e.g. asprintf */ void create_pdu(int, char **, netsnmp_pdu **, struct OIDStruct **, int, long); /* hardware mode */ int mode = DEFAULT; /* uptime counter */ unsigned int uptime = 0, sleep_usecs = 0; unsigned int lastcheck = 0; unsigned long global_timeout = DFLT_TIMEOUT; static int ifNumber = 0; #ifdef DEBUG static char *implode_result; #endif static int session_retries = 2; static long pdu_max_repetitions = 4096L; int main(int argc, char *argv[]) { netsnmp_session session, *ss; netsnmp_pdu *pdu; netsnmp_pdu *response; netsnmp_variable_list *vars; int status, status2; int count = 0; /* used for: the number of interfaces we receive, the number of regex matches */ int i, j, k; int errorflag = 0; int warnflag = 0; int lastifflag = 0; int crit_on_down_flag = 1; int get_aliases_flag = 0; int match_aliases_flag = 0; int get_names_flag = 0; int print_all_flag = 0; int err_tolerance = 50; int coll_tolerance = -1; u64 speed = 0; int bw = 0; size_t size,size2; struct ifStruct *interfaces = NULL; /* current interface data */ struct ifStruct *oldperfdata = NULL; /* previous check interface data */ struct OIDStruct *OIDp; char *hostname=0, *community=0, *list=0, *oldperfdatap=0, *prefix = 0; char *user = 0, *auth_proto = 0, *auth_pass = 0, *priv_proto = 0, *priv_pass = 0; char *exclude_list = 0; #ifdef INDEXES char *indexes=0; #endif /* INDEXES */ #ifdef HAVE_GETADDRINFO struct addrinfo *addr_list, *addr_listp; #endif /* HAVE_GETADDRINFO */ struct timeval tv; struct timezone tz; long double starttime; regex_t re, exclude_re; int ignore_count = 0; int trimdescr = 0; int opt; double inload = 0,outload = 0; u64 inbitps = 0,outbitps = 0; char *ins, *outs; char outstr[MAX_STRING]; memset(outstr, 0, sizeof(outstr)); String out; out.max = MAX_STRING; out.len = 0; out.text = outstr; char perfstr[MAX_STRING]; memset(perfstr, 0, sizeof(perfstr)); String perf; perf.max = MAX_STRING; perf.len = 0; perf.text = perfstr; struct OIDStruct lastOid; static char **oid_ifp; static char **oid_vals; static char **if_vars; static char **oid_aliasp; oid_ifp = oid_if_bulkget; oid_vals = oid_vals_default; if_vars = if_vars_default; char *progname = strrchr(argv[0], '/'); if (*progname && *(progname+1)) progname++; else progname = "check_interfaces"; /* parse options */ static struct option longopts[] = { {"aliases", no_argument, NULL, 'a'}, {"match-aliases", no_argument, NULL, 'A'}, {"bandwidth", required_argument, NULL, 'b'}, {"community", required_argument, NULL, 'c'}, {"down-is-ok", no_argument, NULL, 'd'}, {"errors", required_argument, NULL, 'e'}, {"out-errors", required_argument, NULL, 'f'}, {"hostname", required_argument, NULL, 'h'}, #ifdef INDEXES {"interfaces", required_argument, NULL, 'i'}, #endif /* INDEXES */ {"auth-proto", required_argument, NULL, 'j'}, {"auth-phrase", required_argument, NULL, 'J'}, {"priv-proto", required_argument, NULL, 'k'}, {"priv-phrase", required_argument, NULL, 'K'}, {"mode", required_argument, NULL, 'm'}, {"perfdata", required_argument, NULL, 'p'}, {"prefix", required_argument, NULL, 'P'}, {"regex", required_argument, NULL, 'r'}, {"exclude-regex", required_argument, NULL, 'R'}, {"if-names", no_argument, NULL, 'N'}, {"debug-print", no_argument, NULL, 'D'}, {"speed", required_argument, NULL, 's'}, {"lastcheck", required_argument, NULL, 't'}, {"user", required_argument, NULL, 'u'}, {"trim", required_argument, NULL, 'x'}, {"help", no_argument, NULL, '?'}, {"timeout", required_argument, NULL, 2}, {"sleep", required_argument, NULL, 3}, {"retries", required_argument, NULL, 4}, {"max-repetitions", required_argument, NULL, 5}, {NULL, 0, NULL, 0} }; while ((opt = getopt_long(argc, argv, "aAb:c:dDe:f:h:i:j:J:k:K:m:Np:P:r:R:s:t:u:x:?", longopts, NULL)) != -1) { switch(opt) { case 'a': get_aliases_flag = 1; break; case 'A': get_aliases_flag = 1; /* we need to see what we have matched... */ match_aliases_flag = 1; break; case 'b': bw = strtol(optarg, NULL, 10); break; case 'c': community = optarg; break; case 'd': crit_on_down_flag = 0; break; case 'D': print_all_flag = 1; break; case 'e': err_tolerance = strtol(optarg, NULL, 10); break; case 'f': coll_tolerance = strtol(optarg, NULL, 10); break; case 'h': hostname = optarg; break; case 'i': #ifdef INDEXES indexes = optarg; #endif /* INDEXES */ break; case 'j': auth_proto = optarg; break; case 'J': auth_pass = optarg; break; case 'k': priv_proto = optarg; break; case 'K': priv_pass = optarg; break; case 'm': /* mode switch */ for (i=0; modes[i]; i++) { if (!strcmp(optarg, modes[i])) { mode = i; break; } } break; case 'N': get_names_flag = 1; break; case 'p': oldperfdatap = optarg; break; case 'P': prefix = optarg; break; case 'r': list = optarg; break; case 'R': exclude_list = optarg; break; case 's': speed = strtoull(optarg, NULL, 10); break; case 't': lastcheck = strtol(optarg, NULL, 10); break; case 'u': user = optarg; break; case 'x': trimdescr = strtol(optarg, NULL, 10); break; case 2: /* convert from ms to us */ global_timeout = strtol(optarg, NULL, 10) * 1000UL; break; case 3: /* convert from ms to us */ sleep_usecs = strtol(optarg, NULL, 10) * 1000UL; break; case 4: session_retries = atoi(optarg); break; case 5: pdu_max_repetitions = strtol(optarg, NULL, 10); break; case '?': default: exit(usage(progname)); } } argc -= optind; argv += optind; if (coll_tolerance == -1) { /* set the outErrors tolerance to that of inErrors unless explicitly set otherwise */ coll_tolerance = err_tolerance; } if (!(hostname)) { exit(usage(progname)); } #ifdef HAVE_GETADDRINFO /* check for a valid hostname / IP Address */ if(getaddrinfo(hostname, NULL, NULL, &addr_list)) { printf("Failed to resolve hostname %s\n", hostname); exit(3); } /* name is resolvable - pass it to the snmplib */ freeaddrinfo(addr_list); #endif /* HAVE_GETADDRINFO */ if (!community) community = default_community; if (exclude_list && !list) /* use .* as the default regex */ list = ".*"; /* get the start time */ gettimeofday(&tv, &tz); starttime=(long double)tv.tv_sec + (((long double)tv.tv_usec)/1000000); /* parse the interfaces regex */ if (list) { status = regcomp(&re, list, REG_ICASE|REG_EXTENDED|REG_NOSUB); if (status != 0) { printf("Error creating regex\n"); exit (3); } if (exclude_list) { status = regcomp(&exclude_re, exclude_list, REG_ICASE|REG_EXTENDED|REG_NOSUB); if (status != 0) { printf("Error creating exclusion regex\n"); exit (3); } } } /* set the MIB variable if it is unset to avoid net-snmp warnings */ if (getenv("MIBS") == NULL) setenv("MIBS", "", 1); #ifdef DEBUG benchmark_start("Start SNMP session"); #endif if (user) /* use snmpv3 */ ss=start_session_v3(&session, user, auth_proto, auth_pass, priv_proto, priv_pass, hostname); else ss=start_session(&session, community, hostname); #ifdef DEBUG benchmark_end(); #endif if (mode == NONBULK) { oid_ifp = oid_if_get; size = (sizeof(oid_if_get) / sizeof(char *)) - 1; oid_aliasp = oid_alias_get; } else if (mode == BINTEC) { oid_ifp = oid_if_bintec; size = (sizeof(oid_if_bintec) / sizeof(char *)) - 1; oid_aliasp = oid_alias_bintec; } else { oid_ifp = oid_if_bulkget; size = (sizeof(oid_if_bulkget) / sizeof(char *)) - 1; oid_aliasp = oid_alias_bulkget; } /* allocate the space for the interface OIDs */ OIDp = (struct OIDStruct *) calloc(size, sizeof(struct OIDStruct)); if (mode == CISCO) { if_vars = if_vars_cisco; oid_vals = oid_vals_cisco; } /* get the number of interfaces, and their index numbers * * We will attempt to get all the interfaces in a single packet * - which should manage about 64 interfaces. * If the end interface has not been reached, we fetch more packets - this is * necessary to work around buggy switches that lie about the ifNumber */ while (lastifflag==0) { /* build our request depending on the mode */ if (count==0) create_pdu(mode, oid_ifp, &pdu, &OIDp, 2, pdu_max_repetitions); else { /* we have not received all interfaces in the preceding packet, so fetch the next lot */ if (mode == BINTEC || mode == NONBULK) pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); else { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = 0; pdu->max_repetitions = pdu_max_repetitions; } snmp_add_null_var(pdu, lastOid.name, lastOid.name_len); } #ifdef DEBUG implode_result = implode(", ", oid_ifp + count); benchmark_start("Send SNMP request for OIDs: %s", implode_result); #endif /* send the request */ status = snmp_synch_response(ss, pdu, &response); #ifdef DEBUG benchmark_end(); free(implode_result); #endif if (sleep_usecs) usleep(sleep_usecs); if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; if (count==0) { /* assuming that the uptime and ifNumber come first */ /* on some devices the ifNumber is not available... */ while (!ifNumber) { if (!(memcmp(OIDp[0].name, vars->name, OIDp[0].name_len * sizeof(oid)))) { /* uptime */ if (vars->type == ASN_TIMETICKS) /* uptime is in 10ms units -> convert to seconds */ uptime = *(vars->val.integer) / 100; } else if (!memcmp(OIDp[1].name, vars->name, OIDp[1].name_len * sizeof(oid))) { /* we received a valid IfNumber */ ifNumber = *(vars->val.integer); if (ifNumber == 0) { /* there are no interfaces! Stop here */ printf("No interfaces found"); exit (0); } } else { addstr(&out, "(no IfNumber parameter, assuming 32 interfaces) "); ifNumber = 32; } vars = vars->next_variable; } interfaces = (struct ifStruct*)calloc((size_t)ifNumber, sizeof(struct ifStruct)); oldperfdata = (struct ifStruct*)calloc((size_t)ifNumber, sizeof(struct ifStruct)); #ifdef DEBUG fprintf(stderr, "got %d interfaces\n", ifNumber); #endif } else { /* subsequent replies have no ifNumber */ } for (vars = vars; vars; vars = vars->next_variable) { /* * if the next OID is shorter * or if the next OID doesn't begin with our base OID * then we have reached the end of the table :-) * print_variable(vars->name, vars->name_length, vars); */ /* save the OID in case we need additional packets */ memcpy(lastOid.name, vars->name, (vars->name_length * sizeof(oid))); lastOid.name_len = vars->name_length; if ((vars->name_length < OIDp[2].name_len) || (memcmp(OIDp[2].name, vars->name, (vars->name_length - 1) * sizeof(oid)))) { #ifdef DEBUG fprintf(stderr, "reached end of interfaces\n"); #endif lastifflag++; break; } /* now we fill our interfaces array with the index number and * the description that we have received */ if (vars->type == ASN_OCTET_STR) { if (trimdescr && trimdescr < vars->val_len) { interfaces[count].index = (int) vars->name[(vars->name_length - 1)]; MEMCPY(interfaces[count].descr, (vars->val.string)+trimdescr, vars->val_len - trimdescr); TERMSTR(interfaces[count].descr, vars->val_len - trimdescr); } else { interfaces[count].index = (int) vars->name[(vars->name_length - 1)]; MEMCPY(interfaces[count].descr, vars->val.string, vars->val_len); TERMSTR(interfaces[count].descr, vars->val_len); } count++; } } if (count < ifNumber) { if (lastifflag) { #ifdef DEBUG fprintf(stderr, "Device says it has %d but really has %d interfaces\n", ifNumber, count); #endif ifNumber = count; } else { #ifdef DEBUG fprintf(stderr, "Sending another packet\n"); #endif } } else { lastifflag++; if (count > ifNumber) { #ifdef DEBUG fprintf(stderr, "Device says it has %d but really has %d interfaces\n", ifNumber, count); #endif ifNumber = count; } #ifdef DEBUG fprintf(stderr, "%d interfaces found\n", ifNumber); #endif } } else { /* * FAILURE: print what went wrong! */ if (status == STAT_SUCCESS) printf("Error in packet\nReason: %s\n", snmp_errstring(response->errstat)); else if (status == STAT_TIMEOUT) { printf("Timeout while reading interface descriptions from %s\n", session.peername); exit(EXITCODE_TIMEOUT); } else snmp_sess_perror("snmp_bulkget", ss); exit (2); } /* * Clean up: * free the response. */ if (response) { snmp_free_pdu(response); response = 0; } } if (OIDp) { free(OIDp); OIDp = 0; } /* we should have all interface descriptions in our array */ /* now optionally fetch the interface aliases */ if (match_aliases_flag) { lastifflag = 0; count = 0; /* allocate the space for the alias OIDs */ OIDp = (struct OIDStruct *) calloc(1, sizeof(struct OIDStruct)); while (lastifflag==0) { /* build our request depending on the mode */ if (count==0) create_pdu(mode, oid_aliasp, &pdu, &OIDp, 0, ifNumber); else { /* we have not received all aliases in the preceding packet, so fetch the next lot */ if (mode == BINTEC || mode == NONBULK) pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); else { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = 0; pdu->max_repetitions = ifNumber - count; } snmp_add_null_var(pdu, lastOid.name, lastOid.name_len); } #ifdef DEBUG implode_result = implode(", ", oid_aliasp + count); benchmark_start("Send SNMP request for OIDs: %s", implode_result); #endif /* send the request */ status = snmp_synch_response(ss, pdu, &response); #ifdef DEBUG benchmark_end(); free(implode_result); #endif if (sleep_usecs) usleep(sleep_usecs); if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; for (vars = vars; vars; vars = vars->next_variable) { /* * if the next OID is shorter * or if the next OID doesn't begin with our base OID * then we have reached the end of the table :-) * print_variable(vars->name, vars->name_length, vars); */ /* save the OID in case we need additional packets */ memcpy(lastOid.name, vars->name, (vars->name_length * sizeof(oid))); lastOid.name_len = vars->name_length; if ((vars->name_length < OIDp[0].name_len) || (memcmp(OIDp[0].name, vars->name, (vars->name_length - 1) * sizeof(oid)))) { #ifdef DEBUG fprintf(stderr, "reached end of aliases\n"); #endif lastifflag++; break; } /* now we fill our interfaces array with the alias */ if (vars->type == ASN_OCTET_STR) { i = (int) vars->name[(vars->name_length - 1)]; if (i) { MEMCPY(interfaces[count].alias, vars->val.string, vars->val_len); TERMSTR(interfaces[count].alias, vars->val_len); } } count++; } if (count < ifNumber) { if (lastifflag) { #ifdef DEBUG fprintf(stderr, "Device has %d interfaces but only has %d aliases\n", ifNumber, count); #endif } else { #ifdef DEBUG fprintf(stderr, "Sending another packet for aliases\n"); #endif } } else lastifflag++; } else { /* * FAILURE: print what went wrong! */ if (status == STAT_SUCCESS) printf("Error in packet\nReason: %s\n", snmp_errstring(response->errstat)); else if (status == STAT_TIMEOUT) { printf("Timeout while reading interface aliases from %s\n", session.peername); exit(EXITCODE_TIMEOUT); } else snmp_sess_perror("snmp_bulkget", ss); exit (2); } /* * Clean up: * free the response. */ if (response) { snmp_free_pdu(response); response = 0; } } } /* now retrieve the interface values in 2 GET requests * N.B. if the interfaces are continuous we could try * a bulk get instead */ for (j = 0; j < ifNumber; j++) { /* add the interface to the oldperfdata list */ if (interfaces[j].descr) strcpy_nospaces(oldperfdata[j].descr, interfaces[j].descr); if (!interfaces[j].ignore) { /* fetch the standard values first */ if (create_request(ss, &OIDp, oid_vals, interfaces[j].index, &response)) { for (vars = response->variables; vars; vars = vars->next_variable) { k = -1; /* compare the received value to the requested value */ for ( i = 0; oid_vals[i]; i++) { if (!memcmp(OIDp[i].name, vars->name, OIDp[i].name_len*sizeof(oid))) { k = i; break; } } switch(k) /* the offset into oid_vals */ { case 0: /* ifAdminStatus */ if (vars->type == ASN_INTEGER && *(vars->val.integer)==2) { /* ignore interfaces that are administratively down */ interfaces[j].admin_down= 1; ignore_count++; } break; case 1: /*ifOperStatus */ if (vars->type == ASN_INTEGER) /* 1 is up(OK), 5 is dormant(assume OK) */ interfaces[j].status = (*(vars->val.integer)==1 || *(vars->val.integer)==5)?1:0; break; case 2: /* ifInOctets */ if (vars->type == ASN_COUNTER) interfaces[j].inOctets = *(vars->val.integer); break; case 3: /* ifInDiscards */ if (vars->type == ASN_COUNTER) interfaces[j].inDiscards = *(vars->val.integer); break; case 4: /* ifInErrors or locIfInCRC */ if (vars->type == ASN_COUNTER || vars->type == ASN_INTEGER) interfaces[j].inErrors = *(vars->val.integer); break; case 5: /* ifOutOctets */ if (vars->type == ASN_COUNTER) interfaces[j].outOctets = *(vars->val.integer); break; case 6: /* ifOutDiscards */ if (vars->type == ASN_COUNTER) interfaces[j].outDiscards = *(vars->val.integer); break; case 7: /* ifOutErrors or locIfCollisions */ if (vars->type == ASN_COUNTER || vars->type == ASN_INTEGER) interfaces[j].outErrors = *(vars->val.integer); break; } } if (response) { snmp_free_pdu(response); response = 0; } } /* now fetch the extended oids (64 bit counters etc.) */ if (create_request(ss, &OIDp, oid_extended, interfaces[j].index, &response)) { for (vars = response->variables; vars; vars = vars->next_variable) { k = -1; /* compare the received value to the requested value */ for ( i = 0; oid_extended[i]; i++) { if (!memcmp(OIDp[i].name, vars->name, OIDp[i].name_len*sizeof(oid))) { k = i; break; } } switch(k) /* the offset into oid_extended */ { case 0: /* ifHCInOctets */ if (vars->type == ASN_COUNTER64) interfaces[j].inOctets = convertto64((vars->val.counter64), 0); break; case 1: /* ifHCOutOctets */ if (vars->type == ASN_COUNTER64) interfaces[j].outOctets = convertto64((vars->val.counter64), 0); break; case 2: /* ifInUcastPkts */ if (vars->type == ASN_COUNTER) interfaces[j].inUcast = *(vars->val.integer); break; case 3: /* ifOutUcastPkts */ if (vars->type == ASN_COUNTER) interfaces[j].outUcast = *(vars->val.integer); break; case 4: /* ifSpeed */ /* don't overwrite a high-speed value */ if (vars->type == ASN_GAUGE && !(interfaces[j].speed)) interfaces[j].speed = *(vars->val.integer); break; case 5: /* ifHighSpeed */ if (vars->type == ASN_GAUGE) /* convert to bits / sec */ interfaces[j].speed = ((u64)*(vars->val.integer)) * 1000000ULL; break; case 6: /* alias */ if (vars->type == ASN_OCTET_STR) MEMCPY(interfaces[j].alias, vars->val.string, vars->val_len); break; case 7: /* name */ if (vars->type == ASN_OCTET_STR) MEMCPY(interfaces[j].name, vars->val.string, vars->val_len); break; } } if (response) { snmp_free_pdu(response); response = 0; } } /* now fetch the Cisco-specific extended oids */ if (mode == CISCO && create_request(ss, &OIDp, oid_extended_cisco, interfaces[j].index, &response)) { for (vars = response->variables; vars; vars = vars->next_variable) { k = -1; /* compare the received value to the requested value */ for ( i = 0; oid_extended_cisco[i]; i++) { if (!memcmp(OIDp[i].name, vars->name, OIDp[i].name_len*sizeof(oid))) { k = i; break; } } switch(k) /* the offset into oid_extended_cisco */ { case 0: /* portAdditionalOperStatus */ if (vars->type == ASN_OCTET_STR) interfaces[j].err_disable = !!(vars->val.string[1] & (unsigned char)32u); break; } } if (response) { snmp_free_pdu(response); response = 0; } } } } if (list) { /* * a regex was given so we will go through our array * and try and match it with what we received * * count is the number of matches */ count = 0; for (i=0; i < ifNumber; i++) { /* When --if-name is set ignore descr in favor of name, else use old behaviour */ if (get_names_flag) status = !regexec(&re, interfaces[i].name, (size_t) 0, NULL, 0) || (get_aliases_flag && !(regexec(&re, interfaces[i].alias, (size_t) 0, NULL, 0))); else status = !regexec(&re, interfaces[i].descr, (size_t) 0, NULL, 0) || (get_aliases_flag && !(regexec(&re, interfaces[i].alias, (size_t) 0, NULL, 0))); status2 = 0; if (status && exclude_list) { if (get_names_flag) status2 = !regexec(&exclude_re, interfaces[i].name, (size_t) 0, NULL, 0) || (get_aliases_flag && !(regexec(&re, interfaces[i].alias, (size_t) 0, NULL, 0))); else status2 = !regexec(&exclude_re, interfaces[i].descr, (size_t) 0, NULL, 0) || (get_aliases_flag && !(regexec(&exclude_re, interfaces[i].alias, (size_t) 0, NULL, 0))); } if (status && !status2) { count++; #ifdef DEBUG fprintf(stderr, "Interface %d (%s) matched\n", interfaces[i].index, interfaces[i].descr); #endif } else interfaces[i].ignore = 1; } regfree(&re); if (exclude_list) regfree(&exclude_re); if (count) { #ifdef DEBUG fprintf(stderr, "- %d interface%s found\n", count, (count==1)?"":"s"); #endif } else { printf("- no interfaces matched regex"); exit (0); } } /* let the user know about interfaces that are down (and subsequently ignored) */ if (ignore_count) addstr(&out, " - %d %s administratively down", ignore_count, ignore_count!=1?"are":"is"); if (OIDp) { free(OIDp); OIDp = 0; } /* calculate time taken, print perfdata */ gettimeofday(&tv, &tz); if (lastcheck) lastcheck=(starttime - lastcheck); /* do not use old perfdata if the device has been reset recently * Note that a switch will typically rollover the uptime counter every 497 days * which is infrequent enough to not bother about :-) * UPTIME_TOLERANCE_IN_SECS doesn't need to be a big number */ if ((lastcheck + UPTIME_TOLERANCE_IN_SECS) > uptime) lastcheck = 0; if (oldperfdatap && lastcheck && oldperfdatap[0]) parse_perfdata(oldperfdatap, oldperfdata, prefix); for (i=0;i (oldperfdata[i].inErrors + (unsigned long) err_tolerance) || interfaces[i].outErrors > (oldperfdata[i].outErrors + (unsigned long) coll_tolerance)) ) { if (oldperfdatap && !interfaces[i].ignore) { if (get_names_flag && strlen(interfaces[i].name)) addstr(&perf, "[WARNING] %s", interfaces[i].name); else addstr(&perf, "[WARNING] %s", interfaces[i].descr); if (get_aliases_flag && strlen(interfaces[i].alias)) addstr(&perf, " (%s) has", interfaces[i].alias); else addstr(&perf, " has"); /* if we are not in cisco mode simply use "errors" */ if (mode != CISCO) addstr(&perf, " errors"); else { if (interfaces[i].inErrors > (oldperfdata[i].inErrors + (unsigned long) err_tolerance)) addstr(&perf, " CRC errors"); if (interfaces[i].outErrors > (oldperfdata[i].outErrors + (unsigned long) coll_tolerance)) addstr(&perf, " collisions"); } if (get_names_flag && strlen(interfaces[i].name)) addstr(&out, ", %s has %lu errors", interfaces[i].name, (interfaces[i].inErrors + interfaces[i].outErrors - oldperfdata[i].inErrors - oldperfdata[i].outErrors)); else addstr(&out, ", %s has %lu errors", interfaces[i].descr, (interfaces[i].inErrors + interfaces[i].outErrors - oldperfdata[i].inErrors - oldperfdata[i].outErrors)); warnflag++; warn++; } } if (lastcheck && (interfaces[i].speed || speed)) { inbitps = (subtract64(interfaces[i].inOctets, oldperfdata[i].inOctets) / (u64)lastcheck) * 8ULL; outbitps = (subtract64(interfaces[i].outOctets, oldperfdata[i].outOctets) / (u64)lastcheck) * 8ULL; if (speed) { inload = (long double)inbitps / ((long double)speed/100L); outload = (long double)outbitps / ((long double)speed/100L); } else { /* use the interface speed if a speed is not given */ inload = (long double)inbitps / ((long double)interfaces[i].speed/100L); outload = (long double)outbitps / ((long double)interfaces[i].speed/100L); } if ( (bw > 0) && ((int)inload > bw || (int)outload > bw)) warn++; } if (interfaces[i].status && !interfaces[i].ignore) { if (!(warn)) addstr(&perf, "[OK]"); else addstr(&perf, "[WARNING]"); if (get_names_flag && strlen(interfaces[i].name)) addstr(&perf, " %s is up", interfaces[i].name); else addstr(&perf, " %s is up", interfaces[i].descr); } if (lastcheck && (interfaces[i].speed || speed) && (inbitps > 0ULL || outbitps > 0ULL)) { gauge_to_si(inbitps, &ins); gauge_to_si(outbitps, &outs); addstr(&perf, " %sbps(%0.2f%%)/%sbps(%0.2f%%)", ins, inload, outs, outload); free(ins); free(outs); } if (perf.len > 0u && perf.text[(perf.len - 1u)] != '\n') { addstr(&perf, "\n"); } } } if (errorflag) printf("CRITICAL:"); else if (warnflag) printf("WARNING:"); else printf("OK:"); #ifdef DEBUG fprintf(stderr, " %d interfaces found", ifNumber); if(list) printf(", of which %d matched the regex. ", count); #else if(list) printf(" %d interface%s found", count, (count==1)?"":"s"); else printf(" %d interface%s found", ifNumber, (ifNumber==1)?"":"s"); #endif /* now print performance data */ printf("%*s | interfaces::check_multi::plugins=%d time=%.2Lf", (int)out.len, out.text, (count - ignore_count), (((long double)tv.tv_sec + ((long double)tv.tv_usec/1000000)) - starttime )); if (uptime) printf(" %sdevice::check_snmp::uptime=%us", prefix?prefix:"", uptime); for (i=0;ihigh) << 32) + val64->low; return (temp64); } u64 subtract64(u64 big64, u64 small64) { if (big64 < small64) { /* either the device was reset or the counter overflowed */ if ((lastcheck + UPTIME_TOLERANCE_IN_SECS) > uptime) /* the device was reset, or the uptime counter rolled over * so play safe and return 0 */ return 0; else { /* we assume there was exactly 1 counter rollover * - of course there may have been more than 1 if it * is a 32 bit counter ... */ if (small64 > OFLO32) return (OFLO64 - small64 + big64); else return (OFLO32 - small64 + big64); } } else return (big64 - small64); } netsnmp_session *start_session(netsnmp_session *session, char *community, char *hostname) { netsnmp_session *ss; /* * Initialize the SNMP library */ init_snmp("snmp_bulkget"); /* setup session to hostname */ snmp_sess_init(session); session->peername = hostname; /* bulk gets require V2c or later */ if (mode == NONBULK) session->version = SNMP_VERSION_1; else session->version = SNMP_VERSION_2c; session->community = (u_char *)community; session->community_len = strlen(community); session->timeout = global_timeout; session->retries = session_retries; /* * Open the session */ SOCK_STARTUP; ss = snmp_open(session); /* establish the session */ if (!ss) { snmp_sess_perror("snmp_bulkget", session); SOCK_CLEANUP; exit(1); } return(ss); } netsnmp_session *start_session_v3(netsnmp_session *session, char *user, char *auth_proto, char *auth_pass, char *priv_proto, char *priv_pass, char *hostname) { netsnmp_session *ss; init_snmp("snmp_bulkget"); snmp_sess_init(session); session->peername = hostname; session->version = SNMP_VERSION_3; session->securityName = user; session->securityModel = SNMP_SEC_MODEL_USM; session->securityNameLen = strlen(user); if (priv_proto && priv_pass) { if (!strcmp(priv_proto, "AES")) { session->securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN); session->securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; } else if (!strcmp(priv_proto, "DES")) { session->securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN); session->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; } else { printf("Unknown priv protocol %s\n", priv_proto); exit(3); } session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; session->securityPrivKeyLen = USM_PRIV_KU_LEN; } else { session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; session->securityPrivKeyLen = 0; } if (auth_proto && auth_pass) { if (!strcmp(auth_proto, "SHA")) { session->securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else if (!strcmp(auth_proto, "MD5")) { session->securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN); session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; } else { printf("Unknown auth protocol %s\n", auth_proto); exit(3); } session->securityAuthKeyLen = USM_AUTH_KU_LEN; } else { session->securityLevel = SNMP_SEC_LEVEL_NOAUTH; session->securityAuthKeyLen = 0; session->securityPrivKeyLen = 0; } if ((session->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) || (session->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV)) { if(generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (unsigned char *)auth_pass, strlen(auth_pass), session->securityAuthKey, &session->securityAuthKeyLen) != SNMPERR_SUCCESS) printf("Error generating AUTH sess\n"); if (session->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (unsigned char *)priv_pass, strlen(priv_pass), session->securityPrivKey, &session->securityPrivKeyLen) != SNMPERR_SUCCESS) printf("Error generating PRIV sess\n"); } } session->timeout = global_timeout; session->retries = session_retries; /* * Open the session */ SOCK_STARTUP; ss = snmp_open(session); /* establish the session */ if (!ss) { snmp_sess_perror("snmp_bulkget", session); SOCK_CLEANUP; exit(1); } return(ss); } int usage(char *progname) { int i; printf( #ifdef PACKAGE_STRING PACKAGE_STRING "\n\n" #endif "Usage: %s -h [OPTIONS]\n", progname); printf(" -c|--community\t\tcommunity (default public)\n"); printf(" -r|--regex\t\tinterface list regexp\n"); printf(" -R|--exclude-regex\tinterface list negative regexp\n"); printf(" -e|--errors\t\tnumber of in errors (CRC errors for cisco) to consider a warning (default 50)\n"); printf(" -f|--out-errors\tnumber of out errors (collisions for cisco) to consider a warning (default same as in errors)\n"); printf(" -p|--perfdata\t\tlast check perfdata\n"); printf(" -P|--prefix\t\tprefix interface names with this label\n"); printf(" -t|--lastcheck\t\tlast checktime (unixtime)\n"); printf(" -b|--bandwidth\t\tbandwidth warn level in %%\n"); printf(" -s|--speed\t\toverride speed detection with this value (bits per sec)\n"); printf(" -x|--trim\t\tcut this number of characters from the start of interface descriptions\n"); printf(" -m|--mode\t\tspecial operating mode ("); for (i=0; modes[i]; i++) { printf("%s%s", i?",":"", modes[i]); } printf(")\n"); #ifdef INDEXES printf(" -i|--interfaces\t\tinterface list\n"); #endif /* INDEXES */ printf(" -j|--auth-proto\tSNMPv3 Auth Protocol (SHA|MD5)\n"); printf(" -J|--auth-phrase\tSNMPv3 Auth Phrase\n"); printf(" -k|--priv-proto\tSNMPv3 Privacy Protocol (AES|DES)\n"); printf(" -K|--priv-phrase\tSNMPv3 Privacy Phrase\n"); printf(" -u|--user\t\tSNMPv3 User\n"); printf(" -d|--down-is-ok\tdisables critical alerts for down interfaces\n"); printf(" -a|--aliases\t\tretrieves the interface description\n"); printf(" -A|--match-aliases\talso match against aliases (Option -a automatically enabled)\n"); printf(" -D|--debug-print\tlist administrative down interfaces in perfdata\n"); printf(" -N|--if-names\t\tuse ifName instead of ifDescr\n"); printf(" --timeout\t\tsets the SNMP timeout (in ms)\n"); printf(" --sleep\t\tsleep between every SNMP query (in ms)\n"); printf(" --retries\t\thow often to retry before giving up\n"); printf(" --max-repetitions\t\tsee \n"); printf("\n"); return 3; } /* * tokenize a string containing performance data and fill a struct with * the individual variables * * e.g. interfaces::check_multi::plugins=2 time=0.07 11::check_snmp::inOctets=53273084427c outOctets=6370502528c inDiscards=0c outDiscards=3921c inErrors=3c outErrors=20165c inUcast=38550136c outUcast=21655535c speed=100000000 21::check_snmp::inOctets=5627677780c outOctets=15023959911c inDiscards=0c outDiscards=0c inErrors=0c outErrors=5431c inUcast=34020897c outUcast=35875426c speed=1000000000 */ int parse_perfdata(char *oldperfdatap, struct ifStruct *oldperfdata, char *prefix) { char *last=0, *last2=0, *word, *interface=0, *var; char *ptr; #ifdef DEBUG int plugins; int uptime_old; #endif u64 value=0; char *valstr; /* first split at spaces */ for ( word = strtok_r(oldperfdatap, " ", &last); word; word = strtok_r(NULL, " ", &last)) { if((ptr = strstr(word, "::check_multi::plugins="))) { #ifdef DEBUG /* check multi perfdata found */ plugins = strtol(strchr(word, '=') + 1, NULL, 10); fprintf(stderr, "Found %d plugins\n", plugins); #endif continue; } if((ptr = strstr(word, "device::check_snmp::"))) { #ifdef DEBUG /* uptime found */ uptime_old = strtol(strchr(word, '=') + 1, NULL, 10); fprintf(stderr, "Found %u uptime\n", uptime_old); #endif continue; } if((ptr = strstr(word, "::check_snmp::"))) { /* new interface found, get its name (be aware that this is the "cleaned" string */ interface = strtok_r(word, ":", &last2); /* remove any prefix */ if (prefix) { if (strlen(interface)>strlen(prefix)) interface = interface + strlen(prefix); } #ifdef DEBUG if (interface) fprintf(stderr, "interface %s found\n", interface); #endif word = (ptr + strlen("::check_snmp::")); } /* finally split the name=value pair */ valstr = strchr(word, '='); if (valstr) value = strtoull(valstr + 1, NULL, 10); var = strtok_r(word, "=", &last2); if (interface && var && valstr) set_value(oldperfdata, interface, var, value, valstr + 1); } return (0); } /* * fill the ifStruct with values */ void set_value(struct ifStruct *oldperfdata, char *interface, char *var, u64 value, char *valstr) { int i; static char **if_vars; if (mode == CISCO) if_vars = if_vars_cisco; else if_vars = if_vars_default; for (i=0; i < ifNumber; i++) { if (strcmp(interface, oldperfdata[i].descr) == 0) { if (strcmp(var, if_vars[0]) == 0) oldperfdata[i].inOctets = value; else if (strcmp(var, if_vars[1]) == 0) oldperfdata[i].outOctets = value; else if (strcmp(var, if_vars[2]) == 0) oldperfdata[i].inDiscards = value; else if (strcmp(var, if_vars[3]) == 0) oldperfdata[i].outDiscards = value; else if (strcmp(var, if_vars[4]) == 0) oldperfdata[i].inErrors = value; else if (strcmp(var, if_vars[5]) == 0) oldperfdata[i].outErrors = value; else if (strcmp(var, if_vars[6]) == 0) oldperfdata[i].inUcast = value; else if (strcmp(var, if_vars[7]) == 0) oldperfdata[i].outUcast = value; else if (strcmp(var, if_vars[8]) == 0) oldperfdata[i].speed = value; continue; } } } /* * pass this function a list of OIDs to retrieve * and it will fetch them with a single get */ int create_request(netsnmp_session *ss, struct OIDStruct **OIDpp, char **oid_list, int index, netsnmp_pdu **response) { netsnmp_pdu *pdu; int status, i; struct OIDStruct *OIDp; /* store all the parsed OIDs in a structure for easy comparison */ for (i = 0; oid_list[i]; i++); OIDp = (struct OIDStruct *) calloc(i, sizeof(*OIDp)); /* here we are retrieving single values, not walking the table */ pdu = snmp_pdu_create(SNMP_MSG_GET); for (i = 0; oid_list[i]; i++) { #ifdef DEBUG2 fprintf(stderr, "%d: adding %s\n", i, oid_list[i]); #endif OIDp[i].name_len = MAX_OID_LEN; parseoids(i, oid_list[i], OIDp); OIDp[i].name[OIDp[i].name_len++] = index; snmp_add_null_var(pdu, OIDp[i].name, OIDp[i].name_len); } pdu->non_repeaters = i; pdu->max_repetitions = 0; *OIDpp = OIDp; #ifdef DEBUG implode_result = implode(", ", oid_list); benchmark_start("Send SNMP request for OIDs: %s", implode_result); #endif status = snmp_synch_response(ss, pdu, response); #ifdef DEBUG benchmark_end(); free(implode_result); #endif if (sleep_usecs) usleep(sleep_usecs); if (status == STAT_SUCCESS && (*response)->errstat == SNMP_ERR_NOERROR) { return(1); } else if (status == STAT_SUCCESS && (*response)->errstat == SNMP_ERR_NOSUCHNAME) { /* if e.g. 64 bit counters are not supported, we will get this error */ return(1); } else { /* * FAILURE: print what went wrong! */ if (status == STAT_SUCCESS) printf("Error in packet\nReason: %s\n", snmp_errstring((*response)->errstat)); else if (status == STAT_TIMEOUT) { printf("Timeout fetching interface stats from %s ", ss->peername); for (i = 0; oid_list[i]; i++) { printf("%c%s", i?',':'(', oid_list[i]); } printf(")\n"); exit(EXITCODE_TIMEOUT); } else { printf("other error\n"); snmp_sess_perror("snmp_bulkget", ss); } exit(2); } return(0); } int parseoids(int i, char *oid_list, struct OIDStruct *query) { /* parse oid list * * read each OID from our array and add it to the pdu request */ query[i].name_len = MAX_OID_LEN; if (!snmp_parse_oid(oid_list, query[i].name, &query[i].name_len)) { snmp_perror(oid_list); SOCK_CLEANUP; exit(1); } return(0); } void create_pdu(int mode, char **oidlist, netsnmp_pdu **pdu, struct OIDStruct **oids, int nonrepeaters, long max) { int i; static char **oid_ifp; if (mode == NONBULK) *pdu = snmp_pdu_create(SNMP_MSG_GET); else if (mode == BINTEC) { /* we cannot use a bulk get for bintec * and the oids don't increment properly */ *pdu = snmp_pdu_create(SNMP_MSG_GET); } else { /* get the ifNumber and as many interfaces as possible */ *pdu = snmp_pdu_create(SNMP_MSG_GETBULK); (*pdu)->non_repeaters = nonrepeaters; (*pdu)->max_repetitions = max; } for (i = 0; oidlist[i]; i++) { parseoids(i, oidlist[i], *oids); snmp_add_null_var(*pdu, (*oids)[i].name, (*oids)[i].name_len); } }