1589 lines
55 KiB
C
1589 lines
55 KiB
C
/*
|
|
*
|
|
* COPYRIGHT:
|
|
*
|
|
* This software is Copyright (c) 2011,2012 NETWAYS GmbH, William Preston
|
|
* <support@netways.de>
|
|
*
|
|
* (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 <stdio.h>
|
|
|
|
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <regex.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
|
|
/* getenv */
|
|
#include <stdlib.h>
|
|
|
|
/* getopt_long */
|
|
#include <getopt.h>
|
|
|
|
#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<ifNumber;i++) {
|
|
if (interfaces[i].descr && !interfaces[i].ignore) {
|
|
int warn = 0;
|
|
|
|
if ((!interfaces[i].status || interfaces[i].err_disable) && !interfaces[i].ignore && !interfaces[i].admin_down) {
|
|
if (crit_on_down_flag) {
|
|
addstr(&perf, "[CRITICAL] ");
|
|
errorflag++;
|
|
/* show the alias if configured */
|
|
if (get_names_flag && strlen(interfaces[i].name)) {
|
|
addstr(&out, ", %s", interfaces[i].name);
|
|
addstr(&perf, "%s is down", interfaces[i].name);
|
|
} else {
|
|
addstr(&out, ", %s", interfaces[i].descr);
|
|
addstr(&perf, "%s is down", interfaces[i].descr);
|
|
}
|
|
if (interfaces[i].err_disable)
|
|
addstr(&perf, " (errdisable)");
|
|
if (!interfaces[i].admin_down) {
|
|
if (get_aliases_flag && strlen(interfaces[i].alias))
|
|
addstr(&out, " (%s) down", interfaces[i].alias);
|
|
else
|
|
addstr(&out, " down");
|
|
if (interfaces[i].err_disable)
|
|
addstr(&out, " (errdisable)");
|
|
}
|
|
} else {
|
|
addstr(&perf, "[OK] ");
|
|
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);
|
|
}
|
|
} else if (interfaces[i].admin_down && print_all_flag) {
|
|
addstr(&perf, "[OK] %s is down (administrative down)",
|
|
(get_names_flag && strlen(interfaces[i].name)) ? interfaces[i].name : interfaces[i].descr);
|
|
}
|
|
|
|
/* check if errors on the interface are increasing faster than our defined value */
|
|
else if (oldperfdata[i].inErrors && oldperfdata[i].outErrors &&
|
|
(interfaces[i].inErrors > (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;i<ifNumber;i++) {
|
|
if (interfaces[i].descr && !interfaces[i].ignore && (!interfaces[i].admin_down || print_all_flag)) {
|
|
printf(" %s%s::check_snmp::", prefix?prefix:"", get_names_flag ? interfaces[i].name : oldperfdata[i].descr);
|
|
printf("%s=%lluc %s=%lluc", if_vars[0], interfaces[i].inOctets, if_vars[1], interfaces[i].outOctets);
|
|
printf(" %s=%luc %s=%luc", if_vars[2], interfaces[i].inDiscards, if_vars[3], interfaces[i].outDiscards);
|
|
printf(" %s=%luc %s=%luc", if_vars[4], interfaces[i].inErrors, if_vars[5], interfaces[i].outErrors);
|
|
printf(" %s=%luc %s=%luc", if_vars[6], interfaces[i].inUcast, if_vars[7], interfaces[i].outUcast);
|
|
if (speed)
|
|
printf(" %s=%llu", if_vars[8], speed);
|
|
else
|
|
printf(" %s=%llu", if_vars[8], interfaces[i].speed);
|
|
}
|
|
}
|
|
printf("\n%*s", (int)perf.len, perf.text);
|
|
|
|
#ifdef DEBUG
|
|
benchmark_start("Close SNMP session");
|
|
#endif
|
|
snmp_close(ss);
|
|
#ifdef DEBUG
|
|
benchmark_end();
|
|
#endif
|
|
|
|
SOCK_CLEANUP;
|
|
return ((errorflag)?2:((warnflag)?1:0));
|
|
}
|
|
|
|
|
|
void print64(struct counter64 *count64, unsigned long *count32)
|
|
{
|
|
|
|
if (!(isZeroU64(count64))) {
|
|
char buffer[I64CHARSZ+1];
|
|
printU64(buffer, count64);
|
|
#ifdef DEBUG
|
|
printf("64:%s", buffer);
|
|
#else
|
|
printf("%s", buffer);
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUG
|
|
printf("32:%lu", *count32);
|
|
#else
|
|
printf("%lu", *count32);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
u64 convertto64(struct counter64 *val64, unsigned long *val32)
|
|
{
|
|
u64 temp64;
|
|
|
|
if ((isZeroU64(val64)))
|
|
{
|
|
if (val32)
|
|
temp64 = (u64)(*val32);
|
|
else
|
|
temp64 = 0;
|
|
}
|
|
else
|
|
temp64 = ((u64)(val64->high) << 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 <hostname> [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 <http://www.net-snmp.org/docs/man/snmpbulkwalk.html>\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);
|
|
}
|
|
}
|