/*
 *  check_snmp_interface_status . A Nagios plugin to check the status of an network interface
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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; see the file COPYING. If not, write to the
 *  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include "check_snmp_interface_status.h"

#define VERSION "1.1"
#define NETSNMP_DS_WALK_INCLUDE_REQUESTED		1
#define NETSNMP_DS_WALK_PRINT_STATISTICS		2
#define NETSNMP_DS_WALK_DONT_CHECK_LEXICOGRAPHIC	3

oid             objid_mib_ifdescr[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 2 };
oid             objid_mib_ifname[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 1 };
oid             objid_mib_ifalias[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 18 };

int             numprinted = 0;
int             reps = 10, non_reps = 0;

/*
 OID description:

ifOperStatus OBJECT-TYPE
              SYNTAX  INTEGER {
                          up(1),
                          down(2),
                          testing(3)
                      }
              ACCESS  read-only
              STATUS  mandatory
              DESCRIPTION
                      "The current operational state of the interface.
                      The testing(3) state indicates that no operational
                      packets can be passed."
              ::= { ifEntry 8 }

*/

/*
ifAdminStatus OBJECT-TYPE
              SYNTAX  INTEGER {
                          up(1),       
                          down(2),
                          testing(3)   
                      }
              ACCESS  read-write
              STATUS  mandatory
              DESCRIPTION
                      "The desired state of the interface.  The
                      testing(3) state indicates that no operational
                      packets can be passed."
              ::= { ifEntry 7 }
*/

/*
 * usage function : print the help
 *
 */

void usage( void )
{
  fprintf( stderr, "USAGE:check_snmp_interface " );
  fprintf( stderr, " -H HOST -C COMMUNITY -k interface number (Integer)\n\n" );
  fprintf( stderr,
       " Options :\n"
       "  -H HOST\tHostname/IP to query\n"
       "  -C COMMUNITY\tSNMP community name\n"
       "  -k Interface\tifNumber Value\n"
       "  -d Interface\tifDescr value\n"
       "  -n Interface\tifName value\n"
       "  -o Interface\tifAlias value\n"
       "  -w \t\tGenerate Warning if interface is down\n"
       "  -f \t\tOutput Performance Data\n"
       "  -a PROTOCOL\tset authentication protocol (MD5|SHA1|SHA224|SHA256|SHA384|SHA512)\n"
       "  -A PASSPHRASE\tset authentication protocol pass phrase\n"
       "  -L LEVEL\tset security level (noAuthNoPriv|authNoPriv|authPriv)\n"
       "  -U USER-NAME\tset security name (e.g. SNMPv3User)\n"
       //"  -P PROTOCOL\tset privacy protocol (AES|AES192|AES192C|AES256|AES256C|AES192C2|AES256C2)\n"
       "  -P PROTOCOL\tset privacy protocol (AES|AES192|AES192C|AES256|AES256C)\n"
       "  -X PASSPHRASE\tset privacy protocol pass phrase\n"
       "  -t Timeout \tseconds before the plugin times out (default=6)\n"
       "  -p port\tSNMP port (default 161)\n"
       "  -D Admin Down\ti|w|c ignore(OK)|warn|crit if the interface is Admin Down(default critical)\n"
       "  -h -?\t\tPrint this help\n"
       "  -V \t\tPrint Version\n"
       "  -v VERSION\tSNMP VERSION=[1|2|3], (2 for SNMP v2c)(2 is default)\n" );
}

/*
 * main function : -> parse command line args
 */
int main( int argc, char *argv[] )
{
  int opt;
  int exitcode = UNKNOWN;
  char *community = NULL;
  char *hostname = NULL;
  char *interfacenumber = NULL;
  int version = SNMP_VERSION_2c;

  /* Print the help if not arguments provided */ 
  if( argc==1 )
    {
      usage();
      return( UNKNOWN );
    }
  /*
   * get the common command line arguments 
   */

  while( (opt = getopt(argc, argv, "?hVwfU:t:k:C:H:v:L:a:P:A:X:p:D:d:n:o:") ) != -1 )
  {
    switch( opt )
    {
    case '?':
    case 'h':
      /* print help */
      usage();
      exit(UNKNOWN);

    case 'V':
      print_version();
      exit( UNKNOWN );

    case 't':
      /* Timeout */
      if( !is_integer(optarg) )
        {
          printf( "Timeout interval (%s)must be integer!\n", optarg );
          exit( UNKNOWN );
        }

      timeout = atoi( optarg );
      break;

      case 'p':
      /* SNMP Port */
      if( !is_integer(optarg) )
      {
        printf("Port (%s)must be integer!\n", optarg);
        exit(UNKNOWN);
      }

      port = strdup( optarg );
      break;

    case 'C':
      /* SNMP Community */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The Community String is required for this option.\n" );
        exit( UNKNOWN );
      }
      else
        community = strdup(optarg);
      break;

    case 'H':
      /* SNMP Hostname */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The Hostname or IP address is required for this option\n" );
        exit( UNKNOWN );
      }
      else
        hostname = strdup(optarg);
      break;

    case 'k':
      /* Parse the interface number which tell the program what to check */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The ifNumber is required for this option\n" );
        exit( UNKNOWN );
      }
      else
      {
        intstruct.interfacenumber = strdup(optarg);
        ifDescrName = strdup( "" );
        ifnumber = true;
      }
      break;

    case 'v':
      /* SNMP Version */
      if( strcmp(optarg, "2") == 0 )
        {
          version = SNMP_VERSION_2c;
        }
      else if( strcmp(optarg, "1") == 0 )
        {
          version = SNMP_VERSION_1;
        }
      else if( strcmp(optarg, "3") == 0 )
        {
          version = SNMP_VERSION_3;
        }
      else
        {
          printf( "Incorrect SNMP Version specified.\n" );
          exit( UNKNOWN );
        }
      break;
      
      case 'L':
      /* set the security level */
      if ( strcmp( optarg, "authPriv" ) == 0 )
        {
          securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
        }
      else if( strcmp( optarg, "authNoPriv" ) == 0 )
        {
          securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
        }
      else if( strcmp( optarg, "noAuthNoPriv" ) == 0 )
        {
          securityLevel = SNMP_SEC_LEVEL_NOAUTH;
        }
      else
        {
          printf( "Incorrect SNMP security level (noAuthNoPriv|authNoPriv|authPriv).\n" );
          exit( UNKNOWN );
        }
      break;

    case 'w':
      Warn = true;
      break;

    case 'f':
      Perfdata = true;
      break;

    case 'D':
      if( strcmp( optarg, "w" ) == 0 )
        {
          adminStatus = WARNING;
        }
      else if( strcmp(optarg, "c") == 0 )
        {
          adminStatus = CRITICAL;
        }
      else if( strcmp(optarg, "i") == 0 )
        {
          adminStatus = OK;
        }
      else
        {
          printf( "Incorrect Admin Down option i|w|c \n" );
          exit( UNKNOWN );
        }
      break;
      
    case 'U':
      /* SNMP User Name */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The user name is required for SNMPv3\n" );
          exit( UNKNOWN );
      }
      else
        securityName = strdup( optarg );
      break;
      
    case 'a':
      /* SNMP 3 Authenication Protocol */
      if( strcmp( optarg, "MD5" ) == 0 )
        {
          authProtocol = 1;
        }
      else if( strcmp(optarg, "SHA1") == 0 )
        {
          authProtocol = 2;
        }
      else if( strcmp(optarg, "SHA512") == 0 )
        {
          authProtocol = 3;
        }
      else if( strcmp(optarg, "SHA224") == 0 )
        {
          authProtocol = 4;
        }
      else if( strcmp(optarg, "SHA256") == 0 )
        {
          authProtocol = 5;
        }
      else if( strcmp(optarg, "SHA384") == 0 )
        {
          authProtocol = 6;
        }
      else
        {
          printf( "Incorrect SNMP Authenication Protocol specified (MD5|SHA1).\n" );
          exit( UNKNOWN );
        }
      break;

    case 'A':
      /* SNMP 3 Authenication Password */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The Authenication Password is required for SNMPv3\n" );
        exit( UNKNOWN );
      }
      else
        our_v3_passphrase = strdup(optarg);
      break;

      case 'P':
      /* SNMP 3 Privacy Protocol */
      if (strcmp(optarg, "AES256") == 0)
        {
          privProtocol = 1;
        }
      else if (strcmp(optarg, "AES") == 0 || strcmp(optarg, "AES128") == 0 || strcmp(optarg, "AES-128") == 0)
        {
          privProtocol = 2;
        }
      else if (strcmp(optarg, "AES192") == 0)
        {
          privProtocol = 3;
        }
      else if (strcmp(optarg, "AES192C") == 0)
        {
          privProtocol = 4;
        }
      else if (strcmp(optarg, "AES256C") == 0)
        {
          privProtocol = 5;
        }
      /*else if (strcmp(optarg, "AES192C2") == 0)
        {
          privProtocol = 6;
        }
      else if (strcmp(optarg, "AES256C2") == 0)
        {
          privProtocol = 7;
        }*/
      else
        {
          printf( "Incorrect SNMP Privacy Protocol specified (DES|AES).\n" );
          exit( UNKNOWN );
        }
      break;
      
    case 'X':
      /* SNMP 3 Privacy Password */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The Privacy Password is required for SNMPv3\n" );
        exit( UNKNOWN );
      }
      else
        our_v3_priv_passphrase = strdup(optarg);
      break;

    case 'd':
      /* SNMP ifDescr Name */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The ifDescr is required for this option.\n" );
        exit( UNKNOWN );
      }
      else
      {
        ifDescrName = strdup( optarg );
        ifdescr = true;
        intstruct.interfacenumber = strdup( "" );
      }
      break;

    case 'n':
      /* SNMP ifName Name */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The ifName is required for this option.\n" );
        exit( UNKNOWN );
      }
      else
      {
        ifDescrName = strdup( optarg );
        ifname = true;
        intstruct.interfacenumber = strdup( "" );
      }
      break;

    case 'o':
      /* SNMP ifAlias Name */
      if( strncmp( optarg, "-", 1 ) == 0 )
      {
        printf( "The ifAlias is required for this option.\n" );
        exit( UNKNOWN );
      }
      else
      {
        ifDescrName = strdup( optarg );
        ifalias = true;
        intstruct.interfacenumber = strdup( "" );
      }
      break;

    }
  }
  
  //Cannot have more than one of -d, -n, -k, -o at the same time
  int ref_count = (ifdescr ? 1 : 0) + (ifname ? 1 : 0) + (ifnumber ? 1 : 0) + (ifalias ? 1 : 0);
  if( ref_count > 1 )
  {
    printf( "Cannot use ifDescr (-d) or ifName (-n) or ifNumber (-k) or ifAlias (-o) at the same time. Only one option is allowed.\n\n" );
    usage( );
    exit( UNKNOWN );
  }

  //Need to have an option -d or -k or -n or -o on the command line
  if( ref_count == 0 )
  {
    printf( "Need to add one option ifDescr (-d) or ifName (-n) or ifNumber (-k) or ifAlias (-o) on the command line.\n\n" );
    usage( );
    exit( UNKNOWN );
  }

  // Validate required parameters
  if( hostname == NULL )
  {
    printf( "Hostname (-H) is required.\n\n" );
    usage( );
    exit( UNKNOWN );
  }

  if( version != SNMP_VERSION_3 && community == NULL )
  {
    printf( "Community string (-C) is required for SNMPv1/v2c.\n\n" );
    usage( );
    exit( UNKNOWN );
  }

  if( version == SNMP_VERSION_3 && securityName == NULL )
  {
    printf( "Security name (-U) is required for SNMPv3.\n\n" );
    usage( );
    exit( UNKNOWN );
  }

  // Run the SNMP check
  exitcode = checkInterface( hostname, community, intstruct.interfacenumber, version, ifDescrName );

  free(community);
  free(hostname);

  if( exitcode != OK )
    return exitcode;

  exitcode = check_and_print( );

  return exitcode;
}

int checkInterface( char *host, char *community, char *interface, int version, char *interfacename )
{

    netsnmp_session session, *ss;
    netsnmp_pdu *pdu;
    netsnmp_pdu *response;
    netsnmp_variable_list *vars;

    oid anOID[MAX_OID_LEN];
    size_t anOID_len;
    oid             name[MAX_OID_LEN];
    size_t          name_length;
    oid             root[MAX_OID_LEN];
    size_t          rootlen;

    int status;
    int count=1;
    int int_status;
    int index=0;
    int exitval = OK;
    int running;
    int check;
    char oid_str[512];
    char *hostport;
    char *indexnumber = NULL;

    /*
     * Initialize the SNMP library
     */
    init_snmp("check_snmp_interface_status");

    /*
     * Initialize a "session" that defines who we're going to talk to
     */
    snmp_sess_init( &session );                   /* set up defaults */
    // The name of the Host:port that we are testing.
    size_t hostport_len = strlen( host ) + 1 + strlen( port ) + 1;
    hostport = malloc( hostport_len );
    snprintf( hostport, hostport_len, "%s:%s", host, port );
    session.peername =  hostport;

    // Set the session timeout. Adjusted becaust the default retry is 6 to it takes 6 times longer than the define of ONE_SEC
    if( timeout )
      session.timeout = timeout * 166666;

    /* set up the authentication parameters for talking to the server */
    if( version == SNMP_VERSION_3 )
    {

    /* Use SNMPv3 to talk to the remote server */

    /* set the SNMP version number */
    session.version=SNMP_VERSION_3;

    if( securityLevel == SNMP_SEC_LEVEL_AUTHPRIV )
      {
        session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
      }
    else if( securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV )
      {
        session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
      }
    else if( securityLevel == SNMP_SEC_LEVEL_NOAUTH )
      {
        session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
      }
        
    /* set the SNMPv3 user name */
    session.securityName = strdup( securityName );
    session.securityNameLen = strlen(session.securityName);

    if( ( securityLevel == SNMP_SEC_LEVEL_AUTHPRIV ) || ( securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV ) )
    {

      /* set the Authentication method  */
      if( authProtocol == 1 )
      {
#ifndef NETSNMP_DISABLE_MD5
        session.securityAuthProto = usmHMACMD5AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
#else
        fprintf(stderr, "MD5 auth protocol not available in this net-snmp build.\n");
        exit(UNKNOWN);
#endif
      }
      else if( authProtocol == 2 )
      {
        session.securityAuthProto = usmHMACSHA1AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
      }
      else if( authProtocol == 3 )
      {
        session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
      }
      else if( authProtocol == 4 )
      {
        session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
      }
      else if( authProtocol == 5 )
      {
        session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
      }
      else if( authProtocol == 6 )
      {
        session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
        session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol);
        session.securityAuthKeyLen = USM_AUTH_KU_LEN;
      }

      /* set the authentication key to a hashed version of our passphrase (which must be at least 8 characters long) */
      if (generate_Ku(session.securityAuthProto,
                      session.securityAuthProtoLen,
                      (u_char *) our_v3_passphrase, strlen(our_v3_passphrase),
                      session.securityAuthKey,
                      &session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
          fprintf(stderr, "Error generating Ku from authentication pass phrase. \n");
          exit(1);
      }

      if( securityLevel == SNMP_SEC_LEVEL_AUTHPRIV )
      {
        /* set the Privacy method  */
        if( privProtocol == 1 )
        {
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
           session.securityPrivProto = usmAES256PrivProtocol;
           session.securityPrivProtoLen = OID_LENGTH(usmAES256PrivProtocol);
           session.securityPrivKeyLen = USM_PRIV_KU_LEN;
#else
           fprintf(stderr, "AES-256 priv protocol not available in this net-snmp build.\n");
           exit(UNKNOWN);
#endif
        }
        else if( privProtocol == 2 )
        {
           session.securityPrivProto = usmAESPrivProtocol;
           session.securityPrivProtoLen = OID_LENGTH(usmAESPrivProtocol);
           session.securityPrivKeyLen = USM_PRIV_KU_LEN;
        }
        else if( privProtocol == 3 )
        {
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
           session.securityPrivProto = usmAES192PrivProtocol;
           session.securityPrivProtoLen = OID_LENGTH(usmAES192PrivProtocol);
           session.securityPrivKeyLen = USM_PRIV_KU_LEN;
#else
           fprintf(stderr, "AES-192 priv protocol not available in this net-snmp build.\n");
           exit(UNKNOWN);
#endif
        }
        else if( privProtocol == 4 )
        {
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
           session.securityPrivProto = usmAES192CiscoPrivProtocol;
           session.securityPrivProtoLen = OID_LENGTH(usmAES192CiscoPrivProtocol);
           session.securityPrivKeyLen = USM_PRIV_KU_LEN;
#else
           fprintf(stderr, "AES-192-Cisco priv protocol not available in this net-snmp build.\n");
           exit(UNKNOWN);
#endif
        }
        else if( privProtocol == 5 )
        {
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
           session.securityPrivProto = usmAES256CiscoPrivProtocol;
           session.securityPrivProtoLen = OID_LENGTH(usmAES256CiscoPrivProtocol);
           session.securityPrivKeyLen = USM_PRIV_KU_LEN;
#else
           fprintf(stderr, "AES-256-Cisco priv protocol not available in this net-snmp build.\n");
           exit(UNKNOWN);
#endif
        }

        /* set the Privacy key to a hashed version of our passphrase (which must be at least 8 characters long) */
        if (generate_Ku(session.securityAuthProto,
                    session.securityAuthProtoLen,
                    (u_char *) our_v3_priv_passphrase, strlen(our_v3_priv_passphrase),
                    session.securityPrivKey,
                    &session.securityPrivKeyLen) != SNMPERR_SUCCESS) {
            fprintf(stderr, "Error generating a key (Ku) from the supplied privacy pass phrase. \n");
            exit(1);
        }
      }
    }
    }    
    else /* we'll use the insecure (but simplier) SNMPv1 or 2 */
    {
    /* set the SNMP version number */
    session.version = version;

    /* set the SNMP community name used for authentication */
    session.community = strdup( community );
    session.community_len = strlen( session.community );
    }

    /*
     * Open the session
     */
    SOCK_STARTUP;
    ss = snmp_open( &session );         /* establish the session */

    if (!ss) {
      snmp_sess_perror("ack", &session);
      SOCK_CLEANUP;
      exit(1);
    }


    if( ifdescr || ifname || ifalias )
    {
      if( ifname )
      {
        memmove(root, objid_mib_ifname, sizeof(objid_mib_ifname));
        rootlen = sizeof(objid_mib_ifname) / sizeof(oid);
      }
      
      if( ifdescr )
      {
        memmove(root, objid_mib_ifdescr, sizeof(objid_mib_ifdescr));
        rootlen = sizeof(objid_mib_ifdescr) / sizeof(oid);
      }

      if( ifalias )
      {
        memmove(root, objid_mib_ifalias, sizeof(objid_mib_ifalias));
        rootlen = sizeof(objid_mib_ifalias) / sizeof(oid);
      }
      
      /*
       * setup initial object name 
       */
      memmove(name, root, rootlen * sizeof(oid));
      name_length = rootlen;

      running = 1;

      check = !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
              NETSNMP_DS_WALK_DONT_CHECK_LEXICOGRAPHIC);

      while (running) {
          /*
           * create PDU for walk request and add object name to request.
           * SNMPv1 does not support GETBULK — use GETNEXT instead.
           */
          if (version == SNMP_VERSION_1) {
              pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
          } else {
              pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
              pdu->non_repeaters = non_reps;
              pdu->max_repetitions = reps;    /* fill the packet */
          }
          snmp_add_null_var(pdu, name, name_length);

          /*
           * do the request 
           */
          status = snmp_synch_response(ss, pdu, &response);
          if (status == STAT_SUCCESS) {
              if (response->errstat == SNMP_ERR_NOERROR) {
                  /*
                   * check resulting variables 
                   */
                  for (vars = response->variables; vars;
                       vars = vars->next_variable) {
                      if ((vars->name_length < rootlen)
                          || (memcmp(root, vars->name, rootlen * sizeof(oid))
                              != 0)) {
                          /*
                           * not part of this subtree 
                           */
                          running = 0;
                          continue;
                      }
                      numprinted++;
                      //print_variable(vars->name, vars->name_length, vars);
                      // The vars->val.string variable has the name of the interface.
                      
                      {
                          // Null-terminate the SNMP value for safe comparison
                          char ifval_buf[256];
                          size_t copy_len = vars->val_len < sizeof(ifval_buf) - 1 ? vars->val_len : sizeof(ifval_buf) - 1;
                          memcpy(ifval_buf, vars->val.string, copy_len);
                          ifval_buf[copy_len] = '\0';

                          if( strcmp( interfacename, ifval_buf) == 0 )
                          {
                              char buffer[1024];
                              char *tok;
                              // Puts the OID name and index in to the buffer
                              snprint_objid( buffer, sizeof(buffer), vars->name, vars->name_length );
                              tok = strtok( buffer, "." );
                              tok = strtok( NULL, "." );
                              if( tok )
                                  indexnumber = strdup( tok );
                              running = 0;
                              break;
                          }
                      }

                      if ((vars->type != SNMP_ENDOFMIBVIEW) &&
                          (vars->type != SNMP_NOSUCHOBJECT) &&
                          (vars->type != SNMP_NOSUCHINSTANCE)) {
                          /*
                           * not an exception value 
                           */
                          if (check
                              && snmp_oid_compare(name, name_length,
                                                  vars->name,
                                                  vars->name_length) >= 0) {
                              fprintf(stderr, "Error: OID not increasing: ");
                              fprint_objid(stderr, name, name_length);
                              fprintf(stderr, " >= ");
                              fprint_objid(stderr, vars->name,
                                           vars->name_length);
                              fprintf(stderr, "\n");
                              running = 0;
                              exitval = 1;
                          }
                          /*
                           * Check if last variable, and if so, save for next request.  
                           */
                          if (vars->next_variable == NULL) {
                              memmove(name, vars->name,
                                      vars->name_length * sizeof(oid));
                              name_length = vars->name_length;
                          }
                      } else {
                          /*
                           * an exception value, so stop 
                           */
                          running = 0;
                      }
                  }
              } else {
                  /*
                   * error in response, print it 
                   */
                  running = 0;
                  if (response->errstat == SNMP_ERR_NOSUCHNAME) {
                      printf("End of MIB\n");
                  } else {
                      fprintf(stderr, "Error in packet.\nReason: %s\n",
                              snmp_errstring(response->errstat));
                      if (response->errindex != 0) {
                          fprintf(stderr, "Failed object: ");
                          for (count = 1, vars = response->variables;
                               vars && count != response->errindex;
                               vars = vars->next_variable, count++)
                              /*EMPTY*/;
                          if (vars)
                              fprint_objid(stderr, vars->name, vars->name_length);
                          fprintf(stderr, "\n");
                      }
                      exitval = 2;
                  }
              }
          } else if (status == STAT_TIMEOUT) {
              fprintf(stderr, "Timeout: No Response from %s\n",
                      session.peername);
              running = 0;
              exitval = 1;
          } else {                /* status == STAT_ERROR */
              snmp_sess_perror("check_snmp_interface_status", ss);
              running = 0;
              exitval = 1;
          }
          if (response)
              snmp_free_pdu(response);
      }

      if( (( ifdescr == true ) || ( ifname == true ) || ( ifalias == true )) && ( indexnumber != NULL ) )
      {
        free( interface );
        interface = strdup( indexnumber );
        intstruct.interfacenumber = interface;
      }
      else
      {
        printf( "Unknown Interface\n" );
        SOCK_CLEANUP;
        exit( UNKNOWN );
      }
    }



    
    while(oids[index])
      {
        snprintf( oid_str, sizeof(oid_str), "%s%s", oids[index], interface );
        
        /*
         * Create the PDU for the data for our request.
         * We're going to GET the system.IfOperStatus.x status.
         */
        pdu = snmp_pdu_create(SNMP_MSG_GET);
        anOID_len = MAX_OID_LEN;
      //oid *snmp_parse_oid(const char *, oid *, size_t *);
        if (!snmp_parse_oid( oid_str, anOID, &anOID_len)) {
          snmp_perror(".1.3.6.1.2.1.1.1.0");
          SOCK_CLEANUP;
          exit(1);
        }

        snmp_add_null_var(pdu, anOID, anOID_len);
      
        /*
         * Send the Request out.
         */
        status = snmp_synch_response(ss, pdu, &response);

        /*
         * Process the response.
         */
        if ( status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR )
          {
            /* manipuate the information ourselves */
            for( vars = response->variables; vars; vars = vars->next_variable )
              {
                if( (vars->type == SNMP_ENDOFMIBVIEW) || (vars->type == SNMP_NOSUCHOBJECT) || (vars->type == SNMP_NOSUCHINSTANCE) )
                  {
                    printf( "No Such Instance currently exists at this OID\n" );
                    SOCK_CLEANUP;
                    exit( UNKNOWN );
                  }
                // This would be the interface name returned as a string
                if( vars->type == ASN_OCTET_STR )
                  {
                    size_t cplen = vars->val_len < sizeof(intstruct.name) - 1 ? vars->val_len : sizeof(intstruct.name) - 1;
                    memcpy( intstruct.name, vars->val.string, cplen );
                    intstruct.name[cplen] = '\0';
                  }
                // This would be the interface operational status and admin status returned as an integer
                if( vars->type == ASN_INTEGER )
                  {
                    // This is the Interface Operational Status
                    if( strcmp( oids[index], snmpIfOperStatus ) == 0 )
                      intstruct.interfacestatus = *vars->val.integer;

                    // This is the Interface Admin Status
                    if( strcmp( oids[index], snmpIfAdminStatus ) == 0 )
                      intstruct.admininterfacestatus = *vars->val.integer;
                  }
              }
            } 
          else
            {
              /*
               * FAILURE: print what went wrong!
               */
              if( status == STAT_SUCCESS )
              {
                fprintf( stderr, "Error in packet\nReason: %s\n", snmp_errstring(response->errstat) );
                SOCK_CLEANUP;
                exit( UNKNOWN );
              }
              else if( status == STAT_TIMEOUT )
              {
                fprintf( stderr, "Timeout: No response from %s.\n", session.peername );
                SOCK_CLEANUP;
                exit( UNKNOWN );
              }
              else
              {
                snmp_sess_perror( "check_snmp_interface_status", ss );
                SOCK_CLEANUP;
                exit( UNKNOWN );
              }
          }
      index++;
    };

    /*
     * Clean up:
     *  1) free the response.
     *  2) close the session.
     */
    if( response )
      snmp_free_pdu( response );
    snmp_close( ss );

    SOCK_CLEANUP;

    return exitval;
}

/*
 * check_and_print : 
 *
 *    arguments :  
 */
int check_and_print( )
{
  int exitstatus = OK;
  char *status = NULL;

  if( intstruct.admininterfacestatus == 2 )
    {
      if( adminStatus == OK )
        {
          printf ("OK:");
          exitstatus = OK;
          status = strdup( "up" );
        }
      else if ( adminStatus == WARNING )
        {
          printf ("WARNING:");
          exitstatus = WARNING;
          status = strdup( "down" );
        }
      else if ( adminStatus == CRITICAL )
        {
          printf ("CRITICAL:");
          exitstatus = CRITICAL;
          status = strdup( "down" );
        }

      if( ifnumber )
        ifDescrName = strdup( intstruct.name );

      printf( " Interface %s (index %s) is administratively %s.\n", ifDescrName, intstruct.interfacenumber, status );
    } 
  else
    {
      switch(intstruct.interfacestatus )
        {
          case up:
            printf ( "OK:" );
            exitstatus = OK;
            status = strdup( "up" );
          break;

          case down:
            if( Warn )
              {
                printf ( "WARNING:" );
                exitstatus = WARNING;
              }
            else
              {
                printf ( "CRITICAL:" );
                exitstatus = CRITICAL;
              }
            status = strdup( "down" );
          break;
            
          case testing:
            printf ( "UNKNOWN:" );
            exitstatus = UNKNOWN;
            status = strdup( "testing" );
          break;
        }

      if( ifnumber )
        ifDescrName = strdup( intstruct.name );
         
      printf( " Interface %s (index %s) is %s.", ifDescrName, intstruct.interfacenumber, status );

      if( Perfdata )
        printf( " | 'Status'=%d", intstruct.interfacestatus );
        
      printf( " \n" );
    }

  return exitstatus;
}

void print_version( void )
{
  printf("check_snmp_interface_status Plugin version %s\nDistributed under the terms of the GNU General Public License\n", VERSION);
}

/*
 *   Returns 1 if the supplied number is an integer, 0 if not
 */
int is_integer( char *number )
{
  long int n;
  if ( strspn( number, "-0123456789 " ) != strlen (number) )
    return(0);
  errno = 0;
  n = strtol( number, NULL, 10 );
  if( (errno != ERANGE) && (n >= INT_MIN) && (n <= INT_MAX) )
    return( 1 );
  return( 0 );
}
