Source for file Port.php

Documentation is available at Port.php

  1. <?php
  2. /**
  3.  * Port.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                      Sean Boran (FreeNAC Core Team)
  13.  * @author            Hector Ortiz (FreeNAC Core Team)
  14.  * @author            Thomas Seiler (contributer)
  15.  * @copyright                   2007 FreeNAC
  16.  * @license                     http://www.gnu.org/copyleft/gpl.html   GNU Public License Version 2
  17.  * @version                     SVN: $Id$
  18.  * @link                        http://www.freenac.net
  19.  *
  20.  */
  21.  
  22. /**
  23.  * The Port class describes the interfaces to information on Ports and Switches
  24.  * This class extends the {@link Common} class.
  25.  */
  26. class Port extends Common
  27. {
  28.    private $props=array();
  29.    
  30.    /** 
  31.    * The constructor takes the parameters needed to generate a Port object
  32.    * Access is read-only.
  33.    * @param object $object      A copy of the Request
  34.    */
  35.    function __construct($object)
  36.    {
  37.       #If we don't receive an object, DENY
  38.       if (!$object)
  39.          DENY('No object received in constructor');
  40.       parent::__construct();    
  41.       if (($object instanceof VMPSRequest|| ($object instanceof SyslogRequest))
  42.       {
  43.          # Get needed parameters from object
  44.          $switchip=trim($object->switch);
  45.          $portname=trim($object->port);
  46.          $domain=trim($object->vtp);
  47.          $lastvlan=trim($object->lastvlan);
  48.    
  49.          #In case we have a DENY as result from vmpsd_external, so no vlan would come in the object. If so, set vlan to
  50.          # '--NONE--' which should deny access
  51.          if (($object instanceof SyslogRequest&& (!$lastvlan))
  52.             $lastvlan="--NONE--";
  53.          
  54.          # Invalid parameters?
  55.          /*if ((strlen($switchip) < 8) || (strlen($portname) <1)) {
  56.             DENY('Invalid parameters');
  57.          }*/
  58.         
  59. #         $query=<<<EOF
  60. #         SELECT sw.id AS switch_id, sw.ip AS switch_ip, sw.name AS switch_name, sw.comment AS switch_comment, sw.notify AS notify, p.default_vlan, p.last_vlan,
  61. #            p.id AS port_id, p.name AS port_name, p.default_vlan, l.id AS office_id, l.name AS office,b.name AS building
  62. #            FROM switch sw LEFT JOIN port p ON sw.id=p.switch and p.name='$portname' LEFT JOIN location l ON sw.location=l.id
  63. #            LEFT JOIN building b ON l.building_id=b.id WHERE sw.ip='$switchip' limit 1;
  64. #EOF;
  65.          $query=<<<EOF
  66. SELECT sw.id AS switch_id, sw.ip as switch_ip, sw.name AS switch_name,
  67.        sw.comment AS switch_comment, sw.notify AS notify, sw.location,
  68.        sw.ap AS sw_ap, sw.scan AS sw_scan, sw.hw AS sw_hw,
  69.        sw.last_monitored AS sw_last_monitored, sw.up AS sw_up,
  70.        sw.vlan_id AS sw_vlan_id,
  71.        p.id AS port_id, p.name AS port_name, p.comment AS port_comment,
  72.        p.restart_now AS port_restart_now, p.default_vlan,
  73.        p.last_vlan, p.last_activity AS port_last_activity, p.auth_profile AS port_auth_profile,
  74.        p.staticvlan AS port_staticvlan
  75.        FROM switch sw
  76.        LEFT JOIN port p ON sw.id=p.switch AND p.name='$portname'
  77.        WHERE sw.ip='$switchip' LIMIT 1;
  78. EOF;
  79.  
  80.      $this->logger->debug($query,3);
  81.          if ($temp=mysql_fetch_one($query))
  82.          {
  83.             #Information found in DB.
  84.             if (is_array($temp))
  85.                $this->props=$temp;
  86.             $location new Location($this->location);
  87.             $this->props['office_id'$location->getid();
  88.             $this->props['office'$location->getname();
  89.             $this->props['building'$location->getbuilding_name();
  90.  
  91.             #Initialize control flags
  92.         if ($this->switch_ip)
  93.                $this->props['switch_in_db']=true;
  94.             else
  95.                $this->props['switch_in_db']=false;
  96.  
  97.             if ($this->port_name)
  98.                $this->props['port_in_db']=true;
  99.             else
  100.                $this->props['port_in_db']=false;
  101.             
  102.             #Just in case we didn't get a port_name from the DB
  103.         if (!$this->port_name)
  104.                $this->port_name=$portname;
  105.          }
  106.          else
  107.          {
  108.             #No information found in DB, so get data from the request
  109.             $this->props['switch_ip']=$switchip;
  110.             $this->props['port_name']=$portname;
  111.             $this->props['switch_name']=gethostbyaddr($switchip);
  112.             
  113.             #Initialize control flags
  114.         $this->props['switch_in_db']=false;
  115.             $this->props['port_in_db']=false;
  116.      }
  117.  
  118.          #Should we lookup patch information?
  119.          #Chech first if we have this port in the db and if we have an office Id for this port.
  120.          if ($this->conf->lastseen_patch_lookup && $this->port_in_db && $this->office_id)
  121.          {
  122.             #If so, lookup Users
  123.             $query="SELECT GROUP_CONCAT(Surname) AS Surname FROM users WHERE PhysicalDeliveryOfficeName='" $this->office "'";
  124.             $this->logger->debug($query,3);
  125.             $users=v_sql_1_select($query);
  126.             $this->props['users_in_office']=$users;
  127.  
  128.             #Then, patchcable information
  129.             $query="SELECT CONCAT(outlet,', {$this->office}, ',comment) FROM patchcable WHERE port='" . $this->port_id "'";
  130.             $this->logger->debug($query,3);
  131.             $patch_details=v_sql_1_select($query);
  132.             $this->props['patch_details']=$patch_details;
  133.          }
  134.  
  135.          #If the object is a syslog message, lookup the vlan_id for that vlan and set it to last_vlan
  136.          if ($object instanceof SyslogRequest)
  137.          {
  138.             $query="SELECT id FROM vlan WHERE default_name='$lastvlan';";
  139.             $this->logger->debug($query,3);
  140.             $temp_vlan=v_sql_1_select($query);
  141.             if ($temp_vlan && ($temp_vlan>0))
  142.                $this->props['last_vlan']=$temp_vlan;
  143.             else
  144.                $this->props['last_vlan']=0;
  145.          }
  146.       }
  147.       else
  148.       {
  149.          #Object is an unknown instance
  150.          DENY('Unknown instance of object passed to the constructor');
  151.       }
  152.    }
  153.  
  154.    /**
  155.    * Universal Accessor Method
  156.    * We are redirecting all unresolved method calls to this handler,
  157.    * so that we can emulate arbitraty accessor methods.
  158.    * With this trick, the user can add new fields to the system tables
  159.    * and will be able to access them in the policy as
  160.    * $system->getDBFieldName() without haveing to change this class
  161.    * @throws            If the db field does not exist, Log Error and Deny as default action
  162.    * @return mixed      Property
  163.    */
  164.    public function __call($methodName, $parameters) {
  165.       # If methodname starts with get
  166.       if (substr($methodName,0,3) == "get") {
  167.          $dbfieldname = substr($methodName,3);
  168.          foreach(array_keys($this->propsas $key{
  169.             if (strtolower($key) == strtolower($dbfieldname)) {
  170.                if (is_numeric($this->props[$key]))
  171.                {
  172.                   if (stristr($this->props[$key],'.'))
  173.                      return $this->props[$key];
  174.                   else if $this->props[$key)
  175.                      return (int)$this->props[$key];
  176.                   else return false;
  177.                }
  178.                else
  179.                {
  180.                   return $this->props[$key];
  181.                }
  182.             }
  183.          }
  184.       }
  185.       $this->logger->debug("Field $methodName doesn't exist",2);
  186.    }
  187.  
  188.    /**
  189.    * Get the value of one property if it exists
  190.    * @param mixed $key        Property to lookup
  191.    * @return mixed        The value of the wanted property, or false if such a property doesn't exist
  192.    */
  193.    protected function __get($key)                            
  194.    {
  195.       if (is_array($this->props))
  196.       {
  197.          if (array_key_exists($key,$this->props))
  198.          {
  199.             if (is_numeric($this->props[$key]))
  200.             {
  201.                if (stristr($this->props[$key],'.'))
  202.                   return $this->props[$key];
  203.                else if $this->props[$key)
  204.                   return (int)$this->props[$key];
  205.                else return false;
  206.             }
  207.             else
  208.             {
  209.                return $this->props[$key];
  210.             }
  211.          }
  212.       }
  213.       else
  214.       {
  215.          return false;
  216.       }
  217.    }
  218.  
  219.    /**
  220.    * Set the value of one property if it exists
  221.    * @param mixed $key        Property to lookup
  222.    * @param mixed $value    Value to set the desired property to
  223.    * @return boolean        True if successful, false otherwise
  224.    */
  225.    private function __set($key,$value)
  226.    {
  227.       if (is_array($this->props))
  228.       {
  229.          if (array_key_exists($key,$this->props))
  230.          {
  231.             $this->props[$key]=$value;
  232.             return true;
  233.          }
  234.          else
  235.          {
  236.             $this->logger->debug("Property $key not found",2);
  237.             return false;
  238.          }
  239.       }
  240.       else
  241.       {
  242.          return false;
  243.       }
  244.    }
  245.  
  246.    /**
  247.    * Return all properties assigned to this system. This method is here only for debugging purposes, please delete it after
  248.    * @return array      All properties present in this object
  249.    */
  250.    public function getAllProps()                        
  251.    {
  252.       return $this->props;
  253.    }
  254.  
  255.    /**
  256.    * Get the default vlan assigned to a Port
  257.    * @return mixed     Vlan assigned to a port, false otherwise
  258.    */
  259.    public function getPortDefaultVlan()
  260.    {
  261.     if ($this->conf->use_port_default_vlan)
  262.        return $this->default_vlan;
  263.     else
  264.     {
  265.            $this->logger->logit("Option use_port_default_vlan not enabled",LOG_WARNING);
  266.        return false;
  267.         }
  268.    }
  269.  
  270.    /**
  271.    * Get a vlan based on switch location. This vlan should be used as an exception to the regular process
  272.    * @return mixed    Vlan to assign, false otherwise
  273.    */
  274.    public function vlanBySwitchLocation($vlan_id=0)
  275.    {
  276.       if ($this->conf->vlan_by_switch_location)
  277.       {
  278.          $vlan_id = mysql_real_escape_string($vlan_id);
  279.          if ( $vlan_id > 0 )
  280.          {
  281.             #Lookup a vlan_id to assign depending on the switch location
  282.             $query=<<<EOF
  283. SELECT vs.vlan_name 
  284.    FROM vlanswitch vs 
  285. WHERE vs.swid='{$this->switch_id}'
  286. AND vs.vid='$vlan_id'; 
  287. EOF;
  288.             $this->logger->debug($query,3);
  289.             $result v_sql_1_select($query);
  290.             if ($result)
  291.                $this->props['exception_vlan']=$result;
  292.             else
  293.                $this->props['exception_vlan']=false;
  294.             return $this->props['exception_vlan'];
  295.          }
  296.          else
  297.          {
  298.             $this->props['exception_vlan']=false;
  299.             return $this->props['exception_vlan'];
  300.          }
  301.       }
  302.       else
  303.       {
  304.          $this->logger->logit("Option vlan_by_switch_location not enabled",LOG_WARNING);
  305.          return false;
  306.       }
  307.    }
  308.  
  309.    /**
  310.    * Get the vlan used by a system seen on this same port during the last 2 hours.
  311.    * This is usefull to assign a vlan for VMs without causing flapping.
  312.    * @return mixed    Vlan to assign, false otherwise
  313.    */
  314.    public function getVMVlan()
  315.    {
  316.       if ($this->conf->vm_lan_like_host)
  317.       {
  318.          #Lookup the last_vlan assigned to the last system which was lastseen on this port in the previous 2 hours
  319.          $query=<<<EOF
  320.          SELECT s.lastvlan AS lastvlan FROM systems s INNER JOIN port p ON
  321.             s.lastport=p.id INNER JOIN switch sw ON p.switch=sw.id AND p.name='{$this->port_name}'
  322.             AND sw.ip='{$this->switch_ip}' WHERE DATE_SUB(CURDATE(), INTERVAL 2 HOUR) <= s.lastseen
  323.             ORDER BY lastseen DESC LIMIT 1;
  324. EOF;
  325.          $this->logger->debug($query,3);
  326.          $vm_vlan=v_sql_1_select($query);
  327.          if ($vm_vlan)
  328.             return $vm_vlan;
  329.          else
  330.             return false;
  331.       }
  332.       else
  333.       {
  334.          $this->logger->logit("Option vm_lan_like_host is not enabled",LOG_WARNING);
  335.          return false;
  336.       }
  337.    }
  338.  
  339.    /**
  340.    * Tell if this switch is in the DB
  341.    * @return boolean         Value of the 'switch_in_db' property
  342.    */
  343.    public function isSwitchInDB()
  344.    {
  345.       return $this->switch_in_db;
  346.    }
  347.  
  348.    /**
  349.    * Tell if this port is in the DB
  350.    * @return boolean            Value of the 'port_in_db' property
  351.    */
  352.    public function isPortInDB()
  353.    {
  354.       return $this->port_in_db;
  355.    }
  356.  
  357.    /**
  358.    * Insert a switch or port into the DB if it doesn't exist.
  359.    * @return boolean        True if an insert operation was performed, false otherwise
  360.    */
  361.    public function insertIfUnknown()
  362.    {
  363.       $counter=0;
  364.       if ($this->check_calling_method())
  365.       {
  366.          #Insert switch in database if it doesn't exist
  367.          if (!$this->isSwitchInDB())
  368.          {
  369.             $query="INSERT INTO switch SET ip='{$this->switch_ip}', name='{$this->switch_name}',comment='';";
  370.             $this->logger->debug($query,3);
  371.             $res=mysql_query($query);
  372.             if ($res)
  373.             {
  374.                #Switch has been inserted, lookup its id
  375.                $query="SELECT id FROM switch WHERE ip='{$this->switch_ip}' LIMIT 1;";
  376.                $this->logger->debug($query,3);
  377.            $this->props['switch_id']=v_sql_1_select($query);
  378.  
  379.                #If we have its id, change the value of our control flag
  380.                if ($this->switch_id)
  381.                   $this->switch_in_db=true;
  382.  
  383.                #Log it and increase our internal counter to indicate that an insert has been done
  384.                $string="New switch entry {$this->switch_ip} ({$this->switch_name}), please update the description.";
  385.                $this->logger->logit($string);
  386.                #log2db('info',$string);
  387.                $counter++;
  388.             }
  389.             else
  390.             {
  391.                $this->logger->logit(mysql_error(),LOG_ERR);
  392.                return false;
  393.             }   
  394.          }
  395.          #Insert port in database if it doesn't exist
  396.          if (!$this->isPortInDB())
  397.          {
  398.             $query="INSERT INTO port SET name='{$this->port_name}', switch='{$this->switch_id}', last_vlan='{$this->last_vlan}', last_activity=NOW();";
  399.             $this->logger->debug($query,3);
  400.             $res=mysql_query($query);
  401.             if ($res)
  402.             {
  403.                #Port has been inserted, lookup its id
  404.            $query="SELECT id FROM port WHERE name='{$this->port_name}' AND switch='{$this->switch_id}' LIMIT 1;";
  405.                $this->logger->debug($query,3);
  406.                $this->props['port_id']=v_sql_1_select($query);
  407.  
  408.                #If we have its id, change the value of our control flag
  409.            if ($this->port_id)
  410.               $this->port_in_db=true;
  411.  
  412.                #Log it and increase our internal counter to indicate that an insert has been done. Also, if we have patchcable
  413.                #information, add it to the log message
  414.                if ($this->conf->lastseen_patch_lookup)
  415.                   $string="New port {$this->port_name}. Location from patchcable: {$this->getPatchInfo()}";
  416.                else
  417.                   $string="New port {$this->port_name} in switch {$this->switch_ip} ({$this->switch_name})"; 
  418.                $this->logger->logit($string);
  419.                #log2db('info',$string);
  420.                $counter++;
  421.             }
  422.             else
  423.             {
  424.                $this->logger->logit(mysql_error(),LOG_ERR);
  425.                return false;
  426.             }
  427.          }
  428.          #Return true if our counter is greater than zero. Thus we know that an insert operation has been performed
  429.          if ($counter)
  430.             return "{$this->port_name{$this->switch_ip}({$this->switch_name})";
  431.          else
  432.             return false;
  433.       }
  434.       else
  435.          return false;
  436.    }
  437.  
  438.    /**
  439.    * Get patch information related to this port
  440.    * @return mixed    Patch information
  441.    */
  442.    public function getPatchInfo()
  443.    {
  444.       if ($this->conf->lastseen_patch_lookup)
  445.       {
  446.          return $this->patch_details .'('$this->users_in_office .')';
  447.       }
  448.       else
  449.       {
  450.          $this->logger->logit("Option lastseen_patch_lookup not enabled\n",LOG_WARNING);
  451.          return false;
  452.       }
  453.    }   
  454.  
  455.    /**
  456.    * Catch DB inserts.
  457.    * Check if the function is called from a postconnect method in an object which is a child of Policy.
  458.    * This function should be called from inside the update method. To prevent inserting of code in other methods, new code
  459.    * should be added.
  460.    * This is a basic checking, in the future this code may be enhanced.
  461.    * @return boolean            True if function was called from a valid method, false otherwise
  462.    */
  463.    function check_calling_method()
  464.    {
  465.       $backtrace = debug_backtrace();
  466.       array_shift($backtrace);  //Remove call to check_calling_method from the backtrace;
  467.       $ok=0;
  468.       #Are we calling from a child of EndDevice which uses CallWrapper?
  469.       if (isset($backtrace[1]['class']) && isset($backtrace[3]['class']) &&
  470.       ( (strcasecmp(get_parent_class($this),'Port')) == 0 ) && (strcasecmp($backtrace[3]['class'],'Callwrapper') ==0 ))
  471.       {
  472.          #If so, do the necessary corrections to our backtrace
  473.          $temp=array_shift($backtrace);
  474.          $backtrace[0]=$temp;
  475.       }
  476.  
  477.       {
  478.          #Check if we are using callwrapper
  479.          if ( (strcasecmp($backtrace[2]['class'],'Callwrapper')==0) && (strcasecmp($backtrace[2]['function'],'__call')==0))          
  480.          {
  481.             #Check if the class is a child of Policy and if calling method is postconnect
  482.             #if ( ($backtrace[4]['class'] instanceof Policy) && (strcasecmp($backtrace[4]['function'],'postconnect')!=0) )
  483.             if (strcasecmp($backtrace[4]['function'],'postconnect')!=0)
  484.             {
  485.                $this->logger->logit(