<?
//****************************************************************************
// Minotaur - a PHP layer-2 traceroute script
// MAC address traceroute script ver o.oo6 09/25/2003
//    by Min-Hsao Chen minhsao@mintrix.net
//    http://www.mintrix.net/projects/
//
// The purpose of this script is to trace through a switch network and find 
// the port where a specific mac address is connect.
//     Go to http://www.mintrix.net/projects/ for more details
//
// Requirements:
//     - This script requires the network switches to be cisco based switches.
//        - will work on other IOS based or CATos based switches
//     - NEW!!! WILL work with TACACS and no TACACS switches
//     - NEW!!! added support for etherchannel thanks to G.A.
//     - NEW!!! New telnet function that speed up the process
//     
// Disclaimer
//    This script is open source … if you are going to use this script please have the 
//    courtesy and send me an email and say hi.  This is script is under the GNU general 
//    public license. This script is provided "AS IS".  I will not be responsible 
//    for anything that happens because of this script.  
//    If there are any questions please email me. 
//    I will try to answer your question as soon as I can.  Thank you very much 
//    and have fun tracing.
//    
//****************************************************************************

// user edit area

$snmppublicstr="public"// public snmp string 
$consolepw="cisco";// console password
$enablepw="enable"// enable password
//

// edit with caution from this point on
?>
<!-- Main HTML -->
<div align="left">
  <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="72%" id="AutoNumber1">
    <tr>
      <td width="23%">
      <img border="0" src="minotaurlogoB.jpg" width="108" height="131"></td>
      <td width="77%" align="left"><b><font size="5" face="Verdana">MINOTAUR</font></b><small><br>
      a layer-2 traceroute script <br>
ver. o.oo6 ... <br>
09/25/2003<br>
<a href="mailto:minhsao@mintrix.net">minhsao@mintrix.net</a>
</small>

      </td>
    </tr>
  </table>
</div>
<form ENCTYPE="multipart/form-data" method="post" action="mactrace006.php">

Username:
<input type="text" name="username" value="<? echo "$username";?>" size="20"  size="13"><br>

Password:
<input type="password" name="password" value="<? echo "$password";?>" size="20" size="13"><br>

MAC or IP ADDRESS :
<input type="text" name="address" value="<? echo "$address";?>" size="20" size="16"><br>

<i>Seed switch IP (optional)</i> :
<input type="text" name="seedip" value="<? echo "$seedip"?>"size="20"  size="16"><br>

<input type="submit" value="Go get it"  >
</form>


<?
// main program

if ($username)
{
 echo 
"please wait... <br>\n ";
 
flush();        
    
// Sets start time.
$timeparts explode(" ",microtime());
$starttime $timeparts[1].substr($timeparts[0],1);


// get the gateway if needed
if (isitip($address)){
  
$localrouter=findgateway($address);
  if (!
$localrouter) {       // default router
      
$adevice=explode(".",$address); // finding router ip
      
$temp=array_pop($adevice);
      
array_push($adevice,"20");   // default router 4th octect
      
$localrouter=implode(".",$adevice);
  }
  echo 
"gateway IP : $localrouter<br>";
  
$mac=getmacfromtelnet($localrouter,$username,$password,$ostype,$address);
  
}else{
  
$mac=$address;
  echo 
"MAC: $mac <br>";
}
// got mac
flush();

// get seed device if needed
if (!$seedip){
    
//echo "getting seed device... from $localrouter <br>";
    
$seedip=getseedip($localrouter,$username,$password,$ostype,$address);
}
$nextip=$seedip// device IP is the next ip
// got seed


$mac=str_replace("-","",$mac);  // strip out the dashes 
$mac=str_replace(".","",$mac);  // strip out the dots
$mac=str_replace(":","",$mac);  // strip out the colons 
$mac=strtolower($mac);

  


  echo 
"Seed device : $seedip <br>";
  
flush();
  
$count=-1;
  echo 
"<pre>";    
  echo 
"<b>OS TYPE        FROM IP            PORT                TO </b><br>";
  
flush();
  while (
$nextip){    
      
      
$typeofosgetostypebysnmp($nextip,$snmppublicstr);
      
$nextip=rtrim($nextip);
     echo 
"$typeofos         <a href=\"telnet:\\\\$nextip\">$nextip</a>        ";// telnet option
      
$nextip=locatethismac($username,$password,$nextip,$typeofos,$mac);
      if (
$nextip){
           echo 
"            $nextip";
         }else{
           echo 
"            $mac";
         }
      
$count+=1;
        echo 
"<br>";
      
      
flush();

  }
// end while
  
echo "</pre>";
  echo 
"<br> $count hop(s) from the seed device<br>";
  
  
//total time taken
  
$timeparts explode(" ",microtime());
  
$total_time = ($timeparts[1].substr($timeparts[0],1)) - $starttime;
  echo 
"<br><font size=\"0\"><i>Total time: ".substr($total_time,0,4)." secs.</i></font><br>";
}else {
  echo 
"Please user your TACACS UserID and Password <br>";
}

//functions

function getseedip($routerip,$username,$password,$ostype,$address){
// this function finds the seedip from the default gateway.
       
$command="show ip arp $address";
       
$data=getdatafromdevice($routerip,$username,$password,$command,$typeofos); // get the info
       // $ndata=str_replace("\n","<br>",$data);
       // echo "$ndata <br>";
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$address /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression splitf
       
$port=rtrim($parts[5]); // pick out the interface from ip arp
       // echo "$port <br>";
       
$seedswitch=get_cdp_neighbor($username,$password,$routerip,$typeofos,$port);
       
// echo "seedip = $seedswitch <br>";
       
return $seedswitch;
}

function 
isitip($address){
   
$dotcount=substr_count($address,".");
   if (
$dotcount=="3"){
         return 
true;
    }else{
         return 
false;
    }
// end of isitip


// find the ip's local router functions
function findgateway($device){
// this function finds the default gateway of the device
   
$tracearray=traceroute($device);       
   
$numberofdevices=count($tracearray);
   
//var_dump($tracearray);
   
$routerip=preg_split("/ +/",$tracearray[$numberofdevices-2]);
   
//echo "gateway IP : $routerip[2] <br>";
   
return $routerip[2];
}


Function 
traceroute ($destip) {
    
// traceroute function that does a ICMP traceroute.    
    // not the best way to do it .. bit it will do for now
    
exec("traceroute -I $destip",$tracearray);
    return 
$tracearray;  
}

// end of finding gateway functions


function getmacfromtelnet($routerip,$username,$password,$ostype,$ipaddress){
// this function gets the arptable by telneting into the router and do 
// a "show ip arp ip address" then return the mac address
// this might be able to be speed up    
    
$command="show ip arp ".$ipaddress;
    
$data=getdatafromdevice($routerip,$username,$password,$command,$ostype);
    
$data=cleandata($data,$command,$ostype);
    
$rawtablearray=explode("\n",$data); // breaks the string to array units per line
    
$title=array_shift($rawtablearray); // take out the title
    
       
$line=$rawtablearray[0];
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$mac=str_replace(".","",$parts[3]);
       echo 
"MAC: $mac = IP: $ipaddress <br>";    
        
        return 
$mac// returns mac address
}

function 
str_split($chaine$length=1)
$retourFALSE;
  
$incrmt= (int)$length;
  if (
$incrmt)
  { 
$retour= array();
    
$offset0;
    
$limitestrlen($chaine);
    while (
$offset $limite)
    { 
$retour[]= substr($chaine$offset$incrmt);
      
$offset += $incrmt;
    }
  }
// if (0 < $incrmt)
  
return ($retour);
}

function 
convertmac ($mac$typeofos){
// converts xxxxxxxxxxxx to 
//   xx-xx-xx-xx-xx-xx for catos
// or
//   xxxx.xxxx.xxxx for ios

  
if ($typeofos=="catos"){
      
$macarray=str_split($mac2);
      
$output=implode ("-",$macarray);
  }else{
      
$macarray=str_split($mac4);
      
$output=implode (".",$macarray);
  }
  
  return 
$output// converted mac address    
}


function 
locatethismac ($username,$password,$device,$typeofos,$mac){
// This function locate the mac address and returns an array of the port and ip
 
  
$mac convertmac ($mac,$typeofos); 
  if (
$typeofos=="catos"){
       
$command="show cam $mac";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       // $ndata=str_replace("\n","<br>",$data);
       // echo "$ndata <br>";
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$mac /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$port=$parts[2];
  }elseif (
$typeofos=="L3IOS"){
       
$command="show bridge $mac";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       // $ndata=str_replace("\n","<br>",$data);
       // echo "$ndata <br>";
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$mac /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$port=$parts[2];
  }elseif (
$typeofos=="2950/3550"){   
       
$command="show mac-address-table";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$mac /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$port=rtrim($parts[4]);
  }elseif (
$typeofos=="C1900"){   
       
$command="show mac address $mac";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       
$data=cleandata($data,$command,$ostype);
       
$mac=strtoupper($mac);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$mac /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$port=$parts[1].$parts[2];
  }else {
       
$command="show mac address $mac";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       // $ndata=str_replace("\n","<br>",$data);
       // echo "$ndata <br>";
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$lineimplode ("",preg_grep ("/$mac /"$list));
       
$parts=preg_split"/ +/"$line ); // perl expression splitf
       
$port=rtrim($parts[3]);
       
  }
  
  if (
$port=="") {
      echo 
"<font color=\"#FF0000\"><b>Can not find MAC address in $device</b></font> <br>";
      exit (
"Try another Seed Device<br><br>");
  }else { 
    
$oneport=explode("-",$port); // ignore trunking in catos ex. 2/1-2 will only be 2/1
    
$nextip=get_cdp_neighbor($username,$password,$device,$typeofos,$oneport[0]);
    echo 
"$port    ";  // display port on the switch
  
}
      
  
  return 
$nextip;
}


function 
get_cdp_neighbor($username,$password,$device,$typeofos,$port){ 
// special thanks to Goldberg Alain (alain@towersemi.com) 
// for the etherchannel detection

       
if (strstr($port,"BVI")){
         return 
$device;
       }elseif (
strstr($port,"Po")){
                
$command="show int $port | inc Members";
                
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
                
$data=cleandata($data,$command,$ostype);
                
$list=explode("\n",$data);
                
$linepreg_grep ("/channel: | channel: /"$list);
                
$line=implode("",$line);
                
$parts=preg_split"/ +/"$line ); // perl expression split
                
$port=$parts[5];
       }elseif (
strstr($port,"Vlan")){
            
$port="";
       }
       
$command="show cdp neighbor $port detail";
       
$data=getdatafromdevice($device,$username,$password,$command,$typeofos); // get the info
       
$data=cleandata($data,$command,$ostype);
       
$list=explode("\n",$data);
       
$linepreg_grep ("/IP Address: | IP address: /"$list);
       
$line=implode("",$line);
       
$parts=preg_split"/ +/"$line ); // perl expression split
       
$nextip=$parts[3];
       return 
$nextip;
}


function 
getostypebysnmp($deviceip,$cstring){
// This function uses snmp to get the switch type
// default switch type is IOS
   
    
$ostypeoid=".1.3.6.1.2.1.1.1.0";
    
$rawosdata=snmpget($deviceip$cstring,$ostypeoid);
    if (
strstr($rawosdata,"L3")){
       
$output="L3IOS";
    }elseif(
strstr($rawosdata,"1900")){
       
$output="C1900";    
    }elseif(
strstr($rawosdata,"Catalyst")){
       
$output="catos";
    }elseif(
strstr($rawosdata,"2950")||strstr($rawosdata,"3550")){
       
$output="2950/3550";
    }elseif(
strstr($rawosdata,"3500")||strstr($rawosdata,"2900")){
       
$output="ios";
    }else {
       
$output="ios";
       echo 
" <font color=\"#FF0000\"><b>UNKnown Switch type </b></font><br>";
       echo 
" email me the text below <br>";
       echo 
" $rawosdata <br>";
    }
    
    return 
$output;
}

function 
getostype($username,$password,$device,$ostype){
// old method of getting os type
    
$command="xxx";
    
$data=getdatafromdevice($device,$username,$password,$command,$ostype);
    
$data=cleandata($data,$command,"1");
    
$array=explode("\n",$data);
    
$ostype=array_pop($array);
    return 
$ostype;
}

function 
cleandata($incoming,$cmd,$os) {
// remove all header for clean data section
  
$in_array=explode("\n",$incoming);
  
$out=array();
  
$found="";
  
$typeofos="";
  foreach (
$in_array as $in){
     if (!
$found){
        
$parts=explode("#",$in);
        
$cmd=rtrim($cmd);
    
$part=rtrim($parts[1]);
        if (
$part==$cmd){
      
$found="1";
      
$typeofos="ios";
      }else{
       
$parts=explode("(enable)",$in);
           
$part=trim(rtrim($parts[1]));
           if (
$part==$cmd){
       
$found="1";
       
$typeofos="catos";
       }
      }
    }else{
       
array_push($out,$in);
    }
  }

  if (!
$found) {
       echo 
"ERROR : BAD LOGIN OR PASSWORD";
       
$incoming=preg_replace("/\n/","<br>",$incoming);
       echo 
"<br> ERROR DEBUG <br>";
       echo 
" if this is the first time you get this message hit GO GET IT AGAIN <br>";
       echo 
" if you continue to get is message plese email me the text below so <br>";
       echo 
" I can debug the script <br>";
       echo 
"<b>Please email me this error so I can fix the issue</b> <br>";
       echo 
$incoming// print $res
       
echo "<br> END OF ERROR DEBUG <br>";
       
var_dump($res);
       exit(
"Cant find enable maybe timed out due to slow response from the device <br> \n");
  }
  if (
$os) {
     
array_push($out,$typeofos);
  }
  
$output=implode("\n",$out);
  return 
$output// clean data list
//end cleandata



function getdatafromdevice($device,$username,$password,$command,$ostype){
// This function gets the data from the device via port 23 aka telnet
  
$consolepw=$GLOBALS["consolepw"];
  
$enablepw=$GLOBALS["enablepw"];
  
  if (
$device != ""){
      
$fp fsockopen($device23,$errno$errstr1);
  }

  if(!
$fp){    
      
fclose($fp);
      echo 
"Impossible to connect because : <br> $errstr <br>\n"//can't connect
        
exit ("Please Enter a Valid IP ADDRESS <br> \n");         
  } else {
      
// logging in get info
      
      
fclose($fp);
      
$tn = new telnet($device,23); 
      
$loginprmpt=$tn->read_till(":");
      if (
strstr($loginprmpt,"word")){    // no tacacs
          
$tn->write("$consolepw\r\n");
        
$tn->write("enable\r\n");
        
$tn->read_till("word:"); 
        
$tn->write("$enablepw\r\n");  
      }else{                             
// with tacacs
        //echo "tacacs <br>";
          //flush();
        
if ($ostype=="C1900"){  // special case c1900s

             
sleep(20);
             
$tn->write("k\r\n"); 
             
$tn->read_till("word:");  
             
$tn->write("$consolepw\r\n");
             
$tn->write("enable\r\n"); 
           
$tn->read_till("word:");
           
$tn->write("enabelpw\r\n");
           
        }else {                
// everything else
           //echo "$username <br>";
           //flush();    
           
$tn->read_till("sername");
       
$tn->write("$username\r\n"); 
           
$temp=$tn->read_till("ssword:"); 
           
//echo"temp: $temp<br>";
           
$tn->write("$password\r\n"); 
           
$tn->write("enable\r\n");
           
$temp=$tn->read_till("Password:"); 
           
//echo"temp: $temp<br>";
           
$tn->write("$password\r\n"); 
           
//flush();
        
}
      
      }
           

     if (
$ostype=="catos") {
         
$tn->write("set length 0 \r\n");
      } else {
         
$tn->write("terminal length 0 \r\n");
      }
     
$tn->write("$command\r\n");
     
$tn->write("exit\r\n");
     if (
$ostype=="catos") {
         
$res=$tn->read_till("enable)exit");
      } else {
         
$res=$tn->read_till("#exit");
      }
     }
     
$tn->close(); 
   return 
$res;
 }

// telnet class

class Telnet 
        
/* (c) thies@thieso.net */ 



        
var $sock NULL



        function 
telnet($host,$port) { 
        
$this->sock fsockopen($host,$port); 
                
socket_set_timeout($this->sock,2,0); 
        } 



    function 
close() { 
        if (
$this->sock
            
fclose($this->sock); 
        
$this->sock NULL
        } 



    function 
write($buffer) { 
                
$buffer str_replace(chr(255),chr(255).chr(255),$buffer); 
        
fwrite($this->sock,$buffer); 
        } 



        function 
getc() { 
                return 
fgetc($this->sock); 
        } 



    function 
read_till($what) { 
        
$buf ''
                while (
1) { 
                        
$IAC chr(255); 



                        
$DONT chr(254); 
                        
$DO chr(253); 



                        
$WONT chr(252); 
                        
$WILL chr(251); 



                        
$theNULL chr(0); 



                        
$c $this->getc(); 



                        if (
$c === false
                          return 
$buf



                        if (
$c == $theNULL) { 
                                continue; 
                        } 



                        if (
$c == "\021") { 
                                continue; 
                        } 



                        if (
$c != $IAC) { 
                                
$buf .= $c



                                if (
$what == 
(
substr($buf,strlen($buf)-strlen($what)))) { 
                                        return 
$buf
                                } else { 
                                        continue; 
                                } 
                        } 



                        
$c $this->getc(); 



                        if (
$c == $IAC) { 
                                
$buf .= $c
                        } else if ((
$c == $DO) || ($c == $DONT)) { 
                                
$opt $this->getc(); 
                        
// echo "we wont ".ord($opt)."\n"; 
                                
fwrite($this->sock,$IAC.$WONT.$opt); 
                        } elseif ((
$c == $WILL) || ($c == $WONT)) { 
                                
$opt $this->getc(); 
                        
// echo "we dont ".ord($opt)."\n"; 
                                
fwrite($this->sock,$IAC.$DONT.$opt); 
                        } else { 
                        
// echo "where are we? c=".ord($c)."\n"; 
                        

                } 



        } 


?>