Source for file Switch_SNMP.php

Documentation is available at Switch_SNMP.php

  1. <?php
  2. /**
  3.  * Switch_SNMP.php
  4.  *
  5.  * PHP version 5
  6.  *
  7.  * LICENSE: This program is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU General Public License as published
  9.  * by the Free Software Foundation.
  10.  *
  11.  * @package                     FreeNAC
  12.  * @author                      Hector Ortiz (FreeNAC Core Team)
  13.  * @copyright                   2007 FreeNAC
  14.  * @license                     http://www.gnu.org/copyleft/gpl.html   GNU Public License Version 2
  15.  * @version                     SVN: $Id$
  16.  * @link                        http://www.freenac.net
  17.  */
  18.  
  19. /**
  20.  * This class is intended to perform SNMP queries to all kind of switches.
  21.  * This class extends the {@link Common} class.
  22.  */
  23. class Switch_SNMP extends Common
  24. {
  25.    private $props=array()
  26.  
  27.    /**
  28.    * Get an OID which returns only a single value, in other words, use this method when you're expecting a single value, not an array
  29.    * @param mixed $community        SNMP community to use
  30.    * @param mixed $oid            OID to query for
  31.    * @return mixed            The value of the OID
  32.    */  
  33.    private function getSingleOID($community,$oid)
  34.    {
  35.       ## Continue if there is a valid ip and host is not down
  36.       if ($this->switch_ip && $this->host_down)
  37.       {
  38.          ## This test is to avoid performing SNMP queries on hosts which are not responsive
  39.          if (( $this->description && (strcmp($oid,'1.3.6.1.2.1.1.1')==0))
  40.          {
  41.             ## Try to get the description. This OID should be available on all switches.
  42.             ## Try 3 times before declaring switch dead
  43.             for ($i 0$i 3$i++)
  44.             {
  45.                $temp=@snmprealwalk($this->switch_ip,$community,$oid);
  46.                if ($temp)
  47.                   $i=4;
  48.             }
  49.             if $temp )
  50.             {
  51.                ## We have tried 3 times and we didn't receive a response
  52.                $this->logger->logit("No response from {$this->switch_ip}. Host seems to be down. If it is up, check your SNMP community.");
  53.                ## so mark host as down
  54.                $this->props['host_down'true;
  55.                return false;
  56.             }
  57.          }
  58.          else
  59.          {
  60.             ## We already have the description, which means that host is up, so get the OID we are interested in
  61.             $this->logger->debug("Retrieving OID $oid from switch {$this->switch_ip}",3);
  62.             $temp=@snmprealwalk($this->switch_ip,$community,$oid);
  63.          }
  64.          if ($temp)
  65.          {
  66.             ## There is information for the OID we asked for, clean it
  67.             $temp=array_map("remove_type",$temp);
  68.             ## And get the first value of the returned array
  69.             $temp=array_shift($temp);
  70.             return $temp;  
  71.          }
  72.          else
  73.          {
  74.             ## Apparently the host is up, but there is not information available, then this OID is not present on the queried device
  75.             $this->logger->debug("OID $oid is not present on switch {$this->switch_ip}",3);
  76.             return false;
  77.          }
  78.       }
  79.       else
  80.       {
  81.          ## Useless to continue if we don't have a valid IP or host is down
  82.          return false;
  83.       }
  84.    }
  85.  
  86.    /**
  87.    * Get an OID which returns an array, in other words, use this method when you're expecting an array
  88.    * @param mixed $community            SNMP community to use
  89.    * @param mixed $oid                  OID to query for
  90.    * @return mixed                      The value of the OID, false otherwise
  91.    */
  92.    private function getArrayOID($community,$oid)
  93.    {
  94.       ## Continue if there is a valid ip and host is not down
  95.       if ($this->switch_ip && ! $this->host_down )
  96.       {
  97.          ## This test is to avoid performing SNMP queries on hosts which are not responsive
  98.          if (( ! $this->description ) && (strcmp($oid,'1.3.6.1.2.1.1.1')==0))
  99.          {
  100.             ## Try to get the description. This OID should be available on all switches.
  101.             ## Try 3 times before declaring switch dead
  102.             for ($i = 0; $i < 3; $i++)
  103.             {
  104.                $temp=@snmprealwalk($this->switch_ip,$community,$oid);
  105.                if ($temp)
  106.                   $i=4;
  107.             }
  108.             if ( ! $temp )
  109.             {
  110.                ## We have tried 3 times and we didn't receive a response
  111.                $this->logger->logit("No response from {$this->switch_ip}. Host seems to be down. If it is up, check your SNMP community.");
  112.                ## so mark host as down
  113.                $this->props['host_down'] = true;
  114.                return false;
  115.             }
  116.          }
  117.          else
  118.          {
  119.             ## We already have the description, which means that host is up, so get the OID we are interested in
  120.             $this->logger->debug("Retrieving OID $oid from switch {$this->switch_ip}",3);
  121.             $temp=@snmprealwalk($this->switch_ip,$community,$oid);
  122.          }
  123.          if ($temp)
  124.          {
  125.             ## There is information for the OID we asked for, clean it and return the array
  126.             $temp=array_map("remove_type",$temp);
  127.             return $temp;
  128.          }
  129.          else
  130.          {
  131.             ## Apparently the host is up, but there is not information available, then this OID is not present on the queried device
  132.             $this->logger->debug("OID $oid is not present on switch {$this->switch_ip}",3);
  133.             return false;
  134.          }
  135.       }
  136.       else
  137.       {
  138.          ## Useless to continue if we don't have a valid IP or host is down
  139.          return false;
  140.       }
  141.    }
  142.    
  143.    /**
  144.    * Get the first element of an array.
  145.    * @param array $array_var        Array we want to manipulate
  146.    * @return mixed            First value in the array, false otherwise
  147.    */
  148.    private function getFirstFromArray($array_var)
  149.    {
  150.       if (is_array($array_var))
  151.       {
  152.          $temp_array=array();
  153.          ## Clean out empty values
  154.          foreach ($array_var as $record)
  155.          {
  156.             if ($record)
  157.                $temp_array[]=$record;
  158.          }
  159.          $temp=array_shift($temp_array);
  160.          return $temp;
  161.       }     
  162.       return false;
  163.    }
  164.  
  165.    /**
  166.    * Get the list of physical interfaces on the switch
  167.    * @param mixed $community        SNMP query to use
  168.    */
  169.    private function pollInterfaces($community=false)
  170.    {
  171.       ## If no community has been specified, use the one defined in etc/config.inc
  172.       if (! $community)
  173.       {
  174.          global $snmp_ro;
  175.          $community=$snmp_ro;
  176.       }
  177.       ## Query for this system description if we haven't yet done so
  178.       if ( ! $this->description)
  179.          $this->pollDescription($community);
  180.       ## Don't continue if host has been marked as down
  181.       if ( $this->host_down )
  182.          return false;
  183.       ## Textual name of the interfaces
  184.       $interfaces=$this->getArrayOID($community,'1.3.6.1.2.1.31.1.1.1.1');
  185.       ## Has the interface a physical connector?
  186.       $physical=$this->getArrayOID($community,'1.3.6.1.2.1.31.1.1.1.17');
  187.       ## Value of the instance of the ifIndex object
  188.       #$ports=$this->getArrayOID($community,'1.3.6.1.2.1.17.1.4.1.2');
  189.       $result=array();
  190.       # Ports contains a list of ports which are in the switch
  191.       #if (! $ports || ! $interfaces || ! $physical)
  192.       if (! $interfaces || ! $physical)
  193.          return false;
  194.       foreach ($interfaces as $k => $v)
  195.       {
  196.          $if_index=get_last_index($k);
  197.          if (stristr(array_find_key($if_index, $physical,'.',1),'true'))
  198.          {
  199.             #This is the last test, it means the interface is a physical one
  200.             $result[$k]=$v;
  201.          }
  202.       }
  203.       $this->props['interfaces']=$result;
  204.    }
  205.  
  206.    /**
  207.    * Get the description of this switch
  208.    * @param mixed $community            SNMP query to use
  209.    */
  210.    private function pollDescription($community=false)
  211.    {
  212.       ## If no community has been specified, use the one defined in etc/config.inc
  213.       if (! $community)
  214.       {
  215.          global $snmp_ro;
  216.          $community=$snmp_ro;
  217.       }
  218.       ## Query for this system description if we haven't yet done so
  219.       if ( ! $this->description )
  220.       {
  221.          ## Textual description on the entity
  222.          $this->props['description']=$this->getSingleOID($community,'1.3.6.1.2.1.1.1');
  223.       }
  224.    }
  225.  
  226.    /**
  227.    * Get the switch name
  228.    * @param mixed $community            SNMP query to use
  229.    */
  230.    private function pollName($community=false)
  231.    {
  232.       ## If no community has been specified, use the one defined in etc/config.inc
  233.       if (! $community)
  234.       {
  235.          global $snmp_ro;
  236.          $community=$snmp_ro;
  237.       }
  238.       ## Query for this system description if we haven't yet done so
  239.       if ( ! $this->description)
  240.          $this->pollDescription($community);
  241.       ## Don't continue if host has been marked as down
  242.       if ( $this->host_down )
  243.          return false;
  244.       ## Query for this system name if we haven't yet done so
  245.       if ( ! $this->name )
  246.       {
  247.          ## An administratively-assigned name for this managed node
  248.          $this->props['name']=$this->getSingleOID($community,'1.3.6.1.2.1.1.5');
  249.       }
  250.    }
  251.  
  252.    /**
  253.    * Get the switch location defined in the switch
  254.    * @param mixed $community            SNMP query to use
  255.    */
  256.    private function pollLocation($community=false)
  257.    { 
  258.       ## If no community has been specified, use the one defined in etc/config.inc
  259.       if (! $community)
  260.       {
  261.          global $snmp_ro;
  262.          $community=$snmp_ro;
  263.       }
  264.       ## Query for this system description if we haven't yet done so
  265.       if ( ! $this->description)
  266.          $this->pollDescription($community);
  267.       ## Don't continue if host has been marked as down
  268.       if ( $this->host_down )
  269.          return false;
  270.       ## Query for this system's location if we haven't yet done so
  271.       if ( ! $this->location )
  272.       {
  273.          ## The physical location of this node
  274.          $this->props['location']=$this->getSingleOID($community,'1.3.6.1.2.1.1.6');
  275.       }
  276.    }  
  277.  
  278.    /**
  279.    * Get the contact details defined in the switch
  280.    * @param mixed $community            SNMP query to use
  281.    */
  282.    private function pollContact($community=false)
  283.    {
  284.       ## If no community has been specified, use the one defined in etc/config.inc
  285.       if (! $community)
  286.       {
  287.          global $snmp_ro;
  288.          $community=$snmp_ro;
  289.       }
  290.       ## Query for this system description if we haven't yet done so
  291.       if ( ! $this->description)
  292.          $this->pollDescription($community);
  293.       ## Don't continue if host has been marked as down
  294.       if ( $this->host_down )
  295.          return false;
  296.       # Query for this system's contact information if we haven't yet done so
  297.       if ( ! $this->contact)
  298.       {
  299.          ## The textual identification of the contact person for this managed node
  300.          $this->props['contact']=$this->getSingleOID($community,'1.3.6.1.2.1.1.4');
  301.       }
  302.    }
  303.  
  304.    /**
  305.    * Get the serial number of this switch
  306.    * @param mixed $community            SNMP query to use
  307.    */
  308.    private function pollSerial_Number($community=false)
  309.    {
  310.       ## If no community has been specified, use the one defined in etc/config.inc
  311.       if (! $community)
  312.       {
  313.          global $snmp_ro;
  314.          $community=$snmp_ro;
  315.       }
  316.       ## Query for this system description if we haven't yet done so
  317.       if ( ! $this->description)
  318.          $this->pollDescription($community);
  319.       ## Don't continue if host has been marked as down
  320.       if ( $this->host_down )
  321.          return false;
  322.       ## Query for this system's serial number if we haven't yet done so
  323.       if ( ! $this->serial_number)
  324.       {
  325.          ## The vendor-specific serial number string for the physical entity
  326.          $this->props['serial_number']=$this->getFirstFromArray($this->getArrayOID($community,'1.3.6.1.2.1.47.1.1.1.1.11'));
  327.       }
  328.    }
  329.  
  330.    /**
  331.    * Get the model of this switch
  332.    * @param mixed $community            SNMP query to use
  333.    */
  334.    private function pollModel($community=false)
  335.    {
  336.       ## If no community has been specified, use the one defined in etc/config.inc
  337.       if (! $community)
  338.       {
  339.          global $snmp_ro;
  340.          $community=$snmp_ro;
  341.       }
  342.       ## Query for this system description if we haven't yet done so
  343.       if ( ! $this->description)
  344.          $this->pollDescription($community);
  345.       ## Don't continue if host has been marked as down
  346.       if ( $this->host_down )
  347.          return false;
  348.       ## Query for this system's model if we haven't yet done so
  349.       if ( ! $this->model)
  350.       {
  351.          ## The vendor-specific model name identifier string associated with this physical component.
  352.          $this->props['model']=$this->getFirstFromArray($this->getArrayOID($community,'1.3.6.1.2.1.47.1.1.1.1.13'));
  353.       }
  354.    }
  355.  
  356.    /**
  357.    * Get the hardware version of this switch
  358.    * @param mixed $community            SNMP query to use
  359.    */
  360.    private function pollHardware($community=false)
  361.    {
  362.       ## If no community has been specified, use the one defined in etc/config.inc
  363.       if (! $community)
  364.       {
  365.          global $snmp_ro;
  366.          $community=$snmp_ro;
  367.       }
  368.       ## Query for this system description if we haven't yet done so
  369.       if ( ! $this->description)
  370.          $this->pollDescription($community); 
  371.       ## Don't continue if host has been marked as down
  372.       if ( $this->host_down )
  373.          return false;
  374.       ## Query for this system's hardware version if we haven't yet done so
  375.       if ( ! $this->hardware)
  376.       {
  377.          ## Vendor-specific hardware revision
  378.          $this->props['hardware']=$this->getFirstFromArray($this->getArrayOID($community,'1.3.6.1.2.1.47.1.1.1.1.8'));
  379.       }
  380.    }
  381.  
  382.    /**
  383.    * Get the interface descriptions 
  384.    * @param mixed $community            SNMP query to use
  385.    */
  386.    private function pollInterface_Descriptions($community=false)
  387.    {
  388.       ## If no community has been specified, use the one defined in etc/config.inc
  389.       if (! $community)
  390.       {
  391.          global $snmp_ro;
  392.          $community=$snmp_ro;
  393.       }
  394.       ## Query for this system description if we haven't yet done so
  395.       if ( ! $this->description)
  396.          $this->pollDescription($community);
  397.       ## Don't continue if host has been marked as down
  398.       if ( $this->host_down )
  399.          return false;
  400.        ## Query for this system's interface descriptions if we haven't yet done so
  401.       if ( ! $this->interface_descriptions )
  402.       {
  403.          ## Alias name for an interface, as defined by a network manager
  404.          $this->props['interface_descriptions']=$this->getArrayOID($community,'1.3.6.1.2.1.31.1.1.1.18');
  405.       }
  406.    }
  407.  
  408.    /**
  409.    * Get the firmware version of this switch
  410.    * @param mixed $community            SNMP query to use
  411.    */
  412.    private function pollFirmware($community=false)
  413.    {
  414.       ## If no community has been specified, use the one defined in etc/config.inc
  415.       if (! $community)
  416.       {
  417.          global $snmp_ro;
  418.          $community=$snmp_ro;
  419.       }
  420.       ## Query for this system description if we haven't yet done so
  421.       if ( ! $this->description)
  422.          $this->pollDescription($community);
  423.       ## Don't continue if host has been marked as down
  424.       if ( $this->host_down )
  425.          return false;
  426.       ## Query for this system's firmware version if we haven't yet done so
  427.       if ( ! $this->firmware )
  428.       {
  429.          ## Vendor-specific firmware revision
  430.          $this->props['firmware']=$this->getFirstFromArray($this->getArrayOID($community,'1.3.6.1.2.1.47.1.1.1.1.9'));
  431.       }
  432.    }
  433.  
  434.    /**
  435.    * Get the software version present on this switch
  436.    * @param mixed $community            SNMP query to use
  437.    */
  438.    private function pollSoftware($community=false)
  439.    {
  440.       ## If no community has been specified, use the one defined in etc/config.inc
  441.       if (! $community)
  442.       {
  443.          global $snmp_ro;
  444.          $community=$snmp_ro;
  445.       }
  446.       ## Query for this system description if we haven't yet done so
  447.       if ( ! $this->description)
  448.          $this->pollDescription($community);
  449.       ## Don't continue if host has been marked as down
  450.       if ( $this->host_down )
  451.          return false;
  452.       ## Query for this system's software version if we haven't yet done so
  453.       if ( ! $this->software )
  454.       {
  455.          ## Vendor-specific software revision
  456.          $this->props['software']=$this->getFirstFromArray($this->getArrayOID($community,'1.3.6.1.2.1.47.1.1.1.1.10'));
  457.       }
  458.    }
  459.  
  460.    /**
  461.    * Initialize the Switch_SNMP object
  462.    * @param mixed $ip        IP address of the switch
  463.    */
  464.    public function __construct($ip)
  465.    {
  466.       parent::__construct();
  467.       ## Check if there is SNMP support
  468.       if ( ! defined('SNMP_NULL'))
  469.       {
  470.          $this->logger->logit("Your installation of PHP lacks support for SNMP", LOG_ERR);
  471.          $this->props['host_down']=true;
  472.       }
  473.       else
  474.       {
  475.          ## There is SNMP support, validate IP address
  476.          $valid = true;
  477.          $tmp = explode(".", $ip);
  478.          if(count($tmp) < 4)
  479.          {
  480.             $valid = false;
  481.          }
  482.          else
  483.          {
  484.             foreach($tmp AS $sub)
  485.             {